Workflow complet d’un paiement, de la création server-side jusqu’à la confirmation webhook.
1. Installer le SDK
npm install izichangepay-sdk
pnpm add izichangepay-sdk
yarn add izichangepay-sdk
2. Initialiser le client
// lib/izipay.ts
import { IziPayClient } from 'izichangepay-sdk';
if (!process.env.IZIPAY_API_KEY) {
throw new Error('IZIPAY_API_KEY is required');
}
export const izipay = new IziPayClient({
apiKey: process.env.IZIPAY_API_KEY,
});
3. Créer le payment intent
Côté backend, créez le PaymentIntent au moment du checkout, puis redirigez le client.
// pages/api/checkout.ts (Next.js API route, Express, etc.)
import { izipay } from '../../lib/izipay';
export default async function handler(req, res) {
const { orderId, totalXof } = req.body;
const intent = await izipay.paymentIntents.create({
requestedCurrencyType: 'fiat',
currencyRequested: 'XOF',
// Chaîne décimale en unité majeure (ex "5000" = 5000 XOF), jamais des centimes ni un number JSON.
amountRequested: totalXof,
acceptedCoins: ['USDT.TRC20', 'USDT.BEP20'],
merchantReference: orderId, // votre identifiant interne
returnUrl: `https://shop.example/orders/${orderId}/done`,
// Stocké tel quel, renvoyé sur les webhooks — utile pour corréler.
metadata: { orderId, userId: req.user.id },
});
// Stockez `intent.id` lié à votre commande pour retrouver la commande
// quand le webhook arrivera.
await db.orders.update({ id: orderId, izipayIntentId: intent.id });
// Redirigez vers le widget de paiement.
res.redirect(intent.paymentUrl);
}
Idempotency : si vous appelez create() deux fois avec le même idempotencyKey (auto-généré par le SDK ou passé explicitement), vous récupérez le même intent, pas de doublon.
4. Le client paye
Le client est sur la page de paiement (intent.paymentUrl) :
- Sélectionne sa crypto + le réseau
- Saisit prénom, nom, email (obligatoires) + adresse de remboursement (facultatif)
- Voit l’adresse de dépôt + le QR code + le montant exact à envoyer + un timer
- Envoie les fonds depuis son wallet
À la première confirmation on-chain, l’intent passe en confirming. Dès que le réseau confirme la transaction, il passe en completed (ou en irregular si le montant est hors plage configurée).
5. Recevoir le webhook
Quand l’intent atteint completed, IzichangePay envoie un POST signé à votre endpoint.
Express
Next.js (Pages)
Next.js (App)
import express from 'express';
import { IziPayClient } from 'izichangepay-sdk';
import { izipay } from './lib/izipay';
const app = express();
// IMPORTANT: le body brut est requis pour vérifier la signature.
app.post(
'/webhooks/izipay',
express.raw({ type: 'application/json' }),
async (req, res) => {
let event;
try {
event = IziPayClient.validateWebhook(
req.body,
req.headers['x-izipay-signature'] as string,
process.env.IZIPAY_WEBHOOK_SECRET!,
);
} catch (err) {
return res.status(400).json({ error: (err as Error).message });
}
switch (event.type) {
case 'payment_intent.completed': {
const { intentId, merchantReference } = event.data as {
intentId: string; merchantReference?: string;
};
// Idempotence côté receveur : pré-check dans votre DB.
await db.orders.markPaidIfPending(merchantReference, intentId);
break;
}
case 'payment_intent.expired': {
// ...
break;
}
}
// Acquittement rapide — IzichangePay retentera si vous renvoyez 5xx.
res.json({ received: true });
},
);
app.listen(3000);
// pages/api/webhooks/izipay.ts
import { IziPayClient } from 'izichangepay-sdk';
import getRawBody from 'raw-body';
export const config = { api: { bodyParser: false } };
export default async function handler(req, res) {
const rawBody = await getRawBody(req);
let event;
try {
event = IziPayClient.validateWebhook(
rawBody,
req.headers['x-izipay-signature'],
process.env.IZIPAY_WEBHOOK_SECRET!,
);
} catch (err) {
return res.status(400).json({ error: err.message });
}
// ... traitement
res.json({ received: true });
}
// app/api/webhooks/izipay/route.ts
import { IziPayClient } from 'izichangepay-sdk';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(req: NextRequest) {
const rawBody = await req.text();
let event;
try {
event = IziPayClient.validateWebhook(
rawBody,
req.headers.get('x-izipay-signature'),
process.env.IZIPAY_WEBHOOK_SECRET!,
);
} catch (err) {
return NextResponse.json({ error: (err as Error).message }, { status: 400 });
}
// ... traitement
return NextResponse.json({ received: true });
}
6. Tester de bout en bout
- Côté dashboard, Développeurs → Webhooks → ⋯ → Envoyer un événement test. Vérifiez que votre endpoint répond 200 et que
validateWebhook() ne lance pas.
- En mode test, créez un intent depuis un client REST (
curl ou Postman) et payez depuis un wallet sur le testnet de la crypto choisie.
- Vérifiez les logs côté serveur ET côté dashboard (chaque appel webhook est tracé : statut HTTP, durée, body de réponse).
Erreurs courantes
| Symptôme | Cause | Fix |
|---|
IziPayWebhookError: missing_signature | Le proxy entre le client et votre serveur supprime les headers | Configurer le proxy pour préserver X-IziPay-* |
IziPayWebhookError: invalid_signature | Body JSON parsé puis re-stringifié (les espaces diffèrent) | Utilisez le body brut (Buffer/string), pas l’objet parsé |
IziPayWebhookError: expired_timestamp | Votre horloge serveur est désynchronisée | Vérifiez NTP / le décalage avec date -u |
| L’événement n’arrive jamais | Endpoint marqué failing après 5 échecs consécutifs | Allez dans Webhooks → réactivez après avoir fixé votre serveur |
Prochaine étape