# Test mode (/guides/test-mode)



<!-- generated from docs/wallet-api/QUICKSTART.md §10 — edit THAT file -->

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 [#101-simulate-a-deposit]

```bash
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 [#102-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 [#103-what-differs-from-live]

* **Native coins only** — `GET /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's** — `chain_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.
