- WooCommerce
- Magento
- Shopify Soon
Refunds, voids, and reversals
Learn how to manage your transactions by issuing refunds, voiding payments, or reversing authorizations.
Overview
Give money back to a customer, or cancel a payment, with one of three actions. Which one you use depends on where the payment is in its lifecycle:
| Action | Use it when | What happens |
|---|---|---|
| Refund | The payment is captured and refundable is true. |
The money goes back to the customer. You can refund the full amount or several partial amounts. |
| Void | The payment is captured (SUCCESSFUL) and hasn't settled into a statement yet. |
The payment becomes VOIDED. |
| Reverse | The payment is authorized but not captured. | The hold on the customer's card is released and the payment becomes REVERSED. |
Before you act, call find-payment and check the matching eligibility field:
refundable: True if the payment is eligible for a refund.voidable: True if a captured (SUCCESSFUL) payment can be voided before it settles into a statement.reversible: True if the authorization can be reversed (authorized but not yet captured).
A
trueflag means the action is allowed right now, but the call can still fail, for example because of an acquiring bank cut-off. Handle errors from these endpoints.
Payment statuses and the transitions between them are documented in Payment lifecycle.
Refunds
Refunds return funds to a customer for a payment that has already been captured. Full and partial refunds are supported, and you can issue multiple partial refunds against one payment. Each call to refund-payment creates a separate refund object. The amount is validated against the remaining refundable amount (also reduced by any disputed amount). Requesting more than remains returns AMOUNT_EXCEEDS_LIMIT.
Refunding a payment
To refund a payment, first verify that refundable is true. Then use refund-payment. Provide the payment_id of the transaction (your external_id is also accepted).
- Full refund: If you don't specify an amount, the remaining refundable amount is returned to the customer.
- Partial refund: Specify an
amountin the smallest currency unit (e.g., satang for THB) to refund a specific portion of the payment.
Example: Partial refund
await fetch('https://api.reservepay.com/merchants/refund-payment', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Api-Version': '2025-04-01',
'Content-Type': 'application/json'
},
body: JSON.stringify({
payment_id: 'pay_1234567890',
amount: 5000 // Refunds 50.00 THB
})
})
Refund lifecycle
Each refund has its own rfn_ identifier and status:
PENDING → SUCCESSFUL
PENDING → FAILED
Save the refund ID returned by refund-payment. A successful response means the refund was accepted for processing, not that the customer has the money yet. Track it by polling find-refund until status is SUCCESSFUL or FAILED, or use list-refunds-by-payment to see every refund on a payment. Refunds don't send webhooks.
If the payment later gets a dispute, its status changes to DISPUTED even though the refunds still exist. Read the refund objects to know what was refunded.
Avoiding duplicate refunds
Send refunds for the same payment one at a time, never in parallel. If a refund call times out, check what already exists before you try again, as described in Idempotency and timeouts.
Eligibility for refunds
A payment can be refunded when it has been captured, still has an amount left to refund, and was made with a payment method that supports refunds. Otherwise refund-payment returns NOT_REFUNDABLE. Check refundable on each payment rather than assuming by payment method. For an authorization you haven't captured, reverse it instead.
Returning funds when refunds are not supported
When a payment's method doesn't support refunds, send the money back as a payout with send-payout-to-contact. Ask the customer for their bank account or PromptPay number first. A PromptPay payment doesn't tell you where to send money back.
The payout is a separate transaction. It comes out of your available balance, carries the normal payout fee, and doesn't change the original payment's status.
Use a unique payout external_id, such as return_order_12345_1, and store the link between that payout and the original payment in your system. See Sending funds for contact setup and destination fields.
Voids
A void cancels a captured payment before it settles, so no money moves and no refund is needed. You can usually void a payment until the end of the day it was captured, and only before it's included in a daily statement. Check voidable for the exact answer.
Check that voidable is true, then call void-payment with the payment_id. The payment becomes VOIDED. After the window closes, refund the payment instead.
Reversals
A reversal releases the hold on an authorized payment that you haven't captured. You can reverse a payment for up to 7 days after it was created. See Authorization and capture.
Check that reversible is true, then call reverse-payment with the payment_id. The payment becomes REVERSED.
Dive deeper into these advanced integration topics to unlock Reservepay's full potential with our in-depth guides.
Issue refunds, void transactions before they settle, and reverse authorizations when needed.
Read the guideSeparate authorization from capture to hold funds without charging. Perfect for pre-orders or reservations
Read the guideBuild your own POS, or work with our app by sending commands or using deep links.
Read the guideBuild profiles by attaching customer data. Gain insights, reduce fraud risk, and create experiences that drive loyalty.
Read the guideSend payouts to your primary bank account or to your contacts via bank account, mobile number or national ID.
Read the guideStay informed about payment events with real-time webhooks. Automate your order processing, and more...
Read the guideFilter payments and payouts, export CSVs, read balance holds, and reconcile daily statements.
Read the guideLook up what each Reservepay failure code means when a payment is declined.
Read the guideNo account yet?
Start integrating all these amazing features into your app or website by creating your own merchant account today. It's free to sign up and only takes a few minutes to get started.
