Revenue Connectors · Stripe

Connect Stripe to Admaxxer

Forward Stripe webhook events (checkout.session.completed, payment_intent.succeeded, charge.succeeded) to Admaxxer so every order, subscription, and one-off payment shows up in your revenue attribution, blended MER, and cohort LTV. Five-minute setup, signed via the Stripe-Signature header, visitors stitched via metadata.admaxxer_visitor_id.

Open Revenue Settings Back to all connectors

1. Prerequisites

2. Create a Stripe Restricted Key

Admaxxer never needs write access to your Stripe account — only read access to verify webhook context. Use a Restricted Key, not a Secret Key.

  1. In the Stripe dashboard, go to Developers › API keys › Restricted keys › + Create restricted key.
  2. Name it Admaxxer Revenue Read.
  3. Set the following permissions:
    • ChargesRead
    • PaymentIntentsRead
    • Checkout SessionsRead
    • RefundsRead
    • Everything else — None
  4. Click Create key. Copy the rk_live_... value.
  5. Paste the key into Admaxxer › Revenue Settings › Stripe.

3. Add the webhook endpoint

Stripe will send signed webhook events to a single Admaxxer URL. Replace <YOUR_WEBSITE_ID> with the UUID shown in your Revenue Settings.

https://admaxxer.com/api/pixel/webhooks/stripe/<YOUR_WEBSITE_ID>
  1. In the Stripe dashboard, go to Developers › Webhooks › + Add endpoint.
  2. Paste your Admaxxer webhook URL into Endpoint URL.
  3. Under Listen to, select Events on your account.
  4. Click + Select events and pick:
    • checkout.session.completed
    • payment_intent.succeeded
    • charge.succeeded
  5. Click Add endpoint. Stripe shows a Signing secret starting with whsec_....
  6. Click Reveal, copy the whsec_... value, and paste it into Admaxxer Revenue Settings › Stripe › Webhook signing secret.

The signing secret is what makes the Stripe-Signature header verifiable. Without it, Admaxxer rejects every inbound webhook with 401 invalid_signature.

4. Stamp the visitor ID on every checkout

This is the step that turns a Stripe payment into an attributed revenue event. The Admaxxer pixel exposes window.admx.getVisitorId() for client-side reads, and a server-side Cookie/header fallback for server-driven checkouts.

Stripe Checkout (recommended)

// Server-side (Node.js + stripe-node)
const session = await stripe.checkout.sessions.create({
  mode: 'payment',
  payment_method_types: ['card'],
  line_items: [/* ... */],
  metadata: {
    admaxxer_visitor_id: req.cookies['admx_vid']
      || req.headers['x-admx-vid']
      || ''
  },
  success_url: 'https://example.com/thanks',
  cancel_url: 'https://example.com/cart',
});

Payment Intents (custom integration)

const intent = await stripe.paymentIntents.create({
  amount: 4900,
  currency: 'usd',
  metadata: {
    admaxxer_visitor_id: req.cookies['admx_vid'] || ''
  },
});

The pixel writes the visitor ID to a 1p cookie (admx_vid) on first page view. Your server reads that cookie when building the Stripe object. If the user pays cross-device, the visitor ID is also recoverable from the x-admx-vid request header set by the pixel on every fetch.

5. Verify the connection

  1. In Stripe, go to Developers › Webhooks, select your Admaxxer endpoint, and click Send test webhook.
  2. Choose checkout.session.completed and send.
  3. In Admaxxer, open Revenue Settings. The "Last 50 webhook deliveries" panel should show the test with status ok.
  4. Run a real test purchase in Stripe test mode. Inspect the resulting Checkout Session in Stripe and confirm metadata.admaxxer_visitor_id is non-empty.
  5. Within 3 seconds, the order should appear in the Admaxxer attribution dashboard, joined to the visitor's acquisition source.

6. Troubleshooting

Webhook returns 401 invalid_signature
Signing secret mismatch. Re-copy the whsec_... value from Stripe (Developers › Webhooks › Reveal) into Admaxxer Revenue Settings. Make sure you didn't accidentally copy the Restricted Key or a Publishable Key — only the webhook signing secret validates inbound payloads.
Order appears as unattributed
metadata.admaxxer_visitor_id was empty on the Stripe object. Common causes: the pixel hasn't fired yet (consent banner blocking), the cookie was stripped by a SameSite=Strict policy on a cross-domain checkout, or your server isn't reading the cookie when building the Checkout Session. Inspect the Stripe object in the dashboard and confirm metadata is populated.
Same order recorded twice
You configured two different Stripe webhook endpoints both pointing at the same Admaxxer URL. Remove one; we deduplicate on Stripe object ID but only after the request lands, and double-firing wastes your Stripe webhook quota.
Refunds not subtracting from MER
You haven't subscribed the webhook to charge.refunded. Add it to the same endpoint — no second endpoint required.

Frequently asked

Do I need a full Stripe API key, or can I use a Restricted Key?
A Restricted Key with read access to Charges and Payment Intents (write off) is sufficient — Admaxxer never needs to mutate Stripe data, only read inbound webhook events and (optionally) the original Charge for tax/refund context. Restricted Keys are the recommended setup; never paste a full Secret Key into a third-party tool.
Which Stripe webhook events should I forward?
Three events cover every Admaxxer revenue path: checkout.session.completed (Stripe Checkout flows), payment_intent.succeeded (Payment Intents API), and charge.succeeded (legacy Charges and most one-off payment paths). You can forward all three to the same Admaxxer endpoint — we deduplicate on the Stripe object ID, so a single transaction firing across multiple events isn't double-counted.
How does Stripe-Signature verification work?
Stripe signs every webhook with HMAC-SHA256 using your endpoint's signing secret (whsec_...). The Stripe-Signature header carries a timestamp plus the signature; Admaxxer rebuilds the signed payload (timestamp + . + body), HMACs it with the signing secret, and compares constant-time. Mismatches return 401 immediately. The 5-minute timestamp window is enforced to block replay attacks.
Where do I put metadata.admaxxer_visitor_id?
Two places, depending on which Stripe API you use: (1) On Checkout Sessions, set metadata.admaxxer_visitor_id when calling stripe.checkout.sessions.create — it propagates to every downstream event. (2) On Payment Intents created directly, set metadata.admaxxer_visitor_id on the PaymentIntent. The Admaxxer pixel exposes window.admx.getVisitorId() so your server can fetch the value when constructing the Stripe object.
What if I have refunds or chargebacks?
Forward charge.refunded and charge.dispute.closed to the same Admaxxer endpoint. We treat refunds as negative revenue events linked to the original charge by ID, and lost disputes identically. Pending disputes show up flagged in the dashboard but don't subtract from MER until resolved.

Next steps