How to Add a Paywall to an AI-Built App Without a Backend (The Minimal-Code Guide)

How to Add a Paywall to an AI-Built App Without a Backend (The Minimal-Code Guide)

The situation

You built the app with an AI assistant. The front end is sharp, the prompt flows work, and it deployed on Vercel in under an hour. There is no server, no database, and no auth system. Then the analytics show real people using it, and the obvious thought lands. I should charge for this.

Here is the reframe that makes this tractable. "No backend" does not mean "no money handling." It means you get to choose the paywall that pushes the hard parts onto a provider. Card processing, tax, receipts, disputes: there is a service that does each one for you. Your only job is to pick the layer you need and wire up as little as possible.

This guide walks three options, from zero code to a single serverless function, and tells you honestly what each one does and does not lock down. Read it, pick your stage, and ship.

The honest architecture picture first

Every real paywall has four ingredients:

  • A way to take money
  • A way to know who paid
  • A way to check that someone paid
  • A way to revoke or renew access

A hosted checkout provider handles the first one for you, and with a subscription it mostly handles the fourth one too, since the provider manages the billing cycle. That leaves the knowing and the checking, which are the parts you have to decide about in a no-backend app.

Set expectations early. If your paywall gates a landing page and never opens the real app, the zero-code path is easy and safe. If you gate features inside a real app, you have two honest choices. Either accept one small serverless function, or accept lower security, because a check that runs only in the browser is trivially bypassable. There is no third option that hides that reality. Pick your tradeoff and move on.

The fastest thing in the world is a Stripe Payment Link. Log into the Stripe dashboard, create a payment link, attach a one-time price or a subscription, and put that URL behind your CTA button. A customer clicks, pays on Stripe's hosted checkout, and you get the money and an alert.

That is a complete flow for one kind of product. If you sell a downloadable asset, a prompt library, a Figma file, or a paid report, Stripe takes the payment and you deliver the file however you already handle fulfillment. You can also watch the payments page or wire a webhook to see who paid. That is where the limits start.

What a Payment Link will not do is lock features inside your app. There is no entitlement system, no license key, and no way for your app to ask whether a visitor has access and get a reliable answer. If your whole paywall is one link on a landing page, enforcement is nil. A determined visitor reads the page source, finds the app route, and walks straight in.

Use Option A when the product is a one-time download, or when your app already reads a paid status you manage somewhere else, like a spreadsheet or a Notion page you check by hand.

Option B: the no-backend-on-your-side path with Lemon Squeezy licenses

Lemon Squeezy is a merchant of record, which means it absorbs the entire payment stack: checkout, card storage, global tax compliance, receipts, refunds, and disputes. It costs 5% plus $0.50 per transaction. Stripe has owned it since July 2024, so it is not going anywhere. Because it is a merchant of record, you never touch the tax liability yourself.

The part that matters for a no-backend app is the license key system. When a customer buys, Lemon Squeezy issues a license key and delivers it by email or via webhook. Your app asks the buyer to paste the key, then validates it against Lemon Squeezy's license API. It also ships an affiliate system out of the box, which is free marketing leverage for zero extra work.

The flow looks like this:

  1. Customer hits your checkout URL
  2. Lemon Squeezy renders an overlay checkout in your page
  3. Buyer pays, and Lemon Squeezy handles tax, the receipt, and the record
  4. The buyer receives a license key
  5. Your app asks for the key
  6. You validate it against the license API

Validation is one request. The endpoint is POST https://api.lemonsqueezy.com/v1/licenses/validate. In curl:

curl -X POST https://api.lemonsqueezy.com/v1/licenses/validate \
  -H "Content-Type: application/json" \
  -d '{"license_key": "LICENSE_KEY_FROM_CUSTOMER"}'

In JavaScript you fetch the same endpoint from your code and read the license_status field. A status of 1 means the key is active, so you unlock the feature. That one number is your paywall.

Now the security caveat, stated plainly. If the check runs only in the browser, a determined user can patch it out. Open the network tab, see the call, return a fake response, and walk in. That sounds worse than it is. For an indie web app this level is fine, and honesty beats hardening theater. The day you want real enforcement, you move the validation into a serverless function. That is Option C.

