Accept card and mobile money payments for your association, church, tontine or business with one API call. Payers complete payment on CafonaPay's hosted checkout, and you get the result by signed webhook.
Test mode: keys start with sk_test_ and no real money moves.
In the CafonaPay app, open your organization, then Developers › Create key. The key is shown once, so store it on your server and never in a browser or mobile app.
POST https://cafonapay.com/api/v1/checkout/sessions
Authorization: Bearer sk_test_…
Content-Type: application/json
{
"amount": 5000, // minor units: 5000 = $50.00 CAD, 5000 = 5 000 FCFA
"currency": "CAD", // CAD USD EUR XAF XOF NGN GHS KES
"description": "Membership dues 2027",
"success_url": "https://your-site.org/thanks",
"cancel_url": "https://your-site.org/donate",
"customer_email": "payer@example.com",
"payment_methods": ["card", "mobile_money", "wallet"],
"metadata": { "member_id": "42" }
}The response includes a url. Redirect the payer there.
{
"id": "pay_01J…",
"object": "checkout.session",
"url": "https://cafonapay.com/checkout/pay_01J…",
"status": "requires_payment_method",
"amount": 5000, "amount_fee": 0, "amount_net": 5000,
"currency": "CAD",
"expires_at": "…"
}Add a webhook endpoint under Developers › Webhooks. CafonaPay sends payment.succeeded and payment.failed events, retrying for up to about 15 hours (after 1 min, 5 min, 30 min, 2 h, 12 h). Always verify the signature:
// Node.js
import crypto from "node:crypto";
function verify(rawBody, header, secret) {
// header: "t=1730000000,v1=5f2c…"
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false; // replay window
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
// Express: app.post("/cafonapay/webhooks", express.raw({ type: "application/json" }), …)
// Header name: CafonaPay-SignatureThen confirm by retrieving the session (GET /api/v1/checkout/sessions/:id) before fulfilling an order.
GET /api/v1/checkout/sessions/:id retrieve one session GET /api/v1/payments?limit=25 latest payments (max 100) GET /api/v1/balance your organization's available balance
Charged to the organization on successful payments, never to the payer: card 2.9%, mobile money 2.0%, CafonaPay balance 0.5%.
Any future expiry date and any CVC work with test cards. Real card numbers are refused in test mode.
{ "error": { "code": "invalid_request", "message": "…", "fields": { "amount": "…" } } }401 invalid_api_key · 400 invalid_request · 404 not_found · 409 conflicts · 5xx retry with backoff.