Adicione assinaturas Stripe ao seu app web de IA

Monetize seu app web de IA adicionando assinaturas Stripe (mensal e anual).

Roadmap e recursos

Pré-requisitos (antes de começar)

Checklist de pré-requisitos

Confirme que você tem estes itens prontos antes de começar.

Antes de seguir este tutorial do Stripe, confirme que seu app web já tem o seguinte pronto:

1. Seu app já está no ar (URL em produção obrigatória)

O Stripe Checkout só funciona em um site implantado de verdade — ele NÃO funciona dentro do ambiente de preview do Google AI Studio.

Vídeo de deploy

Assistir ao tutorial de deploy

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

---

2. O Firebase está totalmente configurado (um único vídeo cobre tudo)

Sua configuração do Firebase já precisa estar concluída, incluindo: - Firebase Authentication habilitado - Firestore Database criado - Seu app web conectado ao Firebase (usando sua configuração do Firebase)

Vídeo de configuração do Firebase

Assistir ao tutorial do Firebase

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

---

✔ Depois que esses pré-requisitos estiverem prontos, você pode seguir do Passo 1 → Passo 5 sem problemas.

Criar planos de assinatura mensal e anual

Abra o Stripe e mude para o Test Mode

Crie um plano de assinatura recorrente mensal

Crie um plano de assinatura recorrente anual

Salve os valores do Stripe necessários para uso posterior

Conecte os pagamentos do Stripe ao seu app

Abrir Firebase → Extensions

Instale "Run Payments with Stripe" (da Invertase)

Cole sua Stripe Secret Key nas configurações da extensão

Crie um webhook no Stripe (selecione os eventos necessários)

- 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

Copie a Webhook Endpoint URL do Firebase Functions e cole no Stripe

Copie o Signing Secret do Stripe e cole no Firebase (Webhook Secret)

Salve e confirme que tudo está conectado

Configurar páginas de sucesso e cancelamento de pagamento

Abra seu projeto no Google AI Studio

Rode o Prompt #1: criar a configuração de billing (URL em produção + Price IDs)

Prompt: criar configuração de billing

Crie um arquivo de configuração de cobrança e preencha com meus valores reais…

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.

Rode o Prompt #2: criar as páginas de sucesso + cancelamento

Prompt: criar páginas de sucesso e cancelamento

Adicione duas rotas/páginas simples para os redirecionamentos do 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.

Rode os prompts e confirme que os arquivos/páginas foram adicionados

Faça o botão de upgrade abrir o Stripe Checkout

(Opcional) Crie um botão de upgrade

Rode o Prompt #3: adicionar o modal de escolha mensal/anual

Prompt: adicionar UI de escolha de upgrade

Adicione uma UI simples para escolha de upgrade sem redesenhar o 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.

Rode o Prompt #4: conectar a escolha ao Stripe Checkout

Prompt: conectar o Stripe Checkout

Implemente o checkout de assinatura do Stripe usando a 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)

Teste pagamentos usando sua URL em produção

Rode o Prompt #5: atualizar a conta de Free → Pro automaticamente

Prompt: detecção do plano Pro

Adicione a detecção do plano Pro (somente leitura) a partir do 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.

Rode o Prompt #6: adicionar Manage Subscription e conectá-lo ao Stripe Billing Portal

Prompt: botão de gerenciar assinatura

Tarefa: adicionar um botão "Manage Subscription" funcional para usuários 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.

Adicionar limites Gratuito vs Pro (configuração SaaS real)

Rode o Prompt #7: adicionar limites Free vs Pro

Prompt: adicionar limites de uso

Tarefa: adicionar limites de uso Free/Pro SEM tocar nem mudar o sistema existente de 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.

Adicionar regras do Firestore (usuários só acessam seus próprios dados)

Importante: estas regras de segurança do Firestore são apenas um exemplo. Você DEVE revisá-las e adaptá-las à estrutura de dados do seu próprio app web. Cada app precisa de regras que combinem com suas coleções e modelo de dados 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; } } } }

Adicionar regras do Storage (usuários só acessam os próprios arquivos)

Importante: estas regras de segurança do Storage são apenas um exemplo. Você DEVE revisá-las e adaptá-las ao caminho do seu próprio projeto (por exemplo, o nome da pasta uploads) e aos seus limites. Cada app precisa de regras de storage que combinem com a sua própria estrutura.

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) Rode um prompt rápido para pequenos ajustes de UI (por exemplo, atualizar os preços no modal)

Você pode rodar um prompt extra depois para polir detalhes da UI (como atualizar os preços no modal), mas não fornecemos um prompt específico aqui porque depende muito das suas personalizações de UI.