Refund a payment
Returns money to the payer for a successful payment or capture, in full or in part, either through the gateway that took the payment or as an offline record of a refund made by other means (manual true). A refund is its own transaction (a ref_ id) attached to the source; it reduces the source's balance and the completed totals of the charge, instalment and contract the source paid. Note the route: POST /c/api/payments/:id/refund, singular. An authorisation is not refunded; it is voided (Void an authorisation) or captured first (Capture an authorised payment).
Where you can do this
- Merchant View: Refund a transaction (Refund)
- Merchant View: Refund
- API: Refund a payment (
POST /payments/{payment_id}/refund) — amount, reason, manual true for an offline refund; :id is a payment, capture or transaction id, or the payment's alt_key - API: Refund a capture (
POST /captures/{capture_id}/refund) — the same body against a capture id; same server rules, without Shuttle's lookup and count checks
Before you start
- The source is a payment or a capture. Authorisations, voids and refunds cannot be refunded.
- The source is in status SUCCESS, PENDING or UNATTRIBUTED and has a
balanceabove zero (the part not yet refunded; for an UNATTRIBUTED payment, its full amount). - The source has been processed (has a
processeddate) unless it is UNATTRIBUTED, and its contract and the allocations that record which charges it paid still exist. - The gateway the source was taken on is active. An archived or inactive gateway blocks the refund even when it is offline.
- For a gateway refund (
manualabsent or false): the gateway accepts a refund of this kind for the payment method type, a full refund when the amount equals the original amount, a partial refund otherwise. The gateway advertises this as the features FULL_REFUND, PARTIAL_REFUND and MULTI_REFUND under itspayment_method_types. - From Merchant View: the instance option
refundis on;refund_offlineis on for the Offline choice to be offered.
Inputs
| Input | Required | Meaning and constraints |
|---|---|---|
| amount | no | Decimal as a string ("10.00"), above zero and no more than the source's balance. Omitted: the whole remaining balance. Any number of decimals is accepted server-side; Merchant View allows at most two and at least one minor unit. |
| reason | no | Free text. Omitted: recorded as "No reason given". The public spec marks it required; Merchant View limits it to 100 characters; the server does not limit its length. |
| manual | no | true records an offline refund: no gateway call, no money moves, no fee, gateway_status MANUAL. Default false. |
Rules
Checked by the API means Shuttle refuses the request however it is sent; a screen name means only that screen refuses it, and a direct API call would be accepted.
| Rule | Checked by | What you see |
|---|---|---|
:id on the payments route must resolve to a PAYMENT or CAPTURE transaction; a prefixed id is looked up directly, anything else is searched as the payment's alt_key (newest match wins) | The API | NOT_FOUND: "Payment not found" |
An alt_key that matches two payments, the older created within the last six months, is ambiguous | The API | INVALID_REQUEST: "Duplicate alt_key" |
| The source must be SUCCESS, PENDING or UNATTRIBUTED | The API | VALIDATION_ERROR (BOLT-9005) |
amount must be above zero; zero, negative or a source with nothing left to refund | The API | INVALID_AMOUNT (BOLT-1168) |
amount must not exceed the remaining balance | The API | VALIDATION_ERROR (BOLT-9003) |
| A source already fully refunded is reported as such, with a distinct message when the earlier refund was offline | The API | VALIDATION_ERROR (BOLT-1148: "already refunded" / "already refunded manually") |
The source must have a processed date (unless UNATTRIBUTED), an existing contract, and intact charge allocations | The API | INVALID_AMOUNT (BOLT-1168) when unprocessed; VALIDATION_ERROR (BOLT-1148) when the contract or an allocation is missing |
| The source's gateway must be active, also for an offline refund | The API | INVALID (BOLT-7185, archived or inactive gateway) |
| The source must have a payment connector | The API | VALIDATION_ERROR (BOLT-9008) |
A gateway refund needs the gateway to accept the refund type for the payment method type: TOTAL_AMOUNT when amount equals the source's original amount, PARTIAL_AMOUNT otherwise. The type is judged against the original amount, not the balance, so a second refund that clears the balance is sent as partial; a gateway that only accepts full refunds rejects it | The API | VALIDATION_ERROR (BOLT-9006) |
| A gateway that fails to answer leaves the refund UNRESOLVED and fails the call | The API | INVALID (BOLT-1147) |
A gateway decline is not an error response: the call returns the refund with status DECLINED and the gateway's gateway_message; UNRESOLVED likewise. Only a request that produced no refund record fails | The API | HTTP 200 with refund.status DECLINED or UNRESOLVED |
| Any refund is allowed while the gateway's refund types allow it; the server has no notion of "one refund only". The gateway's MULTI_REFUND feature is advisory and is applied by Merchant View only | The API | none |
Merchant View locks the whole amount to balance; a partial amount is editable only when the gateway advertises PARTIAL_REFUND, the whole-amount toggle only with FULL_REFUND; an offline refund allows both | Merchant View screen | inputs disabled |
Merchant View sends manual true when Offline is chosen, when the source is itself an offline (MANUAL) payment, or when the gateway lacks MULTI_REFUND and the amount differs from the balance; it forces Offline when the gateway advertises neither FULL_REFUND nor PARTIAL_REFUND; it warns that a partial refund on a gateway without MULTI_REFUND leaves the remainder unrefundable via the gateway | Merchant View screen | "This will record the refund only..." warnings; confirmation modal |
Merchant View rejects a partial amount below one minor unit, above balance, or with more than two decimals; the server accepts any scale | Merchant View screen | Refund button disabled |
What happens
- The caller receives a refund object (
ref_id,amount,reason,reference,status,gateway_status,gateway_message,processed,source_transaction,payment) with status SUCCESS, PENDING, DECLINED or UNRESOLVED. A decline still returns HTTP 200; checkstatus. Only a request that could not create a refund at all returns an error. - On SUCCESS or PENDING the source's
balancefalls andrefundedrises by the amount; the charge, instalment and contract completed totals fall with it and the contract's last transaction becomes the refund. Refunding a capture also reduces the parent authorisation's balance. A payment taken in a terminal batch has its batch item amount reduced to what remains. - An offline refund (
manualtrue) moves no money: it is recorded SUCCESS at once withgateway_statusMANUAL and no fee, and can be reversed with Undo an offline refund. A gateway refund returns the money through the gateway, records the gateway's fee (from its response or its refund settings) and cannot be reversed here. - Webhooks: REFUND.SUCCESS, REFUND.PENDING, REFUND.FAILED or REFUND.UNRESOLVED for the refund; PAYMENT.UPDATE for the refunded payment (CAPTURE.UPDATE for a refunded capture, plus PAYMENT.UPDATE for its authorisation); CONTRACT.UPDATE on success or pending unless the contract is a one-off due at once or still being set up. No email or SMS is sent to the payer.
- Later: a PENDING refund is completed when the gateway confirms it, at which point the totals move and REFUND.SUCCESS arrives. An UNATTRIBUTED source becomes SUCCESS as a side effect of being refunded.
Who can do this
- Roles: Admin only.
- Permission keys (for clients managing permissions directly):
payment_transaction.refundon any one of the source's branding, channel, designation, initiative, legal entity, gateway or receipt template; Admin's*satisfies it. Whether a plainpayment_transactiongrant satisfies the sub-key is not traced. Without it the caller sees INVALID (HTTP 400) carrying Shuttle's permission message rather than NO_PERMISSION. Merchant View requirespayment_transactionto open the screen andpayment_transaction.refundon the source's initiative (or*) to show the form, otherwise "You do not have permissions to refund this transaction.". - Admin app options:
refundshows the Refund control;refund_offlineoffers the Offline choice (Offline is forced regardless when the gateway advertises neither FULL_REFUND nor PARTIAL_REFUND). - API keys: an instance session reaches the route with the same server permission check; no additional option applies.
Related
Updated 31 minutes ago