Profil : boutique vendant des produits physiques ou digitaux, base prix en XOF / EUR / USD, veut accepter crypto sans s’exposer à la volatilité.

Démo live — IziShop

Exactement ce cas d’usage : une boutique de démonstration (Tron testnet, TRX / USDT) avec catalogue, panier et checkout crypto en modale via embed.js. Essayez le parcours d’achat de bout en bout.

Stack recommandée

  • Backend : Node.js / Python avec le SDK officiel
  • Auto-conversion activée (cryptos reçues → XOF sur compte mobile money)
  • Auto-settlement à 100% sur chaque PaymentIntent complété
  • Mode whitelist sur les payouts manuels (sécurité)
  • disputeManager = platform (IzichangePay gère les paiements hors plage)

Flow

[Client] → Checkout site → [Votre backend] → izipay.paymentIntents.create()

                                          intent.paymentUrl

[Client] redirigé → [Widget IzichangePay] → paye en crypto

                            [IzichangePay] webhook payment_intent.completed

                            [Votre backend] marque la commande payée

                            Auto-settlement déclenché → XOF sur Wave/MoMo

Code minimal

1. Checkout

// POST /api/checkout
import { izipay } from './lib/izipay';

export default async function checkout(req, res) {
  const order = await db.orders.create({
    userId: req.user.id,
    items: req.body.items,
    totalXof: computeTotal(req.body.items),
    status: 'pending_payment',
  });

  const intent = await izipay.paymentIntents.create({
    requestedCurrencyType: 'fiat',
    currencyRequested: 'XOF',
    amountRequested: order.totalXof,
    acceptedCoins: ['USDT.TRC20', 'USDT.BEP20', 'USDC.BEP20'],
    merchantReference: order.id,
    returnUrl: `${process.env.SITE_URL}/orders/${order.id}/done`,
    metadata: { orderId: order.id, userId: req.user.id },
    idempotencyKey: `checkout-${order.id}`,
  });

  await db.orders.update({ id: order.id, izipayIntentId: intent.id });
  res.redirect(intent.paymentUrl);
}

2. Webhook

// POST /webhooks/izipay
app.post('/webhooks/izipay', express.raw({ type: 'application/json' }), async (req, res) => {
  const event = IziPayClient.validateWebhook(req.body, req.headers['x-izipay-signature'], SECRET);

  if (event.type === 'payment_intent.completed') {
    const { intentId, merchantReference } = event.data;

    // Idempotence côté receveur
    const order = await db.orders.findById(merchantReference);
    if (!order || order.status === 'paid') return res.json({ received: true });

    await db.orders.markPaid(order.id, { izipayIntentId: intentId, paidAt: new Date() });
    await queue.add('send-order-confirmation-email', { orderId: order.id });
  }

  if (event.type === 'settlement.completed') {
    // Optionnel : tracer dans votre compta
    await db.settlements.insert(event.data);
  }

  res.json({ received: true });
});

3. Page de confirmation

returnUrl peut servir un état provisoire : le client voit “Confirmation en cours”. Côté front, écoutez l’événement réel via SSE ou re-fetch périodique de /api/orders/:id/status qui lit votre DB (mise à jour par le webhook).

Configurations dashboard

  • Paramètres → Cryptos : activez USDT.TRC20, USDT.BEP20, USDC.BEP20
  • Paramètres → Frais : porteur de frais customer (le payeur règle le montant de base + les frais, vous recevez le montant de base). L’enum vaut {merchant, customer}, défaut merchant.
  • Paramètres → Dispute : disputeManager = platform (vous ne voulez pas gérer)
  • Paramètres → Retraits : whitelist_only + ajoutez vos adresses cold wallet
  • Settlement account : votre numéro Wave / OM principal
  • Automation rule : on_payment_intent_success → settlement 100% sur les 3 cryptos vers ce compte

Anti-fraud

  • KYB obligatoire : pas de paiements live sans compte vérifié
  • Webhooks signés HMAC-SHA256 (header X-IziPay-Signature au format sha256=<hex>) + tolérance anti-replay 300s (5 min) par défaut
  • Idempotency-Key par commande → impossible de payer deux fois la même

Frais

Les frais (payin, settlement) sont configurés côté plateforme et visibles dans le dashboard (Paramètres → Frais). Le porteur de frais (merchant ou customer) détermine qui les absorbe.