MenuStripe

Revenue attribution

Stripe

Connect Stripe with a restricted read-only key and a webhook so ProductLense records charges, refunds, disputes and the exact fee on each payment.

ProductLense reads your Stripe customers, subscriptions, charges, refunds and disputes, with the fee from each balance transaction. ProductLense only reads: it never writes to your Stripe account, never refunds, cancels or changes anything, and never moves money.

What you need

FieldSecretWhere to find it
Secret keyYesA restricted key (rk_live_...) from Developers → API keys. A full secret key (sk_live_...) also works; a restricted key is safer.
Webhook signing secretYesThe whsec_... secret of the webhook endpoint you point at ProductLense.

Every plan includes one billing connection per site, Free too; Growth and Scale allow any number. See plans and limits.

Permissions

Create a restricted key with Read access to:

  • Customers
  • Subscriptions
  • Charges (includes refunds and disputes)
  • Balance transactions
  • Invoices
  • PaymentIntents

ProductLense calls GET /v1/customers, /v1/subscriptions, /v1/charges, /v1/refunds, /v1/payment_intents/{id} and /v1/invoice_payments. No write permission is needed or used.

Set up

  1. In Stripe, open Developers → API keys → Create restricted key and give it the read permissions above.
  2. In ProductLense, open your site's Settings → Billing connections → Stripe, paste the key and click Connect and import history. ProductLense makes one read call to check it.
  3. Copy the webhook URL from the connection card.
  4. In Stripe, open Developers → Webhooks → Add endpoint, paste the URL and select the events below.
  5. Copy the endpoint's signing secret into the Stripe connection in ProductLense.

Webhook

URL: https://api.productlense.app/api/webhooks/stripe/<connection-id> (ProductLense shows the exact URL after you save).

Events ProductLense uses:

EventWhat ProductLense does
charge.succeededRecords the charge with its fee
charge.refundedRecords the refunds on the charge
refund.created, refund.updated, charge.refund.updatedRecords or updates the refund
charge.dispute.createdRecords a chargeback with the dispute fee
customer.created, customer.updatedUpdates the customer and its metadata
customer.subscription.created, customer.subscription.updated, customer.subscription.deletedUpdates the subscription
invoice.paidLinks the subscription to its charge

Verification: the Stripe-Signature header (timestamp plus HMAC-SHA256 with your signing secret). Deliveries older than 5 minutes are rejected.

What ProductLense reads

  • Customers: email, name, country and metadata.
  • Subscriptions: plan, interval, amount and status.
  • Payments: successful charges, linked to their invoice and subscription.
  • Refunds and chargebacks: refunds from the refunds API (fetched by charge when not embedded), and disputes recorded as chargebacks.
  • History: the last 12 months on first connect, then a nightly reconcile at 03:00 UTC from the last sync minus 48 hours.

How fees are derived

The fee is balance_transaction.fee, what Stripe kept on that payment. Stripe reports it in your settlement currency, so ProductLense converts it to the charge currency with the balance transaction's exchange rate. Refunds carry the refund's balance transaction fee, and chargebacks carry the dispute fee. The fee is exact, not estimated. When Stripe returns no balance transaction for a payment, its fee is recorded as 0.

Match payments to visitors

ProductLense reads the user id from the customer's metadata, in this order: pl_user_id, user_id, userId, uid. Use the same id you pass to identify. A ProductLense visitor id in pl_visitor_id or visitor_id is also accepted. Without either, ProductLense falls back to the customer email, matched to the email trait sent with identify, or to a user id that is that email.

When you create the Stripe customer
const customer = await stripe.customers.create({
  email: user.email,
  metadata: { pl_user_id: user.id },
});

See how payments are attributed.

    ↑ ↓ to move, Enter to open