> ## Documentation Index
> Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Refund Limits by Payment Method

> Why the same refund request succeeds on one payment method and is rejected on another: partial-refund availability, refund windows and deadlines, how many refunds one order allows, duplicate-request protection, refunds before and after capture, closed user accounts, and issuer-declined card refunds.

The [Refund FAQ](/docs/en/support/refund-faq) covers the rules that apply to every
refund: amount validation, idempotent retries, cross-currency conversion,
asynchronous status, and fee return. This page answers the question that comes
after those: **why the identical request succeeds on one payment method and is
rejected on another.** Cross-currency arithmetic lives in
[Exchange Rates](/docs/en/support/exchange-rates-faq), and the error codes involved
are in [Error Codes & Limits Supplement](/docs/en/support/error-codes-and-limits-supplement).

The short version: **refund capability is a property of the payment method, not
of your account.** Two methods enabled on the same account, charged through the
same integration, can have completely different refund behaviour.

## Why does the same refund request succeed on one payment method and fail on another?

Because each payment method carries its own refund rules, and Waffo does not
soften them. The API surface is identical for every method — the difference is
which requests the method accepts.

The table below groups the behaviour we see by **payment-method category**.
Treat it as the shape of the variation you have to design for, not as a lookup
table for a specific method: the exact rules for the methods enabled on your
account are part of your payment-method configuration.

| Payment-method category | Full refund | Partial refund | Multiple partial refunds | Notable limit |
| - | - | - | - | - |
| Cards | Yes | Depends on the configuration; some card setups are full-refund only | Where partial refunds are available | Can be **declined by the issuing bank**, even after Waffo accepted the request |
| E-wallets | Yes | Commonly yes | Commonly yes, sometimes only after it is enabled for your account | Duplicate-request protection and minimum intervals between requests |
| Prepaid electronic money | Yes | Yes | **No** — one refund per original transaction | Refundable only within a fixed window after the original transaction |
| Post-pay / credit (BNPL-style) | Yes | Yes, while the user's account is active | Depends on the method | The window is tied to the method's credit-retention period; a **closed user account** blocks partial refunds |
| Bank transfer / virtual account | Depends on the method — several are **not refundable through the API** at all | Same | Same | Where the API cannot refund, the money is returned outside the payment method (see [Bank Transfers & Virtual Accounts](/docs/en/support/bank-transfer-and-virtual-account-faq)) |
| Methods with separate authorization and capture | Yes | **Only after capture** | After capture | Reversal of a captured payment has its own deadline, counted from the payment |

Three consequences worth designing for:

1. Do not hardcode "this method supports partial refunds" in your own code.
   Submit the refund and handle the rejection.
2. Do not assume that because one method on your account supports something,
   another does.
3. A refund that Waffo accepted can still end in a failed final state.

## Is partial refund available on every payment method I have enabled?

