- Stack Builder (recommended)
- Manual integration
10 minutes, zero copy-paste code. Fill in your keys inside SandPay, download a pre-filled starter zip, and run
./setup.sh. Works on macOS, Linux, and Windows.Create a SandPay account
Sign up at sandpay.dev/sign-up. Onboarding takes ~30 seconds: email, password, first operator.
Generate an API key
Go to
/settings → API keys → Create new key. The sp_sk_test_… key is shown only once — keep the tab open to carry it into step 3 without having to retype it.Choose your stack
Click “Open Integration guide with this key →” in the modal, or navigate manually to
/integration (in the sidebar, under Dev → Integration). The Stack Builder page lists 5 backends and 4 mobile clients. Select:- One backend from: Supabase Edge Functions, Next.js API Routes, Express, Hono, FastAPI
- Zero or more clients: Next.js web, Flutter, React Native, iOS native, Android native
Download and run
Click “Download zana-app.zip”. Unzip, then:The script confirms your values (or prompts for them if you didn’t enable secret inclusion), then writes the
.env files in the right places. Follow the displayed commands to deploy Supabase, start the backend, start the frontend, and start the mobile app.First payment
From your web or mobile app, trigger a test payment:
- Number:
+250788123456(default Rwanda MTN) - Amount:
25000 - Reference:
ORDER-TEST-001
status: SUCCESS, then the payment.completed webhook asynchronously a few seconds later with the synthesised operator raw field — it faithfully reproduces the native MTN/Orange/Moov/Airtel shape (with _simulated: true at the top level).Testing locally with Supabase (Edge Functions)
Testing locally with Supabase (Edge Functions)
Two modes, depending on whether you want to use Docker. The full details
(and the network direction table) are in the starter’s
README.md.- Option A — Full local (Docker required).
supabase startruns everything on your machine, thensupabase functions serve --env-file supabase/.env. ⚠️ Gotcha: an Edge Function runs inside a Docker container, solocalhostrefers to the container, not your machine. To reach SandPay (on the host atlocalhost:3800), the function must usehttp://host.docker.internal:3800. Clients point at local Supabase (http://127.0.0.1:54321); the SandPay webhook points athttp://localhost:54321/functions/v1/sandpay-webhook. (On an Android emulator, the host is reached via10.0.2.2, not127.0.0.1.) - Option B — Mixed (cloud functions + local clients, no Docker). Edge
Functions stay on Supabase cloud; your clients run locally.
The cloud cannot reach your
localhost:3800, so expose SandPay via ngrok with a reserved static domain (free, 1 domain):ngrok http 3800 --domain=your-name.ngrok-free.app, thensupabase secrets set SANDPAY_BASE_URL=https://your-name.ngrok-free.app. Advantage: the URL stays the same across restarts → set the secret once. (cloudflaredquick-tunnels andngrok httpwithout--domainrotate the URL on every restart — avoid them.) The webhook then points at the cloud function.
Next steps
Integration Guide
The full end-to-end contract — the canonical reference.
Configure webhooks
Receive async transaction statuses with HMAC signature verification.
All scenarios
10 simulation scenarios for testing every operator error case.
Operator guides
MTN, Orange, Moov, Airtel specifics.
Node.js SDK
@sandpay/node — full types, webhook helpers.
FAQ & integration tips
Webhook not firing locally, commission/
net_amount, reconciliation — the questions you actually run into.