Getting Started
Track Revenue
Revlly supports first-party revenue attribution for Stripe, Lemon Squeezy, Polar, Shopify, and manual/API payments. Connect providers from Dashboard -> Profile -> Revenue. All provider credentials are encrypted with INTEGRATIONS_ENCRYPTION_KEY.
Overview
For Lemon Squeezy, Polar, and Shopify, Revlly tries to create the provider webhook automatically when NEXT_PUBLIC_APP_URL is a public HTTPS URL and the provider token has webhook write access. If automatic setup is blocked, the dashboard keeps the import connection active and shows the manual webhook endpoint to add in the provider dashboard.
Supported providers
| Provider | Import path | Webhook route |
|---|---|---|
| Stripe | Checkout sessions, charges, PaymentIntent metadata | /api/webhooks/stripe |
| Lemon Squeezy | Orders API and signed order webhooks | /api/webhooks/lemonsqueezy |
| Polar | Orders API and Standard Webhooks | /api/webhooks/polar |
| Shopify | Admin Orders API and signed order webhooks | /api/webhooks/shopify |
Recommended attribution
Adding the script tracks visitors, sessions, referrers, UTMs, goals, and hosted checkout return-page fallbacks. To connect revenue to the visitor who paid, pass Revlly metadata into the provider when you create checkout.
For Stripe Checkout, call window.revlly.buildStripeMetadata() in the browser and send the result to your backend with the selected price or plan:
const revlly_metadata = window.revlly?.buildStripeMetadata({ product_name: "Pro",}) ?? {};
await fetch("/api/checkout", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ price_id: "price_123", revlly_metadata }),});Your backend should sanitize the payload, keep only attribution fields, and include them in provider metadata, custom data, or Shopify cart/order attributes:
website_idvisitor_idsession_idsourceutm_sourceutm_mediumutm_campaign
Stripe server example:
import Stripe from "stripe";import { buildRevllyStripeMetadata } from "@/lib/revlly-stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(request: Request) { const { revlly_metadata } = await request.json(); const metadata = buildRevllyStripeMetadata({ websiteId: "YOUR_WEBSITE_ID", tracking: revlly_metadata, extra: { product_name: "Pro" }, });
const session = await stripe.checkout.sessions.create({ mode: "payment", line_items: [{ price: "price_123", quantity: 1 }], success_url: "https://example.com/thanks?session_id={CHECKOUT_SESSION_ID}", cancel_url: "https://example.com/pricing", metadata, payment_intent_data: { metadata }, });
return Response.json({ id: session.id });}For subscription checkouts, attach the same metadata to the Checkout Session and subscription_data.metadata:
await stripe.checkout.sessions.create({ mode: "subscription", line_items: [{ price: "price_123", quantity: 1 }], success_url: "https://example.com/thanks?session_id={CHECKOUT_SESSION_ID}", cancel_url: "https://example.com/pricing", metadata, subscription_data: { metadata },});Revlly uses the first tracked Stripe customer payment to attribute later renewals when renewal invoices do not include visitor/session metadata.
Checkout fallback
When a customer returns with ?session_id=cs_..., the browser tracker reports the Stripe Checkout session while preserving the visitor/session attribution.
Lemon Squeezy can use ?order_id=ORDER_ID on the success URL. Polar can use ?checkout_id=CHECKOUT_ID. Shopify revenue is imported through the Admin Orders API and signed order webhooks; add the same attribution fields as cart/order attributes for visitor-level matching.