Quickstart
Take your first crypto payment in about five minutes. You'll get your API key, create a checkout, send a test customer to pay, and see the payment land on your server.
Every ZyraPay request goes to https://api.zyrapay.net/v1 and authenticates with your project's x-api-key. You create a project and get that key from the dashboard — the four steps below take you all the way from key to a verified crypto payment.
Before you begin
You'll need three things:
- A ZyraPay account with a payment project — create one in the dashboard; that's where your API key comes from.
- A server endpoint that can receive an HTTPS
POSTfor the webhook — for local development, a tunnel works (see Test locally below). - Node.js or
curlto make the requests below.
Take a payment in 4 steps
- 1
Get your API key
Sign in to the ZyraPay dashboard and open (or create) a payment project. Each project has its own API key — copy it and store it on your server as
ZYRA_API_KEY. You'll send it as thex-api-keyheader on every request below.Your server (.env)ZYRA_API_KEY=4f3c…e9 # from your project in the dashboard ZYRA_WEBHOOK_SECRET=… # from the project's webhook settingsTreat your API key like a password
The key is shown in full only once when the project is created (ZyraPay stores just a SHA-256 hash). Keep it server-side — never ship it in browser or mobile code. If it leaks, rotate it from the dashboard. See Authentication. - 2
Create a checkout
Checkouts are created with your API key on the
/v1/checkoutroute. Each item needs aname, anamount, and aquantity; the total is the sum ofamount × quantity. All checkouts are charged in Pi.POST/v1/checkoutx-api-keycurl -X POST https://api.zyrapay.net/v1/checkout \ -H "x-api-key: $ZYRA_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-ORD-1001" \ -d '{ "items": [ { "name": "T-Shirt", "amount": 5, "quantity": 2 }, { "name": "Sticker", "amount": 1, "quantity": 3 } ], "expiresInMinutes": 30, "successUrl": "https://my-store.example.com/success", "cancelUrl": "https://my-store.example.com/cancel", "webhookUrl": "https://my-store.example.com/zyra/webhook", "metadata": { "orderId": "ORD-1001" } }'The response gives you the hosted checkout URL:
Response (201){ "success": true, "checkoutId": "chk_9f2a1b3c4d5e6f70", "checkoutUrl": "https://checkout.zyrapay.net/chk_9f2a1b3c4d5e6f70", "expiresAt": "2026-06-14T12:30:00.000Z", "totalAmount": 13, "items": [ /* … */ ] }Pricing details
Checkouts expire after 30 minutes by default. If you send an itemcurrencyofUSD,USDT, orDOLLAR, the total is converted to Pi at a live rate at creation time — but the checkout is always stored and charged in Pi. - 3
Redirect the customer to the checkout
Send the buyer to
checkoutUrl. ZyraPay mints a dedicated wallet for the checkout, and the hosted page collects the payment and updates live over a WebSocket as funds land.JavaScript// Browser redirect after creating the checkout window.location.href = checkout.checkoutUrl; // …or in Next.js / Express, send a 303 to the same URL res.redirect(303, checkout.checkoutUrl);On completion (or cancellation) the hosted page returns the customer to the
successUrl/cancelUrlyou provided. - 4
Receive and verify the webhook
When the payment is observed on-chain, ZyraPay
POSTs an HMAC-signed webhook to the checkout'swebhookUrl(falling back to the project's). Every payment webhook carries these headers:HeadersContent-Type: application/json User-Agent: Zyra-BN/1.0 X-Project-ID: <projectId> X-Event-Type: checkout.paid X-Timestamp: <epoch-millis> X-Zyra-Signature: sha256=<hex>A
checkout.paidbody looks like this:Body{ "type": "checkout.paid", "data": { "projectId": "665f…", "amount": 13, "checkoutId": "chk_9f2a1b3c4d5e6f70", "from": "G…payer", "transactionId": "<pi-payment-id>", "metadata": { "orderId": "ORD-1001" } }, "timestamp": 1750000000000, "projectId": "665f…", "signature": "<hex>" }Always verify the signature before trusting the payload. It is an HMAC-SHA256 of the JSON body with the
signaturefield excluded, keyed on yourWEBHOOK_SECRET:server.js — Expressconst crypto = require("crypto"); const express = require("express"); const app = express(); app.use(express.json()); function verifyZyraWebhook(req, secret) { const header = req.get("X-Zyra-Signature") || ""; // "sha256=<hex>" const received = header.replace("sha256=", ""); // The server signs the payload object WITHOUT the `signature` field. const { signature, ...payload } = req.body; const expected = crypto .createHmac("sha256", secret) .update(JSON.stringify(payload)) .digest("hex"); return crypto.timingSafeEqual( Buffer.from(expected, "hex"), Buffer.from(received, "hex") ); } app.post("/zyra/webhook", (req, res) => { if (!verifyZyraWebhook(req, process.env.ZYRA_WEBHOOK_SECRET)) { return res.status(400).send("invalid signature"); } const { type, data } = req.body; if (type === "checkout.paid") { // Fulfill the order — data.metadata.orderId, data.amount, etc. fulfillOrder(data.metadata.orderId); } // Acknowledge quickly with a 2xx so ZyraPay stops retrying. res.sendStatus(200); });That's the full loop
You created a project, opened a checkout, redirected the customer, and verified the payment from a signed webhook. Everything else in these docs builds on this same pattern.
Test locally
There is no separate sandbox base URL — every request goes to https://api.zyrapay.net/v1. You isolate development by using a dedicated test project (with its own API key and webhook URL) and pointing its webhooks at your machine through a tunnel. Keep test amounts small — payments are real.
- 1
Run your webhook handler and expose it
Start the handler from step 4 locally, then open a public HTTPS tunnel to its port so ZyraPay can reach it:
ngrok http 4000 # → https://random-id.ngrok.app -> http://localhost:4000 - 2
Point the test checkout at the tunnel
Pass the tunnel URL as the checkout's
webhookUrl(a per-checkout URL overrides the project default):cURLcurl -X POST https://api.zyrapay.net/v1/checkout \ -H "x-api-key: $ZYRA_API_KEY_TEST" \ -H "Content-Type: application/json" \ -d '{ "items": [{ "name": "Test item", "amount": 1, "quantity": 1 }], "webhookUrl": "https://random-id.ngrok.app/zyra/webhook" }' - 3
Pay and watch it arrive
Open the returnedcheckoutUrl, pay with your Pi wallet, and confirm your handler verifiesX-Zyra-Signatureand returns2xx. Run the same signature verification in test that you run in production — testing that path locally is the point. If an expected webhook never arrives, reconcile withGET /v1/checkout/{checkoutId}rather than waiting.
Next steps
- Authentication — API keys, rotation, and the
Idempotency-Keyheader. - Advanced — safe retries, refunds, errors, and rate limits.
- Going Live — the production checklist.