Concevoir des systèmes fintech à l’échelle
Frontières claires autour de l’argent, commandes idempotentes et grands livres compréhensibles par la finance — pas seulement un Postgres plus gros.
Les systèmes monétaires cassent cher : doubles débits, settlements coincés dans des états ambigus, tableaux de rapprochement qui divergent de la base de référence. La solution est rarement une base plus grosse — ce sont des frontières explicites entre initiation, autorisation, compensation et rapprochement, chacune avec ses invariants et sa piste d’audit.
Intention de paiement idempotente
Les retries sont inévitables. Le client doit fournir une clé d’idempotence stable ; le serveur doit la traiter comme une clé métier unique, pas comme un en-tête optionnel. L’esquisse ci-dessous montre le chemin nominal : renvoyer la même intention si la clé a déjà été traitée.
import { randomUUID } from "node:crypto";
type CreatePayment = {
amount: number;
currency: string;
/** Client-generated; retries MUST reuse the same key */
idempotencyKey: string;
};
export async function createPaymentIntent(cmd: CreatePayment) {
const existing = await store.findByIdempotencyKey(cmd.idempotencyKey);
if (existing) return existing;
const intent = {
id: randomUUID(),
...cmd,
status: "requires_confirmation" as const,
};
await store.save(intent);
return intent;
}Grand livre et rapprochement
Si la finance ne peut pas expliquer votre grand livre en une session tableau blanc, l’opération paiera la taxe pour toujours. Modélisez les soldes en événements append-only quand c’est possible ; rendez « pourquoi ce montant est-il là ? » vérifiable depuis les données, pas le savoir tacite. Couplez cela à des jobs de rapprochement qui alignent les relevés PSP sur les événements internes — et alertez quand l’écart dépasse un seuil, pas quand quelqu’un remarque une feuille Excel.
Débit et débogage vont ensemble : un gros QPS ne sert à rien si chaque incident devient une enquête sur plusieurs jours. Investissez dans l’observabilité des flux monétaires aussi tôt que dans le cache.
