Ezypay Payment Gateway
Use this skill for Ezypay subscription billing and card collection work.
Source
Read references/ezypay-api.md first. It holds environments, base URLs, auth, the Malaysia scope, the end-to-end server flow, the full endpoint catalogue, statuses, error codes, amount rules, and the signature summary.
Read references/ezypay-billing.md for request and response field tables: customers, payment methods, invoices, refunds, plans, subscriptions, future invoices, checkout, transactions, credit notes, settlements, reports, and partner billing.
Read references/ezypay-webhooks.md for every event type, payload shapes, HMAC-SHA1 verification with the official test vector, retries, and duplicates.
Read references/ezypay-payment-methods.md for the Payment Capture Page (iframe, postMessage, styling, mobile WebViews), Apple Pay, Google Pay, payment method payloads, sandbox test data, and the Malaysia card failure codes.
Read references/account-setup.md for onboarding, credentials, sandbox access, go-live checklists, fees model, and settlement timing.
Read references/source-map.md to find the official page for any fact and to see doc coverage.
Core Workflow
- Confirm the model: on-demand invoices (you schedule) or Ezypay subscriptions (Ezypay schedules). Checkout is a hosted one-off card page.
- Confirm credentials: partner
client_idandclient_secret, plus per-merchant username, password, and merchant ID. Each branch has its own merchant account. - Get a bearer token from the identity host with
grant_type=passwordand reuse it until it expires (60 minutes). - Create the customer with
POST /v2/billing/customersand storeid. - Collect the card with the Payment Capture Page iframe; read the token from
postMessageand checklink_status. - Create a local pending payment, then
POST /v2/billing/invoiceswithexternalInvoiceIdas your idempotency key, or create a plan and subscription. - Treat Checkout
successUrlparameters and iframe messages as UX only. - Verify the
X-Ezypay-Signaturewebhook header (HMAC-SHA1 hex of the raw body with your client key), dedupe onrequestId, then requeryGET /v2/billing/invoices/{invoiceId}. - Settle on
invoice_paidwith matching invoice id,externalInvoiceId, merchant ID, currencyMYR, and your item amount. - Handle
invoice_past_due: showfailedPaymentReason, replace invalid payment methods, retry, write off, or record external payment.
Core Facts
- Sandbox hosts:
identity-sandbox.ezypay.com,vault-sandbox.ezypay.com,api-sandbox.ezypay.com,hosted-global-sandbox.ezypay.com. - Production hosts:
identity.ezypay.com,vault.ezypay.com,api-global.ezypay.com,hosted-global.ezypay.com. - Billing base:
https://api-sandbox.ezypay.com/v2(paths start/billing/...). Webhooks base:.../v2/webhooks. - Token:
POST https://identity-sandbox.ezypay.com/token, form-encoded, scopeintegrator billing_profile create_payment_method offline_access. - Every billing call sends
Authorization: Bearer <token>and themerchant: <MERCHANT_ID>header. - Malaysia: Visa and Mastercard; Apple Pay and Google Pay are beta. No bank direct debit, no PayTo, no wallet, no FPX or DuitNow.
- 3D Secure is not supported and CVV is not required. Charge in
MYRonly (no cross-border); decimals allowed. - Amounts are decimal major units in
{ "currency": "MYR", "value": 49.90 }, not sen. - Ezypay fees can be added to the invoice as extra items (
transaction_fee,load_fee,failed_payment_fee). - Webhook signature:
X-Ezypay-Signature= lowercase hex HMAC-SHA1(clientKey, raw body). Retries hourly, up to 24 times. - Refund:
PUT /v2/billing/invoices/{invoiceId}/refund, empty body for full; trackcredit_note_paidorcredit_note_failed. - Settlement holding period is T+3 business days for Malaysia; frequency weekly or monthly.
Critical Rules
- Keep
client_secret, merchant password, client key, and bearer tokens server-side. The PCP iframe URL exposes a token, so build it per session. - Never mark paid from the iframe message, Checkout
successUrlquery string, or invoice create response (PROCESSINGis not paid). - Verify the signature over the exact raw bytes before parsing JSON; use constant-time comparison.
- Register a
clientKeyon every webhook; without one there is nothing to verify. - Send a unique
externalInvoiceIdper invoice so retries of your own request do not double charge. - Compare your charged item, not the invoice total, when customer-paid fees are on.
- Store invoice
id,documentNumber,customerId,paymentMethodToken, and merchant ID before settling. - Use one merchant account per branch; never share one across branches.
- Do not promise FPX, DuitNow, e-wallets, or 3DS for Malaysia; they are not offered.
- Confirm Apple Pay and Google Pay enablement with Ezypay before launch; both are beta.
Completion Criteria
An Ezypay integration is done only when:
- Tokens are fetched server-side, cached, and refreshed or re-created after 60 minutes, within the token rate limit.
- Customers are created and synced on profile changes; PCP returns a token linked to the customer.
- Invoices or subscriptions are created server-side with the
merchantheader and a uniqueexternalInvoiceId. - A webhook per merchant is registered with a
clientKeyand the minimum events fromreferences/ezypay-webhooks.md. - The webhook handler verifies
X-Ezypay-Signatureover the raw body, dedupesrequestId, requeries the invoice, and settles once. - Failed payments show reason and fees, support retry, write-off, record external payment, and Pay Now.
- Refunds and chargebacks update local state from credit note events.
- Tests cover bad signature, replayed
requestId, wrong merchant, wrong currency or amount,PAST_DUE, refund failure, and a paid sandbox invoice using the test cards. - Deploy docs list
EZYPAY_*variables, webhook URL, client key, and sandbox versus production hosts.
Loophole Review Loop
When asked whether an Ezypay plan is "100% confident", repeat until no factual gaps remain:
- List trust boundaries: browser iframe, postMessage listener, Checkout redirect, backend token cache, identity host, billing API, vault, webhook receiver, raw-body parser, database, fulfilment, refund job, retry job.
- Check bypasses: forged postMessage from another origin, fake
successUrlquery, unsigned webhook, signature over re-serialised JSON, wrong client key, sandbox and production mix, wrong merchant header, invoice reused across customers, duplicaterequestId,PROCESSINGtreated as paid, fee items breaking amount checks, expired token mid-batch, invalid payment method auto-rebill stop, refund with low wallet balance, chargeback reversal after paid. - Add fixes: origin check on
messageevents, server requery of the invoice, raw-body HMAC with constant-time compare, per-merchant webhook and key, idempotent settlement keyed on invoice id,externalInvoiceIduniqueness, event log, separate env config. - Re-read the relevant reference file for any gap.
- State what still depends on Ezypay account setup: enabled methods, fee configuration, retry settings, checkout attempt limit, settlement schedule, Apple Pay domain registration, and Google Pay approval.


