QuataPay
Developer

Code examples

Drop-in snippets in Node, Python, and PHP for the most common operations.

Browse developer docs
Back to developer docs

Create a payment (Node.js)

import { randomUUID } from "node:crypto";

const res = await fetch("https://api.quatapay.com/api/v1/gateway/payments", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.QPAY_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": randomUUID(),
  },
  body: JSON.stringify({
    amount: 1500,
    currency: "XAF",
    customer_reference: order.id,
    return_url: "https://shop.example.com/pay/return",
    cancel_url:  "https://shop.example.com/pay/cancel",
  }),
});
const { data } = await res.json();
return data.checkout_url;

Create a payment (Python)

import os, uuid, requests

resp = requests.post(
    "https://api.quatapay.com/api/v1/gateway/payments",
    headers={
        "Authorization": f"Bearer {os.environ['QPAY_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "amount": 1500,
        "currency": "XAF",
        "customer_reference": order_id,
        "return_url": "https://shop.example.com/pay/return",
        "cancel_url":  "https://shop.example.com/pay/cancel",
    },
    timeout=30,
)
resp.raise_for_status()
return resp.json()["data"]["checkout_url"]

Create a payment (PHP)

<?php
$ch = curl_init("https://api.quatapay.com/api/v1/gateway/payments");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("QPAY_KEY"),
        "Content-Type: application/json",
        "Idempotency-Key: " . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "amount" => 1500,
        "currency" => "XAF",
        "customer_reference" => $orderId,
        "return_url" => "https://shop.example.com/pay/return",
        "cancel_url"  => "https://shop.example.com/pay/cancel",
    ]),
]);
$res = json_decode(curl_exec($ch), true);
return $res["data"]["checkout_url"];

Verify a webhook signature

// Node — rawBody is the request body Buffer, exactly as received
import crypto from "node:crypto";

const timestamp = req.headers["x-quatapay-timestamp"] || "";
const provided = Buffer.from(req.headers["x-quatapay-signature"] || "");

// Refuse a replay: the timestamp must be within 300 seconds of now.
const fresh =
  /^\d+$/.test(timestamp) &&
  Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)) <= 300;

// The signature covers "<timestamp>.<raw body>".
const expected = Buffer.from(
  "sha256=" +
    crypto
      .createHmac("sha256", process.env.QPAY_WEBHOOK_SECRET)
      .update(`${timestamp}.`)
      .update(rawBody)
      .digest("hex"),
);

if (
  !fresh ||
  expected.length !== provided.length ||
  !crypto.timingSafeEqual(expected, provided)
) {
  return res.status(400).end();
}