bayarlah Payment Gateway
Use this skill for bayarlah (Beez Fintech Sdn Bhd) Upay Web API integration work.
Source
Read references/bayarlah-api.md before giving factual API guidance or writing integration code. It contains the endpoint catalogue, domains, activation and check-in, the request signature, every request and response field table, callback and event-push verification, polling schedules, refunds, cancellation, status values, and error codes.
Read references/account-setup.md when the user asks how to get API parameters, what to send to bayarlah, where activation codes come from, env var names, WeChat refund authorization, settlement timing, or pricing.
Read references/source-map.md when choosing which bayarlah docs page applies, or to check coverage and what the docs do not publish.
Core Workflow
- Get
vendor_sn,vendor_keyandapp_idfrom bayarlah technical support (there is no self-serve sandbox in the docs). - Generate a store activation code in the merchant platform
https://s.bayarlah.net, then call/terminal/activateonce per terminal, signed withvendor_sn+vendor_key. - Persist the returned
terminal_snandterminal_keyserver-side; call/terminal/checkinon the first payment call of each calendar day and save the new key. - Create a local pending order with a unique
client_snbefore calling bayarlah. - For website or kiosk QR: call
/upay/v2/precreatewithpayway("1000"DuitNow,"3"WeChat), amount in cents, andnotify_url; renderqr_codeorqr_code_image_url. - For WeChat in-browser: 302-redirect to
https://qr.bayarlah.net/gatewaywith an uppercase MD5 querysign; treatreturn_urlas display only. - Poll
/upay/v2/queryafter a successful pre-order and also acceptnotify_urlcallbacks; stop when either gives a finalorder_status. - Verify callbacks:
notify_urluses SHA256WithRSA with the bayarlah public key; Event Order Notification usesMD5(body + app_secret). - Settle idempotently only when
client_sn,sn,total_amount, terminal andorder_status = PAIDmatch. - Refund with
/upay/v2/refundand a stablerefund_request_no; use/upay/v2/cancelfor unknown-state WeChat payments.
Core Facts
- API domain:
https://api.bayarlah.net(only one domain is documented; no sandbox domain is published). - All API requests are JSON, UTF-8,
Content-Type: application/json; all response values are strings. - Request signature:
sign = MD5(raw_body_bytes + key), sent asAuthorization: <sn> <sign>. - Activation signs with
vendor_sn/vendor_key; all other calls sign withterminal_sn/terminal_key. - Amounts are in cents (sen), numeric strings up to 10 digits (
"100"= RM1.00). - Responses wrap results: top-level
result_code("200"or"400"), thenbiz_response.result_codeandbiz_response.data. - Pre-order success:
biz_response.result_code = "PRECREATE_SUCCESS"ordata.order_status = "CREATED". - Final order statuses:
PAID,PAY_CANCELED,REFUNDED,PARTIAL_REFUNDED,CANCELED. - Pre-order payment window is about 4 minutes; documented polling schedule ends with a final poll at 6 minutes.
notify_urlcallback retries at 1s, 5s, 30s, 600s; reply with the stringsuccess.- Activation codes are valid 7 days and have limited uses; never hard-code them.
- Refunds allowed within three months of payment; cancel/revoke only on the same day, once per order.
client_sncannot be reused after a failed payment; create a new one.
Critical Rules
- Keep
vendor_key,terminal_key,app_secretand the RSA public key config server-side only. - Sign the exact bytes you send; do not re-serialize JSON after signing.
- Store the newest
terminal_keyatomically after check-in; the old key stops working. - Do not mark paid from
return_url, a displayed QR, or a frontend result; use query or a verified callback. - Verify callback signatures over the raw body before parsing JSON, using constant-time comparison.
- Use the correct verification method per channel: RSA for
notify_url, MD5 +app_idcheck for Event Order Notification. - Make settlement idempotent across polling,
notify_urlretries and event-push retries. - Keep
refund_request_nounchanged when retrying the same refund; change it for a new refund. - When a
pay(CPM) call does not return a clear result, query, then callcancelif still unknown. - Use HTTPS for any URL bayarlah pushes to.
- Do not assume unpublished values (full payway list, sub_payway list, hex case of the API MD5, RSA signature encoding); confirm with bayarlah support.
Completion Criteria
A bayarlah integration is done only when:
- Activation runs once, and
terminal_sn/terminal_keypersist in durable server storage. - Daily check-in runs before the first payment call of the day and saves the new key.
- Every API call is signed as
MD5(body + key)over the exact sent bytes with the rightsn. - Pre-order stores
sn,client_sn, amount and expectedpaywaybefore showing the QR. - Polling follows the documented schedule and stops on a final status or a verified callback.
- Callback handlers verify signatures over the raw body and reply
success. - Settlement checks
client_sn,sn,total_amount,terminal_snandorder_status, and is idempotent. - Tests cover bad signature, duplicate callback, wrong amount, expired pre-order, expired terminal key, refund retry, and a successful
PAIDorder. - Deployment docs list the env vars in
references/account-setup.md.
Loophole Review Loop
When asked whether a bayarlah strategy is "100% confident", run this loop until no factual gaps remain:
- List trust boundaries: browser, QR display,
return_url, backend signer, terminal key store, bayarlah API,notify_urlreceiver, event-push receiver, raw-body parser, database, polling job, refund job, merchant platform. - Check bypasses: fake
return_urlsuccess, forged callback, signature over parsed JSON, wrong key (vendor vs terminal), stale terminal key after check-in, lost check-in response, reusedclient_sn, amount in ringgit instead of cents, wrongpayway, polling stopped too early, duplicate callbacks, partial refund double-sent, cancel after the same day, DuitNow refund assumptions. - Add fixes: server-side signing over sent bytes, atomic key rotation with retry using the old key, unique
client_sn, cents conversion, raw-body verification,app_idcheck, durable event log, idempotency keys, stablerefund_request_no. - Re-read
references/bayarlah-api.mdsections relevant to any gap. - State remaining uncertainty where the docs are silent: sandbox, payway appendix, MD5 hex case, RSA encoding, DuitNow refunds, rate limits, IP allowlists, settlement and pricing.


