> ## 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.

# Order Amounts, Limits & Declines

> Why an amount that looks valid is rejected, how same-currency and cross-currency orders are checked differently, what processingAmountLimits min/max/multiple mean, which limit produced which error code, how payment method type decides whether your customer can pay at all, how to read a card decline, and how long an unpaid order stays open.

Everything about why an order was refused before the customer ever saw a
payment page, plus how to read a decline once they did. For what the order
fields mean see the
[Order creation field guide](/docs/en/support/order-create-field-guide); for
confirming outcomes see
[Confirming payment results](/docs/en/support/payment-results-faq); for the fixed
per-currency decimal table see
[Currency](/docs/en/developer-docs/core-concepts/currency).

## Why was my amount rejected for having too many decimals?

Because there are **two** precision rules, not one, and they apply to different
amounts:

| Layer | Applied to | Effect |
| - | - | - |
| **Order currency, ISO decimals** | Your `orderAmount`, always | Rejected at creation with `A0003` if the amount has more decimals than the order currency's ISO 4217 definition, or breaks that currency's multiple rule. |
| **Processing precision and multiple** | The amount that is actually charged | If the charged amount cannot be expressed in the processing precision, or breaks the payment method's multiple rule, that payment method is removed. If none survives you get `A0018`. |

The second layer is where same-currency and cross-currency diverge, and it is
the part worth internalising.

## Same-currency and cross-currency are checked differently. How?

The second layer is applied to **whichever amount the customer is charged**:

* **Same-currency order** — the charged amount *is* your `orderAmount`, so the
  strict processing precision and multiple are applied directly to what you
  submitted. Some currencies have a processing precision **narrower than their
  ISO decimals**: those currencies accept two decimals by the ISO rule but are
  processed to whole units. An amount with a fractional part in such a currency
  passes the first layer and is then rejected by the second.
* **Cross-currency order** — the charged amount is the **converted** amount, so
  the strict check lands there and not on your `orderAmount`. Waffo converts,
  then adjusts the result to the processing currency's precision and the
  payment method's multiple, so an `orderAmount` carrying decimals the
  processing side could not accept is no longer your problem.

**You have to know which of the two scenarios you are in.** The same
`orderAmount` in the same currency can be accepted in one and rejected in the
other, and the difference is entirely which currency the customer pays in. If
you price in one currency and charge in another, do not carry over the
same-currency validation you built for your domestic flow.

## Which direction does Waffo round the converted amount?

**Up.** After conversion, the amount is adjusted upward to fit the processing
currency's precision and then, where the payment method has one, upward to the
next valid multiple. Rounding only ever appears in **cross-currency** orders —
a same-currency order is never re-rounded, it is validated and passed through.

The residual is reported to you: the inquiry response for a cross-currency
order returns the converted amount, the rate applied, and the adjustment made,
so you can reconcile the charged amount against your priced amount without
recomputing it. See
[Exchange rates](/docs/en/support/exchange-rates-faq) for when the rate is fixed.

## Why was 10.50 rejected when 10 was accepted?

Because the limit on an amount is not just a range — there is also an **integer
multiple**. A payment method can require that the amount be a whole multiple of
some unit, in which case any value that is not a multiple of it is refused
regardless of how comfortably it sits inside the min/max.

The multiple is dimensioned by **payment method plus the currency actually being
processed**, so:

* The same payment method can carry a multiple in one currency and none in
  another.
* Two payment methods in the same currency can carry different multiples.
* A method with a multiple almost always has **no decimal part at all** — if a
  method demands multiples of a whole unit, `10.50` cannot ever satisfy it.

Amounts of this shape are the single most common cause of "my amount is inside
the limits and still rejected".

## How do I find the limits before I create the order?

Read them from the payment method query response. Each available method returns
`processingAmountLimits`, keyed by the currency being processed, with three
fields:

| Field | Meaning |
| - | - |
| `min` | Lowest acceptable amount in that currency. |
| `max` | Highest acceptable amount. |
| `multiple` | The amount must be a whole multiple of this. Zero or absent means no multiple constraint. |

