Preloader

Full Integration Examples

Complete end-to-end examples showing a full payment flow: authenticate, initiate payment, redirect customer, and verify on return.

<?php
// PaymentController.php (Laravel)

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class PaymentController extends Controller
{
    private string $baseUrl;
    private string $clientId;
    private string $secretKey;
    private Client $http;

    public function __construct()
    {
        $this->baseUrl   = config('services.quatapay.base_url');
        $this->clientId  = config('services.quatapay.client_id');
        $this->secretKey = config('services.quatapay.secret_key');
        $this->http      = new Client(['timeout' => 30]);
    }

    /**
     * Step 1: Get access token (cache it for 50 minutes)
     */
    private function getAccessToken(): string
    {
        return cache()->remember('quatapay_token', now()->addMinutes(50), function () {
            $res  = $this->http->post($this->baseUrl . '/authentication', [
                'json' => [
                    'client_id' => $this->clientId,
                    'secret_id' => $this->secretKey,
                ],
            ]);
            $data = json_decode($res->getBody(), true);
            return $data['data']['token'];
        });
    }

    /**
     * Step 2: Initiate payment — redirect customer to payment page
     */
    public function checkout(Request $request)
    {
        $orderId = 'ORDER-' . uniqid();
        $token   = $this->getAccessToken();

        try {
            $res  = $this->http->post($this->baseUrl . '/payment/create', [
                'json' => [
                    'amount'     => number_format($request->amount, 2, '.', ''),
                    'currency'   => 'XAF',
                    'return_url' => route('payment.success'),
                    'cancel_url' => route('payment.cancel'),
                    'custom'     => $orderId,
                ],
                'headers' => [
                    'Authorization' => 'Bearer ' . $token,
                    'Accept'        => 'application/json',
                ],
            ]);
            $data = json_decode($res->getBody(), true);
            return redirect()->away($data['data']['payment_url']);
        } catch (RequestException $e) {
            return back()->with('error', 'Payment initiation failed. Please try again.');
        }
    }

    /**
     * Step 3: Verify payment after customer returns
     */
    public function success(Request $request)
    {
        $payToken = $request->query('token');
        $token    = $this->getAccessToken();

        try {
            $res  = $this->http->get($this->baseUrl . '/payment/success/' . $payToken, [
                'headers' => [
                    'Authorization' => 'Bearer ' . $token,
                    'Accept'        => 'application/json',
                ],
            ]);
            $data = json_decode($res->getBody(), true);

            if ($data['type'] === 'success') {
                $trxId   = $data['data']['trx_id'];
                $orderId = $data['data']['custom'];
                // Mark order paid in your DB
                return view('payment.success', compact('trxId', 'orderId'));
            }
        } catch (RequestException $e) {
            // Log error
        }

        return view('payment.failed');
    }
}
// routes/payment.js  (Node.js + Express)

const express = require('express');
const router  = express.Router();

const BASE_URL   = process.env.QUATAPAY_BASE_URL;
const CLIENT_ID  = process.env.QUATAPAY_CLIENT_ID;
const SECRET_KEY = process.env.QUATAPAY_SECRET_KEY;

let cachedToken = null;
let tokenExpiry = 0;

async function getAccessToken() {
  if (cachedToken && Date.now() < tokenExpiry) return cachedToken;

  const res  = await fetch(`${BASE_URL}/authentication`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ client_id: CLIENT_ID, secret_id: SECRET_KEY }),
  });
  const data = await res.json();
  cachedToken = data.data.token;
  tokenExpiry = Date.now() + 50 * 60 * 1000; // 50 minutes
  return cachedToken;
}

// Step 2: Initiate payment
router.post('/checkout', async (req, res) => {
  const token   = await getAccessToken();
  const orderId = 'ORDER-' + Date.now();

  const response = await fetch(`${BASE_URL}/payment/create`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount: parseFloat(req.body.amount).toFixed(2),
      currency: 'XAF',
      return_url: `${process.env.APP_URL}/payment/success`,
      cancel_url: `${process.env.APP_URL}/payment/cancel`,
      custom: orderId,
    }),
  });

  const data = await response.json();
  res.redirect(data.data.payment_url);
});

// Step 3: Verify payment
router.get('/success', async (req, res) => {
  const { token: payToken } = req.query;
  const token = await getAccessToken();

  const response = await fetch(`${BASE_URL}/payment/success/${payToken}`, {
    headers: { 'Authorization': `Bearer ${token}`, 'Accept': 'application/json' },
  });

  const data = await response.json();
  if (data.type === 'success') {
    const { trx_id, custom } = data.data;
    // Mark order paid in your DB
    return res.render('payment-success', { trxId: trx_id, orderId: custom });
  }
  res.render('payment-failed');
});

