QuataPay
Developer

Initiate a payment

The end-to-end flow for taking a payment from your customer.

Browse developer docs
Back to developer docs

1. Create a payment intent

POST https://api.quatapay.com/api/v1/gateway/payments
Authorization: Bearer qpay_live_xxx
Content-Type: application/json
Idempotency-Key: 9b2c4f3e-...

{
  "amount": 1500,
  "currency": "XAF",
  "customer_reference": "order_12345",
  "description": "Lunch combo #3",
  "return_url": "https://shop.example.com/pay/return",
  "cancel_url": "https://shop.example.com/pay/cancel"
}

Amount is in the smallest currency unit (whole XAF for now — sub-unit support coming with the multi-currency rollout).

With a test key (qpay_test_…), use the sandbox path instead — POST https://api.quatapay.com/api/v1/gateway/sandbox/payments. It mirrors the live API exactly; only the path and key prefix differ.

2. Redirect the customer

The response is an envelope with a nested payment object and a hosted data.checkout_url. Redirect the customer to it. They confirm in their QuataPay app or with mobile money, then are returned to your return_url or cancel_url.

{
  "success": true,
  "data": {
    "payment": {
      "id": "pay_01HZ...",
      "slug": "abcd1234",
      "status": "requested",
      "amount": 1500,
      "currency": "XAF"
    },
    "checkout_url": "https://quatapay.com/checkout/abcd1234"
  },
  "error": null
}

3. Trust the webhook, not the redirect

The redirect tells the customer the payment finished. The webhook tells your server it actually settled. Listen for gateway.payment.succeeded before you fulfil the order — never rely on the redirect alone, since the customer can bookmark or share the success URL.

Idempotency

Send a fresh UUID in Idempotency-Key on every create call. We cache the response for 24 hours so retries don't double-charge the customer — replays return the exact same payload.