Aggiungi abbonamenti Stripe alla tua app web di IA

Monetizza la tua app web di IA aggiungendo abbonamenti Stripe (mensile e annuale).

Roadmap e risorse

Requisiti (prima di iniziare)

Checklist dei prerequisiti

Assicurati di avere questi elementi pronti prima di iniziare.

Prima di seguire questo tutorial Stripe, assicurati che la tua app web abbia già pronto quanto segue:

1. La tua app è già distribuita (è richiesta una URL in produzione)

Stripe Checkout funziona solo su un sito veramente distribuito — NON funziona dentro l'ambiente di anteprima di Google AI Studio.

Video sul deploy

Guarda il tutorial di deploy

https://youtu.be/HGs-4QjyjHQ?si=EBUOpPx38n9vVROR

---

2. Firebase è completamente configurato (un solo video copre tutto)

La tua configurazione di Firebase deve essere già completa, inclusi: - Firebase Authentication abilitato - Firestore Database creato - La tua app web collegata a Firebase (usando la tua configurazione Firebase)

Video di configurazione di Firebase

Guarda il tutorial di Firebase

https://youtu.be/juEfJERgBE8?si=je5VF4gKMUJXl8kX

---

✔ Una volta pronti questi requisiti, puoi continuare con il Passo 1 → Passo 5 senza problemi.

Creare piani di abbonamento mensile e annuale

Apri Stripe e passa al Test Mode

Crea un piano di abbonamento ricorrente mensile

Crea un piano di abbonamento ricorrente annuale

Salva i valori Stripe richiesti per un uso successivo

Collega i pagamenti Stripe alla tua app

Apri Firebase → Extensions

Installa "Run Payments with Stripe" (di Invertase)

Incolla la tua Stripe Secret Key nelle impostazioni dell'estensione

Crea un webhook in Stripe (seleziona gli eventi richiesti)

- checkout.session.completed

- customer.subscription.created

- invoice.paid

- invoice.payment_failed

- customer.subscription.updated

- customer.subscription.deleted

- invoice.upcoming

- payment_intent.succeeded

- payment_intent.payment_failed

- customer.subscription.paused

- charge.refunded

- charge.dispute.created

Copia la Webhook Endpoint URL da Firebase Functions e incollala in Stripe

Copia il Signing Secret di Stripe e incollalo in Firebase (Webhook Secret)

Salva e conferma che tutto sia collegato

Configurare le pagine di successo e annullamento del pagamento

Apri il tuo progetto in Google AI Studio

Esegui il Prompt #1: crea la configurazione del billing (URL distribuita + Price ID)

Prompt: creare la configurazione del billing

Crea un file di configurazione del billing e riempilo con i miei valori reali…

Create a billing config file and fill it with my real values (no placeholders). Use EXACTLY: DEPLOYED_URL = "https://deployment-url.com" MONTHLY_PRICE_ID = "price_MONTHLY_HERE" YEARLY_PRICE_ID = "price_YEARLY_HERE" Create: src/billingConfig.js (or .ts if the project is TypeScript) and export: - DEPLOYED_URL - MONTHLY_PRICE_ID - YEARLY_PRICE_ID - A function successUrl() that returns the correct URL for /success - A function cancelUrl() that returns the correct URL for /failure Important: - Detect if the app uses hash routing automatically: If existing routes contain "/#/" then use DEPLOYED_URL + "/#/success" and "/#/failure" Otherwise use DEPLOYED_URL + "/success" and "/failure" Do not change any existing UI or app logic. Return only the new file content.

Esegui il Prompt #2: crea le pagine di successo + annullamento

Prompt: creare le pagine di successo e di annullamento

Aggiungi due rotte/pagine semplici per i redirect di Stripe…

Add two simple routes/pages for Stripe redirects: success and failure. Success page: - Text: "Payment successful" - Redirect to dashboard after 1.5 seconds Failure page: - Text: "Payment cancelled" - Redirect to dashboard after 1.5 seconds Use the same routing style already used in the project (hash or normal). Do not redesign UI. Return exact files changed.

Esegui i prompt e conferma che i file/le pagine siano stati aggiunti

Fa' in modo che il pulsante Upgrade apra Stripe Checkout