Option C: the minimal-backend-but-not-a-server path

This path adds one serverless function and a tiny store, but no server to manage. Vercel deploys this site, so it is the natural home for the pattern. The shape is three moving parts.

A webhook handler on Vercel that receives checkout.webhook events from your merchant of record. Always verify the HMAC signature with your provider's secret first, then upsert a record that says this customer paid.

A key-value store. Vercel KV, Turso, Upstash, or Neon all work. Pick one that hands you a client and a URL. You are storing one row per customer: email, plan, status, and renewal date.

An access check endpoint, /api/check-access, that your frontend calls before it unlocks the app. It asks for the email or reads a session, looks up that customer in the store, and returns true or false.

That is the whole system. The webhook writes, the check reads. Because verification happens server-side in a function, bypassing it means finding a real flaw instead of deleting a line of JavaScript. That is meaningfully harder.

For subscriptions, handle the renewals. Your MoR sends events when a subscription renews and when a payment fails. Write both. The renewal event keeps access valid, and the failed payment event is your revocation trigger.

Which one fits your stage

PathBackend workEnforcementBest for
Stripe Payment LinkNoneNone inside your appOne-time downloads and assets
Lemon Squeezy overlay and license keyNoneModerate, client-side checkApps unlocked by a license key
Vercel function plus MoR webhookOne function and a KV storeStrong, server-sideSubscriptions and real revocation

Choose Option A when you ship a static site and sell a downloadable asset. One link, done.

Choose Option B when your app unlocks features by license key and you accept moderate enforcement. Most indie AI apps stay here happily for a long time.

Choose Option C when you sell a subscription and want real revocation when a card fails. Wire the renewal and failed payment webhooks into a store before your subscriber list gets big, because tracking renewals by hand does not scale.

Refunds and support deserve a mention. With a merchant of record, Lemon Squeezy or Paddle owns refunds and disputes. An unhappy customer gets handled by people whose job it is, and you keep your attention on the product. This is the quiet reason the no-backend path stays light: the human-heavy work is outsourced too.

Pitfalls to dodge

Client-side checks are decoration, not a paywall. A check in the browser stops a casual user and no one else. That is the accepted tradeoff of Option B, and it is fine as long as you name it to yourself.

Webhooks without signature verification are spoofable. Anyone who can POST to your function can mark themselves as paid. Verify the HMAC signature with your provider's secret before trusting any payload. It is five lines and it is non-negotiable.

Do not store raw license keys in plain HTML that ships to every visitor. If the page source contains the keys, the paywall is a formality. The key has to come from the buyer, not from your code.

Subscriptions need the renewal webhook, or your users churn silently. If you only check whether someone ever paid once, a card failing in month three leaves them with access on a payment that stopped. Wire the failed payment event and revoke.

And for AI apps with per-use cost, pair the paywall with usage caps. A heavy user on a $9 plan can burn more in model fees than the plan price. The free tiers and paid gates guide covers exactly this. Cap usage, set clear limits, and the paywall survives an enthusiastic customer.

Start here

The recommendation in one sentence: ship the Lemon Squeezy overlay with license keys this week, then add the webhook plus KV layer the day you sell subscriptions or need real revocation.

The overlay gets you charging today with zero infrastructure. The license key gives you a working gate that verifies against a real API. When the app grows enough that silent churn or client-side bypass costs you real money, the Vercel function path is a weekend of work, and you already understand every moving piece.

Two reads pair well with this one. The merchant of record comparison explains why MoR providers absorb tax and disputes, and the Stripe versus Paddle versus Lemon Squeezy breakdown walks the pricing differences. When pricing structure is next on your list, the free tiers and paid gates guide is the natural follow-up.

No backend, no excuse. Charge something this week.

Want this done for your product?

Crawled SEO helps founders and small teams get found, cited, and recommended in Google and AI search. If you have an app with no traffic and want it fixed properly, that is exactly what we do.

Request your free audit