# Cancel or refund a payment

Whether undoing a payment is a void, a reversal, or a refund, and what each one does to the money and to your records.

4 questions, 6 outcomes

## How did the customer pay?

https://devportal.qa.winkpg.io/docs/decide/cancel-or-refund-a-payment/how-the-customer-paid.md

Cards and bank debits are undone on different rails, with different operations and a different calendar.

**Choose one**

- By card. Credit, debit, or prepaid, online or in person. Leads to: Has the payment settled?.
- By ACH bank debit. The customer gave a routing number and an account number. Leads to: What happened to the debit?.

### Has the payment settled?

https://devportal.qa.winkpg.io/docs/decide/cancel-or-refund-a-payment/has-the-card-payment-settled.md

Before settlement, the undo is a cancel and the money never moves. After it, the undo is a credit that sends the money back.

**Choose one**

- No. It's authorized or captured, and its batch hasn't closed. The customer sees a pending charge, not a posted one. Leads to: Are you canceling all of it?.
- Yes. Its batch closed and the funds moved. The settlement status reads SettlementSucceeded. Leads to: Refund it.
- I don't know, and my code shouldn't have to. You undo payments from a support screen or an automated process. Leads to: Let the transaction tell you.

#### Are you canceling all of it?

https://devportal.qa.winkpg.io/docs/decide/cancel-or-refund-a-payment/how-much-you-are-canceling.md

A void is always for the full amount. Releasing part of a payment is a different operation, and not every processor offers it.

**Choose one**

- Yes. The whole payment shouldn't happen. A duplicate order, or a customer who changed their mind. Leads to: Cancel it before it settles.
- No. Only part of the amount. One item out of stock, or a discount applied after the order. Leads to: Release part of the amount.

##### Cancel it before it settles

https://devportal.qa.winkpg.io/docs/decide/cancel-or-refund-a-payment/cancel-it-before-it-settles.md

A void or a reversal keeps the payment out of the batch, so the money never leaves the customer's account.

**What to build**

Send the cancel the transaction offers: a Void or a Reversal, on the operations endpoint. The platform normally offers one of the two for a given transaction, depending on the merchant's processor. Both change the original transaction rather than creating a new one, and the response confirms the operation was applied, not just queued.

**Worth knowing**

- Confirm the operation is in the transaction's allowedActions list before you send it. The processor, its reversal window, and the merchant's own settings all change that list, and none of them is visible from your side.
- A batch can close between your read and your request. The refusal is a 409 with OPERATION_NOT_ALLOWED_IN_STATE, and it carries the current allowed actions, so read them off the error and send one of those.
- A reversal sends a message to the processor, so it can be refused. Read the transaction back when you need the processor's own answer.

**Where to go next**

