MolnPayDocs
Guides

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:

CallResult
POST /v1/test/deposits/{id}/confirmcredited; deposit.confirmed
POST /v1/test/deposits/{id}/failnever money; deposit.failed
POST /v1/test/deposits/{id}/orphana 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 onlyGET /v1/chains on 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, and decimals_differ_from_default can never be true — the BSC trap is mainnet-only. Rehearse it once against live with signing disabled.
  • Chain metadata is mainnet'schain_id, explorer_url and the explorer_url on 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/test routes refuse a live key with 403 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.

On this page