Machine à états des commandes
Le module fait évoluer la commande à travers une petite machine à états déterministe. Les plateformes nomment ces états différemment, mais le modèle logique est universel — et le contrat de traitement l'est aussi.
États
| État logique | Entré lorsque… | Terminal ? | Ce que voit le client |
|---|---|---|---|
| PENDING_PAYMENT | La commande est créée, en attente du webhook | Non | Page de paiement hébergée (puis chargement) |
| PAID | payment.succeeded, montant réconcilié | Oui | Page de confirmation de commande |
| PAYMENT_ERROR | payment.failed ou payment.declined | Oui | Page d'erreur avec lien de nouvelle tentative |
| CANCELED | Action admin ou expiration de session | Oui | Page d'erreur si le client revient tardivement |
Transitions
[début] ──validateOrder()──→ PENDING_PAYMENT
│
┌──────────────┼──────────────┐
▼ ▼ ▼
PAYMENT_ERROR CANCELED PAID
(payment.failed) (admin / timeout) (payment.succeeded)
│
┌─────┘
▼
(webhook en doublon
→ no-op, reste PAID)
Chaque état terminal est un puits : dès qu'une transaction est en PAID, PAYMENT_ERROR ou CANCELED, aucun webhook ultérieur ne doit la faire muter. L'idempotence est le contrat.
Correspondance des états par plateforme
| État logique | PrestaShop | Shopify | Shopware 6 |
|---|---|---|---|
PENDING_PAYMENT | PS_OS_BANKWIRE | financial_status = pending | État : in_progress |
PAID | PS_OS_PAYMENT | financial_status = paid | État : paid |
PAYMENT_ERROR | PS_OS_ERROR | financial_status = voided | État : failed |
CANCELED | PS_OS_CANCELED | financial_status = voided | État : cancelled |
Contrat d'idempotence
Les webhooks peuvent être livrés plusieurs fois : coupures réseau, nos propres relances, rejeux manuels depuis la Console. Votre gestionnaire doit court-circuiter le traitement si la commande est déjà dans un état terminal :
if order.state == PAID and event == payment.succeeded:
return 200 # no-op, déjà traité
LodinPay peut ainsi relancer agressivement sans risque, et toute une famille de bugs subtils liés aux doubles écritures disparaît de votre côté.
Cas limites
- Le webhook arrive avant que la commande soit committée. Mettez en place une petite boucle de réessai sur la recherche de la commande (3 × 1 s) avant de renvoyer
500. Nous relancerons, et votre prochain essai trouvera la commande. - Montant incohérent. Si
webhook.amountdiffère deorder.amountde plus de0.01, rejetez avec500. N'appliquez surtout pas silencieusement le nouveau montant — c'est presque toujours une tentative d'altération ou un bug amont qui mérite enquête. payment.failedtardif aprèsPAID. Cela ne devrait pas arriver, mais le cas échéant : conservezPAID, journalisez l'anomalie, remontez au support avec letransactionId.- Commande annulée par un admin, suivie d'un
payment.succeeded. Lodin remboursera automatiquement. ConservezCANCELED, journalisez l'anomalie.
Voir aussi
- Flux de paiement — les étapes qui pilotent ces transitions.
- Spécification des webhooks — types d'événements et format de la charge utile.
- Modèle de sécurité — pourquoi la réconciliation des montants n'est pas négociable.