- [Refund or void a payment](https://devportal.qa.winkpg.io/docs/blueprints/refund-or-void-a-payment.md)
- [Refunds, voids, and reversals](https://devportal.qa.winkpg.io/docs/guides/refunds-voids-and-reversals.md)
- [Run an operation on a transaction](https://devportal.qa.winkpg.io/docs/api/transactionOperationsExecuteOperation.md)

##### Release part of the amount

https://devportal.qa.winkpg.io/docs/decide/cancel-or-refund-a-payment/release-part-of-the-amount.md

Release only what you're not going to collect, and leave the rest of the payment in place.

**What to build**

If the payment is an authorization you haven't captured yet, capture only the amount you're keeping: the unused part of the hold is released. If it's already captured, send a Reversal with an amount. The rest stays authorized, and you can send further partial reversals until the reversed total reaches the authorized amount.

**Worth knowing**

- Confirm the operation is in the transaction's allowedActions list before you send it. The processor, its reversal window, and the merchant's own settings all change that list, and none of them is visible from your side.
- Not every processor supports a partial reversal. One that doesn't refuses the request with REVERSAL_PARTIAL_NOT_SUPPORTED, and your options are a full cancel or waiting for settlement and refunding the difference.
- An authorization can be captured once. Capturing less releases the remainder for good, so capture when the order's final amount is known.

**Where to go next**

- [Authorize now, capture later](https://devportal.qa.winkpg.io/docs/blueprints/authorize-now-capture-later.md)
- [Refunds, voids, and reversals](https://devportal.qa.winkpg.io/docs/guides/refunds-voids-and-reversals.md)
- [Run an operation on a transaction](https://devportal.qa.winkpg.io/docs/api/transactionOperationsExecuteOperation.md)

#### Refund it

https://devportal.qa.winkpg.io/docs/decide/cancel-or-refund-a-payment/refund-it.md

The money already moved, so the undo is a credit: a new transaction that sends the amount back.

**What to build**

Send a Refund on the operations endpoint, for the full amount or for part of it. The response carries a second transaction id, for the Return transaction the refund creates. Read that transaction to learn whether the credit was approved, and watch its settlement to learn when the money left.

**Worth knowing**

- A refund isn't instant and can be declined. Treating the operation response as proof the customer has their money back is the most common reconciliation bug on this path.
- Send an explicit amount on every refund after the first. An omitted amount asks for the original total, not what's left of it, and is refused.
- An empty allowed-actions list on a settled payment usually means the merchant has refunds turned off, not that the payment is in the wrong state.
- Send an idempotency key, so a refund retried after a timeout returns the first refund rather than issuing a second.

**Where to go next**

- [Refund or void a payment](https://devportal.qa.winkpg.io/docs/blueprints/refund-or-void-a-payment.md)
- [Refunds, voids, and reversals](https://devportal.qa.winkpg.io/docs/guides/refunds-voids-and-reversals.md)
- [Transaction lifecycle and settlement](https://devportal.qa.winkpg.io/docs/guides/transaction-lifecycle-and-settlement.md)

#### Let the transaction tell you

https://devportal.qa.winkpg.io/docs/decide/cancel-or-refund-a-payment/let-the-transaction-tell-you.md

Every transaction publishes the operations it accepts right now, so your code reads the answer instead of working it out.

**What to build**

Read the transaction and keep the entries in its allowedActions list that are Void, Reversal, or Refund. Send the one that's left. That list already accounts for settlement, the processor's capabilities, its reversal window, and the merchant's refund setting, and it's the same evaluation the merchant's own screens use, so the API and the screen agree.

**Worth knowing**

- An empty result is an answer, not a failure: nothing about this transaction can be undone. A decline, an already voided payment, and a zero-dollar verification all land there.
- The create response doesn't carry the list. Read the transaction back when you need to know what a payment you just created accepts.
- A batch can close between your read and your request. The refusal is a 409 with OPERATION_NOT_ALLOWED_IN_STATE, and it carries the current allowed actions, so read them off the error and send one of those.

**Where to go next**

- [Read a transaction](https://devportal.qa.winkpg.io/docs/api/transactionsGet.md)
- [Refunds, voids, and reversals](https://devportal.qa.winkpg.io/docs/guides/refunds-voids-and-reversals.md)
- [Refund or void a payment](https://devportal.qa.winkpg.io/docs/blueprints/refund-or-void-a-payment.md)

### What happened to the debit?

https://devportal.qa.winkpg.io/docs/decide/cancel-or-refund-a-payment/what-happened-to-the-debit.md

Some ACH undos are yours to request, and one arrives from the customer's bank whether you asked for it or not.

**Choose one**

- I need to stop it, or give the money back. The undo is your decision. Leads to: Void the debit before it's sent, refund it after it clears.
- The customer's bank sent it back. The transaction carries a return code such as R01. Leads to: Handle the return; it isn't a refund.

#### Void the debit before it's sent, refund it after it clears

https://devportal.qa.winkpg.io/docs/decide/cancel-or-refund-a-payment/void-or-refund-the-debit.md

ACH has no hold and no card batch, so there's no reversal. There's a void before origination and a refund after.

**What to build**

Before the processor originates the debit to the ACH network, send a Void. Once the debit has cleared, send a Refund, which creates a new linked transaction that clears on the ACH rail on its own schedule. As on a card, read the transaction's allowedActions list to learn which one applies.

**Worth knowing**

- Transaction.Settled never fires for ACH. Subscribe to Transaction.AchStatusChanged and Transaction.Returned instead.
- A debit can be returned after it reported as cleared. Hold off on irreversible fulfillment for as long as your business can afford to.

**Where to go next**

- [ACH payments](https://devportal.qa.winkpg.io/docs/guides/ach-payments.md)
- [Refunds, voids, and reversals](https://devportal.qa.winkpg.io/docs/guides/refunds-voids-and-reversals.md)
- [Accept an ACH payment](https://devportal.qa.winkpg.io/docs/blueprints/accept-an-ach-payment.md)

#### Handle the return; it isn't a refund

https://devportal.qa.winkpg.io/docs/decide/cancel-or-refund-a-payment/handle-the-return.md

The receiving bank refused the debit and pulled the money back. There's nothing for you to undo.

**What to build**

Record the return code and reason the transaction carries, and mark the order unpaid. Don't send a refund: the bank already took the funds back, and a returned debit no longer offers one. When you still need the money, the next step is a new debit once you have account details that work.

**Worth knowing**

- A return can arrive days after the debit reported as settled. That's a late return, and it's normal for ACH.
- Subscribe to Transaction.Returned so a return reaches your systems without anyone checking a screen.
- Drive a return in the sandbox before you go live, rather than waiting days for a real one.

**Where to go next**

- [ACH payments](https://devportal.qa.winkpg.io/docs/guides/ach-payments.md)
- [Simulate an ACH return](https://devportal.qa.winkpg.io/docs/blueprints/simulate-an-ach-return.md)
- [Accept an ACH payment](https://devportal.qa.winkpg.io/docs/blueprints/accept-an-ach-payment.md)

- [Decision guides](https://devportal.qa.winkpg.io/docs/decide.md): every decision guide this instance publishes.

## See also

- [All documentation](https://devportal.qa.winkpg.io/llms.txt): the machine-readable index of every public page on this site.
