Payments API — for your backend
Call this host over HTTPS from your server (e.g. loan core, wallet service). Collect repayments with Paystack Checkout, disburse to M-Pesa, verify charges, and read transfer status. Paystack secrets never go in your mobile app — they stay on this API host. Official Paystack field reference: paystack.com/docs.
- Integration guide (
/payments/developers) — flows, HTTP codes, webhooks. GET /api/payments— JSON index of routes and request shapes (same on any host that serves this API)./payments/test— browser playground (Checkout + sample disburse).- Markdown in the repo:
docs/LOAN-APP-PAYMENTS-INTEGRATION.md,docs/PAYMENTS-API.md(curl).
What you implement
Repayment: POST /api/payments/initialize → redirect the borrower to authorization_url → on return, GET /api/payments/verify?reference=… (and/or handle charge.success via webhook — extend the webhook handler with your ledger logic).
M-Pesa payout: POST /api/payments/disburse. Amounts under KES 2000 complete in one step (mode: "automatic"). Amounts ≥ KES 2000 return an approval_token; your product calls POST /api/payments/disburse/approve with the OTP policy the API owner documents for you.
Transfer status / SMS text: GET /api/payments/transfer?reference=… or ?transfer_code=TRF_…
Authentication you will see
- No Bearer (default): you only send JSON bodies / query params. The host calls Paystack using its own server keys.
- Bearer (when the API owner enables it): send
Authorization: Bearer <key-they-issue-you>on/api/payments/*routes (not on Paystack's webhook callback — that usesx-paystack-signature).
Webhook URL (for Paystack Dashboard)
Whoever owns the Paystack account registers one URL per mode (test / live). It must hit this codebase on the host you integrate against:
https://payment.pewang.company/api/paystack/webhookPath is always /api/paystack/webhook on the same origin as your API calls (e.g. if your base URL is https://pewang.company, the webhook is https://pewang.company/api/paystack/webhook).
Endpoints (summary)
GET /api/payments — full machine-readable list.
POST /api/payments/initialize — start Checkout; returns authorization_url, reference.
GET /api/payments/verify?reference=… — confirm a charge.
GET /api/payments/transfer?… — transfer status + confirmation_message.
POST /api/payments/disburse — M-Pesa payout; see settlement / message for pending vs success. 400 if the host's Paystack balance cannot cover the amount.
POST /api/payments/disburse/approve — large-amount approval step.
Sandbox / demos
When testing against a host the operator configured for demos, you may use Safaricom 0711164069 / 254711164069 in recipient_phone (local 07… is normalized to 2547…).
Deployment operators only — server configuration
Integrators do not set these; the team that deploys this project does (e.g. Vercel). Listed so operators can match GET /api/payments and support tickets.
PAYSTACK_SECRET_KEY— Paystack secret on the server.PAYSTACK_PUBLIC_KEY— optional client Checkout.PAYSTACK_WEBHOOK_SECRET— optional; webhook HMAC (defaults to secret key).PAYMENT_APPROVAL_OTP— server PIN for/disburse/approvewhen amount ≥ KES 2000.PAYMENTS_REQUIRE_BEARER/PAYMENTS_API_KEYS— optional integrator gate.PAYSTACK_KES_MOBILE_BANK_CODE— optional (defaultMPESA).PAYMENTS_TRANSFER_NOTIFY_URL/PAYMENTS_TRANSFER_NOTIFY_SECRET— optional fan-out after transfer webhooks.
CLI / hosting: docs/VERCEL-CLI.md