Docs › Quickstarts › Next.js
BackendIntégrer NaabigaPay en Next.js
Une route App Router crée le lien avec la clé secrète, un bouton client redirige vers le paiement, et une seconde route vérifie le webhook.
Next.js est à la fois front et serveur. La règle reste la même : la clé secrète (sk_live_…) ne vit que dans vos Route Handlers (côté serveur), jamais dans un composant client. Le bouton appelle votre route, votre route appelle NaabigaPay.
1.La route qui crée le lien
Le composant client appelle cette route ; c'est elle qui appelle POST/v1/payment-links avec la clé secrète, puis renvoie l'url de paiement.
import { NextResponse } from 'next/server';
const NPAY = 'https://api.pay.naabiga.com/api/v1';
const SECRET = process.env.NPAY_SECRET; // sk_live_... : reste côté serveur
// Votre bouton client appelle CETTE route, jamais NaabigaPay directement.
export async function POST(req) {
const { commande, amount } = await req.json();
const r = await fetch(`${NPAY}/payment-links`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${SECRET}`,
'Content-Type': 'application/json',
'Idempotency-Key': `cmd-${commande}`, // évite un double lien en cas de retry
},
body: JSON.stringify({
amount, // entier, en FCFA
reference: commande, // votre numéro de commande
description: `Commande ${commande}`,
return_url: 'https://monsite.com/merci',
cancel_url: 'https://monsite.com/panier',
single_use: true,
}),
});
if (!r.ok) {
return NextResponse.json({ error: await r.text() }, { status: 502 });
}
const link = await r.json();
return NextResponse.json({ url: link.url }); // le client ouvre cette URL
}Pour un montant libre, remplacez amount par amount_min, amount_max et une description.
2.Le bouton côté client
Un composant client ('use client') qui appelle votre route, récupère l'url, puis redirige le navigateur vers la page de paiement.
'use client';
import { useState } from 'react';
export function BoutonPayer({ commande, montant }) {
const [chargement, setChargement] = useState(false);
async function payer() {
setChargement(true);
// Appel de VOTRE route serveur (elle seule détient la clé secrète).
const r = await fetch('/api/naabigapay', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ commande, amount: montant }),
});
const { url } = await r.json();
window.location.href = url; // page de paiement NaabigaPay
}
return (
<button onClick={payer} disabled={chargement}>
{chargement ? 'Redirection…' : 'Payer'}
</button>
);
}3.Vérifier le webhook
NaabigaPay envoie POST à l'URL enregistrée dans le portail marchand, avec les en-têtes X-NPay-Event et X-NPay-Signature (HMAC-SHA256 du corps brut). Forcez le runtime Node (le module crypto n'existe pas sur l'Edge), lisez le corps brut avec req.text(), recalculez le HMAC et comparez en temps constant.
import crypto from 'crypto';
export const runtime = 'nodejs'; // crypto Node requis (pas Edge)
const WEBHOOK_SECRET = process.env.NPAY_WEBHOOK_SECRET;
export async function POST(req) {
// Corps BRUT, non reparsé : la signature porte sur ces octets exacts.
const raw = await req.text();
const signature = req.headers.get('x-npay-signature') || '';
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(raw, 'utf8')
.digest('hex');
const a = Buffer.from(signature);
const b = Buffer.from(expected);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return new Response('signature invalide', { status: 400 });
}
const { event, data } = JSON.parse(raw);
if (event === 'payment.succeeded' && req.headers.get('x-npay-event') === 'payment.succeeded') {
// data.merchant_reference = VOTRE numéro de commande (la reference du lien).
marquerCommandePayee(data.merchant_reference);
}
return new Response('ok', { status: 200 });
}Astuce. Ne créditez rien depuis le bouton client : le retour navigateur peut être manqué ou rejoué. Seul ce webhook signé, reçu côté serveur, confirme le paiement.
Référence rapide
| Méthode | Endpoint | Rôle |
|---|---|---|
| POST | /v1/payment-links | Créer un lien de paiement |
| GET | /v1/payments/{reference} | Statut d'un paiement |
Base : https://api.pay.naabiga.com/api/v1 · Auth : Authorization: Bearer sk_…. Voir la documentation complète et le format des webhooks.