Skip to main content

Sandbox Overview

Sandbox mode lets you exercise the entire integration — create, checkout, callback, webhook, verify — without touching a blockchain. A sandbox payment is a real payment row in every respect except that no deposit address is allocated and no chain is watched; instead, the checkout page shows simulation buttons that drive the payment into the outcome you choose.

Creating a sandbox payment

Add "isSandbox": true to an ordinary create call:

curl -X POST https://api-staging.finomesh.com/api/v1/payments \
-H "X-Api-Key: $FINOMESH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"currencyCode": "USD",
"amount": "49.99",
"callbackUrl": "https://shop.example.com/payment/result",
"webhookUrl": "https://shop.example.com/api/finomesh-webhook",
"isSandbox": true
}'

Your real API key is still required — sandbox changes the post-create lifecycle, not authentication. The create response is the usual { paymentUid, paymentUrl }; the sandbox nature shows up on the hosted checkout, where each offer carries a sentinel deposit address (SANDBOX-DEPOSIT-ADDRESS-DO-NOT-USE) that is invalid on every chain by construction.

Driving the outcome

Open the checkout (https://checkout-staging.finomesh.com/p/{paymentId}) — instead of an address and QR code you get four buttons:

ButtonResulting status
Simulate successSUCCESS
Simulate acceptableACCEPTABLE
Simulate mismatchMISMATCH
Simulate expiryEXPIRED

Each button drives the payment into that terminal state, and everything downstream fires exactly like production: the result screen, the callback redirect (for the three paid outcomes), the signed webhook (all four outcomes), and the verify response.

You can also drive the outcome programmatically — the buttons call a public endpoint, so an end-to-end test suite can too:

curl -X POST https://api-staging.finomesh.com/api/v1/public/payments/{paymentId}/sandbox/simulate \
-H "Content-Type: application/json" \
-d '{ "outcome": "success", "offerId": "{offerId}" }'

outcomesuccess | acceptable | mismatch | expire. offerId (one of the payment's offers, as served to the checkout by GET /api/v1/public/payments/{paymentId}) lets the simulator synthesize a realistic received amount for the paid outcomes; it is optional and unused for expire. Simulating a non-sandbox payment is rejected — a live payment cannot be driven this way.

What to test before going live

  • Callback handler: reads paymentId, ignores status for fulfilment, verifies server-side.
  • Webhook handler: signature verification against the raw body, fast 200, dedupe by paymentId.
  • All four outcomes: SUCCESS and ACCEPTABLE fulfil, MISMATCH holds for review, EXPIRED cancels.
  • Webhook retry: respond 500 once and confirm the redelivery ~15s later succeeds.
  • Expiry without webhook delivery: confirm your reconciliation (poll-verify or manual) catches it.
Staging first

Run sandbox tests against staging (api-staging.finomesh.com). Staging runs the same code as production but settles on test networks, so even non-sandbox rehearsals are safe there.