Aller au contenu

Exemple : une boutique complète

apps/demo-shop est une petite boutique (« Boutique Démo Dakar ») qui intègre Xaalis comme tout commerçant devrait le faire. Elle tient en 250 lignes de TypeScript environ avec le SDK Node, et ses tests s’exécutent contre une vraie installation de Xaalis.

Fenêtre de terminal
yarn workspace @xaalis/demo-shop setup # crée un commerçant de test et fait pointer son webhook vers la boutique
yarn workspace @xaalis/demo-shop dev # http://localhost:4200

1. La commande — créer le paiement pour exactement le total de la commande.

const payment = await xaalis.payments.create(
{
amount: order.total, // XOF entier
client_reference: order.id,
success_url: `${baseUrl}/orders/${order.id}`,
cancel_url: `${baseUrl}/orders/${order.id}`,
metadata: { order_id: order.id },
},
// Un double clic ou une nouvelle tentative ne peut pas créer un second paiement. Préfixée par le mode (B-4).
{ idempotencyKey: `${xaalis.testMode ? "test" : "live"}:order:${order.id}` },
);
return redirect(payment.checkout_url);

2. Le client revient — afficher, sans faire confiance. Arriver sur success_url ne prouve rien. La page affiche « Confirmation en cours… » et interroge Xaalis depuis le serveur :

const p = await xaalis.payments.retrieve(order.paymentId); // de serveur à serveur : ici, on *peut* faire confiance
if (p.status === "succeeded" && p.amount === order.total) markPaid(order);

3. Le webhook — la source de vérité.

const event = constructEvent(rawBody, req.header("xaalis-signature"), webhookSecret); // lève une erreur si la signature est invalide
if (!rememberEvent(event.id)) return ok(); // livraison au moins une fois : ignorer les doublons
if (event.livemode !== isLive) return ok(); // test et production partagent une URL
const p = event.data.object;
if (p.id === order.paymentId && p.amount === order.total && p.currency === "XOF") markPaid(order); // une seule fois

markPaid est idempotent : une commande déjà payée n’est jamais payée une seconde fois, que ce soit le webhook ou la page de retour qui arrive en premier.

Scénario Résultat
Commande un paiement pour exactement le total de la commande, redirection vers la page de paiement hébergée
Le client arrive sur la page de retour avant de payer la commande reste impayée
Le client paie le webhook signé marque la commande comme payée, une seule fois
Paiement refusé la commande est marquée comme échouée
Signature falsifiée · événement rejoué · événement signé avec un mauvais montant · événement de production sur une boutique de test rejeté ou ignoré ; la commande ne change pas
Webhook perdu la vérification côté serveur de la page de retour finalise quand même la commande

Les mêmes règles, en PHP pour WordPress, se trouvent dans l’extension WooCommerce.