code and msg in the response body. The first letter of the code tells you who has to act:
The complete list is in the error code quick reference. This page covers the codes that generate the most support tickets.
I get A0001 “Invalid Api secret key” — where does the API key come from?
The X-API-KEY request header was missing or does not match a key issued to your account. The API key is provided by Waffo during merchant onboarding and is available in the Merchant Portal dashboard.
Common causes:
- Environment mismatch — sandbox and production use different API keys and RSA keys. A sandbox key sent to
https://api.waffo.com(or a production key sent tohttps://api-sandbox.waffo.com) fails withA0001. - The
X-API-KEYheader is missing from the request entirely.
How do I debug A0002 “Invalid signature”?
Waffo verifies the X-SIGNATURE header against the merchant public key you exchanged during onboarding, using SHA256WithRSA. Verification failing means the signature you produced does not match the body Waffo received. Work through this checklist:
- Sign the raw request body string exactly as transmitted — do not JSON-parse and re-stringify it, and do not sign a prettified or re-ordered copy.
- Confirm you are signing with the private key whose public key is registered with Waffo for this environment (sandbox and production RSA keys are separate).
- Confirm the algorithm is
SHA256WithRSA. - If you generate a new key pair, the new public key must be registered with Waffo before you switch.
What usually triggers A0003 “Parameter validation failed”?
A0003 is a request-format rejection. The msg field carries detail; the highest-frequency causes seen in production support are:
-
Field length exceeded. Every string field has a declared maximum — the ones merchants overflow most often on
/api/v1/order/create: -
Amount format. Amounts are strings with at most 2 decimal places — and zero-decimal currencies such as
JPYmust be whole numbers. A real production example:"orderCurrency":"JPY","orderAmount":"200.88"is rejected withA0003;"201"is accepted. See currency handling. -
Format conventions — time is ISO 8601 with milliseconds (
2023-04-01T03:00:00.000Z), currency is ISO 4217, country is ISO 3166-1 alpha-3 (IDN,PHL), anduserPhonefollows+CountryCode-Number, for example+62-08990013157.
What does A0018 mean, and how do I fix it?
In the acquiring API, A0018 is “Payment method in merchant order does not match with current merchant contract” — the payMethod/country/product combination in your order is not one you have signed for.
How to fix:
- Call
/api/v1/paymethodconfig/inquiry. The response lists every contracted pay method with itsproductName(ONE_TIME_PAYMENT/DIRECT_PAYMENT),payMethodName,country, andcurrentStatus(1available,0not available), plus anyfixedMaintenanceRuleswindows. - Send only combinations that appear in that list with
currentStatus1. Which methods you can sign depends on your contracted configuration — check the Merchant Portal or contact integration support to add one. - Two neighbouring codes look similar but ask for more precision, not a contract change:
A0022(specifyproductName— multiple products found) andA0033(specify the pay method country — multiple pay methods found).
My contract covers the payment method — why do I still get A0018 when I pass userCurrency?
A0018 also fires on the currency-pair dimension. The orderCurrency → userCurrency combination (the presentment pair) must be enabled in your contract, independently of the payment method itself. For example, an order with orderCurrency=JPY and userCurrency=USD is rejected with A0018 unless the JPY→USD pair is contracted — even when card payments are.
Two ways out:
- Omit
userCurrency. The Waffo cashier then converts the order amount into the user’s local currency automatically at the current exchange rate. - Enable the pair. Contact your Waffo integration support to add the currency pair to your contract if you need to fix the user-facing currency yourself.
My order was rejected because of the amount. Where do I find the limits?
Each payment method has its own minimum and maximum order amount per currency, and some combinations carry precision rules on top of the usual currency rules (zero-decimal currencies must be whole numbers, and a few methods accept fewer decimal places than the currency normally allows). An amount outside the range or precision is rejected at order creation with an explicit error. You can read the limits programmatically:/api/v1/paymethodconfig/inquiry
returns orderAmountMin and orderAmountMax for each contracted pay method,
alongside its currency. Note that the response has one entry per contract
rather than per pay method, so the same method can appear several times with
different currencies and different limits — match on the currency you are
charging in. The Merchant Portal shows the same configuration.
The limits are contract-dependent fixed values: they are set when the payment method is signed for your account and do not move with exchange rates — including for cross-currency orders. To find the values that apply to you, check the payment method configuration in the Merchant Portal, or ask your Waffo integration contact.
I received E0001 “Unknown status”. Is the payment failed?
No — unknown is not final. Exxxxx errors mean the outcome is not yet determined inside Waffo or a third-party system. Treat the transaction as pending and keep querying (/api/v1/order/inquiry) until you receive a final Success/Failed status. Never show the user a failure or re-charge based on an E code alone.
When retrying the original request, reuse the same paymentRequestId — it is the idempotency key. Reusing it with different parameters fails with A0011 “Idempotent param mismatch error”. See idempotency.
Waffo keeps sending me the same notification. Why?
Your endpoint is not acknowledging it. A recurring support case: a merchant’s webhook endpoint replied withContent-Type: application/octet-stream and {"message":"fail"} — so Waffo kept redelivering the notification. To acknowledge:
- Respond with
Content-Type: application/json. - Respond with body
{"message":"success"}after you have processed the event. Anyfailed/unknownreply, non-200 status, timeout, or unparseable body triggers the retry schedule — see webhook retries. - Make your handler idempotent: retries mean the same event can arrive more than once.