Añade suscripciones de Stripe a tu app web de IA

Monetiza tu app web con IA añadiendo suscripciones de Stripe (mensual y anual).

Hoja de ruta y recursos

Requisitos (antes de empezar)

Lista de requisitos previos

Asegúrate de tener esto listo antes de empezar.

Antes de seguir este tutorial de Stripe, asegúrate de que tu app web ya tenga lista lo siguiente:

1. Tu app ya está desplegada (se necesita una URL en producción)

Stripe Checkout solo funciona en un sitio web desplegado real — NO funciona dentro del entorno de vista previa de Google AI Studio.

Vídeo de despliegue

Ver tutorial de despliegue

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

---

2. Firebase está totalmente configurado (un solo vídeo lo cubre todo)

Tu configuración de Firebase ya debe estar completa, incluyendo: - Firebase Authentication habilitado - Firestore Database creada - Tu app web conectada a Firebase (usando tu configuración de Firebase)

Vídeo de configuración de Firebase

Ver tutorial de Firebase

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

---

✔ Una vez listos estos requisitos, puedes continuar con el Paso 1 → Paso 5 sin problemas.

Crear planes de suscripción mensual y anual

Abre Stripe y cambia a Test Mode

Crea un plan de suscripción mensual recurrente

Crea un plan de suscripción anual recurrente

Guarda los valores de Stripe necesarios para usarlos más tarde

Conectar los pagos de Stripe a tu app

Abrir Firebase → Extensions

Instala "Run Payments with Stripe" (de Invertase)

Pega tu Stripe Secret Key en los ajustes de la extensión

Crea un webhook en Stripe (selecciona los eventos requeridos)

- 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 de Firebase Functions y pégala en Stripe

Copia el Signing Secret de Stripe y pégalo en Firebase (Webhook Secret)

Guarda y confirma que todo esté conectado

Configurar las páginas de éxito y cancelación del pago

Abre tu proyecto en Google AI Studio

Ejecuta el Prompt #1: crear la configuración de facturación (URL desplegada + Price IDs)

Prompt: Crear configuración de facturación

Crea un archivo de configuración de facturación y rellénalo con mis valores reales…

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.

Ejecuta el Prompt #2: crear las páginas de éxito + cancelación

Prompt: Crear páginas de éxito y cancelación

Añade dos rutas/páginas sencillas para las redirecciones de 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.

Ejecuta los prompts y confirma que los archivos/páginas se hayan añadido

Haz que el botón de mejora abra el checkout de Stripe

(Opcional) Crear un botón para mejorar plan

Ejecuta el Prompt #3: añadir el modal de elección mensual/anual

Prompt: Añadir UI de elección de mejora

Añade una UI sencilla para elegir mejora sin rediseñar la 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.

Ejecuta el Prompt #4: conectar la elección a Stripe Checkout

Prompt: Conectar Stripe Checkout

Implementa el checkout de suscripciones con 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)

Prueba los pagos usando tu URL en producción

Ejecuta el Prompt #5: actualizar la cuenta automáticamente de Free → Pro

Prompt: Detección del plan Pro

Añade detección del plan Pro (solo lectura) desde 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.

Ejecuta el Prompt #6: añadir Manage Subscription y conectarlo al Stripe Billing Portal

Prompt: Botón de gestionar suscripción

Tarea: añadir un botón "Manage Subscription" funcional para usuarios 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.

Añadir límites Gratis vs Pro (configuración real de SaaS)

Ejecuta el Prompt #7: añadir límites Free vs Pro

Prompt: Añadir límites de uso

Tarea: añadir límites de uso Free/Pro SIN tocar ni cambiar el sistema existente de facturación/mejora…

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.

Añadir reglas de Firestore (los usuarios solo pueden acceder a sus propios datos)

Importante: estas reglas de seguridad de Firestore son un ejemplo. DEBES revisarlas y adaptarlas para que coincidan con la estructura de datos de tu propia app web. Cada app necesita reglas que se ajusten a sus colecciones y modelo de datos específicos.

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; } } } }

Añadir reglas de Storage (los usuarios solo pueden acceder a sus propios archivos)

Importante: estas reglas de seguridad de Storage son un ejemplo. DEBES revisarlas y adaptarlas para que coincidan con la ruta de tu propio proyecto (por ejemplo, el nombre de la carpeta uploads) y con tus límites. Cada app necesita reglas de storage que se ajusten a su propia estructura.

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); } } }

(Opcional) Ejecuta un prompt rápido para pequeños ajustes de UI (por ejemplo, actualizar los precios en el modal)

Puedes ejecutar un prompt extra más tarde para pulir detalles de la UI (como actualizar los precios en el modal), pero no incluimos un prompt específico aquí porque depende mucho de tus personalizaciones concretas de UI.