Payments
Testing
Build and test your whole integration in test mode. Test payments use Solana devnet and a test MUNY mint, and never mix with live records.
Test mode and live mode#
| Test mode | Live mode | |
|---|---|---|
| Network | devnet (or the simulated chain locally) | mainnet-beta |
| API keys | mk_test_… | mk_live_… |
| Token | Test MUNY mint | MUNY mint |
| Records | livemode: false | livemode: true |
Each environment runs against one network, and every payment, link and refund is stamped with it. Queries are always partitioned by network, so test data can't show up in live reports. Keys only work in their own mode. Checkout and the dashboard show a clear TEST MODE banner.
Simulate a customer payment#
Locally (SOLANA_DRIVER=mock) there are no real wallets. Use the simulate endpoint — or the "Simulate test payment" button on hosted checkout — to have a test customer send a real-shaped transfer through the simulated ledger. Verification, webhooks, notifications and the dashboard update exactly as they would in production.
const payment = await muny.paymentIntents.create({ amount: "500", reference: "TEST-1" });
// A test customer pays — a simulated on-chain transfer carrying the payment's reference
await muny.publicCheckout.simulate(payment.id);
// Verification runs exactly as in production
await sleep(3000);
(await muny.paymentIntents.retrieve(payment.id)).status; // "confirmed"curl -X POST http://localhost:4040/v1/checkout/$PAYMENT_ID/simulate \
-H "Content-Type: application/json" -d '{}'POST /v1/checkout/:id/simulate is refused on mainnet and with the RPC driver.Exercise failure paths
await muny.publicCheckout.simulate(id, { amount: "499" }); // underpaid → failed
await muny.publicCheckout.simulate(id, { amount: "501" }); // overpaid → failed
await muny.publicCheckout.simulate(id, { wrong_recipient: true }); // stays open, failure_code wrong_recipient
await muny.publicCheckout.simulate(id, { wrong_mint: true }); // stays open, failure_code wrong_mint
await muny.publicCheckout.simulate(id, { omit_reference: true }); // never attributed to this paymentEnd-to-end smoke test#
The repository ships a script that runs the full loop against a running API: create, check out, pay, verify, and the treasury balance increases.
pnpm --filter @muny/api smoke:payments
created cb6e9980-… requires_payment 500 MUNY
checkout http://localhost:3030/pay/cb6e9980-…
customer paid (simulated) sig 5G6Y91XWA6aw6erE…
status: submitted
status: confirmed
verified confirmed payer 6qZ4fgzV usd snapshot 60
treasury 2450320 → 2450820Real wallets on devnet#
To test with Phantom, Solflare or Backpack, switch the API to the RPC driver on devnet, fund a devnet wallet with SOL for fees and some test MUNY, and pay from hosted checkout. See the Solana setup guide in the repository (docs/solana.md).
# .env — real devnet wallets
SOLANA_NETWORK=devnet
SOLANA_DRIVER=rpc
SOLANA_RPC_URL=https://api.devnet.solana.com
MUNY_MINT_ADDRESS_DEVNET=<your test MUNY mint>
FEE_PAYER_PRIVATE_KEY=<devnet fee payer, base58>- Switch your wallet to devnet before paying, or verification fails with wrong_network or not_found.
- Use a webhook tunnel (e.g. cloudflared) to receive events locally.
- Read Verify payments to understand each failure code you might see.