Both local and converted currencies appear in that map, so a cross-currency
method reports the limits in the currency the customer will actually be
charged. Validate against `multiple` as well as `min`/`max` in your own
pre-flight check — that is the field most integrations skip.

## Which error means which limit was hit?

| Code | What it means here | What to do |
| - | - | - |
| `A0003` | The order amount's decimals or multiple do not fit the **order currency**. | Fix the amount you submit. |
| `A0008` | The order amount is outside the **contract's** amount range for every candidate agreement. | The amount itself is the problem, not its shape. |
| `A0018` | No payment method survived filtering — including because the **charged** amount fails the processing precision or the method's multiple. | Re-read `processingAmountLimits` and adjust; also check the method and terminal you passed. |
| `A0029` | The card credential you referenced is not usable — unknown, wrong status, or not this user's. | See the token section below. |

Note that `A0018` is a catch-all for "nothing was available", so it is also
what you get for a terminal mismatch or an over-long subscription. Read it as
"filtering left nothing", not as "wrong payment method name". See
[Order creation field guide](/docs/en/support/order-create-field-guide) for the
other `A0018` causes.

## Why is a payment method unavailable to my customer?

Because a payment method's **type** determines how the customer can finish, and
some types can only be completed one way. A method can be restricted to
specific customer terminals, and Waffo enforces that at order creation:

* Waffo holds an allowed-terminal list per payment method. If the
  `userTerminal` you sent is not on that method's list, **the method is removed
  from the candidates**. If nothing is left, creation fails.
* App-launch-only methods are the classic case: some e-wallet, BNPL and digital
  banking methods complete by handing off to a mobile app, so they simply do
  not exist as an option on a desktop browser.

The consequence: **`userTerminal` is not a analytics hint, it is a routing
input.** Sending a desktop value for a mobile session, or defaulting it to one
value for all traffic, will make perfectly available methods disappear and
produce a creation failure your logs will attribute to the wrong thing. Detect
it per session. See
[Order creation field guide](/docs/en/support/order-create-field-guide) for the
accepted values.

## Bank transfer and over-the-counter: why is nothing happening after I create the order?

Because for those types, **creating the order and paying are separate events**.
The customer leaves with an account number, a reference or a slip, and pays
later through their bank or at a counter. Nothing else happens at Waffo until
they do.

* Expect a long gap between creation and result, and expect two notifications:
  one when the order is ready to be paid and one when the money arrives.
* The window in which the customer can still pay is set **per payment method**,
  not by a single platform value, so two bank-transfer methods in the same
  country can close at different times.
* Do not cancel or re-create the order because it has been quiet for hours.
  See
  [Confirming payment results](/docs/en/support/payment-results-faq) for what counts
  as final.

## Can I set the customer's default card when I create the order?

No — order creation is not where the default card is set. Use the dedicated
saved-payment-method endpoint; see
[Tokenization & card payments](/docs/en/support/tokenization-faq).

What order creation *does* do with a saved credential is validate it strictly.
Passing `paymentInfo.userPaymentAccessToken`:

| Situation | Result |
| - | - |
| The token does not exist | Creation fails (`A0029`). |
| The token is not in a usable state | Creation fails. |
| The token belongs to a different `userId` | Creation fails. Tokens are scoped to the customer who created them. |
| You also passed raw card data | Creation fails — the two are mutually exclusive. |

So a saved-card charge either goes ahead on that exact credential or it does
not go ahead at all. There is no silent fallback to another stored card, which
is deliberate: the customer must not be charged on a card you did not name.

## Does `tokenizationNotifyUrl` replace `notifyUrl`?

No. They are separate channels and you need both:

* `notifyUrl` receives the **payment** result, as always.
* The tokenization notification URL receives the **card-binding** result only.

Configuring one has no effect on the other, and a card bound during a payment
produces a notification on each. See
[Tokenization & card payments](/docs/en/support/tokenization-faq) for where the
tokenization URL is configured.

## A card was declined. Should I retry?

Only after you know which of four kinds of decline you got. Waffo maps the card
scheme's response into a Waffo failure code and a retry class:

