Touch 'n Go eWallet (TNG Digital)
Use this skill for direct Touch 'n Go eWallet merchant integrations with TNG Digital Sdn Bhd (TNGD).
Source
Read references/tng-ewallet-api.md before giving factual API guidance or writing integration code. It holds the Mini Program Open API: message structure, RSA256 signing and verification, endpoint catalogue, full field tables for pay, inquiryPayment, notifyPayment, refund, inquiryRefund, authorization and user info APIs, data dictionary, result codes, my.tradePay and my.getAuthCode, and the SFTP settlement file.
Read references/tng-ewallet-web-acquiring.md when the merchant wants a website checkout (no mini program). It documents the alipayplus.acquiring.* API as implemented by the TNGD WooCommerce plugin, the only public source for that flow, and lists its gaps.
Read references/account-setup.md for onboarding, credentials, sandbox vs production, env vars, settlement, and pricing links.
Read references/source-map.md to pick the right official page and to see coverage and what is partner-only.
Core Workflow
- Identify the surface: TNGD Mini Program (Open API +
my.tradePay), website or app checkout (web acquiring API), auto debit (agreement pay), or no-code (Payment Link, QR). No-code needs no API work. - Confirm onboarding is done. TNGD issues Client-Id, Partner ID (Merchant ID), product code, TNGD public key, API host, and SFTP access by email after sandbox UAT. There is no self-serve API key page.
- Generate an RSA key pair; give TNGD the public key; keep the private key server-side.
- Create a local pending payment with a unique
paymentRequestId(mini program) ormerchantTransId(web) before calling TNGD. - Call the create API server-side with a signed request. Verify the response signature with the TNGD public key.
- Mini program: pass
actionForm.redirectionUrltomy.tradePay({ paymentUrl }). Web: redirect the browser tocheckoutUrl. - Treat
my.tradePayresult codes and the browser return URL as UX only. - Handle the payment notification: verify the signature, check IDs and amount, acknowledge with a signed
resultStatus: "S"response at once, then process async and idempotently. - If no final notification arrives, query status (
inquiryPaymentororder.query). Never settle fromUorPROCESSING. - Reconcile daily against the T+1 SFTP settlement CSV.
Core Facts
- Open API URL pattern:
https://{host}/acl/api/{restful_path}, for example/v1/payments/pay. TNGD assigns the host; production endpoints are sent by email. - Every Open API call is
POSTwith JSON body and headersClient-Id,Request-Time(RFC3339, millisecond precision),Signature. - Signature header:
Signature: algorithm=RSA256, keyVersion=<n>, signature=<value>. RSA256 (SHA256withRSA) is default; ECC224 is also listed. - String to sign:
POST <HTTP_URI>\n<Client-Id>.<Request-Time>.<HTTP_BODY>. Responses are verified with<Client-Id>.<Response-Time>.<HTTP_BODY>and the TNGD public key. - Signing is mandatory for all requests and responses, including your acknowledgement of
notifyPayment. - Amounts are
{currency, value}withvaluea string integer in the smallest unit (sen). RM100.00 is"10000". - Cashier payment:
productCode51051000101000100001,paymentFactor.isCashierPayment: true. Agreement pay:productCode51051000101000100031,paymentFactor.isAgreementPay: true,paymentAuthCode= access token. resultStatus:Ssuccess,Ffailed,Uunknown,Aaccepted (followactionForm).inquiryPaymentpaymentStatus:SUCCESS,FAIL,PROCESSING,CANCELLED. Poll every 5 s for up to 1 minute.my.tradePayresult codes:9000success,8000processing,4000failed,6001user cancel,6002network error,6004unknown.- Mini program calls need
extendInfowithcustomerBelongsTo(for example"{\"customerBelongsTo\":\"TNG\"}"). - Idempotency keys:
partnerId+paymentRequestIdfor pay,partnerId+refundRequestIdfor refund. - Settlement is T+1 to the bank account given at onboarding; CSV
MERCHANT_SETTLEMENT_<PartnerID>_<yyyymmdd>is on SFTP at 9 AM T+1. - Web acquiring sandbox base (plugin default):
https://api-sd.tngdigital.com.my; paths/alipayplus/acquiring/order/create.htm,/order/query.htm,/order/refund.htm,/refund/query.htm.
Critical Rules
- Keep the merchant private key, client secret, and SFTP password server-side only.
- Sign the exact bytes you send. Serialize the body once and send that same string.
- Verify every TNGD response and notification signature with the TNGD public key for the same environment before trusting it.
- Never mark paid from
my.tradePay9000,paymentReturnUrl, or the webPAY_RETURNredirect alone. - A
Uresult is not a failure. Do not fail, refund offline, or re-create the payment; query or wait for the notification. notifyPaymentacknowledgement must returnSas soon as the message is received; do the business work asynchronously. Non-Striggers retries and duplicates.- Settle only when
paymentId/acquirementId, your request ID, amount, currencyMYR, and partner ID all match the stored pending payment. - Partial refunds are allowed; total refunded must not exceed the payment. Use a new
refundRequestIdper refund and keep it for inquiry. - Do not invent the production host, notify URL registration, or notification headers; ask TNGD where docs are silent.
- The WooCommerce plugin's notification handler does not verify signatures. Do not copy that gap.
Completion Criteria
A TNG eWallet integration is done only when:
- Create, query, and refund calls run server-side with correct
Client-Id,Request-Time, and RSA256Signature. - Response signatures are verified with the TNGD public key; failures are rejected and logged.
- The notification endpoint verifies the signature, acks with a signed
Sresponse, and processes idempotently. - Amount and currency are checked in sen against the stored order before marking paid.
- A status inquiry job covers missed notifications and never settles
U/PROCESSING. - Refund flow stores
refundRequestId, handlesUwithinquiryRefund, and blocks over-refund. - Tests cover bad signature, duplicate notification, wrong amount,
Uresult, expired order, and success. - Config documents
TNG_CLIENT_ID,TNG_PARTNER_ID,TNG_PRIVATE_KEY,TNG_PUBLIC_KEY, API host, notify URL, and sandbox/live separation.
Loophole Review Loop
When asked whether a TNG eWallet strategy is "100% confident", run this loop until no factual gaps remain:
- List trust boundaries: mini program or browser,
my.tradePayresult, return URL, merchant backend, TNGD Open API, notification receiver, key store, database, refund job, SFTP settlement. - Check bypasses: forged notification, unsigned or wrongly signed body, body re-serialized after signing, wrong environment key, sandbox/live mix, wrong amount unit (ringgit vs sen), reused
paymentRequestIdwith different data (REPEAT_REQ_INCONSISTENT),Utreated as failure, duplicate notification, notification acked only after processing, expired order paid late, over-refund, refundUrefunded offline. - Add fixes: server-side signing, exact-byte signing, response and notification verification, strict ID/amount/currency checks, durable event log, idempotent settlement, inquiry fallback, daily CSV reconciliation.
- Re-read the relevant sections of
references/tng-ewallet-api.mdorreferences/tng-ewallet-web-acquiring.md. - State remaining uncertainty: production host, notify URL setup for web acquiring, exact notification headers,
customerBelongsTovalues, key version, rate limits, and anything TNGD only shares in its partner Postman pack.