No. Partial refund is a capability that has to be available for the specific
payment method **and** enabled for your account — it is the `Refundable` column
on your Service → Payin row. Where the row permits only a full refund, a partial
refund request is rejected with `A0014` ("Order cannot be refunded due to refund
rules") while a full refund on the same order succeeds. The same code is returned
when the row allows one partial refund and you ask for a second.

`A0017` ("Order cannot be refunded due to payment channel not supported") is a
different failure: the payment method itself cannot carry a refund at all, so
neither a partial nor a full refund goes through on it.

Where a payment method could behave in more than one way, **the most
restrictive option applies**. Practically:

* If a method can refund only in full for your account, partial requests fail
  even though the method is capable of partial refunds elsewhere.
* If a method allows only one partial refund for your account, the second
  request fails even though the first succeeded.

Waffo does not silently downgrade a partial refund into a full refund, and does
not queue it for later. If you need a capability that is currently rejected,
ask your Waffo integration contact whether it can be enabled — it is a
configuration change, not something you can pass as a parameter.

## How many refunds can one order take?

Three separate ceilings apply, and you hit whichever is lowest:

| Ceiling | Value | Error when exceeded |
| - | - | - |
| Waffo platform maximum per order | **50** refunds | `A0027` "The refund has exceeded maximum 50 times" |
| Amount | refunded + in-progress + this request ≤ original payment amount | `A0003` — it is treated as a parameter failure, not a refund-rule breach |
| Agreement refund shape | From "one refund only" and "one partial refund per order" up to unlimited partial refunds | `A0014` |

Some methods start at exactly one partial refund per order and can be raised
from there; a few place no limit on the count at all. Because the lowest
ceiling wins, build your reconciliation so that a rejected second refund is a
normal branch, not an incident.

## Is there a deadline for refunding an order?

Yes, and the clock always starts at the **original order**, never at the moment
you decide to refund. The window is a property of the payment-method row on your
account, and you can read it in two places rather than guessing:

| Where | What it shows |
| - | - |
| Service → Payin, `Refund Period` column | The window for that payment method and currency |
| `refundExpiryAt` on the order (Order Inquiry) | The concrete deadline for that order — derived from the window, counted from the order's creation date, landing at the end of that day |

The shape varies by category — prepaid electronic money and post-pay methods tend
to have the shortest windows, cards the longest, subject to the issuer — but the
figures are per method and per account, so do not carry one method's window over
to another.

After the window closes the refund is rejected — there is no override. If your
product allows returns over a long period, check the refund window of the
methods you accept before you publish the policy, and settle late returns
outside the original payment.

## I refunded the order, but its `orderStatus` is unchanged. Did the refund work?

Yes, most likely. **A refund does not change the original order's status.** The
original order stays in the state it reached when it was paid; the refund is a
separate record with its own status, and that is the only place refund progress
appears.

| Where you look | What you see |
| - | - |
| `/api/v1/order/inquiry` on the original order | The original payment status, unchanged by refunds |
| `/api/v1/refund/inquiry` on the refund | `REFUND_IN_PROGRESS` → `ORDER_PARTIALLY_REFUNDED` / `ORDER_FULLY_REFUNDED` / `ORDER_REFUND_FAILED` |
| The refund order notification | The final refund state |

So "the order still shows as paid" is not evidence that the refund was lost. If
your bookkeeping keys refund state off the original order, it will look
permanently stuck — key it off the refund record instead.

## I resent a refund immediately and it was rejected as a duplicate. Was anything refunded?

Probably not by the second request — that is what the protection is for. Two
kinds of guard exist:

* **Duplicate detection.** Some methods reject a second request that carries
  the same original transaction reference *and* the same amount within a short
  window — a rule commonly seen on wallets that otherwise place no limit on the
  number of partial refunds.
* **Minimum interval.** Some methods require a short gap between consecutive
  refund requests on the same order.

Both are protections against accidental double refunds, so do not treat the
rejection as a failure to be retried in a tight loop. The safe pattern:

1. Keep the same `refundRequestId` when you resend the *same* refund — it is
   the idempotency key, and it makes a retry harmless.
2. Do not fire genuine second refunds on the same order back-to-back; let the
   first reach a final state.
3. Between attempts, call `/api/v1/refund/inquiry` rather than resending.

## The user closed or deleted their account. Can I still refund them?

Not always. On post-pay / credit methods a **closed user account blocks partial
refunds**, and once a defined period has passed since the closure the refund is
rejected outright. Full refunds usually remain possible for longer.

There is no way for Waffo to route around a closed account: the money has to go
back to the account it came from. If the refund is rejected for this reason,
settle with the user by other means.

## Can I refund before the payment is captured?

Only in full. On methods where authorization and capture are separate steps:

| Order state | What you can do |
| - | - |
| Authorized, not captured | **Full refund / cancellation only** — no partial refund |
| Captured | Full or partial refund, subject to the method's partial-refund rules |
| Captured, being reversed | Reversal has its own deadline, counted from the payment — read `refundExpiryAt` on the order |

Capture itself has a deadline too, and the capture amount can never exceed the
original authorized amount. If you need to return part of an authorization,
capture the amount you are keeping rather than capturing the whole authorization
and refunding the difference.

## Why can one method on my account only be refunded in full while another accepts several partial refunds?

Because refund capability follows the **payment method**, not the account. It is
routine for one account to have a card method that is full-refund only next to a
wallet method that accepts repeated partial refunds. Nothing about your account,
your contract type, or the API you call changes that.

The design implication is concrete: your refund UI and your customer-service
scripts should be driven by the method that was used on the order, not by a
single account-wide rule.

## Is there a minimum refund amount, and a maximum?

The maximum is always the same: **the original payment amount**, less anything
already refunded or in progress. There is no payment method on which you can
return more than was taken; asking for more is rejected with `A0003`.

The minimum is the practical one people trip over. It is the smallest amount the
currency can express, and on cross-currency orders it is the smallest amount the
*converted* figure can express — which is why a refund that looks non-zero to
you can be rejected. If the converted amount comes out as zero or below, the
refund is rejected with `A0014`; see
[Limits, Currency & Configuration Errors](/docs/en/support/error-codes-and-limits-supplement).

A useful habit for cross-currency orders: refund by **proportion** of the
original order rather than by an absolute figure in your own currency, so the
converted amount scales with the original payment instead of rounding away.

## The refund reports success, but the user says the money has not arrived

Both statements can be true at once. A successful refund means **the refund
request completed successfully** — it does not mean the funds have already landed
in the user's account. Settlement of the refund is keyed off that successful
result, not off the user's balance.

In practice the money follows quickly on most methods, but the two events are
distinct and the gap is outside Waffo's control:

| What you observe | What it tells you |
| - | - |
| `ORDER_FULLY_REFUNDED` / `ORDER_PARTIALLY_REFUNDED` | The refund was accepted and processed by the payment method |
| The user's balance | The payment method's or bank's own posting time |

So the support answer is "the refund has gone through; how quickly it shows up
depends on the user's bank or wallet". Escalate to Waffo support only when the
refund has been in a successful final state for a materially long time and the
user still cannot see it — and bring the original order ID and the refund ID.

## The refund ended in `ORDER_REFUND_FAILED`. What happened, and what do I do?

`ORDER_REFUND_FAILED` is a **final** state — the money did not move and will not
move on that refund record. The most common cause on card payments is that the
**issuing bank declined the refund** after Waffo had already accepted the
request, which is why the failure arrives asynchronously rather than as an API
error.

What to do:

1. Read the refund order notification or `/api/v1/refund/inquiry` for the
   reason.
2. Do not resend with the same `refundRequestId` expecting a different outcome —
   a final state does not reopen.
3. If the decline is not explained by the amount, the window, or the method's
   rules, raise it with Waffo support with the original order ID and the refund
   ID. Refunds that fail for a reason outside your control are handled
   case-by-case.
4. Where the card cannot be credited at all, settle with the user by another
   route.

## Which of these limits apply to me, and where do I look them up?

There is no single "refund capability" field you can read before submitting.
What you can rely on:

* The **Merchant Portal → PayIn** page lists the payment methods enabled for
  your account, with their currencies and amount limits.
* `/api/v1/paymethodconfig/inquiry` returns the same configuration
  programmatically.
* The refund API's rejection is the authoritative answer for a specific
  request: `A0014` means the agreement's refund rules do not allow it (shape,
  count, window, or a converted amount that rounds to nothing), `A0017` means
  the payment method cannot carry a refund at all, `A0027` means the platform
  count ceiling was reached, and `A0003` means the amount exceeds what is left
  or required refund fields are missing and are listed in the response.

Because of that, the robust integration pattern is **attempt, then branch** —
not **predict, then attempt**.

## Where do I look next?

| Question | Page |
| - | - |
| General refund rules, batch refunds, fee return | [Refund FAQ](/docs/en/support/refund-faq) |
| Refund amounts on cross-currency orders | [Exchange Rates](/docs/en/support/exchange-rates-faq) |
| Over- and underpayment on bank transfers | [Bank Transfers & Virtual Accounts](/docs/en/support/bank-transfer-and-virtual-account-faq) |
| Stablecoin refunds and the mandatory refund address | [Stablecoin Exceptions & Refunds](/docs/en/support/stablecoin-exceptions-faq) |
| What each refund-related error code means | [Error Codes & Limits Supplement](/docs/en/support/error-codes-and-limits-supplement) |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.