Lemon Squeezy revenue analytics for Next.js
This guide adds ProductLense to a Next.js site and connects Lemon Squeezy, so every Lemon Squeezy payment is attributed to the channel, campaign and landing page that first brought the customer. It takes four steps and about ten minutes.
1. Add ProductLense to Next.js
The script goes in app/layout.tsx with next/script. Replace SITE_ID with the id from Settings → Sites.
import Script from 'next/script';
import type { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://api.productlense.app/a.js"
data-site="SITE_ID"
strategy="afterInteractive"
/>
</body>
</html>
);
}2. Identify signed-in users
'use client';
import { useEffect } from 'react';
declare global {
interface Window {
productlense?: (...args: unknown[]) => void;
}
}
export function ProductLenseIdentify({ userId, email }: { userId?: string; email?: string }) {
useEffect(() => {
if (userId) window.productlense?.('identify', userId, email ? { email } : undefined);
}, [userId, email]);
return null;
}3. Connect Lemon Squeezy
Setup steps
- In Lemon Squeezy, open Settings → API and create an API key. Note the store id under Settings → Stores.
- In ProductLense, open Settings → Billing connections → Lemon Squeezy and paste the key and store id.
- In Lemon Squeezy → Settings → Webhooks, add a webhook endpoint with the URL ProductLense shows after you save the connection (pattern https://api.productlense.app/api/webhooks/{provider}/{connection_id}).
- Choose a signing secret, tick the events listed below, and paste the same secret into ProductLense.
- ProductLense backfills 12 months of orders and invoices.
Credentials and permissions
| Field | Secret | Where to find it |
|---|---|---|
| API key | Yes, encrypted at rest | Settings → API → create API key. |
| Store ID | No | The numeric store id from Settings → Stores. |
| Webhook signing secret | Yes, encrypted at rest | The signing secret you set when creating the webhook in Settings → Webhooks. |
Webhook
URL pattern, shown with your connection id after you save:
https://api.productlense.app/api/webhooks/lemonsqueezy/{connection_id}Events: order_created, order_refunded, subscription_created, subscription_updated, subscription_cancelled, subscription_payment_success, subscription_payment_refunded, subscription_payment_recovered
Verification: X-Signature header: HMAC-SHA256 of the raw body with your signing secret.
What ProductLense reads
- Customers
- Customers with email.
- Subscriptions
- Subscriptions with status. The subscription object has no price, so revenue comes from orders and invoices.
- Payments
- Orders and subscription invoices.
- Refunds
- The refunded amount on orders and subscription invoices.
- Fees
- Not exposed by the Lemon Squeezy API, so recorded as 0. Profit for this connection is before Lemon Squeezy’s fee.
What ProductLense never touches
- Never creates, changes, refunds or cancels anything: read-only API calls and incoming webhooks only.
- Never moves money, issues payouts, or reads bank account details.
- Never sees full card numbers; providers do not expose them.
- Never sends your customer data to the AI model: Ask and the daily insight see aggregates only.
Match payments to your users
Pass checkout[custom][pl_user_id] in your checkout links to match buyers to visitors by id instead of email.
const url = new URL('https://YOUR_STORE.lemonsqueezy.com/checkout/buy/VARIANT_ID');
url.searchParams.set('checkout[email]', user.email);
url.searchParams.set('checkout[custom][pl_user_id]', user.id);Lemon Squeezy on Next.js: questions
- Where does the ProductLense script go in Next.js?
- In app/layout.tsx with next/script. Add the Script component to app/layout.tsx with strategy="afterInteractive" and data-site. The script is about 2.3 KB, loads with defer, sets no cookies, and records route changes in single-page apps through the history API, so no extra router code is needed.
- How do I identify users in Next.js?
- Call window.productlense('identify', userId) once the signed-in user is known, as in the app/productlense-identify.tsx example on this page. Use the same id you store on Lemon Squeezy as pl_user_id, so Lemon Squeezy payments match by id rather than by email.
- Does ProductLense work with Lemon Squeezy?
- Yes. ProductLense connects to Lemon Squeezy with aPI key, store ID, webhook signing secret, backfills 12 months of history, and stays current through signed webhooks plus a nightly reconcile. One connection is included on Starter; Growth and Scale allow every provider at once on the same site.
- What data does ProductLense read from Lemon Squeezy?
- Customers, subscriptions, payments, refunds and fees, read-only. Payments: orders and subscription invoices. Refunds: the refunded amount on orders and subscription invoices. Fees: not exposed by the Lemon Squeezy API, so recorded as 0. Profit for this connection is before Lemon Squeezy’s fee.
- Can ProductLense change anything in my Lemon Squeezy account?
- No. ProductLense only makes read calls and receives webhooks. It never creates, changes, refunds or cancels anything, never moves money, and never sees card numbers. Credentials are encrypted at rest, and the AI features only ever see aggregated numbers, never your customers’ data.
Free up to 10k events a month. No card.
Add the script, connect Stripe or any of 14 other billing providers, and see revenue by channel the same day.