SaaS · fatturazione e CRM
Sincronizzazione da Stripe Billing al CRM: abbonamenti, pagamenti e MRR come deal, senza eventi persi o duplicati
Un'app da marketplace che replica clienti, abbonamenti e pagamenti Stripe nel CRM come contatti e deal, costruita su un registro di webhook firmati, row-level security per tenant e una riconciliazione giornaliera.
- Settore
- SaaS · fatturazione e CRM
- Tecnologie
- TypeScript · NestJS · PostgreSQL 16 + Drizzle
La sfida
Le aziende che fatturano tramite Stripe volevano che il loro CRM mostrasse chi paga, con quale piano e per quale valore mensile, senza aggiornamenti manuali. I webhook di Stripe non arrivano in ordine, possono arrivare due volte e portano un'istantanea che potrebbe essere già superata: una sincronizzazione che si fida di loro scrive fasi sbagliate e deal duplicati. L'app serve inoltre molte aziende contemporaneamente e ne custodisce le chiavi Stripe, quindi isolamento e gestione dei segreti facevano parte del brief fin dal primo giorno.
La soluzione
Abbiamo costruito un backend ospitato che esegue tre processi da un'unica immagine: API, worker e scheduler. Il punto di ingresso verifica la firma Stripe sul corpo grezzo della richiesta, identifica il tenant solo dal token dell'endpoint e risponde 2xx senza eseguire logica di business. Ogni evento finisce nel registro e in coda con un'unica transazione, con un outbox perché nulla vada perso se Redis si ferma. Prima di ogni scrittura, il worker recupera da Stripe l'oggetto aggiornato e scarta gli eventi superati, poi associa gli abbonamenti ai deal con fase, MRR, valuta e contatto. Una riconciliazione giornaliera recupera tutto ciò che i webhook hanno mancato.
Come si collega
- 01 Verifica del webhook
- 02 Registro e coda
- 03 Stato aggiornato da Stripe
- 04 Associazione al deal
- 05 Scrittura nel CRM
- 06 Riconciliazione giornaliera
Cosa abbiamo realizzato
- Punto di ingresso dei webhook con verifica della firma sul corpo grezzo e risoluzione del tenant tramite il token dell'endpoint
- Registro degli eventi con accodamento transazionale e un processo che svuota l'outbox
- Code con una politica di nuovi tentativi, un circuit breaker e lock per oggetto
- Associazione tra abbonamento e deal con fase, MRR (segnalato quando incompleto), valuta e contatti
- Ricerca del deal che verifica la propria corrispondenza prima di scrivere
- Importazione iniziale dello storico in blocchi riprendibili, più una riconciliazione giornaliera
- Procedura guidata di configurazione in quattro passaggi e 11 lingue, che crea da sola i campi del CRM e le fasi della pipeline
- Più account Stripe per utente, job di conservazione dei dati e statistiche sulle installazioni
- Circa 590 casi di test automatizzati
Funzionalità
- Row-level security di PostgreSQL su ogni tabella di business; le chiavi univoche includono il tenant e l'account Stripe
- Cifratura a busta (envelope encryption) per i segreti salvati
- I campi e le fasi impostati a mano nel CRM non vengono mai sovrascritti
- Gli errori che un nuovo tentativo non può risolvere passano allo stato «richiede intervento» invece di essere ritentati all'infinito
- La pagina di configurazione non carica script di terze parti, perché gli utenti vi incollano una chiave Stripe
- Backup giornalieri, verificati con ripristini di prova
- Log strutturati senza dati personali
Integrazioni
- API e webhook di Stripe Billing
- L'API CRM del cliente: contatti, deal, pipeline e campi personalizzati
- Il marketplace di app del cliente
Tecnologie
Risultato
Distribuita su un server di staging ospitato e in collaudo con un account Stripe reale. Il collaudo e una revisione di sicurezza hanno rilevato dei difetti; quelli interni all'app sono stati corretti e ritestati. La pubblicazione nel marketplace seguirà il collaudo.
Perché leggere da Stripe prima di ogni scrittura?
Un webhook ti dice che qualcosa è cambiato, non che cosa è vero adesso. Due eventi sullo stesso abbonamento possono arrivare nell’ordine sbagliato, e il più vecchio sovrascriverebbe lo stato più recente. Per questo l’app legge dal webhook solo l’ID dell’evento, il suo tipo e l’ID dell’oggetto, poi recupera da Stripe l’oggetto aggiornato prima di scrivere qualsiasi cosa nel CRM. Grazie al registro un evento ripetuto non ha alcun effetto, e la riconciliazione giornaliera chiude ogni lacuna lasciata da una consegna mancata.
Parliamo del tuo progetto
Raccontaci il tuo progetto