# Razorpay

Connect Razorpay with a key pair and a webhook secret so ProductLense records captured payments, refunds and the fee Razorpay charged, GST included.

ProductLense reads your Razorpay customers, subscriptions, payments and refunds, with the fee on each payment. ProductLense only reads: it never writes to your Razorpay account, never refunds, cancels, captures or changes anything.

## What you need

| Field | Secret | Where to find it |
|---|---|---|
| Key ID | No | `rzp_live_...` from Razorpay Dashboard → Account & Settings → API Keys. |
| Key secret | Yes | The secret paired with the key id. |
| Webhook secret | Yes | The secret you set when creating the webhook in Razorpay Dashboard → Webhooks. |

Every plan includes one billing connection per site, Free too; Growth and Scale allow any number. See [plans and limits](/docs/account/plans-and-limits).

## Permissions

Razorpay API keys are not scoped; ProductLense only reads with yours. It calls `GET /v1/customers`, `/v1/subscriptions`, `/v1/plans/{id}` (each plan once, cached), `/v1/payments` and `/v1/refunds`.

## Set up

1. In Razorpay Dashboard, open Account & Settings → API Keys and generate a live key pair.
2. In ProductLense, open your site's Settings → Billing connections → Razorpay, paste the key id and secret, and click Connect and import history.
3. Copy the webhook URL from the connection card.
4. In Razorpay Dashboard → Webhooks, add a webhook with that URL, set a secret and tick the events below.
5. Paste the same secret into the connection in ProductLense.

## Webhook

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

Events ProductLense uses:

- `payment.captured`, `payment.authorized`, `payment.failed`
- `refund.processed`, `refund.created`, `refund.failed`
- every `subscription.*` event

Verification: the `X-Razorpay-Signature` header, an HMAC-SHA256 of the raw body with your webhook secret. Razorpay signatures carry no timestamp; replays are rejected by event id.

## What ProductLense reads

- **Customers**: email, name and notes. Payments made without a Razorpay customer are keyed by the payer's email.
- **Subscriptions**: status, with the amount and interval from the plan.
- **Payments**: captured payments count as revenue. Failed payments are kept with status failed and add nothing to revenue.
- **Refunds**: processed refunds.
- **History**: the last 12 months on first connect, then a nightly reconcile at 03:00 UTC from the last sync minus 48 hours.

INR amounts stay in INR and are also converted to USD at the European Central Bank rate for the payment's day.

## How fees are derived

The fee is the `fee` field on each payment, which Razorpay reports including GST. It is exact, not estimated.

## Match payments to visitors

ProductLense reads the user id from the notes on the customer or on the payment: `pl_user_id`, `user_id`, `userId` or `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.

```js title="Razorpay order with notes (Node)" {4}
const order = await razorpay.orders.create({
  amount: 99900,
  currency: 'INR',
  notes: { pl_user_id: user.id },
});
```

> [!NOTE]
> Razorpay copies order notes onto the payment made against the order, which is where ProductLense reads them.

See [how payments are attributed](/docs/revenue/attribution).
