Skip to main content
Common Errors Troubleshooting covers the codes that generate the most tickets: 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:
  • A0020 and A0021 are per order currency. Raising one currency’s limit does nothing for another.
  • A0021 depends 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.
  • A0008 is a payment-method range, not an account limit. Where to read the ranges is covered in Common Errors Troubleshooting.
If either account-level limit is set too low for your business, it is a configuration change — contact your Waffo integration contact.

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; Hant and Hans are 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.
Anything else is 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.”
A0024 is the code, not A0023. A0023 is “Order cannot be created due to expired token” — an entirely different failure, about a card token, with an entirely different fix. If you have integration notes that map an expired or worsened FX rate to A0023, they are wrong; correct them.
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:
  1. Treat A0024 as retryable. Re-create the order; it picks up a current quote.
  2. 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.
  3. Show the user the refreshed amount if your UI displayed the local amount before order creation.
How rates are quoted and for how long they hold is in Exchange Rates in Payments & Settlement.

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.
Note what 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.
So a rate movement never explains an amount rejection. If an amount used to pass and now fails, look at the account-level daily limit (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/inquiry against production — not against sandbox behaviour.
  • A method that returns A0018 in production while working in the sandbox is usually this, not an integration bug.
Other sandbox/production differences are in Sandbox Testing.

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:
  1. Prefer a partial refund where the goodwill amount allows it.
  2. 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 a tokenId 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.

Where do I look next?