# Connect Polar

> Receive Polar orders, renewals and refunds through a signed webhook and attribute them to visits.

Source: https://metricraven.com/docs/polar  
Markdown: https://metricraven.com/docs/polar.md  
Section: Revenue

Polar sends paid and refunded orders to a webhook signed with the endpoint's secret. Checkout metadata links each order to the visit that started it.

## Set up the webhook

1. **Copy the webhook URL**

   In MetricRaven, open **Settings → Integrations → Polar** and copy the webhook URL.

2. **Add an endpoint in Polar**

   In your Polar organization, open **Settings → Webhooks → Add Endpoint**. Paste the URL, choose the **Raw** format, and select `order.paid` and `order.refunded`. For [subscription goals](https://metricraven.com/docs/goals-funnels#payment-provider-goals), also select `subscription.created`, `subscription.active`, `subscription.updated`, `subscription.canceled`, `subscription.uncanceled` and `subscription.revoked`.

3. **Paste the secret in MetricRaven**

   Copy the endpoint's secret from Polar, paste it into the Polar card and choose **Connect Polar**.

## Pass the visitor ID at checkout

Polar copies checkout `metadata` to the order. Use these keys, and set `externalCustomerId` to your user ID so it matches the Identify API:

| Key | Value |
| --- | --- |
| metricraven_visitor_id | The visitor ID from `getVisitorId()` (24 or 32 hex characters). |
| metricraven_user_id | Optional. Your signed-in user's ID, set on the server, matching the Identify API. |
| metricraven_plan | Optional. A plan name for reports; otherwise the product from the order is used. |

1. **Read the visitor ID in the browser**

   The MetricRaven script exposes `window.analytics.getVisitorId()`. Read it when checkout starts and send it to your checkout code:

   ```javascript
   // Read this in the visitor's browser when they start checkout.
   const visitorId = window.analytics?.getVisitorId() ?? null;
   // Send visitorId to your own checkout endpoint alongside its usual request data.
   // Use the authenticated user ID from your backend; do not trust a browser-supplied user ID.
   ```

2. **Add it to the checkout**

   ```javascript title="Checkout API"
   // Server: create a checkout with the Polar SDK (@polar-sh/sdk).
   const checkout = await polar.checkouts.create({
     products: ["YOUR_PRODUCT_ID"],
     successUrl: "https://example.com/success",
     // Your authenticated user's ID; Polar returns it as customer.external_id.
     externalCustomerId: user?.id,
     metadata: visitorId ? { metricraven_visitor_id: visitorId } : {},
   });
   // Redirect the visitor to checkout.url
   ```

   For a checkout link, pass the visitor ID as `reference_id`; Polar adds it to the order metadata:

   ```javascript title="Checkout link"
   // Browser: Polar copies ?reference_id= from a checkout link into the order metadata.
   const url = new URL("https://buy.polar.sh/YOUR_CHECKOUT_LINK_ID");
   const visitorId = window.analytics?.getVisitorId();
   if (visitorId) url.searchParams.set("reference_id", visitorId);
   window.location.href = url.toString();
   ```

MetricRaven links each payment to a visit using the first of these that is available:

1. The `metricraven_visitor_id` passed through checkout.
2. The `metricraven_user_id` (or the provider's customer external ID), matched to a visitor you linked with the [Identify API](https://metricraven.com/docs/identify-users).
3. An earlier payment from the same provider customer, so renewals keep the original attribution.
4. The checkout email, matched to an email you sent to the Identify API.
5. The checkout email, matched to a browser that called [payment({ email })](https://metricraven.com/docs/attribute-by-email) within an hour of the payment.

Can't set metadata on your checkout? See [Other Polar methods](https://metricraven.com/docs/polar-other-methods) to match by the buyer's email.

> **Verify it works**
>
> After a delivery, the Polar card in **Settings → Integrations** shows **Connected**, the number of verified events and the last event type. A live payment appears in **Revenue** in its own currency; with a visitor ID it is credited to that visit's source in **Traffic**.
>
> If the card says a delivery was rejected, the signing secret doesn't match. Paste it again or generate a replacement.

## Details

- Revenue is the order's `total_amount` (after discounts, including tax), converted from cents. Refunds include refunded tax.
- Orders with `billing_reason: subscription_cycle` are renewals. A subscription cancellation is not a refund.
- Signature: Polar uses Standard Webhooks headers `webhook-id`, `webhook-timestamp` and `webhook-signature`. Deliveries older than five minutes are rejected; MetricRaven accepts both Polar's original and current secret formats.
- Polar's sandbox is a separate environment; connect it only to a test site.
- Each delivery is verified with an HMAC signature over the exact request body, compared in constant time. Unsigned or altered requests are rejected with `401` and never change revenue.
- Redelivered events are recognized and ignored, so provider retries never add a payment twice. Refunds reduce the original payment and never exceed it.
- The signing secret is stored encrypted, bound to this site, and never shown in reports, share links or logs. Disconnecting deletes it; imported revenue stays in your reports.
- Free trials and zero-amount orders are not revenue. A subscription cancellation is not a refund: revenue already paid stays.
- Webhooks deliver new events only. Payments from before you connected are not imported, and live MRR is currently available for Stripe only.
- Use one revenue path per purchase: don't also send the same orders through the Payments API.
