Skip to main content
This page answers the questions merchants ask most often about refunds. Field-level contracts live in the Acquiring API Specification (/api/v1/order/refund, /api/v1/refund/inquiry, Refund order notification).

Can I refund the same order more than once?

It depends on the payment method and your contract configuration:
  • Multiple partial refunds supported — each refund is validated so that refunded + in-progress + current request ≤ original payment amount. A single order allows up to 50 refunds in production; exceeding this returns error A0027. Sandbox and test environments use a lower cap, so hitting A0027 there after only a few refunds is expected, not a bug. Every refund response includes remainingRefundAmount so you can track what is still refundable.
  • Not supported — once one refund has completed for the order, any further refund request is rejected.
Each successful partial refund sets the order’s refund status (refundStatus) to ORDER_PARTIALLY_REFUNDED; it only becomes ORDER_FULLY_REFUNDED when the total successfully refunded amount equals the order amount. The order’s orderStatus itself is not changed by a refund — a fully refunded order still reports PAY_SUCCESS.

Why was my refund rejected?

A refund request must pass all of the following preconditions: Validity windows and multi-refund support are contract-dependent — check your contract in the Merchant Portal or contact integration support for your exact configuration.

My refund request timed out. Can I safely resend it?

Yes. refundRequestId is the idempotency key:
  • Resending with the same ID and same amount returns the current refund status; if the refund is still in an intermediate state, processing is pushed forward.
  • Resending with the same ID but a different amount is rejected.
To retry safely, always resend the identical request. Use a new refundRequestId only when you intend to create an additional refund.

How are cross-currency refund amounts calculated?

When the user paid in a currency different from your order currency, the refund amount you submit (in order currency) is converted to the user’s currency. Two rules apply:
  • Round down — converted refund amounts are always rounded down, so the total refunded never exceeds what the user actually paid.
  • Last refund tops up — with multiple partial refunds, FX rounding could leave the refunded total slightly below the payment amount. The final refund that exhausts the remaining balance returns the full remainder, so a fully refunded order truly reaches ORDER_FULLY_REFUNDED.
The finalDealAmount in the refund inquiry response is the actual amount the user receives in their currency.

I cancelled the order, but the user says they already paid

Cancellation only works on orders still in an intermediate state. If the user has in fact completed the payment, the cancel request fails and the order proceeds as a successful payment — you receive a payment success notification. To return the funds at that point, use the refund API; do not retry the cancel.

How long does a refund take? The status stays in progress

Refunds are asynchronous. A successful /api/v1/order/refund call means the request was accepted (REFUND_IN_PROGRESS), not completed. Waffo then processes the refund with the payment method and automatically retries recoverable failures until a final state is reached.
  • Provide refundNotifyUrl to receive the final result via the refund order notification, or poll /api/v1/refund/inquiry.
  • A refund is final only at ORDER_PARTIALLY_REFUNDED, ORDER_FULLY_REFUNDED or ORDER_REFUND_FAILED — treat REFUND_IN_PROGRESS as pending, not failed.

Do all payment methods support API refunds?

No, and there are two distinct cases. Refunds not permitted. Two codes, two meanings. A0017 (“payment channel not supported”) means the payment method itself cannot carry a refund — full or partial. A0014 (“refund rules”) means the method can refund but your agreement’s refund shape does not allow this request: a partial refund on a full-refund-only row, or a second refund where only one is permitted. Asking for more than the remaining refundable amount is a third thing again — a plain A0003 parameter failure. See Refund Limits by Payment Method. Refunds needing extra fields. Some refunds require additional user details, such as bank account information, because the money is returned by transfer rather than reversed on the original payment. Which fields are required is configured per channel, so do not decide it from the payment method in your own code. Submit the refund; if fields are missing the API returns A0003 together with the list of missing field names, and you resend with those fields filled in. For some payment methods refunds are executed over payout rails on Waffo’s side — your API contract stays the same.

Can I submit many refunds at once?

Yes — the Batch Refunds page under the Portal’s Transaction module. It is enabled per merchant account, so if the page is not there it has not been switched on for you; ask support. The template and upload buttons are visible only to the roles that can already initiate a single refund. The flow is: export the batch refund template (CSV), fill it in, upload it, confirm the summary dialog (record count, total amount, currency), and Waffo creates a Refund Batch ID in the form BRP + MID + YYYYMMDD + a 6-digit sequence — for example BRP<your merchant ID>20260225000001.

My whole batch file was rejected. Why?

Upload validation is all-or-nothing: a single bad row rejects the whole file, and you correct it and upload again. What is checked:
  • The file — must be the exported template in CSV, with exactly its four columns and no extra ones; not corrupted, encrypted or empty; at most 2,000 records and 2 MB; and not a file already uploaded before (duplicates are detected from the file content, not the name).
  • Currency — one refund currency for the entire file, and it must equal each original order’s currency.
  • Each row — the original order exists, is paid and not yet fully refunded, its payment method allows refunds, it is still inside the refund window, the amount is above zero and no more than the remaining refundable amount, and the order has no refund already in progress.
  • Duplicates — the same order may not appear twice in one batch.
  • Batch total — the file’s total must be covered by your pending-settlement balance plus your refund reserve balance at upload time.
The per-row rules are the same preconditions a single refund passes; see “Why was my refund rejected?” above for what each one means.

How do I track a batch after submitting it?

Rows are processed independently: a failing row is marked with its reason and the rest continue — one failure does not stop the batch. The batch list shows total / success / failed / in-progress counts, the batch amount and currency, and the creation and update times. Every refund carries its Refund Batch ID, so you can filter the refund list by batch, open any individual refund’s detail as usual, and export the batch result including failure reasons. There is no retry action on a batch — resubmit the failed rows in a new file, or refund them one at a time.

Do I get the original payment fee back on a partial refund?

The default is refund without fee return. If your contract includes a returned fee, a refund returns the matching share of the original payment fee, rounded down to 2 decimals:
A partial refund returns the pro-rata share, a full refund returns all of it. Because the rule rounds down, the returned fee can land a cent below the figure you get from straight pro-rata arithmetic. It appears as a positive component of the refund row’s Settlement Amount — see Settlement Reports & Exports.