Integration Guide

Stripe Webhooks

Connect Stripe to RELO's data backbone for CPA tracking, purchase attribution, and subscription lifecycle monitoring via server-side webhooks.

On this page
Overview Supported Events Setup How It Works Identity Resolution Event Mapping Testing Security

Overview

RELO's data backbone accepts Stripe webhooks for CPA (Cost Per Action) tracking. When a customer completes a purchase or a subscription event occurs, Stripe sends the event payload to our Go ingest service. These are processed as model.Event records and written to ClickHouse for analytics.

The integration supports one-time purchases, subscription checkouts, and full subscription lifecycle tracking (creation, updates, cancellations).

Supported Events

checkout.session.completed

Fired when a customer completes a Stripe Checkout session. Covers both one-time purchases and initial subscription checkouts. Revenue is extracted from the session's amount_total.

invoice.paid

Fired when a subscription invoice is paid. The handler skips invoices with billing_reason: subscription_create to avoid double-counting the initial checkout (already captured by checkout.session.completed). Only subsequent renewal invoices are processed.

customer.subscription.created / updated / deleted

Subscription lifecycle events. Tracked as custom events for analytics (churn analysis, plan changes, etc.). These events do not carry revenue and do not trigger identity resolution.

Setup

  1. Webhook URL
    Configure your Stripe webhook endpoint to point to:
    POST https://ingest.relo.mx/postback/stripe
  2. Signing Secret
    Retrieve the webhook signing secret from Stripe and set it as the STRIPE_WEBHOOK_SECRET environment variable on the backbone server. Without this, all incoming requests will be rejected.
  3. Events to Subscribe
    Select the following events when configuring the webhook:
    • checkout.session.completed
    • invoice.paid
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
Important

The signing secret must be configured before any events will be accepted. If STRIPE_WEBHOOK_SECRET is empty or missing, the service rejects all Stripe webhook requests with a signature verification failure.

How It Works

Stripe (webhook) | | POST /postback/stripe | Headers: Stripe-Signature | Body: JSON event payload v Go Ingest Service | +-- 1. Verify HMAC-SHA256 signature (5-min timestamp tolerance) | +-- 2. Parse event, dispatch by "type" field | +-- 3. Handler extracts data: | - checkout.session.completed -> revenue, email, currency | - invoice.paid -> revenue, email, currency | - subscription.* -> plan, status, interval | +-- 4. Identity resolution (purchase events only) | email -> SHA-256 hash -> DragonflyDB lookup -> device_id | +-- 5. Write to ClickHouse "events" table (async batch) | +-- 6. Dual-write to Supabase (during migration period)

Purchase Events

For checkout.session.completed and invoice.paid, the handler extracts revenue from the event payload (amount_total or amount_paid, converted from cents to the base currency unit). Identity resolution links the customer email to a unified device ID via DragonflyDB.

Subscription Lifecycle Events

For customer.subscription.* events, the handler records the plan, status, and billing interval as custom event properties. These events do not include customer email in the subscription object, so identity resolution is skipped.

Identity Resolution

Events with Identity Resolution
Events without Identity Resolution

The Stripe subscription object does not contain the customer's email address. These events are recorded without a linked device ID.

The identity resolution flow for purchase events:

Customer email (from Stripe event)
  -> SHA-256 hash
  -> DragonflyDB lookup: id:email:{hash} -> unified device_id
  -> Event written with resolved device_id

Event Mapping

Stripe events are mapped to RELO's universal event schema as follows:

Stripe Event RELO event_name event_type channel
checkout.session.completed purchase purchase stripe
invoice.paid (subscription_cycle) subscription_renewal custom stripe
invoice.paid (subscription_create) Skipped — already captured by checkout.session.completed
customer.subscription.created subscription_created custom stripe
customer.subscription.updated subscription_updated custom stripe
customer.subscription.deleted subscription_cancelled custom stripe
Note

All events are written to the ClickHouse events table with media_source = 'stripe' and channel = 'stripe'. The order_id field is populated with the Stripe session ID, invoice ID, or subscription ID depending on the event type.

Testing

Stripe CLI (Local Development)

Forward Stripe test events to your local backbone instance:

stripe listen --forward-to localhost:4080/postback/stripe

Then trigger test events from another terminal:

stripe trigger checkout.session.completed
stripe trigger invoice.paid
stripe trigger customer.subscription.created

Stripe Test Mode

You can also configure the webhook endpoint in Stripe's test mode to point to your staging or production URL. Use the Stripe test card for generating purchase events:

Test Card

4242 4242 4242 4242

Any future expiry date, any CVC, any billing postal code.

Verifying Events

After triggering a test event, verify it was ingested by querying ClickHouse:

SELECT event_id, event_name, event_type, revenue, order_id
FROM events
WHERE media_source = 'stripe'
ORDER BY event_time DESC
LIMIT 10;

Security