Skip to content
13Étude de casSW · 13 sur 10

Le commerce sur mesure pour un marché qui exige sa propre stack.

Les stacks e-commerce génériques ne savaient pas modéliser les primitives financières et logistiques de ce marché. Paiement fractionné Monobank, expédition Nova Post, multidevise par produit — bâtis de zéro pour une marque artisanale ukrainienne livrant dans 7 locales.

ClientConfidentiel
Année2026
Durée1 yrs
StackTypeScript · Next.js · React · Postgres · Vercel · AWS
Hero image for maker-brand-commerce-platformFig 01 · Hero

La contrainte de l'artisan.

Des produits faits main en Ukraine, expédiés vers sept pays. L'infrastructure financière et logistique sur laquelle repose le marché — Monobank pour les paiements, Nova Post pour l'expédition, les taux de change quotidiens de la Banque nationale d'Ukraine pour la tarification — n'a aucun équivalent dans une stack e-commerce européenne générique.

Le modèle de paiement à lui seul exclut Stripe. Le paiement fractionné de Monobank encaisse un acompte à la confirmation de la commande et déclenche le solde lorsque la commande passe à « prête à expédier ». Cela fait deux volets de paiement, calés sur l'état de production, avec des signatures de webhook ECDSA à chaque transition. Stripe n'a aucune primitive pour cela. Il faudrait le simuler avec deux paiements sans lien et réconcilier l'état soi-même — et tout écart de réconciliation devient une surface de fraude.

Le modèle logistique exclut Shippo. Nova Post, le transporteur dominant en Ukraine, expose un annuaire de villes, de divisions et de classificateurs qui doivent être synchronisés et interrogés localement. Les bordereaux sont générés par programmation au moment de l'expédition et le PDF d'étiquette qui en résulte doit être stocké quelque part de récupérable. Rien dans le monde de l'abstraction des transporteurs génériques ne correspond proprement à cela.

Le modèle de tarification exclut les réglages de devise par région. L'artisan vend certains produits à l'international en EUR, d'autres uniquement en UAH parce que la chaîne d'approvisionnement est locale. Quelques produits portent des prix explicites en UAH ou USD qui diffèrent de la base convertie au taux de change. Des règles par région ne peuvent pas exprimer « ce produit précis a son propre prix en UAH quelle que soit la locale de l'acheteur ». Des prix par produit, si.

Une configuration Stripe + Shippo + WooCommerce ne peut pas modéliser « acompte-maintenant en UAH, solde-à-l'expédition en EUR, livré par Nova Post » sans code sur mesure à chaque jointure. Le développement sur mesure n'était pas une préférence — c'était une exigence du marché.

Ce que nous avons construit.

Une seule plateforme : une vitrine client en 7 locales, un cycle de vie de production (brouillon → en cours → prêt → expédié) et un tableau de bord d'exploitation que la fondatrice pilote seule.

La couche de paiement gère l'intégralité du flux de paiement fractionné de Monobank. Acompte encaissé à la confirmation de la commande. Solde déclenché lorsque l'état de la commande passe à « prête à expédier ». Chaque webhook est vérifié face à une clé publique PEM Monobank mise en cache, via ECDSA SHA256 avec rotation. Les volets de paiement sont des enregistrements en base, liés à l'état de la commande — et non réconciliés après coup.

La couche de tarification lit les prix par produit avant de retomber sur la base convertie au taux de change. Chaque ligne produit a un prix de base en EUR et des colonnes facultatives de prix en UAH et USD. Lorsqu'un prix spécifique est présent, la vitrine l'utilise. Lorsqu'il est absent, le taux de change quotidien de la Banque nationale d'Ukraine convertit la base en EUR. Le taux NBU se synchronise chaque jour via cron.

La couche logistique s'appuie sur un annuaire Nova Post synchronisé. Les villes, divisions et classificateurs sont paginés depuis l'API Nova Post et stockés localement — la sélection d'adresse au paiement interroge la copie locale, pas l'API en direct. Au moment de l'expédition, la plateforme génère un bordereau via l'API de Nova Post et stocke le PDF d'étiquette résultant comme objet présigné dans Cloudflare R2.

Le cycle de vie de production est l'ossature qui relie les volets de paiement et l'expédition. La machine à états de la commande — cinq états, un journal d'événements, un fil de photos d'avancement — existe pour que la transition acompte-vers-solde ait un déclencheur défini et que la génération du bordereau ait un moment défini. La machine à états n'est pas le produit. C'est le cadre porteur des flux financiers et logistiques.

