Skip to main content
Everything the PaymentSheet does in production it also does in the sandbox: scan validation, FX quoting, slide-to-pay, status polling, webhooks, and limit enforcement. This page collects the loops worth running before release.

Point the SDK at the sandbox

The environment is a config value, not a code path — one integration serves testing and production: In the sandbox the sheet shows a SANDBOX pill in its header, so a build pointed at the wrong environment is visible at a glance. Your backend must use its sandbox credentials against the sandbox REST host — sandbox HMAC keys are rejected by production and vice versa (see Environments).

Scanning without a camera

Simulators and emulators have no usable camera. The sheet detects this and swaps the viewport for the paste-QR fallback automatically — paste any VietQR/EMVCo payload string and the flow proceeds identically, because the backend does the authoritative parse (CRC, tags, merchant extraction) either way. The same fallback appears on real devices when the user denies camera access, so the flow is always completable.

The loops to run

1

Sandbox end-to-end

Run the sandbox config with a session from your sandbox backend and a real VietQR payload (paste-QR works fine). The payout runs on the sandbox rail and the intent reaches captured — assert your completed handling debits your ledger and renders the receipt, and that your webhook endpoint received payment_intent.captured.
2

Fail-fast token checks

Pass a blank or mismatched session token and assert an immediate failed with invalid_jwt / intent_mismatch — the SDK validates the token’s structure, expiry, and intent binding before showing any UI.
3

Session expiry mid-flow

Let a session token lapse inside the sheet (its session_expires_at passes); your refreshSession hook should re-mint for the same intent via POST /v1/payment_intents/:id/session and the flow should continue without the user noticing.
4

A limit-blocked payment

Per-user corridor limits are enforced against the same windows in sandbox. Drive a payment over the per-transaction limit and assert the sheet renders the limit copy and your app receives failed with limit_exceeded.
5

Pending and reconciliation

If status polling times out, the result is pending (Flutter and React Native) or failed with timed_out (iOS and Android) — the payment may still settle. Verify you reconcile via the webhook or GET /v1/payment_intents/:id rather than assuming a terminal failure.

Before release: a physical device

Camera scanning, the torch and gallery fallbacks, and the runtime permission prompt (Android) can only be exercised for real on hardware — that’s the pre-release checklist on every platform guide.

Next steps

Environments

Base URLs and how keys map to sandbox vs production.

Webhooks

Verify signatures and reconcile terminal states.

Per-user limits

The corridor limits enforced in both environments.

Authentication

The HMAC signing spec and the session-token contract.