module.exports = router;
// pages/api/payment/initiate.js  (Next.js API route)

let cachedToken = null;
let tokenExpiry = 0;

async function getAccessToken() {
  if (cachedToken && Date.now() < tokenExpiry) return cachedToken;

  const res  = await fetch(`${process.env.QUATAPAY_BASE_URL}/authentication`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      client_id: process.env.QUATAPAY_CLIENT_ID,
      secret_id: process.env.QUATAPAY_SECRET_KEY,
    }),
  });
  const data = await res.json();
  cachedToken = data.data.token;
  tokenExpiry = Date.now() + 50 * 60 * 1000;
  return cachedToken;
}

// POST /api/payment/initiate
export default async function initiateHandler(req, res) {
  if (req.method !== 'POST') return res.status(405).end();

  const token   = await getAccessToken();
  const orderId = `ORDER-${Date.now()}`;

  const response = await fetch(`${process.env.QUATAPAY_BASE_URL}/payment/create`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount: parseFloat(req.body.amount).toFixed(2),
      currency: 'XAF',
      return_url: `${process.env.NEXT_PUBLIC_APP_URL}/payment/success`,
      cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/payment/cancel`,
      custom: orderId,
    }),
  });

  const data = await response.json();
  return res.json({ payment_url: data.data.payment_url, orderId });
}

// -------------------------------------------------------
// pages/payment/success.jsx  (client page)
// -------------------------------------------------------
// export default function PaymentSuccess({ query }) {
//   // token comes from query string after redirect
//   // Call /api/payment/verify?token=xxx from useEffect
// }

// pages/api/payment/verify.js
export async function verifyHandler(req, res) {
  const { token: payToken } = req.query;
  const token = await getAccessToken();

  const response = await fetch(
    `${process.env.QUATAPAY_BASE_URL}/payment/success/${payToken}`,
    { headers: { 'Authorization': `Bearer ${token}`, 'Accept': 'application/json' } }
  );

  const data = await response.json();
  if (data.type === 'success') {
    // Mark order paid in your DB
    return res.json({ success: true, trxId: data.data.trx_id, orderId: data.data.custom });
  }
  return res.json({ success: false });
}
# views.py  (Django)

import requests
from django.shortcuts import redirect, render
from django.core.cache import cache
from django.conf import settings
import uuid

BASE_URL   = settings.QUATAPAY_BASE_URL
CLIENT_ID  = settings.QUATAPAY_CLIENT_ID
SECRET_KEY = settings.QUATAPAY_SECRET_KEY


def get_access_token():
    token = cache.get("quatapay_token")
    if token:
        return token

    response = requests.post(f"{BASE_URL}/authentication", json={
        "client_id": CLIENT_ID,
        "secret_id": SECRET_KEY,
    })
    data  = response.json()
    token = data["data"]["token"]
    cache.set("quatapay_token", token, timeout=3000)  # 50 minutes
    return token


def checkout(request):
    if request.method != "POST":
        return redirect("home")

    amount   = request.POST.get("amount")
    order_id = f"ORDER-{uuid.uuid4().hex[:12].upper()}"
    token    = get_access_token()

    response = requests.post(f"{BASE_URL}/payment/create",
        json={
            "amount":     f"{float(amount):.2f}",
            "currency":   "XAF",
            "return_url": request.build_absolute_uri("/payment/success/"),
            "cancel_url": request.build_absolute_uri("/payment/cancel/"),
            "custom":     order_id,
        },
        headers={
            "Authorization": f"Bearer {token}",
            "Accept":        "application/json",
        },
        timeout=30,
    )

    data = response.json()
    if data.get("type") == "success":
        return redirect(data["data"]["payment_url"])

    return render(request, "payment_error.html")


def payment_success(request):
    pay_token = request.GET.get("token")
    token     = get_access_token()

    response = requests.get(f"{BASE_URL}/payment/success/{pay_token}",
        headers={
            "Authorization": f"Bearer {token}",
            "Accept":        "application/json",
        },
        timeout=30,
    )

    data = response.json()
    if data.get("type") == "success":
        trx_id   = data["data"]["trx_id"]
        order_id = data["data"]["custom"]
        # Mark order paid in your DB
        return render(request, "payment_success.html", {
            "trx_id": trx_id, "order_id": order_id
        })

    return render(request, "payment_failed.html")
Environment Variables — Store your credentials in environment variables, not in code:
# .env
QUATAPAY_BASE_URL=https://quatapay.com/pay/api/v1
QUATAPAY_CLIENT_ID=your_client_id_here
QUATAPAY_SECRET_KEY=your_secret_key_here