F · 01Paiement fractionné Monobank
Acompte encaissé à la confirmation de la commande ; solde déclenché à « prête à expédier ». Les volets de paiement sont des enregistrements en base liés à l'état de la commande. Vérification de webhook ECDSA SHA256 avec rotation de clé PEM.
F · 02Multidevise par produit
Prix de base en EUR avec colonnes facultatives de prix en UAH et USD par produit. Le résolveur de prix lit le prix spécifique avant de retomber sur le taux de change quotidien de la NBU. Contrôle au niveau de l'artisan, pas de la région.
F · 03Intégration Nova Post
Annuaire des villes et divisions synchronisé localement via tâche planifiée. Bordereaux générés au moment de l'expédition via l'API de Nova Post. PDF d'étiquettes stockés comme objets présignés dans Cloudflare R2.
F · 04Cycle de vie de production
Machine à cinq états de commande (brouillon → en cours → prêt → expédié → annulé) avec journal d'événements et fil de photos d'avancement. Ossature pour les volets de paiement et la génération de bordereaux — pas l'objet central.
F · 05Vitrine en 7 locales
Boutique client en sept langues. Les prix par produit et le repli sur le taux de change NBU font que chaque locale voit une tarification correcte en contexte, sans configuration par région.
F · 06Tableau de bord d'exploitation sur mesure
Kanban de production, courbe de revenus en EUR sur 30 jours, pastilles de santé d'intégration (Monobank / Nova Post / R2 / e-mail), affichage de la fraîcheur des crons et un bandeau d'attention. Un seul écran pour toute l'exploitation.

Les choix d'ingénierie fintech.

Vérification de webhook ECDSA. Monobank signe les webhooks de paiement avec une clé privée ; le récepteur doit vérifier face à une clé publique mise en cache, faire tourner le cache lorsque Monobank en publie une nouvelle, et rejeter tout webhook qui ne passe pas le contrôle de signature. Une authentification de pacotille se contenterait de vérifier la valeur d'un en-tête. Une vraie authentification, c'est de l'ECDSA SHA256 face à une clé PEM avec rotation.

Des volets de paiement au niveau de la donnée. Le modèle de paiement fractionné n'est pas une config de passerelle de paiement. C'est un modèle de données : une commande a un ou plusieurs volets de paiement, chacun avec un montant, une devise, un statut et un déclencheur. Le volet d'acompte se déclenche à la confirmation de la commande. Le volet de solde se déclenche au changement d'état. Le journal d'événements porte la piste d'audit. Réconcilier après coup deux paiements API sans lien reviendrait à faire du tableau de bord de Monobank la source de vérité, et non de votre propre base de données. Nous avons refusé ce compromis.

Des prix par produit. La table de tarification comporte trois colonnes de prix facultatives au-delà de la base EUR : UAH, USD et un indicateur « prix sur demande ». Le résolveur de prix les lit par ordre de priorité : prix explicite → base convertie au taux de change → demande. L'artisan contrôle la tarification au niveau du produit, pas au niveau de la région.

La spécificité du marché local est la barrière à l'entrée. Un concurrent peut copier le design de la vitrine. Il ne peut pas faire tourner le même modèle de paiement sans réécrire la couche de paiement pour l'API spécifique de Monobank.

Synchronisation de l'annuaire Nova Post. Appeler l'API Nova Post en direct à chaque saisie d'adresse au paiement coupleraît le flux d'achat à une dépendance externe sans SLA maîtrisé par la plateforme. La synchronisation de l'annuaire tourne comme tâche planifiée : des pages de villes, de divisions et de classificateurs écrites localement, le paiement interroge la copie locale. Le seul appel Nova Post en direct dans le chemin d'achat critique est la génération du bordereau au moment de l'expédition — et cela se produit une fois la commande déjà payée.

Le résultat.

La fondatrice pilote toute l'exploitation depuis un seul écran. Kanban de production par état de commande, courbe de revenus sur 30 jours en EUR, pastilles de santé d'intégration pour Monobank, Nova Post, R2 et l'e-mail, affichage de la fraîcheur des crons et un bandeau d'attention qui fait remonter tout ce qui nécessite une action. Environ 1 à 2 K lignes de composant de tableau de bord — conçu sur mesure, pas générique.

145 commits en 3 semaines. 659 fichiers de tests couvrant le chemin ECDSA de Monobank, la réconciliation de l'annuaire Nova Post, la sémantique des lots du panier, la capture des taux de change et les transitions d'état des volets de paiement. Traces Sentry à 5 % en production avec un nettoyeur de données personnelles. Axiom pour les événements structurés de heartbeat des crons. L'observabilité de niveau production n'a pas été rajoutée après coup — elle faisait partie du premier sprint.

Citation / 04
La stack n'est pas sur mesure parce que nous voulions écrire du code sur mesure. Elle est sur mesure parce que le marché n'a aucun rail prêt à l'emploi.
FondatriceCommerce de marque artisanale
Outcome
Build time
3 weeks
Locales
7
Test files
659
Commits
145
NEXTÉtude de cas 01SW · 01 sur 10
Real estate2022 — 2024

Une app de visite immobilière en RA pour les acheteurs qui ne peuvent pas être sur place.