← Tutte le idee Strumenti online

Retentix

Quando un cliente disdice, gli chiede perché e gli fa un'offerta su misura

Cosa fa

Per chi vende abbonamenti con Stripe o Paddle: quando un cliente vuole disdire, mostra una breve domanda sul motivo e, in base alla risposta, un'offerta (sconto, pausa, piano più economico) per farlo restare.

Come guadagna

Abbonamento per chi vende software in abbonamento. Era in vendita su SideProjectors a circa 2.000 $.

Rendilo tuo

  • Per palestre e corsi con abbonamento, non solo software: stessa logica, clienti diversi.
  • Report mensile in italiano: perché i clienti se ne vanno e quanti ne hai salvati.

Le frasi da scrivere all'AI

1. Base del sito e grafica

Crea un'app web in italiano chiamata "Resta con Noi": per chi vende abbonamenti con Stripe: quando un cliente vuole disdire, mostra una breve domanda sul motivo e, in base alla risposta, un'offerta (sconto, pausa, piano più economico) prima di confermare la disdetta. Usa React, Vite, TypeScript e Tailwind CSS.

Pagine da creare:
1. Pagina iniziale con i risultati di esempio e i prezzi
2. Registrazione e collegamento dell'account Stripe
3. Editor del percorso di disdetta: domande, offerte, testi
4. Codice da incollare nel proprio sito al posto del pulsante "Disdici"
5. Statistiche: motivi, offerte accettate, entrate salvate

Stile: da strumento SaaS: sfondo bianco, viola Stripe #635BFF, verde #16A34A per le entrate salvate, carattere "Inter", anteprima del percorso a destra mentre si modifica. Nessuna immagine generica di stock.

Tutto deve funzionare perfettamente da telefono. Tutti i testi in italiano, chiari e senza parole tecniche.

2. Database, account e sicurezza

Collega il progetto a Supabase e attiva la registrazione con email e password.

Crea queste tabelle:
- "account": account Stripe collegato (Stripe Connect), impostazioni del percorso, collegati all'utente
- "tentativi": cliente, motivo scelto, offerta mostrata, esito (accettata, disdetto), valore mensile dell'abbonamento

Regole di sicurezza obbligatorie:
- ogni utente vede solo i suoi percorsi e tentativi
- le chiavi di Stripe restano sul server
- il piano non si modifica dal browser

Usa esattamente il codice SQL che trovi nei "Dettagli tecnici" di questa scheda: eseguilo nel SQL Editor di Supabase prima di continuare, poi collega le pagine alle tabelle.

3. Il percorso di disdetta

L'utente collega il suo account Stripe con Stripe Connect e costruisce il percorso: una domanda "Perché vuoi disdire?" con 5 motivi (troppo caro, non lo uso abbastanza, manca una funzione, passo a un altro, altro) e per ogni motivo un'offerta: sconto del X% per N mesi, pausa di 1–3 mesi, passaggio a un piano più economico, o nessuna offerta.

Nel suo sito sostituisce il pulsante "Disdici" con il nostro piccolo script, che apre il percorso in una finestra. Se il cliente accetta un'offerta, la Edge Function la applica direttamente su Stripe (codice nei Dettagli tecnici); se conferma la disdetta, l'abbonamento si chiude a fine periodo.

Regola importante: disdire deve restare semplice. Al massimo una domanda e un'offerta, e il pulsante "Disdici comunque" sempre visibile, come richiede la legge sui consumatori.

4. Statistiche

Statistiche: tentativi di disdetta del mese, percentuale salvata, entrate mensili salvate, motivi più frequenti, quale offerta funziona meglio per ogni motivo.

Elenco dei commenti liberi dei clienti ("manca la funzione X"), utili per migliorare il prodotto.

5. Pagamenti con Stripe

Aggiungi un abbonamento a 29 € al mese. Gratis l'utente ha: 50 tentativi di disdetta al mese e solo l'offerta di sconto. Con il piano Pro sblocca: tentativi illimitati, tutte le offerte, test A/B tra offerte e statistiche complete.

A pagamento riuscito il webhook porta l'utente al piano Pro; se l'abbonamento viene disdetto o non rinnovato lo riporta al piano gratuito.

Per la sessione di pagamento e il webhook segui la guida "Pagamenti con Stripe" di Fattibile.

6. Controlli specifici di questo progetto

Oltre ai controlli della guida "Controllo finale", verifica in particolare:
1. Correttezza: verifica in modalità test di Stripe che il pulsante "Disdici comunque" disdica davvero l'abbonamento, sempre, con al massimo due clic.
Correggi quello che non va e scrivimi cosa hai cambiato.

Dettagli tecnici

Applicare l'offerta su Stripe

Applica l'offerta accettata all'abbonamento del cliente, sull'account Stripe collegato: sconto, pausa o disdetta a fine periodo.

import Stripe from "npm:stripe";

const stripe = new Stripe(Deno.env.get("STRIPE_SECRET_KEY")!, { httpClient: Stripe.createFetchHttpClient() });

type Esito =
  | { tipo: "sconto"; percentuale: number; mesi: number }
  | { tipo: "pausa"; mesi: number }
  | { tipo: "disdici" };

export async function applica(accountCollegato: string, abbonamentoId: string, esito: Esito) {
  const opzioni = { stripeAccount: accountCollegato };   // agisce sull'account del cliente

  if (esito.tipo === "sconto") {
    const coupon = await stripe.coupons.create(
      { percent_off: esito.percentuale, duration: "repeating", duration_in_months: esito.mesi },
      opzioni,
    );
    return stripe.subscriptions.update(abbonamentoId, { discounts: [{ coupon: coupon.id }] }, opzioni);
  }

  if (esito.tipo === "pausa") {
    const riprende = Math.floor(Date.now() / 1000) + esito.mesi * 30 * 86_400;
    return stripe.subscriptions.update(
      abbonamentoId,
      { pause_collection: { behavior: "void", resumes_at: riprende } },   // niente fatture nella pausa
      opzioni,
    );
  }

  // Disdetta: resta attivo fino alla fine del periodo già pagato
  return stripe.subscriptions.update(abbonamentoId, { cancel_at_period_end: true }, opzioni);
}

Entrate salvate per motivo

I tentativi di disdetta e la query che mostra, per ogni motivo, quanti clienti sono stati trattenuti e quanto valgono al mese.

create table tentativi (
  id uuid primary key default gen_random_uuid(),
  utente_id uuid not null references auth.users(id) on delete cascade,
  cliente_stripe text not null,
  motivo text not null,
  offerta text,
  esito text not null check (esito in ('accettata', 'disdetto', 'abbandonato')),
  valore_mensile_centesimi int not null,
  commento text,
  creato_il timestamptz not null default now()
);
alter table tentativi enable row level security;
create policy "i miei tentativi" on tentativi for select using (auth.uid() = utente_id);

select motivo,
       count(*) as tentativi,
       count(*) filter (where esito = 'accettata') as salvati,
       round(100.0 * count(*) filter (where esito = 'accettata') / count(*), 1) as percentuale,
       sum(valore_mensile_centesimi) filter (where esito = 'accettata') / 100.0 as euro_al_mese_salvati
from tentativi
where utente_id = auth.uid() and creato_il > now() - interval '30 days'
group by motivo
order by tentativi desc;

Pezzi comuni a tutti i progetti

Glossario

Stripe Connect
La funzione di Stripe che permette a un servizio di agire sull'account Stripe di un cliente, con il suo permesso.
Churn
La percentuale di clienti che disdice l'abbonamento ogni mese.