| Class | What it means | What to do |
| - | - | - |
| **Do not retry** | The scheme has told you this attempt will never succeed — the account is closed, invalid, reported lost or stolen, or the transaction is not permitted for this cardholder. | **Ask the customer for a different card.** Do not retry, including on a schedule. Repeat attempts on these can attract scheme fees. |
| **Retry after the details are updated** | The credential itself is stale or the authentication failed — an expired card, or authentication data that did not verify. | Have the customer re-enter or re-bind the card, then charge again. Retrying the same stored credential unchanged will fail the same way. |
| **Retry later** | A temporary condition on the issuer's side — funds not available right now, a frequency or amount limit reached, the issuer unreachable. | Retry on a delay. For subscriptions this is what the retry strategy already does for you. |
| **Other** | Everything else, including card products unsuitable for this transaction. | Do not assume retryable. Where the guidance is to obtain another form of payment, treat it as the first class. |

Two rules that matter more than the taxonomy:

* **A permanent decline is a card-change prompt, not a retry candidate.** The
  most expensive integration mistake here is looping on a decline that can never
  clear.
* Waffo does not publish a per-code retry ceiling, and neither should your
  runbook assume one. Retry caps for recurring charges follow card scheme rules
  — see
  [Subscription retries & payment methods](/docs/en/support/subscription-retry-and-payment-methods-faq).

## The customer told their bank to stop the recurring payment. What must I do?

**Stop all recurring deductions for that customer immediately.** The schemes
have specific response codes for a cardholder revoking authorization — for a
single merchant or for all merchants — and for a cardholder asking that a
recurring or instalment payment be stopped. These are non-retryable by
definition, and continuing to attempt them is a compliance problem, not just a
failed payment.

Concretely:

* Cancel the subscription rather than letting the retry schedule run out.
* Do not create a new subscription on the same credential.
* If the customer later wants to resume, they must authorize again from
  scratch, with a fresh consent.

## How long does an unpaid order stay open?

There are two Waffo-side defaults, split by whether the method is a card, and
neither is a contract term you should build against:

| Order | Default window |
| - | - |
| Card-type payment methods | A **long** platform default, measured in hours. Cards are given a long window deliberately: results arrive synchronously, and a short window risks closing an order that then succeeds. |
| All other payment methods | A **short** platform default — but in practice superseded by the payment method's own configured timeout, which is the value that governs bank transfer, virtual account and over-the-counter flows. |

The platform defaults are environment configuration and can change; if the
window matters to your business, send `orderExpiredAt` yourself on the methods
that accept a merchant-specified expiry (the `Order Expiry Customization` column
on **Service → Payin** tells you which). It is ignored on methods that do not.
See
[Confirming payment results](/docs/en/support/payment-results-faq) for what
`orderExpiredAt` does and does not control, and for why a payment can still
land after Waffo closed the order.

## Stablecoin: can one payment be flagged with two exception types?

No. An exception carries **exactly one** type — the field is single-valued, so a
payment that is both late and short is recorded under one classification, not
two. The types are:

| Type | Meaning |
| - | - |
| `OVERPAID` | More received than the order amount. |
| `UNDERPAID` | Less received than the order amount. |
| `LATE_PAID` | Funds arrived after the order was already final. |
| `WRONG_TOKEN` | The token received is not the token the order expected, or is not a recognised token at all. |
| `WRONG_CHAIN` | Funds arrived on a chain the order did not expect. |
| `NO_MATCH_ORDER` | No order could be matched to the transfer. |
| `UNSUPPORTED_WALLET_ADDRESS` | The address involved cannot be handled automatically. |
| `BLOCKED` | Funds refused on risk grounds. |

Two ordering rules you can rely on:

* **Token identity is resolved before amount.** An unrecognised or mismatched
  token is classified as a token problem and the amount is never compared — so
  do not expect an `UNDERPAID` on a transfer that was in the wrong token.
* **The return address is checked last, and it wins.** If an exception would
  otherwise have been resolved automatically but the address it would return to
  is not a plain wallet address, the exception is **re-classified as an address
  problem and routed to manual handling**. That is why an overpayment can
  surface as an address exception.

For what happens to the money in each case, see
[Stablecoin exceptions & refunds](/docs/en/support/stablecoin-exceptions-faq).


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