CafonaPay API

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.

1. Get a secret key

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.

2. Create a checkout session

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": "…"
}

3. Receive the result

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-Signature

Then confirm by retrieving the session (GET /api/v1/checkout/sessions/:id) before fulfilling an order.

Other endpoints

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

Fees

Charged to the organization on successful payments, never to the payer: card 2.9%, mobile money 2.0%, CafonaPay balance 0.5%.

Test cards and codes

  • 4242 4242 4242 4242Visa · succeeds
  • 5555 5555 5555 4444Mastercard · succeeds
  • 3782 822463 10005Amex · succeeds
  • 4000 0000 0000 0002Visa · declined
  • 4000 0000 0000 9995Visa · insufficient funds
  • Mobile money approval code123456
  • Mobile money number ending in 0000declined by operator

Any future expiry date and any CVC work with test cards. Real card numbers are refused in test mode.

Errors

{ "error": { "code": "invalid_request", "message": "…", "fields": { "amount": "…" } } }

401 invalid_api_key · 400 invalid_request · 404 not_found · 409 conflicts · 5xx retry with backoff.