We took ProCare Services' HealthyHome Hub from a demo-grade Lovable build to a production-ready app: payments and plan enforcement, security gates where anonymous access had slipped in, and an AI layer we can swap or scale without vendor lock. If you shipped on Lovable and now need to go live safely, this is the blueprint we used.
Productionizing a Lovable app is the process of hardening a no-code or low-code build for real customers: secure authentication and authorization, enforce paid plans, remove data exposure, and add monitoring and rollback so incidents are survivable.
The problem it solves
Most Lovable builds start as internal prototypes. That speed comes with gaps: no real checkout, open endpoints meant for testing, and deep coupling to the platform's AI gateway. HealthyHome Hub had all three. The app worked for demos, but it could not charge customers, it used a brand name that needed a legal rework, and one chat gateway path was reachable without auth.
| Manual or demo practice | Productionized behavior |
|---|---|
| Buttons that say Upgrade but do not charge cards | Real checkout and webhooks. Plan state drives feature access |
| Anonymous access to preview chat paths | Token gate and role checks on every server path |
| AI calls wired directly to a single vendor gateway | Adapter pattern: swap providers without touching features |
| Logs in the console only | Structured logs, alerting, rollbacks, and a safe dark-launch switch |
How the automation works
Our approach for HealthyHome Hub: keep the working product, then layer the minimum secure rails that make it sellable and supportable. We keep Lovable for what it is good at and add a thin Next.js services layer for gating, payments, and adapters.
- Payment and plan enforcement: Checkout, webhook updater, and a single source of truth for plan state. Features read plan state, not feature flags hardcoded around the UI.
- Security gates: All server routes check identity and role. We closed an anonymous path that allowed free AI usage by ensuring every gateway call passed through a token check.
- AI adapter layer: The app had 33 of 45 AI calls routed through one gateway. We inserted a provider adapter so model choice is swappable without touching upstream features.
- Monitoring and rollback: Structured logs and a feature kill-switch per risky surface. Operators can flip off a section without redeploying.
- Brand and compliance: Rebrand variables live in a config module so name and visuals can change without repo surgery. This was necessary because the original product name could not be used publicly.
Step-by-step: how to build it
1) Map the surface: what must be secured first
We start with an inventory pass: pages, server functions, data tables, and AI call sites. For HealthyHome Hub the codebase was large in practice: on the order of fifty thousand lines with many database tables and policies. We tagged three red zones: payments missing, one anonymous chat path, and AI calls wired to a single vendor.
# Lightweight surface scan we use at kickoff
repo-stats \
--count-lines \
--scan "server,api,functions" \
--grep "auth|token|plan|billing|ai|model" \
--out surface.jsonKey gotcha: treat any public demo route as suspect until it passes a token and role check.
2) Add a plan state and gate features by plan
Plan is the source of truth. The UI reads it. A background webhook updates it. Every feature gate reads the same plan key and role.
// plan.ts: one source of truth
export type Plan = "free" | "pro" | "team";
export function canUseFeature(plan: Plan, role: string, feature: string) {
if (role === "admin") return true;
const entitlements: Record<Plan, string[]> = {
free: ["dashboard.view"],
pro: ["dashboard.view", "report.generate"],
team: ["dashboard.view", "report.generate", "export.run"],
};
return entitlements[plan]?.includes(feature) ?? false;
}Gotcha: never scatter feature flags through components. Pipe everything through a single entitlement function and log denials.
3) Wire checkout and webhooks, then drive the UI from plan
We added a real checkout and a webhook receiver that safely updates plan state. The UI renders Upgrade if canUseFeature returns false. After payment, the webhook flips plan, the page revalidates, and the button becomes active without a manual refresh.
// /api/billing-webhook: verify, update plan, log
export async function POST(req: Request) {
const event = await verifyIncoming(req); // signature check
const { customerId, newPlan } = parseEvent(event);
await updatePlanForCustomer(customerId, newPlan);
log("plan.upgraded", { customerId, newPlan });
return new Response("ok");
}Gotcha: do not trust the front end for plan changes. Only webhooks or staff tools can change plan.
4) Put a token gate in front of every server path
One path in HealthyHome Hub allowed anonymous use of a chat tool. We gated every server path with a tiny middleware and a role check. If unauthenticated, we short-circuit.
// middleware.ts: enforce auth on server paths
export function requireAuth(ctx: { user?: { id: string; role: string }} ) {
if (!ctx.user) throw new Error("unauthorized");
return ctx.user;
}
// in a handler
export async function POST(req: Request) {
const user = requireAuth(await getContext(req));
if (user.role !== "member" && user.role !== "admin") throw new Error("forbidden");
// proceed
}Gotcha: add a positive allowlist for public assets. Everything else is private by default.
5) Decouple the AI gateway behind an adapter
We introduced a provider interface so calls no longer depend on one vendor. The app already worked, so the adapter wraps the existing gateway first, then we add alternates.
// ai/provider.ts
export interface AIProvider {
chat(input: { system: string; messages: { role: string; content: string }[] }): Promise<string>;
}
export class GatewayProvider implements AIProvider { /* wraps current gateway */ }
export class AltProvider implements AIProvider { /* wraps alternate */ }
export function getAI(): AIProvider {
return process.env.AI_PROVIDER === "alt" ? new AltProvider() : new GatewayProvider();
}Gotcha: keep prompts and guardrails in one module so both providers share behavior.
6) Add structured logs, alerts, and a kill switch
When something breaks, you need to see it and shut it off without a deploy. We added JSON logs with event names and a per-feature kill switch.
// ops.ts
const FLAGS: Record<string, boolean> = { ai_chat_enabled: true };
export function isOn(key: string) { return FLAGS[key] !== false; }
export function log(event: string, data: Record<string, unknown> = {}) {
console.log(JSON.stringify({ t: Date.now(), event, ...data }));
}Gotcha: gate only the risky call, not the whole product. Users should still sign in and see status.
7) Parameterize the brand and secure the content
The original name could not launch. We lifted name, logo, and legal strings into a brand file. We also put robots rules in place until the new domain was attached.
// brand.ts
export const BRAND = {
name: "HealthyHome Hub",
legalName: "ProCare Services, Inc.",
supportEmail: "support@procare.example",
};Gotcha: keep legal copy in one place so counsel can approve a single file.
Where it gets complicated
- Anonymous test routes: Demo or worker paths often skip auth to move fast. Before launch, make private the default. We closed a single open path that would have enabled unbounded free usage of the AI layer.
- Deep AI coupling: When dozens of calls assume one gateway, ripping it out is risky. Wrap it first. Ship parity through your adapter, then add alternates behind the same interface.
- Payments that look live but are not: An Upgrade button without webhooks is a promise, not revenue. Plan state must change on the server, never in the browser.
- Large codebases: HealthyHome Hub was substantial: tens of thousands of lines, many server functions, and hundreds of data policies. You cannot rewrite it. You defend the edges and stabilize the core.
- Brand and legal constraints: Changing a name late can ripple through code and content. Parameterize branding and legal language so changes are a config push, not a refactor.
What this actually changes
For ProCare Services, we kept the working product and made it sellable and supportable. Payments and plan enforcement turned a demo into a business. Security gates closed an anonymous path. An adapter under the AI layer kept today's behavior while creating a path to swap providers later. The codebase scale was real: roughly 51.6k lines across 85 pages with many server functions and hundreds of data policies. That size is exactly why a defend-the-edges approach works: you harden the surfaces customers touch and avoid a risky rewrite.
Frequently asked questions
Does a Lovable app need to be rewritten to go live?
No. We harden the edges first: payments, auth on server paths, and monitoring. Then we add an AI adapter behind existing calls. You keep the working app and gain the controls you need for production.
How do you prevent anonymous use of paid features?
We gate every server path with an auth check and enforce plan state on the server. Public pages are explicitly allowlisted. Anything that can incur cost or expose data requires an authenticated user with the right role.
Can we keep the existing AI vendor and still be future-proof?
Yes. We wrap the current gateway first so behavior stays the same, then introduce alternates behind a provider interface. You get stability now and options later.
How long does this hardening take?
Most teams see a first pass in one to two weeks: checkout and webhooks, token gates, an AI adapter that wraps the current calls, and log streams. Larger refactors are scheduled once the core is safe.
What do we need to provide?
Access to the repo and hosting, a payment processor account, and a decision on brand language. If the product name must change, we parameterize it early so legal approval is a config update.
If you built on Lovable and now need to launch with payments, security, and a swappable AI layer, we have already walked this path on HealthyHome Hub. See our broader work under custom integration at /services#custom-ai-integration, read why most teams stumble in Why 90 Percent of Automation Projects Fail at /blog/why-90-percent-of-automation-projects-fail, and book a 15 minute call at /book.
Want us to build this for you?
15-minute discovery call. No pitch. We tell you what to automate first.
Book a Discovery Call