How we compute profit per channel
The exact method behind ProductLense’s profit per channel: the attribution chain, net revenue after fees and refunds, ad spend joins, CAC, ROAS and payback.
Profit per channel is the net revenue attributed to an acquisition channel, after processor fees and refunds, minus what you spent on that channel in the same period. This post explains every step ProductLense takes to compute it, and where its limits are. The short reference version is in the methodology docs.
Step 1: record the first touch
Every visitor’s first event stores where they came from: the channel, utm_source, utm_medium, utm_campaign, the referrer domain, the landing page and the time. Channels are classified on arrival: click ids or paid mediums (cpc, ppc, paid, paidsocial, display) are paid, utm_medium=email is email, and the referrer decides between AI assistant, social, organic search, referral and direct.
Because ProductLense is cookieless, an anonymous visitor is one browser on one day. That sounds limiting, but for revenue it rarely matters, because of step 2.
Step 2: link the visitor to a person
When someone signs up or logs in, your code calls:
window.productlense('identify', 'user_123', { email: '[email protected]' });From that moment, the visitor is linked to user_123. Every later visit, on any day, joins the same person, and the first touch stays the one recorded before signup.
Step 3: link the payment to the person
Payments arrive from your billing provider by webhook, by a 12-month backfill when you connect, by a nightly reconcile, or through the payments API. Each one is resolved when it is written:
- the user id in the provider’s customer metadata (
pl_user_id), matched to anidentifycall; - otherwise the customer email, matched to the email you sent in identify traits;
- otherwise a ProductLense visitor id placed in metadata.
The chain is payment → customer → identity → visitor → first touch, and the resolved channel, source, campaign and landing page are stored on the payment row. Resolving at write time means every revenue report is a plain query over one table. Payments that cannot be matched yet are retried nightly, so an identify call that arrives after the payment still credits the right channel.
Step 4: compute net revenue
Gross charges overstate what you keep. For every payment ProductLense stores the amount, the currency, and the fee the provider reports:
charge: net = amount − fee
refund or chargeback: net = −(amount + fee)Refunds subtract the fee too, because most processors keep the original fee when you refund. For merchants of record, the fee is what they keep, including the tax they remit: Paddle’s fee is grand total minus earnings, so net revenue equals your payout. Every amount is converted to USD at the European Central Bank reference rate for its day and also kept in its original currency.
Step 5: subtract ad spend
Ad spend comes from manual entries or a CSV with date, channel, source, medium, campaign, amount, currency. It joins to revenue on channel, source, medium and campaign, the same keys the first touch carries from your UTM tags.
profit(channel, period) = Σ net revenue of payments attributed to channel and paid in period
− Σ ad spend for channel in periodThen three ratios:
CAC = ad spend / new customers
ROAS = attributed net revenue / ad spend
payback = CAC / average net revenue per new customer in their first 30 daysA worked example
An example month for one channel, Google Ads, with round numbers:
| Item | Amount |
|---|---|
| Gross revenue from customers whose first touch was Google Ads | $1,200 |
| Processor fees on those payments | $40 |
| Refunds | $100 |
| Net revenue | $1,060 |
| Google Ads spend | $500 |
| Profit | $560 |
| New customers | 10 |
| CAC | $50 |
| ROAS | 2.12× |
If those ten customers paid $53 net on average in their first 30 days, payback is $50 / $53 ≈ 0.9 months: the channel earns back what each customer cost within the first month.
The same table for a channel with $2,000 of spend and the same revenue would show a loss of $940, with a minus sign and a down arrow next to it. That is the row you want to see before next month’s budget, not after.
What this method does not do
- No multi-touch in v1. First touch gets full credit. Last touch and linear models are planned as a toggle in v1.1, computed by re-resolving the same payments.
- No view-through attribution. An ad someone saw but did not click leaves no trace on your site, so it gets no credit.
- Spend is manual or CSV today. Google Ads and Meta Ads spend connectors are planned for v1.1.
- Identify matters. Without it, ProductLense can only match payments by email or a visitor id in metadata, and anonymous visits on different days stay separate.
- Refunds land in the period they happen. A refund in November for an October sale reduces November’s net revenue for that channel, which matches your cash, not your bookings.
- Some providers hide fees. Lemon Squeezy, Chargebee, Shopify orders and Creem do not expose fees through the APIs ProductLense reads, so profit for those connections is before fees.
Why first touch is the default
For small SaaS companies, the expensive question is which channel to put more money or time into, and that is a question about where customers start. Closing channels such as email and retargeting only work on people another channel already brought. First touch is also the model that is easiest to check by hand, which matters when the number decides a budget.
Profit per channel is on the Growth and Scale plans. The methodology page has every formula.