Refunds & disputes
Giving money back — either because you chose to, or because the customer's bank forced it.
A Refund is you giving money back to a customer, on purpose. A Dispute is a chargeback — the customer's bank pulling the money back, whether you agree or not.
How it works
- You create a Refund against a captured
Charge, the same way as on Stripe.
Name the charge with
charge_id(orcharge, or thepayment_intentit belongs to).amountis optional, in the minor unit; leave it out to refund everything that is left.reasonis optional too:duplicate,fraudulentorrequested_by_customer. - There is no approval step. The refund goes to the provider as soon as you create it, and your balance is debited at that moment, so the money can't be paid out while the refund is in flight.
- The money always goes back to the original payment method. Refunding
into a customer wallet balance was removed: a
destinationother thansourceis refused withE4515. - List Refunds or retrieve one by ID.
- A Dispute is different: it's created exclusively by a webhook from
the provider (
charge.dispute.*) when a customer's bank files a chargeback. You can never create one yourself — you can only list them, retrieve one, and respond to it (submit evidence) to track its status.
Refund statuses
| Status | What it means |
|---|---|
pending | Sent to the provider, which hasn't confirmed it yet. |
requires_action | The provider needs something more before it can pay the customer. This is the only state you can cancel from. |
succeeded | The provider accepted the refund; the customer gets the money back. |
failed | The provider refused it. failure_reason says why, and the debit returns to your balance. |
canceled | Canceled while it required action; the debit returns to your balance. |
failed and canceled are final. A refund that fails still comes back with
200 and status: "failed", so read the status rather than the HTTP code.
Sending the same request again with the same Idempotency-Key returns the
same refund; reusing the key for a different charge or amount is refused.
The refund carries balance_transaction, the debit on your balance, and
failure_balance_transaction, the entry that gives it back when the refund
fails or is canceled.
Refunds created before approval was removed may still read rejected. Refunds
that were waiting for approval were canceled automatically, and their
rejection_reason says so — create them again if you still want to refund.
When a refund is refused up front
Some payment methods have no refund API at the provider — for example
PromptPay and QR payments through SCB or KBank, K PLUS, SCB EASY, KBank
SmartPay, Krungsri card and KMA, Apple Pay and Google Pay. PaymentGateway
refuses a refund on those charges when you create it (E4526) instead of
debiting you for a refund nobody can send; refund the customer outside
PaymentGateway. In the dashboard the Refund action stays visible but
disabled, with the reason.
GET /charges/{id}/refund_methods
tells you ahead of time: its single source entry has available: false and
an unavailable_reason. See the
capability matrix for which
methods support refunds.
You cannot POST a dispute — if you see one, it always originated from the provider's webhook. Your only actions are reading it, updating its metadata, and submitting evidence.
Refunding a destination charge
On a charge with transfer_data.destination and an application_fee_amount,
the connected account bears the refund by default and you keep your
application fee: a full refund of 1,000 with a 200 fee leaves the connected
account owing you 200. Send refund_application_fee: true to give back the
matching share of the fee too (200 × refund / 1,000); it shows up as an
application fee refund once the refund succeeds, and the connected account
then owes nothing. The flag does nothing on a charge that uses
transfer_data.amount. reverse_transfer is accepted and ignored.
Events
A refund sends refund.created when it is created, refund.updated whenever its
status changes, and refund.failed when the provider refuses it. The charge sends
charge.refunded once a refund on it succeeds. See
Refund events.
Common use cases
- Refunding a customer who returned an item.
- Partially refunding an overcharge.
- Responding to a chargeback with evidence (receipt, delivery proof) before the provider's deadline.