Test mode
What mptest keys can and cannot rehearse.
A new project starts in test mode: its keys are mptest…, every event carries
livemode: false, and no chain is behind it. Nothing you send to a test
address on a real network or a testnet is ever seen, and nothing a test payout
"sends" ever leaves. Instead you drive the chain yourself, and everything after
it — the movement, the confirmation threshold, the ledger, the webhooks, the
balances, the payout state machine — is the production code path.
10.1 Simulate a deposit
curl -X POST https://api.molnpay.com/v1/test/deposits \
-H "Authorization: Bearer $WALLET_KEY" -H "Content-Type: application/json" \
-d '{"customer_id":"9ab3…","chain":"bsc","asset":"BNB","decimals":18,
"amount":"20000000000000000"}'The customer must already hold an address on that chain (§4.2). You get the
deposit back as GET /v1/deposits/{id} would return it, and your endpoint
receives deposit.detected then deposit.confirmed — two events, in that
order, exactly as a real transfer produces them. The balance is credited and a
payout can be requested against it. decimals must be echoed, as on
POST /v1/payouts, so the §3 mismatch is rehearsed here rather than in
production.
Pass "confirm": false to stop at detected, then drive the pending deposit
with /confirm or /fail. /orphan acts on an already confirmed deposit:
| Call | Result |
|---|---|
POST /v1/test/deposits/{id}/confirm | credited; deposit.confirmed |
POST /v1/test/deposits/{id}/fail | never money; deposit.failed |
POST /v1/test/deposits/{id}/orphan | a confirmed deposit is reorged out and its credit reversed; deposit.orphaned |
Rehearse orphan. It is the one event integrators skip, and the one that
leaves a ledger permanently wrong when it arrives unhandled in production.
10.2 Payouts complete on their own
POST /v1/payouts on a test key goes through every gate a live payout does —
kind, scope, decimals, address validity, allowlist, the reserve — and then,
within a few seconds, is settled as if broadcast and confirmed: you receive
payout.submitted and payout.confirmed with a synthetic tx_hash, and the
customer's locked balance clears. Above your auto-approve threshold it still
waits in pending_approval for a human, so rehearse your approval handling too.
Of the three signing levels (§1), a test project is gated by the key's flag
only: the service and project switches exist to stop real money, and a test
payout never reaches the signer. Mint the payout key with signing_enabled: true and GET /v1/ping answers signing_enabled: true on it.
10.3 What differs from live
- Native coins only —
GET /v1/chainson a test key lists each chain's native coin (BNB, ETH, AVAX…) and no stablecoins, because a token's testnet deployment lives at a different contract address from mainnet and we will not publish an address that points at nothing. The §4.5 example ("asset":"USDT") therefore cannot succeed on a test key, anddecimals_differ_from_defaultcan never betrue— the BSC trap is mainnet-only. Rehearse it once against live with signing disabled. - Chain metadata is mainnet's —
chain_id,explorer_urland theexplorer_urlon every deposit and payout point at mainnet explorers, where a synthetic hash does not exist. Do not build links from them while testing. - The
/v1/testroutes refuse a live key with403 test_mode_only. There is no override: a route that can mint a credited deposit must not exist for a project whose balances are real money.
Everything else — customers, address derivation, balances, deposits, webhooks, idempotency, pagination, the payout gates — behaves identically in both modes. Build and verify the whole flow in test mode, then re-verify the token-specific paths against live before going live.