Payments
Accepting payments and setting up a paywall
LemonSqueezy is used to handle payments. It has built-in VAT/sales tax compliance, affiliates & marketing tools.
Set up LemonSqueezy
First, create an LemonSqueezy account or sign in. Create a store & add your digital products. It usually takes 1-2 days for it to get verified.
Once that is ready, we’ll set up the app to handle webhooks from LemonSqueezy:
-
Go to
Settings -> Webhooks
and add a new WebhookAdd a new webhook to LemonSqueezy
-
Fill in Callback URL with
https://yourwebsite.com/api/payments/lemonsqueezy
& select all events. -
Add your defined
LEMON_SQUEEZY_WEBHOOK_SECRET
to your.env
file.Select all events & specify your LEMON_SQUEEZY_WEBHOOK_SECRET
Handling webhooks
Under app/api/payments/lemonsqueezy/route.ts
you’ll find an API route that handles both one-time-payments and subscriptions from LemonSqueezy.
It will do the following:
- Verify if the request is legitimate by matching
LEMON_SQUEEZY_WEBHOOK_SECRET
- Get purchased product, based on the
variant_id
- Match the user email with our
profiles
table- If there is a matching user, it will edit
purchase
column of the matching user in theprofiles
table based on the logic you specify in the webhook. Feel free to add any logic here you’ll use in your app to handle page access. - If no matching user is found, it will simply update the
purchases
table with a new entry
- If there is a matching user, it will edit
Storing webhook data
We’ll store all webhook data coming from LemonSqueezy in our own database as well.
Copy-paste the following your Supabase SQL editor:
create table
public.purchases (
id bigint generated by default as identity,
user_email text null,
type text null,
created_at timestamp with time zone null default now(),
purchase_id text null,
payload jsonb null,
constraint purchases_pkey primary key (id)
) tablespace pg_default;
Then, turn on RLS. We don’t need to add any rules as we’ll be using the service key for to interact with this table.
Set up paywall
All demo apps now include a paywall using a credit system. You can modify the logic in app/api/payments/lemonsqueezy/route.ts
to add more credits after a succesfull purchase.
You can adjust the code to handle access based on the purchase
column having
a specific value. This allows for handling lifetime access systems or
subscriptions.
Logic flow
- Check if the demo app is paywalled (specified in
toolConfig.ts
). - Verify if the user has more credits than required for one generation:
- If yes, grant access.
- If no, display the
PaymentModal
component.
let credits;
if (toolConfig.paywall) {
const { data: profile } = await supabase
.from("profiles")
.select("*")
.eq("id", user.id)
.single();
credits = profile.credits;
console.table(profile);
if (credits < toolConfig.credits) {
return <PaymentModal />;
}
}