(Opzionale) Crea un pulsante di upgrade

Esegui il Prompt #3: aggiungi la modale di scelta mensile/annuale

Prompt: aggiungere UI di scelta dell'upgrade

Aggiungi una UI semplice per la scelta dell'upgrade senza riprogettare l'app…

Add a simple upgrade choice UI without redesigning the app. When the user clicks the existing Upgrade button: - Show a small modal (or dropdown) with 2 options: 1) Monthly 2) Yearly - Add a close button. Keep current styling. Do not change any other UI. Return exact files changed.

Esegui il Prompt #4: collega la scelta a Stripe Checkout

Prompt: collegare Stripe Checkout

Implementa il checkout di abbonamento Stripe usando la Firebase Stripe Extension…

Implement Stripe subscription checkout using Firebase Stripe Extension. On Monthly selection: - Create a Firestore doc at: customers/{uid}/checkout_sessions/{autoId} - Fields: price: MONTHLY_PRICE_ID (import from billingConfig) mode: "subscription" success_url: successUrl() (import from billingConfig) cancel_url: cancelUrl() (import from billingConfig) On Yearly selection: - Same but price: YEARLY_PRICE_ID After creating the doc: - Listen to that same checkout session document until it contains a field "url" - Redirect the browser to that url Do not add Stripe secret keys to frontend. Do not touch existing CRUD features. Return exact files changed. Test payments using your deployed live URL (not the preview)

Testa i pagamenti usando la tua URL in produzione

Esegui il Prompt #5: aggiorna automaticamente l'account da Free → Pro

Prompt: rilevamento del piano Pro

Aggiungi il rilevamento del piano Pro (sola lettura) da Firestore…

Add Pro plan detection (read-only) from Firestore. - Listen in real-time (onSnapshot) to: customers/{uid}/subscriptions - Set isPro = true if any subscription status is "active" or "trialing" - If isPro true: - Show a "Pro" badge - Hide the Upgrade button - If isPro false: - Show a "Free" badge - Show the Upgrade button Important: Do NOT block Free users from using the app features. Do not redesign UI. Return exact files changed.

Esegui il Prompt #6: aggiungi Manage Subscription e collegalo allo Stripe Billing Portal

Prompt: pulsante di gestione dell'abbonamento

Compito: aggiungere un pulsante "Manage Subscription" funzionante per gli utenti PRO…

Task: Add a working "Manage Subscription" button for PRO users using Stripe Customer Portal (Firebase Stripe Extension). Context: - The app already uses the Firebase Stripe Extension (customers/{uid} and checkout_sessions work). - The app uses hash routing (/#/dashboard). - Do NOT change the UI design. Only add logic. - The button must work in production (no CORS errors). Requirements: 1) Show "Manage Subscription" button ONLY when the current user is PRO (isPro === true). 2) On click, DO NOT call any cloudfunctions.net URL directly and DO NOT use fetch to Cloud Functions. 3) Instead, create a Firestore document at: customers/{uid}/portal_sessions/{autoId} with fields: - return_url: "https://vloma.com/#/dashboard" 4) After creating the doc, listen in real-time to that same document until the extension writes a field: - url Then redirect the browser to that url (window.location.href = url). 5) Add basic UX: - disable the button while loading - show a friendly alert if no url appears after 20 seconds - log the real error to console Implementation notes: - Use the existing Firebase client (auth + firestore). - Keep existing files structure. Modify only the minimal files needed. - Return the exact files changed and the exact code changes. Deliverable: - A working Manage Subscription button that opens Stripe Customer Portal and returns to /#/dashboard.

Aggiungere limiti Gratuito vs Pro (configurazione SaaS reale)

Esegui il Prompt #7: aggiungi i limiti Free vs Pro

Prompt: aggiungere limiti di utilizzo

Compito: aggiungere limiti di utilizzo Free/Pro SENZA toccare o cambiare il sistema esistente di billing/upgrade…

Task: Add Free/Pro usage limits WITHOUT touching or changing the existing billing/upgrade system. CRITICAL RULES (must follow): - DO NOT modify the existing Stripe checkout / upgrade code. - DO NOT modify how "isPro" is determined in the project. - DO NOT create or use users/{uid}.plan field (do not add plan to Firestore at all). - DO NOT change Firestore rules or Storage rules. - Keep UI the same (only small toast/alert/modal allowed). Limits: - FREE: max 5 files, max 5 folders, max 100MB total stored - PRO: max 50 files, max 50 folders, max 5GB total stored How to implement (safe approach): 1) Use the CURRENT existing "isPro" boolean/state already working in the app. - Whatever source it currently uses (Stripe Extension subscriptions / custom claims / existing code), keep it exactly as-is. - Only READ isPro, do not re-implement it. 2) Track usage from the existing Firestore structure (do not rename collections): - Files count = number of docs in users/{uid}/files - Folders count = number of docs in users/{uid}/folders - Total storage used = sum of file sizes from the file docs (field: size bytes). If old docs don’t have size, treat missing size as 0, and ensure new uploads always write size. 3) Enforce limits ONLY in the UI/Client for now: - Before creating a new file doc or uploading: check limits using current isPro. - Before creating a new folder doc: check limits using current isPro. - If Free exceeds limit: block action and show: “Free plan limit reached. Upgrade to Pro to continue.” Add an “Upgrade” button that triggers the EXISTING checkout flow (call the same function already used in the app). 4) IMPORTANT: After checkout completes, the app MUST refresh/reflect Pro automatically: - Do not add new plan logic. - Just ensure the same existing listener/state updates isPro and the UI re-checks limits. - Add a small debug log (console.log) showing current isPro value after returning from checkout. Deliverable: - Limits work and show upgrade prompt. - Paying and upgrading still changes the app to Pro exactly like before. - Return exact files changed.

