A0001, A0002, A0003, A0018 and
the Exxxxx family. This page covers the rest of the order-creation
rejections — limits, currency, language, FX and configuration — and then the
configuration questions that produce the same “why can’t I…” tickets without
producing an error code at all.
Which error code means which limit? I have three different ones
They are three different ceilings, checked at different levels. Reading them as the same thing is the usual reason a merchant “fixes” the wrong setting.
Three things follow:
A0020andA0021are per order currency. Raising one currency’s limit does nothing for another.A0021depends on the volume already taken today, so the same request can succeed in the morning and fail in the evening. Retrying immediately will not help; retrying the next day will.A0008is a payment-method range, not an account limit. Where to read the ranges is covered in Common Errors Troubleshooting.
I sent a currency I am not contracted for. Do I get A0007?
No — you get A0018 “Payment method in merchant order does not match with
current merchant contract”. There is no currency-specific rejection on the
ordinary order-creation path: the contract lookup matches on your merchant ID
and the order currency together, so a currency none of your agreements carry
simply leaves no agreement to match, and the failure is reported as a
payment-method mismatch. The message misleads; the cause is the currency.
A0007 “Transaction currency is not supported” does exist in the error table,
but it is reserved for a specialised stablecoin-protocol payment path that only
accepts one settlement currency. If you are creating a normal order and see a
currency problem, the code you are debugging is A0018.
/api/v1/paymethodconfig/inquiry returns one entry per contract, each with its
own currency — match on the currency you intend to charge in before you build
the request. The currency-shaped ways to earn an A0018 are worked through in
Do I Have to Enable a Currency?.
A0026 and A0047 — what is wrong with my cashierLanguage?
They separate “you asked for a language we do not have” from “that is not a
valid language tag”.
The value must be an IETF BCP 47 tag in the form
language[-script][-region]:
- language — a two-letter ISO 639-1 code, and it must be one Waffo supports. The supported set cannot be extended by passing something else.
- script — optional, four letters (ISO 15924). If present it must be a
script Waffo uses;
HantandHansare the ones in service. - region — optional, a two-letter ISO 3166-1 alpha-2 code. Any valid country code is accepted here; the region is not restricted to a supported list.
A0047: underscores instead of hyphens (en_US), variant
subtags (de-DE-1996), and private-use extensions (en-US-x-foo) are all
rejected as malformed rather than unsupported. Casing is not an error — en-us
is normalised to en-US.
Cross-currency order rejected because of the exchange rate — which code is it?
A0024 — “Order can not be processed due to the fx rate is expired.”
On a cross-currency order the quoted rate is validated at order creation. If the
quote is no longer valid — it expired, or the current rate has moved against the
quote — the order is rejected outright. Waffo does not silently reprice the
order at the new rate, because that would change what the user pays after you
committed to a price.
What to do:
- Treat
A0024as retryable. Re-create the order; it picks up a current quote. - Do not carry a quote across a long checkout funnel. The longer the gap between quoting a price and creating the order, the more often you will see this.
- Show the user the refreshed amount if your UI displayed the local amount before order creation.
A0023 vs A0024 — a summary I can paste into my code comments
My cross-currency partial refund was rejected with A0014. The amount looked fine
Check the amount after conversion. On a cross-currency order the refund
amount you send is converted before validation, and if the converted amount
comes out as zero or less the refund is rejected with A0014 (“Order cannot
be refunded due to refund rules”).
This bites in two situations:
- A very small partial refund on a currency pair with a large rate — the converted amount rounds down to zero.
- A currency with fewer minor units than you assumed, so the converted amount truncates away entirely.
A0014 does not cover: asking for more than is left. A refund
whose amount, added to everything already refunded or in progress, exceeds the
original payment is rejected as a plain parameter failure — A0003 Parameter validation failed — not as a refund-rule breach. So on a cross-currency order,
read A0014 as “the converted amount did not survive the refund rules” and
A0003 as “you asked for too much”; in both cases check the figure after
conversion rather than the one you sent. The conversion rules are in
Exchange Rates; the per-method refund rules
are in
Refund Limits by Payment Method.
A0022 “multiple products are found” — what am I supposed to specify?
The product. A0022 means the payment method you asked for resolves to more
than one contracted product on your account — for example the same method
signed both for one-off payments and for subscriptions. Waffo will not guess
which one you meant, so the order is rejected instead of being created against
the wrong product.
The fix is to send productName in paymentInfo so exactly one contract
matches. See
Checkout customization
for the parameter combinations, including how payMethodType, payMethodName
and payMethodCountry narrow the match.
Do my signed amount limits change when the exchange rate moves?
No. The minimum and maximum order amounts on your contract are fixed values. They are calculated once, when the payment method is signed for your account, and they do not move with the exchange rate afterwards — including on cross-currency orders. Two details worth knowing:- When your pricing currency differs from the payment method’s own currency, the bounds are converted at signing time and then held: the minimum is rounded down to the currency’s minor units (never to zero — it floors at one minor unit), and the maximum is rounded up.
- A limit you request for your account has to sit inside the range the system calculated. Your range is a subset of it, never wider. If you ask for a wider range, the configuration is rejected rather than clamped.
A0021) or a
configuration change, not at FX. Where to read the values is in
Common Errors Troubleshooting.
The payment failed because of a limit, but not one of mine. What is that?
Some wallet payment methods enforce limits on the user’s own wallet — most commonly an annual ceiling on how much the user may spend through it. When the user hits it, the payment fails, and there is nothing you or Waffo can configure to allow it. How to handle it:- Show the user a neutral message and offer another payment method rather than retrying the same one.
- Do not count these against your own limits or your integration’s health; they are user-side.
- Do not attempt to split the order into smaller payments to get under the ceiling — an annual ceiling is not evaded by splitting, and it degrades the user’s experience.
Why can’t I see Settlements, Fees or Settlement Cycle in my Portal?
Because of what you have signed. Parts of the Merchant Portal are driven by your contracted products, not by your role — an Admin cannot grant them. See Merchant Portal Operations for the menu-level table. On top of the menus, merchants on a gateway-only arrangement — where the signed product is Direct Payment and nothing else — also lose settlement-related columns:
This is expected rather than a defect: with no settlement product there is no
settlement cycle, no fee schedule to display, and no settlement status to report.
Nothing about it is switchable from your side. Order and refund data, exports and
webhooks are unaffected.
If you expected to see settlement data and do not, the question to ask support is
“which products is my account signed for”, not “please grant me the menu”.
A payment method works in the sandbox but fails in production
That is possible by design, and it is the one sandbox difference that surprises people the most. The sandbox deliberately carries payment methods and configurations that are not live in production, so that integration and end-to-end testing can start before a method is switched on for real traffic. So:- Sandbox availability is not evidence of production availability.
- Confirm production coverage against your contract — the Portal’s PayIn page,
or
/api/v1/paymethodconfig/inquiryagainst production — not against sandbox behaviour. - A method that returns
A0018in production while working in the sandbox is usually this, not an integration bug.
How does a user authorize a recurring WeChat Pay charge?
Through one of two flows, chosen by where the user is:
Once the authorization exists, Waffo charges the recurring amounts against it
without further user action, the user or you can terminate the authorization, and
charged orders can be refunded in full or in part. Subscription mechanics
themselves — plan changes, failed deductions, retries, notifications — are in
the Subscription FAQ.
Only the two flows above exist. If you are building against a design that also
lists in-app, mini-program or in-client authorization, those are not available.
Can a refund on the first payment stop a whole recurring series?
Yes, on card recurring series, and this is the failure mode most likely to surprise you months later.
So a customer-service full refund of a first charge silently ends the customer’s
subscription at the payment level, even though your own records still show it as
active. Two safeguards:
- Prefer a partial refund where the goodwill amount allows it.
- When you do fully refund a first charge, treat the series as ended and re-establish the authorization if the customer stays.
Do I need to tell the checkout what device the user is on?
No. The Waffo checkout detects the user’s device and chooses the presentation itself:
You do not pass any device, terminal or authentication-mode parameter for this —
there is nothing to get wrong, and nothing to keep in sync when a payment method
changes its behaviour. Some app-based wallet methods take it one step further on
mobile: the checkout either deep-links straight into the specific app the user
picked, or hands off to the operating system’s app chooser.
What you do control is which payment methods appear at all — see
Checkout customization
and Checkout Display Rules.
I charged a card without tokenizing it. Can I charge it again later?
No. A card payment made without atokenId is a one-off customer-initiated
transaction. Nothing is retained that would let you charge that card again, so
the payment has no merchant-initiated follow-up capability.
To charge a card again later you must tokenize first: complete the tokenization
flow, hold a valid tokenId, and reference that token on the subsequent charges.
There is no way to convert a completed plain-card payment into a token after the
fact — the user has to go through binding.
The full token lifecycle, the CIT/MIT distinction and the
AUTHORIZATION_REQUIRED case are in
Tokenization & Card Payments.