Skip to content

Building with AI agents ​

Building with an AI coding agent (Claude, ChatGPT, Cursor, Copilot, v0, Lovable, Bolt and the like)? This page gives it what it needs to integrate ZevPay Checkout correctly the first time.

Give your agent the docs ​

Start your agent with this link:

https://docs.zevpaycheckout.com/llms.txt

It is a short index of every page, with the facts agents most often get wrong written out inline. From there, every page is available as plain markdown by adding .md to its URL, for example:

https://docs.zevpaycheckout.com/checkout/standard.md

If your agent can read a lot at once, https://docs.zevpaycheckout.com/llms-full.txt is the whole documentation in one file.

Pick the integration ​

You haveUseNotes
A website or app with a backend (Next.js, Express, Laravel, serverless functions, Supabase)Standard CheckoutYour server creates the payment with the secret key; the customer pays on our hosted page and returns to you.
A frontend-only site, or you want the payment form on your own pageInline CheckoutRuns in the browser with the public key. Add one serverless function to verify and receive webhooks.
A WordPress or WooCommerce storeWooCommerce pluginNo code needed.
A mobile appReact Native or Flutter SDK

Rules every integration must follow ​

Paste these into your agent's instructions as they are.

  1. Secret keys stay on the server. sk_test_... and sk_live_... go in server environment variables only, never in browser or mobile code. Public keys (pk_...) are the only keys allowed in the browser.
  2. Amounts are in kobo. ₦2,000 is 200000. Every amount in requests, responses and webhooks is in the currency's smallest unit.
  3. Responses are wrapped. Read fields from data in { "success": true, "data": { ... } }.
  4. Send your own reference (your order or payment ID) on every initialize. It makes retries safe and is how you find the payment later.
  5. The return to your site is not proof of payment. Confirm with verify from your server, or with the webhook. Verify accepts the session ID, our reference, or your own reference. All the options are compared in Confirming payments.
  6. Fulfil from the webhook. charge.success arrives even when the customer closes the page before returning. Use the redirect or callback only to show the customer a result.
  7. Verify the webhook signature over the raw body. HMAC-SHA256 of the exact request text with your webhook secret, hex encoded, compared in constant time with the x-zevpay-signature header. Parse the JSON only after the check passes. See Serverless platforms for Next.js, Vercel and Supabase.
  8. Fulfil exactly once. Webhooks can arrive more than once, and the webhook and your return page may both confirm the same payment. Key fulfilment on data.reference (for example a unique constraint) so a repeat does nothing. Retry initialize with the same reference after a timeout: it returns the same session. See idempotency and retries.
  9. Check the mode. Every webhook and verify response carries livemode. In production fulfil only when it is true.
  10. The webhook URL must be public HTTPS. No localhost or private addresses; use a tunnel such as ngrok while developing. Redirects are not followed.

Test, then go live ​

  1. Build with test keys (sk_test_ / pk_test_). They work before your account verification is approved.
  2. Pay a test session with Simulate successful transfer on the payment page, or POST /v1/checkout/session/{session_id}/simulate-payment with your test secret key. A real, signed charge.success is sent to your test webhook URL.
  3. When everything works, create live keys in the dashboard, swap sk_test_ for sk_live_, and set the production webhook URL on the live key. No other code changes.
  4. Make one small live payment end to end, webhook included, before opening payments to everyone.

Full details: Test Mode.

A brief to paste into your agent ​

Replace the bracketed parts, then paste it in.

text
Integrate ZevPay Checkout into [describe the app, e.g. "a React + Vite site on
Vercel with a Supabase database"]. Customers pay for [what, e.g. "a monthly
membership of NGN 2,000"].

Read first: https://docs.zevpaycheckout.com/llms.txt
Then: https://docs.zevpaycheckout.com/guide/ai-agents.md
      https://docs.zevpaycheckout.com/checkout/standard.md
      https://docs.zevpaycheckout.com/guide/confirming-payments.md
      https://docs.zevpaycheckout.com/webhooks/signatures.md
      https://docs.zevpaycheckout.com/guide/test-mode.md

Build:
1. A server endpoint that calls
   POST https://api.zevpaycheckout.com/v1/checkout/session/initialize
   with header x-api-key: <ZEVPAY_SECRET_KEY from env>, and body:
   amount (kobo), currency "NGN", email, customer_name, callback_url,
   reference (unique per payment, e.g. "<userId>_<YYYY-MM>"), metadata.
   Save the reference with status "pending", then redirect the browser to
   data.checkout_url.
2. A callback page that reads ?status=&merchant_reference= and shows the
   result. It does not grant access by itself.
3. A webhook endpoint that reads the raw body, verifies x-zevpay-signature
   (HMAC-SHA256, hex, constant-time) with ZEVPAY_WEBHOOK_SECRET, then on
   event "charge.success" marks data.merchant_reference as paid exactly once
   (idempotent on data.reference) and grants access. Check livemode.
4. Optional: GET /v1/checkout/session/{reference}/verify with the secret key
   before showing success; fulfil only when data.status is "completed".

Keep all secret keys server-side. Test with sk_test_ keys and the
"Simulate successful transfer" button, then switch to sk_live_ keys.

Common mistakes ​

MistakeWhat happensFix
Amount sent in naira (2000)Customer is charged ₦20Send kobo: 200000
Secret key used in React or mobile codeAnyone can take it from your siteCall initialize from a server function
Body parsed to JSON before checking the signatureSignatures never matchVerify over the raw text, then parse
Access granted from the callback URLAnyone can type ?status=successGrant from the webhook or a verify call
No idempotency on fulfilmentDouble access or double credit on a retried webhookUnique constraint on data.reference
localhost webhook URLRefused when you save itUse a tunnel such as ngrok
Test key in productionPayments never settleUse sk_live_ keys and check livemode

ZevPay Checkout Developer Documentation