Refunds and reversals — business rules
The constraints, eligibility rules, and error semantics for processing refunds and reversals.
These rules govern every refund and reversal you process through Scan to Pay. They're enforced by the platform, the acquiring bank, or scheme rules — depending on the specific rule. Read these once before you build your refund flow.
For the API and worked examples, see Refunds and reversals.
Eligibility at a glance
| Reversal | Full refund | Partial refund | |
|---|---|---|---|
| Debit card | ✓ | ✗ (PASA rules) | ✗ (PASA rules) |
| Credit card | ✓ | ✓ | ✓ |
| Cheque card | ✓ | ✓ | ✓ |
| Multiple per transaction? | One only | One only | Yes, until original is fully refunded |
| After settlement? | No — same-day only | Yes — within issuer window | Yes — within issuer window |
| After original is reversed? | n/a | ✗ | ✗ |
| After original is refunded (full or partial)? | ✗ | ✗ if fully refunded | ✓ until original fully refunded |
General rules
These apply to both reversals and refunds.
| Rule | Detail |
|---|---|
| Authentication | Both operations require the same Basic Auth or JWT Bearer credentials as the create-code flow. See Authentication. |
| Audit trail | Every reversal and refund is recorded on the Scan to Pay Portal as a transaction in its own right, linked to the original. |
| Timeframe enforced by issuer | Scan to Pay submits whatever you request to the bank. The bank decides whether it's still within their reversal/refund window. If they reject, the response surfaces on the webhook with the bank's ISO code. |
| MSISDN linking | A refund or reversal can be processed regardless of whether the original card is still linked to a customer wallet at the time of the refund. |
| Bad credentials | If you attempt either operation with incorrect API credentials, you get 513 SECURITY_VIOLATION rather than 401. The transaction is not modified. |
| Mutual exclusivity | A reversed transaction cannot be refunded. A refunded transaction (full or partial) cannot be reversed. Pick one path per transaction. |
Reversal-specific rules
| Rule | Detail |
|---|---|
| Full amount only | You cannot reverse part of a transaction. The reversal undoes the entire original. If the customer wants a partial credit, use a refund. |
| One per transaction | Only one successful reversal is allowed per transactionId. Subsequent attempts return 514 UNABLE_TO_REVERSE. |
| All card types | Debit, credit, cheque — all supported for reversal. This is one of the main reasons to reverse rather than refund when both options apply. |
| Issuer window | If the bank has already cleared (settled) the transaction, the reversal is rejected at the bank with an ISO response code. The transaction state becomes END_REVERSAL_FAILED. |
| Same-day rule | In practice, reversals work for transactions made on the same business day. Anything older usually needs to be a refund. |
Refund-specific rules
| Rule | Detail |
|---|---|
| Credit and cheque cards only | Debit cards cannot be refunded — this is a PASA scheme rule, not a Scan to Pay limitation. Attempts return 445 INVALID_CLIENT_CARD. |
| Full or partial | Refunds can be processed for any amount up to the original transaction value. |
| Multiple partials allowed | You can refund multiple times against the same transactionId until the cumulative refunded amount equals the original. After that, further refund attempts return 515 UNABLE_TO_REFUND. |
| Acquirer window | Refunds are subject to the acquiring bank's refund window (typically 6 months for credit, varies for cheque). Outside the window, the bank rejects. |
Card-type detection
The original transaction's webhook includes cardInfo.accountType, which is one of:
accountType | Refund eligible? |
|---|---|
CREDIT | ✓ |
CHEQUE | ✓ |
DEBIT | ✗ — use a reversal instead, or alternative payout (EFT, cash) |
NONE (defaulted) | The platform treats NONE as CREDIT for eligibility |
Build your refund UI to check accountType before showing the "Refund" button — disable it for debit cards and route to an alternative refund process (EFT, manual payout).
Errors and what they mean
| Code | Operation | Cause |
|---|---|---|
432 INVALID_AMOUNT | Refund | The amount is zero, negative, or exceeds the remaining refundable balance |
436 INVALID_TRANSACTION_REF | Both | The transactionId doesn't match a known transaction |
444 NOTHING_TO_REVERSE | Reversal | The transaction is already reversed, already refunded, or out of window |
445 INVALID_CLIENT_CARD | Refund | Attempted on a debit card |
513 SECURITY_VIOLATION | Both | Bad credentials |
514 UNABLE_TO_REVERSE | Reversal | The bank rejected the reversal (cleared, outside window, etc.) |
515 UNABLE_TO_REFUND | Refund | The bank rejected the refund (outside window, already refunded, etc.) |
Full error reference: Errors. Map raw error codes to customer-friendly copy before showing them in your back-office UI.
The reversal-on-webhook-failure safety net
When your handler fails to acknowledge a successful webhook within 45 seconds, the platform automatically reverses the transaction. This counts as a reversal under all the rules above — meaning:
- The transaction transitions to
END_REVERSED - You cannot subsequently refund it (it's already been reversed)
- You receive a webhook with
status: REVERSED
This is enforced by the same Quartz reversal scheduler that handles manual reversals. See Webhooks — How it works.
If you're debugging a "why was this transaction reversed?" question and you didn't trigger a reversal yourself, check whether your webhook handler returned non-200 or timed out on the original payment. It's the most common cause.
Card-on-file and recurring payment refunds
If the original transaction was a card-on-file (CCPI) or a recurring payment, the same rules apply. Card-type eligibility for refund is determined by the underlying accountType of the stored card, not by the fact that it's recurring.
What's next
- The refund and reversal API → Refunds and reversals
- Standard error code reference → Errors
- Bank decline codes returned on rejection → ISO response codes
- Webhook payload structure → Webhooks
- Dispute process for cardholder-initiated returns → Customer disputes
Updated 4 days ago
