Turn Your AI App into a Paid SaaS with Claude Code
Add accounts, Stripe credit packs, secure AI credits and dashboards to your AI app, then launch it on Hostinger.
Roadmap & resources
Requirements
Claude Code
Used in Claude Desktop's Code tab, so you never need a terminal. Claude Code needs a paid Claude plan, such as Pro.
Download Claude Desktop
https://claude.com/download
Node.js
Version 20.19 or newer, to run the app on your computer while you build.
Download Node.js
https://nodejs.org/en/download
Supabase
Accounts and the database. The free plan is enough to build and launch.
Open Supabase
https://supabase.link/r4gdwhi
Stripe
Payments for the credit packs. You build and test everything in Stripe's test mode, with no real money.
Open Stripe
https://dashboard.stripe.com
Gemini API key
The key your AI Image Editor already uses as GEMINI_API_KEY. Google may bill image edits depending on your plan, so check its current pricing.
Open Google AI Studio API Keys
https://aistudio.google.com/apikey
Hostinger Node.js Web App hosting
The finished SaaS runs a Node.js backend, so it needs a plan with Node.js Web Apps. Get it now or when you reach Step 6.
Hostinger Node.js Web App Hosting
Your SaaS needs hosting that runs its Node.js backend.
Prepare Your AI App
Start From a Working AI Image Editor
You need the AI Image Editor running with your own Gemini key: upload an image, describe a change, and get an edited image back.
Build Your Own AI Image Editor With Google AI Studio
Don't have the app yet? Build it first, then come back.
/video/build-an-ai-image-editor-with-google-ai-studio
Your code is on GitHub? Open the repository, click Code, then Download ZIP, and unzip it into a folder on your computer.
Open the Project in Claude Code
In Claude Desktop, open the Code tab and choose the app's folder:
Claude Desktop Code Local Select the project folder Open Trust Workspace
Plan the SaaS Conversion
Send this prompt. Claude reads the app and plans the conversion without changing anything yet.
Plan the Paid SaaS
Claude inspects the existing app, finds the AI endpoint, and plans the React + Express SaaS before writing any code.
I want to turn this AI image editor into a paid SaaS. Inspect the project and plan the conversion. Do not change any files yet. 1. What exists today - How the app is structured: server file, frontend files, package.json scripts. - The AI endpoint: which route calls Gemini, and how it reads GEMINI_API_KEY. - Every feature that works now and must keep working. 2. The target architecture (fixed — do not suggest alternatives) - Frontend: React + Vite, rebuilt from the current frontend with the same features and look. - Backend: the existing Node.js + Express server, in the same project. It serves the built frontend and all /api routes, and listens on process.env.PORT. - Supabase: accounts and the database. - Stripe: one-time credit packs with Stripe Checkout and a verified webhook. No subscriptions. - Hosting: a Hostinger Node.js Web App, built with npm run build and started with npm start. 3. The plan - The folder structure after the conversion. - The database tables: profiles, credit packs (defined on the server), purchases, credit transactions and edits. - Every environment variable the app will need, and which ones are server-only. - The order of work: accounts, payments, protected AI usage, dashboards, tests, deployment. Keep the plan short. End with any question you need answered before you start.
Answer Claude's questions, then reply Approved to keep the plan.
Add User Authentication
Create a Supabase Project
Create the project your SaaS will use.
Open Supabase
https://supabase.link/r4gdwhi
Create a new project and save its database password somewhere safe. Wait until the project is ready. Open the project's Connect panel and keep the Project URL and the Publishable key at hand. Claude will ask for them.
The Secret key from Project Settings › API Keys is server-only. Give it only to the server's .env file when Claude asks — never put it in frontend code.
Add Accounts and Profiles
Send this prompt. Claude adds sign-up, sign-in and protected pages, and asks for your Supabase values.
Add Supabase Authentication
Sign-up, sign-in, protected customer pages, profiles and Row Level Security, following the approved plan.
Add Supabase authentication to this app, following the approved plan. SETUP - Ask me for the Supabase Project URL, the Publishable key and the Secret key. The browser uses only the Project URL and the Publishable key. The Secret key stays in the server's .env and is never sent to the browser. - Make sure .env is in .gitignore, and create a .env.example with every variable name and no values. ACCOUNTS - Email and password sign-up and sign-in, sign-out, and a clear error for a wrong password. - Protected customer pages: a signed-out visitor who opens them is sent to sign-in. - The Express server verifies the user's Supabase session on every protected /api request. Never trust a user id sent by the browser. DATABASE - A profiles table, created automatically for each new user, holding the credit balance (starting at 0). - Row Level Security on every table: a customer can read only their own rows and can never change their own credit balance. Only the server changes credits. - Give me the SQL as a migration file and tell me how to run it, or run it for me if you can. Tell me exactly which URLs to add in Supabase Authentication settings for local development. When you finish, run the app, sign up with a test account, sign out and sign in again, and tell me what you checked.
Add the Local URLs in Supabase
Add the local addresses Claude gave you:
Supabase Authentication URL Configuration
You will add the live address here again in Step 6.
Add Stripe Paid Credits
Get Your Stripe Test Key
In the Stripe Dashboard, switch to test mode, then copy your test Secret key from Stripe's API keys page. Claude will ask for it.
Add Credit Packs and Checkout
Send this prompt. Claude adds credit packs, Stripe Checkout and the verified webhook that gives credits.
Add Stripe Credit Packs
One-time credit packs with Stripe Checkout, Express payment endpoints and a verified, idempotent webhook.
Add one-time credit pack purchases with Stripe Checkout. No subscriptions. CREDIT PACKS - Define three packs on the server, for example 20, 50 and 150 credits, each with its price. The browser only sends the pack id; the server decides the credits and the price. CHECKOUT - An Express endpoint for signed-in users that creates a Stripe Checkout session in payment mode for the chosen pack, linked to the user. - Success and cancel pages. The success page only says the payment is being confirmed and shows the balance once it updates. It never adds credits itself. VERIFIED WEBHOOK - An Express webhook endpoint that reads the raw request body and verifies the Stripe signature before anything else. Reject anything that fails. - Give credits only when Stripe confirms the payment is complete. - Make it idempotent: record each Stripe event and session id, so the same payment can never add credits twice, even if Stripe sends it again. - Store each purchase and add a credit transaction, then update the balance, all in one database transaction. SECRETS - Ask me for the Stripe test Secret key and keep it in the server's .env as STRIPE_SECRET_KEY. Never put a Stripe secret in frontend code or in a VITE_ variable. - For local testing, use the Stripe CLI to forward webhooks to the local server, and store the signing secret it gives as STRIPE_WEBHOOK_SECRET. If the Stripe CLI is not installed, tell me how to install it. If it needs me to sign in to Stripe, tell me what to approve. When you finish, buy a pack in test mode with the card 4242 4242 4242 4242, confirm the credits arrive only after the webhook, and tell me what you checked.
Protect AI Usage and Build Dashboards
Charge Credits for Every AI Edit
Send this prompt. The server checks and spends credits before the AI runs, and refunds them when the AI fails.
Enforce Credits on the Server
Server-side credit checks that block unauthorized, unpaid, duplicate and concurrent AI requests.
Protect the AI editing endpoint with credits, enforced only on the Express server. - Only signed-in users with a verified Supabase session can call it. Anyone else gets an error and the AI is never called. - Each edit costs 1 credit. Before calling Gemini, reserve the credit in one atomic database operation that fails if the balance is too low, so two edits sent at the same time can never spend the same credit. - With too few credits, return a clear message and show a Buy Credits button in the app. - If Gemini fails or returns no image, give the credit back automatically and record it. - Accept a request id from the browser and ignore duplicates, so a double click or a retry is charged once. - Record every edit: user, time, cost and result status. - GEMINI_API_KEY stays on the server. Make sure it never reaches the browser, the built files or the logs. When you finish, test a successful edit, an edit with zero credits, a failed edit and two quick edits in a row, and tell me the balance after each.
Build the Customer Dashboard
Send this prompt for the page your customers use every day.
Build the Customer Dashboard
The editor, credit balance, Buy Credits, recent edits and purchase history in one signed-in dashboard.
Build the customer dashboard, shown after sign-in. - The AI image editor, with all of its current features. - The credit balance, always up to date after an edit or a purchase. - Buy Credits, showing the credit packs and opening Stripe Checkout. - Recent edits: date, status and the credits used. - Purchase history: date, pack, credits and amount. Customers see only their own data, read through the server or Row Level Security. Keep the current look, make it work well on desktop and phones, and add clear empty states. Test it with a signed-in test account and tell me what you checked.
Build the Owner Dashboard
Send this prompt. You run the business from this page instead of the Supabase dashboard.
Build the Protected Owner Dashboard
Customers, purchases, AI usage, credit transactions and key numbers, protected on the server for the owner only.
Build a protected owner dashboard at /owner. ACCESS - Only the owner can open it. Ask me for my owner email and store it on the server as OWNER_EMAIL. - Every owner /api route checks the verified Supabase session and that its email matches OWNER_EMAIL. Anyone else gets an error and no data. Never decide this in the browser alone. - Owner routes read data with the server's Secret key; that key never reaches the browser. CONTENT - Customers: email, sign-up date, credit balance and total spent. - Purchases: date, customer, pack, amount and Stripe status. - AI usage: edits per day and failed edits. - Credit transactions: purchases, spends and refunds, newest first. - Key numbers: customers, revenue in test mode, credits sold and credits used. Keep it simple and readable, with search on the customer list. Test it as the owner and as a normal customer, and tell me what each one saw.
Test the Paid SaaS
Run the Full Customer Journey
Send this prompt. Claude tests the whole SaaS in Stripe test mode and fixes only real problems.
Test the Paid SaaS
Ten checks from sign-up to owner dashboard, in Stripe test mode, ending with READY TO LAUNCH or NEEDS ATTENTION.
Test this paid SaaS end to end in Stripe test mode, using the running app. Report each check as pass or fail. 1. A new customer can sign up. 2. The customer can sign out and sign in again. 3. With 0 credits, an edit is blocked with a clear message and the AI is not called. 4. A test purchase with the card 4242 4242 4242 4242 completes in Stripe Checkout. 5. Credits arrive only after the verified webhook, not on the success page alone. 6. A real AI edit works and returns an edited image. 7. The edit takes exactly 1 credit, and a failed edit gives the credit back. 8. The customer dashboard shows the edit and the purchase. 9. The owner dashboard shows the customer, the purchase and the usage. 10. Safeguards: a signed-out request and a normal customer opening owner routes are refused, a replayed webhook adds no credits, and no secret appears in the browser or the built files. Fix only real problems without changing the design, then run the failed checks again. End with exactly one line: READY TO LAUNCH or NEEDS ATTENTION
Try It Yourself
Open the app in your browser as a new customer.
Sign up and sign in work
A test purchase adds credits
An AI edit works and uses 1 credit
The owner dashboard opens only for your owner email
Deploy on Hostinger
Get Hostinger Node.js Web App Hosting
Your SaaS is ready. It needs hosting that runs its Node.js backend.
Get Hostinger Node.js Web App Hosting
Recommended plan: Unlimited, or a Cloud plan. Both run Node.js Web Apps.
USED IN TUTORIAL
Already have one of these plans? Continue below.
Connect Hostinger to Claude
Already connected? Skip to the next task. Otherwise, open the plugin directory:
Claude Desktop Customize Plugins Discover
Find Hostinger and install the official Hostinger plugin. Open the plugin's Connectors tab and connect Hostinger. Sign in to Hostinger in the browser window that opens and approve the access.
Then return to your project's Code session.
Publish the Node.js Web App
Send this prompt. Claude prepares the production app, sets the server variables on Hostinger and deploys it.
Deploy the SaaS to Hostinger
Prepare the React + Express app for production, set the environment variables, and deploy it as a Hostinger Node.js Web App.
Deploy this SaaS to Hostinger as a Node.js Web App with the Hostinger tools. BEFORE DEPLOYING - Use the Hostinger tools from the Hostinger plugin. If they are not available, or I am not signed in, stop and tell me how to connect. Never ask for my password, and never write a token or key into any file. - Check that my Hostinger plan runs Node.js Web Apps. If it does not, stop and tell me. - If .hostinger/site.json exists, deploy to the website it names. Otherwise list my websites and ask which one to use, or offer a new website on a free temporary domain. Never guess, and confirm with me before replacing any existing website. PRODUCTION APP - package.json has npm run build (builds the React frontend) and npm start (starts the Express server). Express serves the built frontend and the /api routes, and listens on process.env.PORT. - Package the source without node_modules, the build folder or any .env file. Hostinger installs and builds it. ENVIRONMENT VARIABLES - Set every server variable on the Hostinger app in one go, using the values from my local .env: the Supabase values, GEMINI_API_KEY, STRIPE_SECRET_KEY and OWNER_EMAIL. Setting them replaces the whole list, so include all of them. - Leave STRIPE_WEBHOOK_SECRET for the next task: the local one does not work in production. DEPLOY AND CHECK - Deploy, watch the build, and fix only real build errors. - Open the live address and check that the app loads and that sign-in works. - Save the website's domain in .hostinger/site.json. Save only the domain. Give me the live address and the full production webhook URL.
Deploying replaces what is on the Hostinger website you choose. Use a new website or a temporary domain, or one you want to replace.
Add the Production Webhook
Register the live webhook URL Claude gave you, still in test mode:
Stripe Developers Webhooks
Stay in test mode Add an endpoint with the production webhook URL Select the checkout.session.completed event Copy the signing secret — it starts with whsec_
Set the Production Webhook Secret
Claude stores the new signing secret on the Hostinger app only, then redeploys.
I created the production Stripe webhook. Ask me for its signing secret, add it to the Hostinger app's environment variables as STRIPE_WEBHOOK_SECRET together with all the existing variables, and redeploy. Never write it into any file. Then confirm the webhook endpoint answers.
Add the Live URL in Supabase
Add the live address as the Site URL and as a redirect URL:
Supabase Authentication URL Configuration
Connect Your Domain
Optional. Already have a domain? Add it here and send the prompt. Afterwards, update the Supabase URLs and the Stripe webhook to the new domain.
Connect My Domain
Click Customize Prompt, add your domain, then copy the prompt.
Connect my domain {{DOMAIN}} to the Node.js Web App you deployed, using the Hostinger tools. - The website is the one saved in .hostinger/site.json. If that file is missing, or you are not sure which domain I mean, ask me — never guess. - First check that the domain is in my Hostinger account. If the connection needs something only I can do, tell me exactly what to do and wait. - Change only what is needed to point the domain at this app, and do not touch email records. - Make sure the domain opens over HTTPS. - Update .hostinger/site.json if the main domain changes. Then list exactly what I must update for the new domain: the Supabase Site URL and redirect URLs, and the Stripe webhook URL.
Your Domain
e.g. myphotoeditor.com
Send Sign-Up Emails From Your Own Domain
Customers can only sign up once their confirmation emails really arrive.
Supabase's built-in email only reaches your own team's addresses, two emails an hour, so new customers would never receive their confirmation email. Choose a provider such as Resend, Postmark, Amazon SES or Brevo, verify your domain there and create SMTP credentials. Then turn on Enable Custom SMTP and enter the sender email, sender name, host, port, username and password.
Supabase Authentication Emails SMTP Settings
Keep SMTP credentials in Supabase
Your SMTP password lets anyone send email as your domain. Enter it only in Supabase's SMTP settings — never in the app, an environment file, the repository or a Claude Code conversation.
Test the Live SaaS
Open the live address as a new customer and repeat the journey, still in test mode.
A new customer can sign up with a real email address, confirm it from the inbox and sign in
A test purchase adds credits through the production webhook
An AI edit works and uses 1 credit
The owner dashboard works only for your owner email
Ready for real customers? Switch Stripe to live mode, replace STRIPE_SECRET_KEY with the live key, add a live webhook endpoint with its own signing secret, and make one small real purchase to confirm.