Aggiungere regole Firestore (gli utenti accedono solo ai propri dati)

Importante: queste regole di sicurezza di Firestore sono un esempio. DEVI rivederle e adattarle alla struttura dati della tua app web. Ogni app ha bisogno di regole adatte alle sue collezioni e al suo modello dati specifici.

firestore.rules

rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { function signedIn() { return request.auth != null; } function isOwner(uid) { return signedIn() && request.auth.uid == uid; } // ------------------------- // Users data (your app data) // ------------------------- match /users/{uid} { allow read, write: if isOwner(uid); // Everything under this user is also only for the owner match /{allPaths=**} { allow read, write: if isOwner(uid); } } // ------------------------- // Stripe Extension // customers/{uid}/... // ------------------------- match /customers/{uid} { // user can read their stripe docs allow read: if isOwner(uid); // user must NOT write to stripe docs directly allow write: if false; // user can create a checkout session doc match /checkout_sessions/{id} { allow create, read: if isOwner(uid); allow update, delete: if false; } // user can create a portal session doc match /portal_sessions/{id} { allow create, read: if isOwner(uid); allow update, delete: if false; } // subscriptions/payments are server-controlled (read only) match /subscriptions/{id} { allow read: if isOwner(uid); allow write: if false; } match /payments/{id} { allow read: if isOwner(uid); allow write: if false; } } } }

Aggiungere regole Storage (gli utenti accedono solo ai propri file)

Importante: queste regole di sicurezza di Storage sono un esempio. DEVI rivederle e adattarle al percorso del tuo progetto (ad esempio, il nome della cartella uploads) e ai tuoi limiti. Ogni app ha bisogno di regole di storage adatte alla propria struttura.

storage.rules

rules_version = '2'; service firebase.storage { match /b/{bucket}/o { function signedIn() { return request.auth != null; } function isOwner(uid) { return signedIn() && request.auth.uid == uid; } match /user_uploads/{uid}/{allPaths=**} { allow read: if isOwner(uid); // write create/update/delete // request.resource null delete، allow write: if isOwner(uid) && (request.resource == null || request.resource.size <= 200 * 1024 * 1024); } } }

(Opzionale) Lancia un prompt rapido per piccole correzioni di UI (per esempio, aggiornare i prezzi nella modale)

Più avanti puoi eseguire un prompt extra per rifinire i dettagli di UI (come aggiornare i prezzi nella modale), ma non forniamo un prompt specifico qui perché dipende molto dalle tue personalizzazioni di UI.