Skip to main content
This walks through the minimum a partner backend needs to do to create a payment intent and hand it off to the Zennopay SDK on mobile. There is one credential to manage — your HMAC signing secret — and no key pair to generate.
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.
That’s it — no RSA key pair, no iss/kid, no JWKS endpoint to publish. Zennopay mints the client session token for you. Store both values as environment variables on your backend:
See Authentication for the full key-management model.

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 an Idempotency-Key. The body carries your opaque user ID, the authorized USD amount in cents, the corridor, and your per-payment KYC + sanctions attestations.
Generate a fresh timestamp, nonce, and signature for every request; the cURL values above only illustrate the request format. Persist the idempotency key with your checkout operation and reuse it on retries. You should receive a 201 Created. The response carries the session_token (and its expiry) alongside the intent — there is no second call to mint one:
Return intent_id and session_token to your app. See Build your session endpoint for a complete Node.js route.

3. Present the PaymentSheet

Pass the intent_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.
The API returns 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 reference zennopay-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.