Arrange sandbox access with partners@zennopay.com. You need a sandbox
key ID and signing secret, a registered app identifier, an enabled Vietnam
corridor, and a configured wallet and prefund balance. This guide uses
vn_vietqr; see Corridors for availability.Environments. Point everything at the sandbox host
(
https://api.sandbox.zennopay.com) while integrating; swap to production
(https://api.zennopay.com) at go-live. Sandbox HMAC keys are rejected by
production and vice versa. See Environments.The .in hosts remain supported alternates for existing integrations. New
integrations should use the canonical .com hosts above.1. Get your credentials
You will receive exactly two things:- A key ID (e.g.
acme_sandbox_2026q2). - A 32-byte signing secret for HMAC-signed API calls.
iss/kid, no JWKS endpoint to publish.
Zennopay mints the client session token for you. Store both values as
environment variables on your backend:
2. Create a payment intent (and get a session token back)
Construct the canonical request string, HMAC-SHA256 it with your signing secret, and send it with the four required headers plus anIdempotency-Key.
The body carries your opaque user ID, the authorized USD amount in cents,
the corridor, and your per-payment KYC + sanctions attestations.
201 Created. The response carries the session_token
(and its expiry) alongside the intent — there is no second call to mint one:
intent_id and session_token to your app. See
Build your session endpoint for a complete
Node.js route.
3. Present the PaymentSheet
Pass theintent_id and session_token to the Zennopay
PaymentSheet on the user’s device. The SDK renders the
whole pay experience natively, in-process — scan → amount + FX quote →
slide-to-pay → result — and delivers one typed PaymentResult to your
callback. No browser, no redirect, no URL scheme.
session_token. Pass that value unchanged to sessionJWT
on iOS or sessionJwt on Android, Flutter, and React Native. These SDK
parameter names do not require you to mint or sign a token.
See the platform guides for the full lifecycle (camera permission, session
refresh, theming, testing):
PaymentSheet overview
Session endpoint
iOS
Android
Flutter
React Native
Test your integration
4. Receive the webhook
After the payment settles (or fails), Zennopay POSTs a signed webhook to your configured endpoint. See Webhooks for the payload shape and signature verification steps.Reference partner starter
The referencezennopay-partner-starter
(v0.2.0+) ships this exact flow — a runnable Express backend that’s
HMAC-only, with no key pair. It reads the same three environment variables:
Upgrading from a pre-0.2.0 starter? The HMAC-only flow removes the RSA
session-signing key entirely. Delete
session_signing_key.pem, drop the
JWT_ISS / kid / JWKS wiring, and stop minting session JWTs — read
session_token straight off the create response instead.Common errors
All 401s use a generic message in the response body. Use the
request_id
field to correlate with internal logs when contacting support.