MONA Pay sandbox: test transactions and checkout without moving money
The sandbox exercises the complete payment-confirmation flow without a real bank transfer. Call POST /api/v1/sandbox/transactions; MONA Pay records a fake incoming transaction and sends it through webhooks, Telegram, email and the hosted-checkout matcher just like a real one.
You can test before linking a bank. MONA Pay creates a dedicated sandbox VA for your account, with a number beginning
SBX…. If you already have a real VA, you can pass that number instead; the transaction still hasis_sandbox: trueand never moves bank funds.
POST /api/v1/sandbox/transactions
The request needs a Bearer token and X-Client-Secret:
curl -X POST https://api.monapay.vn/api/v1/sandbox/transactions \
-H "Authorization: Bearer $MONA_TOKEN" \
-H "X-Client-Secret: $MONA_SECRET" \
-H 'Content-Type: application/json' \
-d '{"amount":250000,"description":"Payment for ORDER10234"}'
When no account number is passed, MONA Pay creates or reuses your SBX… sandbox VA. To test against a linked real VA, add virtual_account_number:
{
"virtual_account_number": "LOCHOA000123456",
"amount": 250000,
"description": "Payment for ORDER10234",
"transaction_code": "SANDBOX-ORDER10234-01"
}
| Field | Type | Required | Notes |
|---|---|---|---|
virtual_account_number |
string, up to 50 characters | no | A real VA or an SBX… VA; omit it to let MONA Pay provide a sandbox VA |
account_number |
string, up to 50 characters | no | A linked real account; only one account reference is needed when you want to select a receiver |
amount |
integer | yes | Fake amount, greater than 0 and no more than 1,000,000,000 VND |
description |
string, 1–255 characters | yes | Fake transfer note; include an order code to test matching |
transaction_code |
string, 1–100 characters | no | Set one to test deduplication; otherwise MONA Pay generates a SANDBOX-… code |
Response 200:
{
"success": true,
"message": "Sandbox transaction accepted",
"data": {
"transaction_code": "SANDBOX-ORDER10234-01",
"virtual_account_number": "SBX000123456",
"account_number": "SBX000123456",
"amount": 250000,
"is_sandbox": true
}
}
The event goes through the same processing and notification channels as a real transfer. Use transaction_code as the deduplication key and is_sandbox to identify test data during reconciliation.
Running webhooks from localhost
Webhooks need a public HTTPS URL; MONA Pay cannot call localhost directly.
cloudflared tunnel --url http://localhost:4400
Paste the HTTPS URL issued by the tunnel into webhook_url; ngrok works too.
If no public URL is available yet, poll GET /api/v1/checkouts/{checkout_id} every few seconds while testing.
The IP addresses page applies when the system goes to production.
Test hosted checkout
Add "sandbox": true when creating a checkout. The test session uses an SBX… VA and returns a checkout_url, displayable QR data and sandbox: true; the hosted page shows a TEST SESSION — do not transfer real money banner.
curl -X POST https://api.monapay.vn/api/v1/checkouts \
-H "Authorization: Bearer $MONA_TOKEN" \
-H "X-Client-Secret: $MONA_SECRET" \
-H "Idempotency-Key: sandbox-ORDER10234" \
-H 'Content-Type: application/json' \
-d '{"amount":250000,"order_code":"ORDER10234","return_url":"https://shop.example/payment/return","sandbox":true}'
Take data.bank.account_number, or the VA number in the checkout response, and pass it as virtual_account_number to /sandbox/transactions. Once the sandbox total reaches amount, the checkout becomes paid and emits CHECKOUT_PAID. See Hosted checkout for the remaining fields.
Three tests to run before going live
- Exact amount: create a 250,000 VND checkout and send one 250,000 VND sandbox transaction. Wait for
CHECKOUT_PAID, verify the signature, confirmpaid, and ensure fulfilment runs once. - Underpayment: create a 250,000 VND checkout and send 200,000 VND. It must stay
pendingwithpartial_amount; do not fulfil it. - Repeat the same transaction code: send the exact same
transaction_codeagain. MONA Pay returns the same 200 response as the first request and does not add the amount again, sopaid_amountstays unchanged. To automate this check, callGET /api/v1/checkouts/{checkout_id}before and after, then comparepaid_amountandpartial_amount.
Sandbox limits
- No real money is sent or received; never scan a sandbox QR to make a transfer.
- Sandbox transactions do not count against the plan transaction quota.
SBX…VAs, QR codes and sandbox checkouts are test-only and must not be given to real payers.- The sandbox tests MONA Pay and your integration; it does not replace a final small real transfer after the bank is linked.
See Webhook payload format, Webhook security and Transaction reconciliation.