Come funziona davvero il tracking degli affiliati con Stripe: cookie, webhook e storni
Se vendi tramite Stripe e stai configurando un programma di affiliazione — o costruendo il tracking internamente — devi risolvere quattro problemi: portare un codice referral al visitatore, trasferirlo sull'oggetto Stripe, ricevere in modo affidabile gli eventi di pagamento e gestire i rimborsi senza pagare commissioni su denaro restituito. Ecco come funziona ogni parte.
1. Portare il codice al visitatore
Un link affiliato assomiglia a yoursite.com/?via=CODE oppure punta a un redirect breve come yoursite.com/r/CODE. Il redirect è più pulito perché non mostra il codice nella barra degli indirizzi e consente di registrare il clic lato server prima di inoltrare il visitatore.
All'arrivo devi ricordare il codice fino alla conversione, che può avvenire minuti o settimane dopo. L'approccio standard è un cookie first-party, per esempio ambassly_ref, impostato per 60 giorni con SameSite=Lax, insieme a una copia in localStorage come percorso di lettura di riserva.
Ecco ciò che molti tutorial omettono: se imposti il cookie con JavaScript lato client, Intelligent Tracking Prevention di Safari ne limita la durata a circa sette giorni, qualunque scadenza tu abbia richiesto. Un visitatore che salva un tutorial e torna ad acquistare tre settimane dopo con Safari perde silenziosamente l'attribuzione. La soluzione è impostare il cookie tramite l'header HTTP Set-Cookie del server invece di document.cookie, perché il limite ITP si applica ai cookie client, non a quelli server. Se non puoi farlo per ogni richiesta, almeno comunica chiaramente la limitazione agli affiliati invece di sottostimare le conversioni senza dirlo.
2. Portare il codice sull'oggetto Stripe
Il cookie serve solo fino all'acquisto. Poi il codice deve viaggiare con il pagamento, perché i cookie non sopravvivono sempre al passaggio verso Checkout ospitato da Stripe e al ritorno.
Stripe offre due posti:
client_reference_idsu una Checkout Session, un campo stringa pensato proprio per associare il tuo riferimento a un pagamento.metadata, un oggetto libero chiave/valore disponibile su Checkout Sessions, PaymentIntents e Subscriptions.
Usare entrambi è il modello più sicuro. Imposta client_reference_id sul codice affiliato e assegna lo stesso valore a metadata.ambassly_ref (o alla chiave scelta). client_reference_id è il campo singolo più idiomatico, ma i metadati arrivano su oggetti successivi come le fatture in modi talvolta più comodi da interrogare. Questa ridondanza non costa nulla.
const session = await stripe.checkout.sessions.create({
client_reference_id: referralCode,
metadata: { ambassly_ref: referralCode },
line_items: [...],
mode: "subscription",
success_url: "...",
cancel_url: "...",
});
3. Ricevere gli eventi in modo affidabile: webhook e idempotenza
Devi ora sapere quando la Checkout Session si completa e, per un abbonamento, quando vengono pagate le fatture successive. Serve un endpoint webhook che ascolti almeno:
checkout.session.completed, per risolvere il codice e creare il referral iniziale.invoice.paid, per creare una commissione a ogni ciclo di fatturazione dei programmi ricorrenti.charge.refundedecustomer.subscription.deleted, per attivare gli storni.
Il dettaglio decisivo è l'idempotenza. La documentazione sui webhook di Stripe chiarisce che lo stesso evento può arrivare più di una volta: Stripe riprova quando non riceve una risposta 2xx pulita e gli errori di rete accadono su entrambi i lati. Se l'handler non è idempotente, il retry di invoice.paid crea una seconda commissione per la stessa fattura e devi pagare due volte l'affiliato per un solo pagamento.
La soluzione è semplice: conserva una tabella degli ID evento Stripe elaborati (stripe_event_id con vincolo univoco) e inserisci l'ID prima di qualsiasi altra operazione. Se l'inserimento fallisce per il vincolo, hai già visto l'evento: ignoralo e restituisci 200. Ogni effetto successivo — creazione di una commissione o aggiornamento del referral — deve avvenire solo dopo l'inserimento riuscito.
4. Storni per rimborso
L'ultima parte è quella di cui ogni programma avrà bisogno e che pochi costruiscono subito: cosa succede quando il cliente riceve un rimborso o annulla e il pagamento viene stornato. Se hai già segnato una commissione come pending o approved e il relativo addebito viene rimborsato, la commissione deve passare a uno stato di storno, non restare silenziosamente nei registri.
Se la commissione era già stata pagata, non puoi annullare il trasferimento. L'approccio trasparente è una rettifica negativa: una nuova voce del libro mastro compensata sul pagamento successivo. In entrambi i casi funziona solo se ogni commissione conserva una cronologia degli eventi, non il solo stato corrente, così tu e l'affiliato potete vedere perché è cambiata.
Per questo conta anche un periodo di attesa. Aspettare da 30 a 45 giorni prima che una commissione diventi pagabile, circa quanto la finestra di rimborso, fa arrivare la maggior parte dei rimborsi prima del pagamento ed elimina quasi del tutto gli storni successivi.
Costruirlo o comprarlo
Nulla di tutto ciò è esotico: un cookie, due campi Stripe, un handler webhook idempotente e una macchina a stati con audit trail. Molti team lo costruiscono in un fine settimana e per un po' funziona. Di solito si rompe proprio nei casi sopra: limite dei cookie Safari, webhook ritentato, rimborso dopo il pagamento. Abbiamo creato Ambassly perché questi tre scenari siano gestiti correttamente una volta sola, invece di essere riscoperti da ogni team di developer tool.
Se lo sviluppi internamente, vale la pena leggere integralmente la documentazione Stripe su Connect e sulla gestione idempotente delle richieste prima di pubblicare il primo handler webhook. È un pomeriggio ben speso in entrambi i casi.