Atome BNPL Payment Gateway
Use this skill for Atome (apaylater) online BNPL integration work.
Source
Read references/atome-api.md before giving factual API guidance or writing integration code. It contains the endpoint catalogue, base URLs, Basic auth, the 10-step checkout flow, full create payment and nested field tables, payment object and states, requery, cancel, refund, callback fields, X-Signature HMAC-SHA256, IP allowlist, amount rules, enums, the full error code table, and test notes.
Read references/atome-plugins-sdk.md when the merchant uses Shopify, WooCommerce, Magento, PrestaShop, OpenCart, EasyStore, Wix, or Salesforce, or opens appPaymentUrl from an iOS, Android, React Native, or Flutter app.
Read references/account-setup.md when the user asks how to register, where credentials come from, test vs production setup, env var names, plugins, Malaysia specifics, pricing, or settlement.
Read references/source-map.md when choosing which official Atome page applies, or to see the list of gaps that only the Atome Account Manager can answer.
Core Workflow
- Identify environment: test (
api.apaylater.net) or production (api.apaylater.com). - Identify surface: native API (web redirect or app
appPaymentUrl) or a platform plugin (Shopify, WooCommerce, Magento, PrestaShop, OpenCart, EasyStore, Wix, Salesforce). - Confirm credentials from the Atome Account Manager: API username, API password, and callback signing secret plus its encoding.
- Run
POST /authonce withcountryCode: "MY"and the callback URL to prove credentials and callback reachability. - Create a local pending payment, then
POST /paymentsserver-side with a uniquereferenceId,MYRamount in sen,callbackUrl,paymentResultUrl,paymentCancelUrl,customerInfo.mobileNumber,shippingAddress, anditems. - Send the customer to
redirectUrl(browser) orappPaymentUrl(in-app via SDK). - Treat
paymentResultUrlandpaymentCancelUrlas browser UX only. - On callback, verify
X-Signatureover the raw body, readreferenceId, then callGET /payments/{referenceId}. - Settle idempotently only when the fetched status is
PAIDandreferenceId, amount, currency, and environment match. - Call cancel when your own checkout hold expires; refund with a unique
refundId.
Core Facts
- Test base:
https://api.apaylater.net/v2. Production base:https://api.apaylater.com/v2. TLS 1.2+. - Auth:
Authorization: Basic base64(username:password), issued by the Atome Account Manager. - Endpoints:
POST /auth,POST /payments,GET /payments/{referenceId},POST /payments/{referenceId}/cancel,POST /payments/{referenceId}/refund. POST /paymentsis idempotent byreferenceId([0-9a-zA-Z-_]{1,40}).- Amounts are integers in the smallest unit; MYR is sen. MYR minimum is RM10.00 (
1000). Max is not documented. - Required create fields:
currency,amount,callbackUrl,paymentResultUrl,customerInfo(withmobileNumberin E.164),shippingAddress(countryCode,lines,postCode),items(itemId,name,quantity,price). - Unpaid payments auto-cancel after 12 hours (default and max).
expirationTimecan shorten it, minimum 300 seconds. - Statuses:
PROCESSING,PAID,FAILED,CANCELLED,REFUNDED. PAIDmeans the first instalment was received; it staysPAIDif the shopper misses later instalments.- Partial refund keeps
PAID; full refund becomesREFUNDED. Refunds are synchronous and idempotent byrefundId. - Callback body is only
{"referenceId": "..."}; Atome retries 5 times in 5 hours, then emails the merchant. - Callbacks are signed with HMAC-SHA256 over the raw body in the
X-Signatureheader with a shared secret; encoding and secret come from the Account Manager. - Malaysia callback source IPs:
47.254.230.20,47.254.238.153; test47.254.247.54. - In Malaysia, shoppers pay in the Atome app (developer portal FAQ lists MY as app only).
Critical Rules
- Keep the API password and callback secret server-side only.
- Never mark paid from
paymentResultUrl, the app return, or the callback body. Always settle fromGET /payments/{referenceId}. - Verify
X-Signatureagainst the raw request bytes before parsing JSON; use constant-time comparison. Do not guess hex vs Base64; confirm with Atome. - Store
referenceId, expected amount (sen), currency, environment, and local order id before redirecting. - Reuse the same
referenceIdwhen retrying a create after a timeout or 5xx; generate a new one only for a genuinely new attempt. - Return HTTP 200 to callbacks after durable recording; make processing idempotent across the 5 retries.
- If your checkout reserves stock or a booking for less than 12 hours, call cancel when the hold expires. If cancel returns
PAYMENT_NOT_CANCELLABLE, requery and fulfil or refund. - Never send decimal amounts;
12.34is invalid, send1234. - Branch on error
code, never onmessage. - Do not mix test credentials with the production base URL, or the reverse.
- Do not reverse an order because a shopper missed a later instalment.
Completion Criteria
An Atome integration is done only when:
POST /authsucceeds for the target environment withcountryCode: "MY".POST /paymentsruns server-side with Basic auth, a stored uniquereferenceId, and amount in sen at or above RM10.00.- The browser redirect only shows a "confirming" page and triggers a server requery; it never fulfils.
- The callback handler verifies
X-Signature(with the confirmed encoding), requeries, and settles idempotently. - Settlement checks status
PAID,referenceId,amount,currency, environment, and local order ownership. - Cancel is wired to local checkout expiry; refunds send a unique
refundIdand handleAMOUNT_NOT_VALIDandUNABLE_TO_REFUND. - Tests cover invalid signature, duplicate callback, callback for unknown
referenceId,FAILED,CANCELLED, partial refund (staysPAID), full refund (REFUNDED), and successfulPAID. - Deployment docs list
ATOME_BASE_URL,ATOME_API_USERNAME,ATOME_API_PASSWORD,ATOME_CALLBACK_SECRET, callback URL, result and cancel URLs, and test/production separation.
Loophole Review Loop
When asked whether an Atome strategy is "100% confident", run this loop until no factual gaps remain:
- List trust boundaries: browser, merchant app webview, backend payment creator, Atome API, Atome app, callback receiver, raw-body parser, database, fulfilment job, refund job, cancel job, and plugin settings.
- Check bypasses: fake
paymentResultUrlhit, forged callback, signature over parsed JSON, wrong secret or encoding, test/production mix, decimal amount, amount below RM10.00, reusedreferenceIdacross orders, stale requery, duplicate callback, payment completed after local hold expired without cancel, duplicate refund withoutrefundId, refund aboverefundableAmount, inactive Atome account blocking refund, and firewall blocking Atome IPs. - Add fixes: server-side create, local pending record first, raw-body HMAC check, mandatory requery, strict amount/currency/reference match, idempotent settlement keyed on
referenceId, cancel on expiry,refundIdidempotency, separate env config, IP allowlist, and/authconnectivity test. - Re-read
references/atome-api.mdsections relevant to any gap. - State remaining uncertainty if it depends on the callback secret encoding, maximum amount, MDR, settlement schedule, rate limits, test shopper accounts, or merchant contract terms.


