A customer cancels an order ten minutes after checkout. Your support tool has a "Refund" button, so someone clicks it. The card was authorised but never captured, so now there are two things on the customer's account: the original hold, still live, and a credit that may or may not have gone anywhere. The customer rings up a week later asking why their balance is still short. That call was avoidable, and it starts with code that treats "give the money back" as one operation when the card networks treat it as three.
Void, refund and reversal are not synonyms. They hit different message types, run on different clocks, cost different amounts, and are only valid at different points in a payment's life. Get the distinction wrong and you either strand a customer's funds or pay fees you didn't have to. Here is what each one actually does, and how Stripe, Adyen and Checkout.com expose them.
What Is an Authorisation Reversal (a Void)?
An authorisation reversal cancels an approved authorisation before it settles, releasing the hold on the cardholder's funds. When you authorise a card, the issuer places a temporary hold that reduces the cardholder's available balance. If you never capture that authorisation, the hold eventually expires on its own, but the expiry window is set by the issuer and the merchant cannot shorten it. A reversal is how you release it immediately.
Visa's own guidance is blunt about the mechanics: a reversal "cancel[s] a previously approved credit card authorization before it settles, thereby releasing the hold on the cardholder's funds." The message goes to the issuer, and the issuer frees the available balance. Because nothing ever settled, a clean pre-settlement reversal typically never posts to the cardholder's statement at all. The pending line just drops off.
Under the hood this is an ISO 8583 reversal message, structurally distinct from the authorisation request that created the hold. You are not sending money anywhere; you are telling the issuer to tear down a hold it is still carrying. That is why a void is only valid while the payment is uncaptured. Once funds settle, there is nothing left to reverse, and you are into refund territory.
One Visa warning is worth hard-coding into your retry logic: do not submit a capture and an authorisation reversal concurrently for the same authorisation, and treat a reversal that times out as an unknown outcome. Retrieve the authorisation's status before you retry, or you will race yourself into an ambiguous state where you can't tell whether the hold is live.
When Can You Void a Card Payment Instead of Refunding?
The decision tree is short, and it turns on one fact: has the authorisation been captured?
- Not captured yet → void it. Free, fast, invisible to the customer.
- Captured and settled → refund it. Slower, sometimes costs you, visible as a separate credit.
The trap is the one in the opening paragraph: issuing a refund against an authorisation that was never captured. Depending on the processor you either get an outright error or, worse, the original hold stays live while a credit is attempted, so the customer is down twice. The fix is to branch on capture state, not to wire every "cancel" button to the refund endpoint.
What Is a Card Refund and How Long Does It Take?
A refund is a new credit transaction sent to the issuer after the original payment has captured. It is not an undo of the original charge; it is a second movement of money in the opposite direction, with its own reference and its own settlement clock. Visa is explicit that reversals "are different from credits or refunds," which return funds after settlement.
The timing is where support tickets come from. Stripe states that after you issue a refund the customer "sees the refund as a credit approximately 5 to 10 business days later, depending upon the bank." That is not Stripe being slow; it is the issuer's posting cycle. Nothing you do in your API call changes it, so the honest thing is to set that expectation in your refund confirmation copy rather than let the customer discover it.
There is a subtlety that blurs the void/refund line inside a PSP. If you refund very soon after the original charge, some processors push it through as a reversal rather than a fresh credit: the original charge drops off the customer's statement and no separate credit line appears. Stripe surfaces exactly this on the refund object as destination_details.card.type = "reversal", and notes these "usually incur lower network fees." Useful, but you cannot rely on it: when a refund does go out as a true credit, it carries an Acquirer Reference Number (ARN) that can take up to seven business days to appear, and when it goes out as a reversal there is no ARN to trace at all. Build your reconciliation to handle both shapes.
Does a Refund Return the Interchange Fee?
Short answer: partly, and usually not to you.
At the scheme level, the interchange portion of a refunded transaction is generally returned — but it flows back to the acquirer, not directly to the merchant, and whether you ever see it depends on your pricing model and contract. This is the well-established industry position rather than a line I can quote verbatim from a public scheme rulebook, so treat the exact mechanics as contract-specific.
At the processor level, the answer is cleaner and less flattering: the processing fee you paid on the original transaction is gone. Stripe says it plainly — its "processing fees from the original transaction aren't returned" on a refund. So a £100 sale you refund in full has still cost you the original processing fee, and may cost you again on the refund itself. On interchange-plus pricing you may see the interchange credit passed through; on blended pricing you almost certainly will not.
This is why the fee maths favours voiding. The EU and UK interchange caps under Regulation (EU) 2015/751 sit at 0.2% for consumer debit and 0.3% for consumer credit, so interchange on a £100 consumer card sale is 20–30p. That is the piece that might come back. The processor's markup, the piece that won't, is often the larger number. Cancel before capture and none of it is ever charged.
How Stripe, Adyen and Checkout.com Handle Voids vs Refunds
Here the three major PSPs genuinely diverge, and the divergence matters if you abstract over more than one. The headline: Adyen and Checkout.com give you a single status-agnostic "reverse" call that figures out void-or-refund for you; Stripe keeps the two strictly separate and refuses to refund an uncaptured auth.
| Operation | Stripe | Adyen | Checkout.com |
|---|---|---|---|
| Void (uncaptured) | Cancel the PaymentIntent: POST /v1/payment_intents/{id}/cancel | POST /payments/{pspReference}/cancels → CANCELLATION | POST /payments/{id}/voids → payment_voided |
| Refund (captured) | POST /v1/refunds | POST /payments/{pspReference}/refunds | POST /payments/{id}/refunds |
| Status-agnostic reverse | Not offered — you must branch | POST /payments/{pspReference}/reversals → CANCEL_OR_REFUND | Payment Reversal API (auto-picks void or refund) |
| Refund an uncaptured auth? | Blocked — must cancel the PaymentIntent | Refund is capture-only | Void is uncaptured-only |
requires_capture and requires_confirmation but not after succeeded. Crucially, "the charge… remains uncaptured and can't be refunded directly. You must cancel the PaymentIntent." Stripe is removing the footgun by refusing the wrong call, which is a reasonable design choice, but it means your code has to know the capture state before it decides which endpoint to hit.
Adyen offers all three. A /cancels call voids an uncaptured payment and fires a CANCELLATION webhook; after capture you can "no longer cancel it" and must use /refunds. The interesting one is /reversals, which "refunds a payment if it has already been captured, and cancels a payment if it has not yet been captured," with the outcome delivered in a CANCEL_OR_REFUND notification. Adyen explicitly recommends it when you do not know whether the payment has captured. There is also a technical-cancel-by-reference path for the window — up to 24 hours after authorisation — where you hold your own reference but not yet Adyen's PSP reference.
Checkout.com mirrors Adyen's shape: voids for uncaptured, refunds for captured, and a Payment Reversal API that "automatically perform[s] the appropriate action depending on the payment's status." A nice operational detail: if you partially capture and then void, Checkout.com releases the remaining held funds, and if you do nothing it "automatically void[s] the remainder… when the authorization expires."
If you are building a payments abstraction over more than one of these, the status-agnostic reverse endpoint is tempting because it collapses your branching. The cost is observability: you get back "we did the right thing" rather than "we voided" or "we refunded," and your ledger has to interpret the webhook to know which actually happened. On Stripe you are forced to make the decision explicit, which is more code but a clearer audit trail. My preference on a ledgered system is to keep the decision in my own code even when the PSP offers to make it for me, precisely so the ledger entry is unambiguous.
Where Chargebacks Fit In
Void and refund are both merchant-initiated and voluntary. A chargeback is the third way money goes back, and it is neither: the cardholder disputes the charge through their issuer, and the reversal is forced on you, with scheme reason codes and representment deadlines attached. The practical point for this article is that a timely void or refund is how you avoid one. A customer who sees their hold released or their refund pending has no reason to call their bank. The deeper mechanics of reason codes and representment are their own topic, covered in Chargeback Reason Codes: Visa vs Mastercard.
What This Means for Payment Engineers in 2026
Concrete steps, in order of how often I see them skipped:
1. Branch on capture state, never on a button label. "Cancel" in your UI should resolve to void-or-refund based on whether the authorisation has captured, not map blindly to one endpoint. 2. Prefer delayed capture for anything cancellable. Authorise at checkout, capture at fulfilment. It turns most cancellations into free voids instead of fee-bearing refunds. 3. Store the PSP reference and the capture status on your order. Adyen's technical cancel and Checkout.com's void both need to know what state the payment is in; so does your own decision logic. 4. Handle both refund shapes in reconciliation. A refund may come back as a true credit with an ARN after up to seven days, or as a reversal with no ARN. Code that only expects one will show phantom unmatched entries. 5. Set the 5-to-10-business-day expectation in refund copy. It is the cheapest support-ticket reduction available. 6. Treat reversal timeouts as unknown. Query status before retrying, and never fire capture and reversal for the same auth at once.
Key Takeaways
- A void (authorisation reversal) cancels an uncaptured hold before settlement. It is free, fast and usually invisible to the customer.
- A refund is a new credit after capture, lands in 5–10 business days, and does not return your original processing fee.
- The interchange portion may return to the acquirer; the processor markup generally does not come back at all, which is why voiding beats refunding on cost.
- Stripe forces an explicit cancel-vs-refund decision and blocks refunds on uncaptured auths; Adyen and Checkout.com add a status-agnostic reverse endpoint that chooses for you.
- The single most common bug is refunding an authorisation that was never captured. Branch on capture state and it disappears.