Airwallex Payments (Malaysia)
Use this skill for Airwallex online payment acceptance work, scoped to what a Malaysian merchant uses (MYR, FPX, DuitNow, local e-wallets, cards, wallets). Airwallex also sells FX, payouts, issuing, spend and treasury; those APIs are listed in the endpoint catalogue only.
Source
Read references/airwallex-api.md first. It is the entry point: environments, auth, amounts, idempotency, end-to-end flow, webhook signature, statuses, errors, rate limits, and the full endpoint catalogue (every documented operation).
Then read the topic file you need:
references/airwallex-payment-intents.md: create, retrieve, list, update, confirm, confirm_continue, capture, cancel, increment authorization, payment attempts;next_action; 3DS.references/airwallex-checkout.md: Hosted Payment Page (redirectToCheckout), Drop-in Element, Checkout Sessions API, Payment Links API.references/airwallex-malaysia-methods.md: FPX, DuitNow, Touch 'n Go, Boost, GrabPay, ShopeePay, Atome, 7-Eleven MY, cards, Apple Pay, Google Pay; limits, settlement, confirm payloads, bank codes, config APIs.references/airwallex-webhooks.md: subscription, delivery headers, signature verification, event list, IP allowlist, retries, Webhook API.references/airwallex-refunds-disputes.md: refunds, cancellations, disputes.references/airwallex-recurring.md: customers, payment consents, payment methods, CIT/MIT, Touch 'n Go recurring.references/airwallex-settlement.md: settlements, settlement records, financial transactions, financial reports.references/airwallex-testing-errors.md: sandbox, test cards, simulation APIs, error codes, access token API.references/account-setup.md: registration, activation, keys, env vars, sandbox vs live, settlement and pricing pointers.references/source-map.md: every official page used, coverage counts, and what was left out.
Core Workflow
- Identify environment: sandbox (
https://api.sandbox.airwallex.com) or production (https://api.airwallex.com). - Pick the surface: Hosted Payment Page (JS
redirectToCheckoutor Checkout Session URL), Drop-in Element, Embedded Elements, Native API (card PAN needs PCI-DSS ROC), Payment Links, or a plugin (WooCommerce, Shopify). - Confirm the account: Payments activated, each payment method activated in Payments > Payment methods, settlement currencies set, webhook created, scoped API key made.
- Server: get an access token with
POST /api/v1/authentication/login(x-client-id,x-api-key), cache it untilexpires_at(30 minutes). - Create a local pending order, then
POST /api/v1/pa/payment_intents/createwithrequest_id(UUID),amount(major units,20.50= RM20.50),currency: "MYR",merchant_order_id,return_url. - Send only
id+client_secretto the browser (valid 60 minutes), or confirm server-side and follownext_action(redirect URL or QR text). - Treat
return_url,successUrl, and the Drop-insuccessevent as UX only. - Verify each webhook: HMAC-SHA256 hex of
x-timestamp + raw bodywith the webhook secret, constant-time compare, check timestamp tolerance, then parse. - Settle idempotently on
payment_intent.succeededafter aGET /api/v1/pa/payment_intents/{id}re-check ofstatus,amount,currency,merchant_order_id. - Use retrieve as fallback when webhooks are late; dedupe events by
id; refunds viaPOST /api/v1/pa/refunds/create.
Core Facts
- Auth:
POST /api/v1/authentication/loginreturnstoken+expires_at; sendAuthorization: Bearer <token>. Login limit 100/min per key. - Amounts are decimals in major units (MYR has 2 decimals). Not sen.
request_idis the idempotency key on every POST (max 64 chars); retry with the same value.- Payment Intent statuses:
REQUIRES_PAYMENT_METHOD,REQUIRES_CUSTOMER_ACTION,REQUIRES_CAPTURE,PENDING,PENDING_REVIEW,SUCCEEDED,CANCELLED(list is not exhaustive). - Fulfil on
payment_intent.succeeded. A failed attempt emitspayment_intent.payment_failedand the intent reverts toREQUIRES_PAYMENT_METHOD(payment_intent.requires_payment_method), so the shopper can retry on the same intent. - Webhook headers:
x-timestamp(Unix ms) andx-signature(hex HMAC-SHA256 overx-timestampstring + raw JSON body, key = that webhook's secret). Reply 200 fast; retries back off for about 3 days; order is not guaranteed. - MY method types:
fpx(needsbank_name),duit_now,tng(needsflow),boost,grabpay,shopee_pay,atome,seven_eleven,card,applepay,googlepay. - FPX: MYR 1.01 to 2,000, T+3, refunds within 180 days, no chargebacks, no recurring.
- Rate limits: production 100 req/s global, 20 req/s per endpoint, 50 concurrent; sandbox 20, 10, 10. HTTP 429
too_many_requests. - Latest API version
2026-08-21; override per call withx-api-version. - Webhook source IPs (production and sandbox) are listed in
references/airwallex-webhooks.md.
Critical Rules
- Keep Client ID, API key, access token, and webhook secret server-side. Only
client_secretmay reach the browser. Never logclient_secret. - Compute the amount on the server; never accept it from the browser.
- Verify the signature on the raw body before any JSON parsing. Use the secret of the exact webhook subscription (each URL has its own). Test events are signed with the key in the
client-secret-keyheader. - Do not mark paid from a redirect,
successUrl, Drop-insuccess, a QR display, or the confirm response alone; FPX, DuitNow, and wallets finish asynchronously. - Re-check status, amount, currency, and
merchant_order_idagainst your record before settling; store intentidand eventidwith unique constraints. - Sandbox and production use different credentials, hosts, webhook secrets, and webhook IPs.
- Create a new Payment Intent when
client_secretexpires; it cannot be refreshed. Update the intent (not a new one) if the cart changes before confirm. - Raw card data via Native API only with PCI-DSS ROC and Airwallex approval; otherwise use HPP, Drop-in, or Elements (SAQ A).
- Use scoped API keys and IP whitelisting for production keys.
- Pricing, limits, activation windows, and availability change; link to official pages, confirm in the web app.
Completion Criteria
An Airwallex integration is done only when:
- Token is fetched server-side, cached, and refreshed on 401 or near
expires_at. - Payment Intent is created server-side with a unique
request_id, server-computedamount,currency,merchant_order_id, andreturn_url. - The frontend gets only
intent_id,client_secret,currency; checkout uses HPP, Drop-in, Elements, a Checkout Session URL, or a server confirm withnext_actionhandling. - A webhook subscription exists for the right environment with at least
payment_intent.succeeded,payment_intent.payment_failed,payment_intent.cancelled, andrefund.*. - The handler verifies
x-signatureoverx-timestamp + raw body, enforces timestamp tolerance, responds 200 quickly, and processes idempotently by eventid. - Settlement re-reads the intent and checks status, amount, currency, and order id; duplicates and out-of-order events cannot double-fulfil.
- Tests cover bad signature, replay, wrong amount or currency, failed payment, expired
client_secret, FPX or DuitNow pending then succeeded (sandbox simulation), refund success and failure. - Deployment docs list
AIRWALLEX_CLIENT_ID,AIRWALLEX_API_KEY,AIRWALLEX_WEBHOOK_SECRET,AIRWALLEX_ENV, return URL, webhook URL, and sandbox/live separation.
Loophole Review Loop
When asked whether an Airwallex strategy is "100% confident", run this loop until no factual gaps remain:
- List trust boundaries: browser, Airwallex.js, hosted page, backend token cache, intent creator, Airwallex API, webhook receiver, raw-body parser, database, fulfilment job, refund job, web app settings, payment method activation.
- Check bypasses: fake success redirect, forged webhook, signature over parsed JSON, wrong webhook secret, sandbox/live mix, stale or leaked token, amount tampering, wrong currency, reused
merchant_order_id, duplicate or out-of-order events, expiredclient_secret, unactivated method (payment_method_not_allowed), FPX over RM2,000, refund failing for lack of refundable funds, DuitNow 5-minute timeout marked failed too early. - Add fixes: server-side creation, raw-body HMAC with constant-time compare, timestamp tolerance, retrieve-before-settle, unique keys on intent and event ids, per-environment config, scoped keys, IP allowlists, status fallback polling.
- Re-read the relevant reference file for any gap.
- State remaining uncertainty that depends on account activation, reserve plan, settlement terms, pricing, or live availability.


