Stripe
Connect your Stripe account to see revenue next to your traffic. Two clicks, read-only, no webhook setup required.
Two distinct Stripe roles
What it does
Connect Stripe once and Produl reads your charges, subscriptions, customers, and payment intents via a restricted read-only key. Revenue metrics appear on:
- Site dashboards — a dedicated Revenue widget with MRR, ARR, new customers, churn, failed payments, revenue trend chart, top customers, and recent transactions.
- Overview — a Revenue-today stat strip plus per-site revenue on the 1×1 cards.
- Live map — revenue total + recent charges feed + per-site revenue panel.
Setup (2 minutes)
- 1
Open the Revenue widget on your site dashboard
Navigate to the site you want to track, find the Revenue Tracking widget (or add one via the dashboard editor), and click Create restricted key in Stripe.
- 2
Produl opens Stripe with the right scopes pre-selected
The button deep-links into Stripe's restricted-key creation form with the key name ("Produl Analytics") and all 11 required read scopes already filled in. You just review and click Create key on Stripe.
Testing first?
There's a test-mode link right below the button that opens the same form but on your Stripe test dashboard. Test keys start withrk_test_and only return data for test-mode charges / subscriptions. - 3
Copy the key back to Produl
Stripe shows the key once. Copy it and paste into the Step 2field in Produl's Revenue widget, then click Connect Stripe. Produl validates the key against Stripe's
/v1/balanceendpoint to confirm the scopes are correct — if anything's missing, you'll see Stripe's exact error text.
No webhooks to configure
Metrics surfaced
Recurring revenue
- MRR — Monthly Recurring Revenue. Computed by summing the unit amount of every active subscription item, normalized to a monthly figure (yearly plans ÷ 12, weekly × 4.33, daily × 30).
- ARR — MRR × 12.
- Active subscriptions — Count of subscriptions with status
active.
Range-scoped (selectable 7d / 30d / custom)
- Total revenue — Sum of succeeded (non-refunded) charges.
- Transactions — Count of succeeded charges.
- New customers — Customers created in range.
- Churned subscriptions — Subscriptions canceled in range.
- Failed payments — Payment intents that didn't complete (requires_payment_method, canceled, or with a
last_payment_error). - Avg order value — Revenue ÷ transactions.
- Revenue per visitor — Revenue ÷ unique visitors (the attribution your traffic analytics cares about).
Rankings & feeds
- Top 5 customers by revenue in range.
- Recent transactions — Last 10 succeeded charges with description + amount + timestamp.
- Daily revenue timeseries — Bar chart of revenue per day, bucketed from charge timestamps.
Permissions & scopes
The pre-scoped key link requests exactly these Stripe scopes — all read-only, nothing else:
| Scope | Used for |
|---|---|
rak_balance | Validation — proves the key can authenticate |
rak_charge | Total revenue, transactions, recent transactions |
rak_customer | Customer count, top customers, new customers |
rak_subscription | MRR, ARR, active subs, churned subs |
rak_invoice | Subscription invoice timing |
rak_product | Product names on subscription items |
rak_price | Pricing info on subscription items |
rak_plan | Legacy plan attribute on sub items |
rak_payment_intent | Failed payments |
rak_refund | Refund-adjusted revenue totals |
rak_dispute | Chargeback tracking |
Rotate or revoke anytime
Troubleshooting
"That doesn't look like a Stripe restricted key"
The key must match rk_live_... or rk_test_... followed by a long alphanumeric string. Make sure you copied the whole key, including the rk_live_ or rk_test_ prefix. Secret keys (sk_...) and publishable keys (pk_...) won't work — only restricted keys.
"Stripe rejected this key: …"
This is Stripe's own error text forwarded verbatim. Common causes:
- Missing scopes — The key was created without one of the 11 required scopes. Delete it on Stripe and use the Create restricted key button so the scopes are filled in correctly.
- Key revoked — Someone deleted the key from Stripe. Create a new one.
- Wrong mode — A test key used in live mode or vice versa; Stripe will reject it.
No revenue shows up after connecting
If the dashboard is empty after a successful connection, check the range — most views default to the last 7 days, and your account might not have any charges in that window. Widen to 30 days or all time. If the Revenue widget shows a live_error note, the connection worked initially but a later fetch failed — the widget fell back to webhook data so the dashboard still renders.
Attribution (visitor → charge)
The tracker exposes getVisitorId(). Pass the returned value as client_reference_id when you create a Stripe Checkout Session and Produl will link the resulting charge back to the exact visitor who converted — including referrer, UTM, and landing page.
import { getVisitorId } from 'produl-tracker/next'
async function handleCheckout(priceId: string) {
const res = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
priceId,
clientReferenceId: getVisitorId(),
}),
})
const { url } = await res.json()
window.location.href = url
}import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(req: Request) {
const { priceId, clientReferenceId } = await req.json()
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: priceId, quantity: 1 }],
success_url: 'https://example.com/welcome',
cancel_url: 'https://example.com/pricing',
client_reference_id: clientReferenceId, // ← Produl visitor id
metadata: { site_id: 'YOUR_PRODUL_SITE_ID' },
})
return Response.json({ url: session.url })
}Why both client_reference_id AND metadata.site_id?
client_reference_id lets Produl match the charge to the visitor. metadata.site_id tells Produl which site the revenue belongs to, since server keys are scoped per-site. Include both for full attribution.Webhook fallback
Produl also accepts Stripe webhooks at https://app.produl.tech/api/webhooks/stripe. When events arrive with metadata.site_idset, Produl saves them and uses them as a fallback data source when the restricted key isn't set or a live fetch fails.
Webhook-sourced metrics are capped at total revenue, transaction count, and daily timeseries — MRR/ARR/customer listings require the API scopes and are zero-filled in fallback mode.
charge.succeeded
invoice.payment_succeeded
invoice.payment_failed
customer.subscription.created
customer.subscription.updated
customer.subscription.deletedTo enable webhooks, register the above events against https://app.produl.tech/api/webhooks/stripe. Add site_id: "YOUR_SITE_ID" to the metadata of any event you want attributed to a specific Produl site.