# Attribute Stripe Payment Links

> What MetricRaven can and can't attribute for Stripe Payment Links, and a webhook that links them to visits.

Source: https://metricraven.com/docs/stripe-payment-links  
Markdown: https://metricraven.com/docs/stripe-payment-links.md  
Section: Revenue

Payment Link revenue is imported like any Stripe payment. Linking it to a visit takes one extra step, because a link can't carry a different visitor ID for each buyer on its own.

> **Warning:** Stripe adds `client_reference_id` from the link's URL to the Checkout Session, but MetricRaven doesn't read that field. Without the webhook below, Payment Link payments show in **Revenue** but aren't credited to a traffic source.

## Your options

- **Switch to Checkout Sessions** created by your server. This is the most reliable way; see [Stripe Checkout](https://metricraven.com/docs/stripe-checkout).
- **Keep Payment Links and add a small webhook** that copies the visitor ID into metadata MetricRaven reads (below).
- **Revenue only:** do nothing. Payments are still imported, just not linked to visits.

## Link Payment Links to visits with a webhook

1. **Add the visitor ID to the link when it's clicked**

   ```javascript title="Your website"
   // On pages with Payment Link buttons, after the MetricRaven script.
   // Adjust the prefix if your links use a custom Stripe domain.
   document.addEventListener("click", (event) => {
     const link = event.target.closest?.('a[href^="https://buy.stripe.com/"]');
     const visitorId = window.analytics?.getVisitorId?.();
     if (!link || !visitorId) return;
     const url = new URL(link.href);
     url.searchParams.set("client_reference_id", visitorId);
     link.href = url.toString();
   });
   ```

2. **Copy it into metadata when the checkout completes**

   In Stripe, add a webhook endpoint for `checkout.session.completed`, then handle it on your server:

   ```javascript title="Webhook handler"
   // Your Stripe webhook endpoint, for the checkout.session.completed event.
   // stripe is your initialized Stripe client; verify the webhook signature first.
   const session = event.data.object;
   const visitorId = session.client_reference_id;
   if (visitorId && /^(?:[0-9a-f]{24}|[0-9a-f]{32})$/.test(visitorId)) {
     const metadata = { metricraven_visitor_id: visitorId };
     // One-time payments: the PaymentIntent.
     if (session.payment_intent) await stripe.paymentIntents.update(session.payment_intent, { metadata });
     // Subscriptions (and links that create customers): the Customer, so renewals are linked too.
     if (session.customer) await stripe.customers.update(session.customer, { metadata });
   }
   ```

   The webhook needs a Stripe key with write access to PaymentIntents and Customers.

## Plan names from the link itself

A Payment Link's own metadata is the same for every buyer, so it can't identify a visitor, but it can label the plan. Set `metricraven_plan` in the link's `payment_intent_data.metadata` (one-time) or `subscription_data.metadata` (subscriptions) when you create it with the API.

## Details

- The webhook usually runs seconds after payment, before MetricRaven imports the charge. If the import happens first, a one-time payment stays unlinked; for subscriptions, the next renewal links the earlier payment too.
- The visitor ID is visible in the checkout URL. It's a random identifier, not personal data, but don't add anything else to the link.
- Stripe pricing tables and buy buttons work the same way: they pass `client-reference-id`, which needs the same webhook.
- Metadata is read when MetricRaven imports the charge, usually within a minute or two of the payment. Set it when you create the object; adding it after the import doesn't change that payment.
- Once one payment from a Stripe customer is linked, that customer's later payments (renewals, upgrades) are linked to the same visitor and user, and their earlier unlinked payments are filled in.
- Only live-mode payments are imported. Test-mode payments never appear in MetricRaven.
