Razorpay conversion tracking: tie INR payments to Meta and Google
Step by step Razorpay conversion tracking: tie each captured INR payment to the ad, campaign and landing page that brought it, net of refunds and fees.
You run ads for a product that bills Indian customers through Razorpay. Meta Ads Manager reports one count of conversions this month, Google Ads reports another, and Razorpay shows a third count of captured payments, a few of them refunded. None of the three answers the question you have: which ads bring paying customers in India, and what did each earn after the refund and the fee?
Razorpay conversion tracking, done properly, means one thing. For every captured Razorpay payment you know which ad, campaign and landing page brought that customer, with refunds and Razorpay's fee taken off. It is a spoke of the revenue attribution for SaaS guide. If part of your billing runs on Stripe, the Stripe revenue attribution guide applies the same chain there.
Why the Meta pixel and Google tag cannot do this
The Meta pixel and the Google tag count a browser event that you fire on your thank-you page, then match it to a click or impression they remember.
Each platform claims the same sale. A person who clicked a Meta ad on Monday and a Google ad on Wednesday before paying is a conversion in both dashboards.
Consent and ad blockers drop events. A visitor who declines the cookie banner, or runs a content blocker, pays without the pixel firing.
Neither platform sees the captured payment. The pixel fires when the page loads, not when Razorpay captures the money.
Neither platform sees the refund. The campaign keeps the credit for money you gave back, and the fee is nowhere in the number.
How ProductLense ties a Razorpay payment to the ad
ProductLense starts from the captured payment and walks back to the first visit:
payment → customer → identity → visitor → first touch- The ProductLense script records the first visit: in the attribution doc's words, "the channel, source, medium, campaign, referrer domain and landing page of that visitor's first recorded event". Those values come from the UTM tags on the ad link.
- Your app calls
identifywith your own user id after sign-up and sends the email as a trait:window.productlense('identify', 'user_123', { email: '[email protected]' }). - ProductLense reads captured payments from Razorpay. The Razorpay doc states: "ProductLense reads the user id from the notes on the customer or on the payment:
pl_user_id,user_id,userIdoruid. Use the same id you pass toidentify." - With no id, it "falls back to the customer email, matched to the
emailtrait sent withidentify, or to a user id that is that email". - The payment is stored with the channel, source, medium, campaign, referrer domain and landing path of that first touch, so reports never re-run attribution.
Razorpay copies order notes onto the payment made against the order, so notes: { pl_user_id: user.id } when you create the order is enough. Payments without a Razorpay customer object are keyed by the payer's email, which is why the email trait matters too.
The model is first touch only. There is no last-touch or multi-touch view.
Connect Razorpay
The connection is read-only. Every plan includes one billing connection per site, Free included.
- In the Razorpay Dashboard, open Account & Settings → API Keys and generate a live key pair. The key id starts with
rzp_live_. - In ProductLense, open your site's Settings → Billing connections → Razorpay, paste the key id and secret, and click Connect and import history.
- Copy the webhook URL from the connection card. It has the form
https://api.productlense.app/api/webhooks/razorpay/<connection-id>. - In Razorpay Dashboard → Webhooks, add a webhook with that URL, set a secret and tick
payment.authorized,payment.captured,payment.failed,order.paid,refund.created,refund.processed,refund.failedand everysubscription.*event, includingsubscription.activated,subscription.chargedandsubscription.cancelled. - Paste the same secret into the connection in ProductLense. Each delivery is verified with the
X-Razorpay-Signatureheader, an HMAC-SHA256 of the raw body.
Captured payments count as revenue, failed ones add nothing, and processed refunds are subtracted. On first connect ProductLense backfills the last 12 months, then reconciles nightly at 03:00 UTC. INR amounts stay in INR and are also converted to USD at the European Central Bank rate for the payment's day. The fee is the fee field Razorpay reports on each payment, which includes GST, so it is exact rather than estimated. The Razorpay integration page lists the same steps and the Razorpay docs page has the full field list.
Tag the ads so the landing page knows the campaign
The first touch can only record what is in the URL. A visit with gclid or fbclid is classified paid, but the value is not stored, so without UTM tags the payment lands under paid with no source and no campaign. Add three UTM parameters to every ad link.
A Meta ad for a campaign named diwali-launch:
https://example.com/pricing?utm_source=facebook&utm_medium=paidsocial&utm_campaign=diwali-launchA Google Search ad for the brand campaign:
https://example.com/pricing?utm_source=google&utm_medium=cpc&utm_campaign=brand-searchBoth platforms let you add tracking parameters at the account, campaign or ad level: the URL parameters field on a Meta ad, and the final URL suffix in Google Ads. Keep the rules ProductLense applies:
utm_mediumofcpc,ppc,paid,paidsocialordisplaymakes the channelpaid.- Source and medium are stored in lower case. Campaign keeps its case, so
Diwali-Launchanddiwali-launchare two campaigns. - Do not tag links between your own pages.
The tags on the first pageview are kept for the life of the site, so a person who clicks the Meta ad in October and pays in December is still a facebook customer.
Add the spend and read profit per campaign
Ad spend enters ProductLense in two ways today: rows you type by hand and CSV files, both under Add spend on the Profit page. The Meta Ads and Google Ads connections exist but go live once each platform finishes its app review. Profit, ad spend and CSV import are on the Growth and Scale plans.
A spend row carries the same four values as the payment, so the Meta campaign above is entered as paid, facebook, paidsocial, diwali-launch. A manual row takes a From and To date, the four values, an amount and a currency. INR is accepted and converted to USD at the ECB rate for the From date. The CSV format is one row per day:
date,channel,source,medium,campaign,amount,currency
2026-10-01,paid,facebook,paidsocial,diwali-launch,4200.00,INR
2026-10-01,paid,google,cpc,brand-search,1850.00,INRMeta and Google both export daily campaign spend as CSV. Reshape it to those seven columns. If one character differs between a spend row and the UTM tag, the spend appears as its own row with no revenue.
The Profit page then shows one row per channel, source, medium and campaign:
Profit = Revenue − Fees − Ad spendRevenue is charges minus refunds, before fees. Fees are the GST-inclusive amounts Razorpay reported. The same row shows CAC (ad spend divided by new customers), ROAS (revenue divided by ad spend) and payback (CAC divided by average first-month net revenue per new customer).
Each payment keeps its INR amount, and the Profit table totals are in USD at the payment day's ECB rate, so Razorpay rupees and any USD billing beside it add up in one table. See profit per channel and the ad spend doc. ProductLense itself is priced in INR for accounts registered in India, with UPI at checkout, as the Indian SaaS use case describes.
Questions founders ask
Does ProductLense change anything in my Razorpay account?
No. The connection only reads. ProductLense calls the customers, subscriptions, plans, payments and refunds endpoints with your key and listens to the webhook you create. It never captures, refunds, cancels or edits anything. To stop, delete the webhook in the Razorpay Dashboard and remove the connection in ProductLense.
What happens when the payment lands before my app calls identify?
The payment is stored without attribution at first. A job runs every day at 04:00 UTC, takes up to 500 unattributed payments from the last 90 days and resolves them again. Once your sign-up flow has called identify for that user, the next run fills in the channel and campaign.
Do I need pl_user_id if I already have the customer's email?
Not strictly. Without an id ProductLense falls back to the customer email, matched to the email trait you sent with identify. The id is the stronger link because it survives an email change and covers a customer who pays with a different address. Setting it costs one line when you create the order.
Why does the Meta pixel report more conversions than ProductLense?
The pixel counts a browser event on your thank-you page, and Meta credits any sale that follows an ad view or click inside its attribution window. ProductLense counts captured Razorpay payments, credits each to a single first touch, and removes refunds. Its figure is lower because it counts money that stayed, once, rather than page loads.
Can I track Razorpay subscriptions and renewals, not just one-off payments?
Yes. The connector reads subscriptions and fetches each plan once for its amount and interval. The subscription.activated, subscription.charged and subscription.cancelled webhooks keep them current. A renewal is a captured payment like any other, credited to the channel that first brought the customer, so that channel keeps earning while the subscription runs.
Which plan do I need for profit per campaign with Razorpay?
Every plan, Free included, allows one billing connection per site, so revenue by channel works from day one. Ad spend entry, CSV import and the Profit page with CAC, ROAS and payback are on Growth and Scale. Accounts registered in India pay in INR, and the read API and MCP server exist from the Starter plan.