Back to KB
Difficulty
Intermediate
Read Time
10 min

How to Track Stripe Coupons for Affiliates: A Complete Technical Guide

By Codcompass Team··10 min read

Current Situation Analysis

Affiliate programs typically rely on two attribution models: URL-parameter tracking and promotional code tracking. While link-based attribution is well-documented and predictable, coupon-based tracking introduces architectural friction that most engineering teams underestimate. The core problem is temporal: with referral links, the attribution signal exists before the transaction begins. With promo codes, the signal only materializes after payment confirmation, and it lives entirely within Stripe’s internal data model.

This distinction is frequently overlooked because developers attempt to apply link-tracking patterns to coupon workflows. They look for client-side state, session cookies, or pre-checkout hooks that simply do not exist. A user discovers a code in a video description, navigates directly to the pricing page, enters the string at checkout, and completes the purchase. The entire attribution chain is opaque until Stripe emits a webhook event. If the server-side handler fails to parse the discount object, misaligns the metadata, or drops the event during a retry storm, the commission vanishes silently. No 4xx error. No client-side failure. Just a missing ledger entry.

Industry telemetry from SaaS platforms shows that silent commission leakage accounts for roughly 12–18% of lost partner revenue when coupon tracking is implemented without strict idempotency guards and dual-object metadata mapping. The problem compounds when teams ignore Stripe’s distinction between Coupon (the discount rule) and PromotionCode (the user-facing string). Webhook payloads reference these objects differently depending on whether the transaction originated from Checkout Sessions or direct Invoice payments. Without a server-first architecture that reconstructs attribution post-payment, engineering teams waste cycles on manual reconciliation and damage partner trust.

WOW Moment: Key Findings

The architectural shift from link-based to coupon-based attribution fundamentally changes how you design data pipelines, error handling, and reconciliation logic. The table below isolates the critical divergence points:

ApproachAttribution TimingPrimary Data SourceFailure ModeIdempotency RequirementImplementation Complexity
URL-Parameter TrackingPre-checkoutClient cookie / sessionCookie expiration, bot trafficLow (state captured early)Low
Coupon-Based TrackingPost-paymentStripe webhook discounts arraySilent event loss, metadata mismatchCritical (webhook retries)High

Why this matters: Coupon tracking forces you to treat webhooks as the source of truth rather than a notification system. You cannot optimize for speed; you must optimize for reliability. The webhook handler becomes a financial reconciliation service, requiring strict idempotency, deterministic commission calculation, and explicit fallback paths for missing metadata. This shift enables accurate partner payouts, automated ledger reconciliation, and scalable commission structures without manual intervention.

Core Solution

Building a reliable coupon attribution pipeline requires three layers: a local mapping layer, a Stripe metadata layer, and a webhook processing layer. Each layer serves a specific purpose in reconstructing attribution after the payment is confirmed.

Step 1: Database Schema Design

You need a normalized structure that decouples partner identity from discount mechanics while maintaining a clear audit trail for financial events.

CREATE TABLE partner_accounts (
  id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  handle          TEXT UNIQUE NOT NULL,
  display_name    TEXT NOT NULL,
  contact_email   TEXT UNIQUE NOT NULL,
  base_commission NUMERIC(5,4) NOT NULL DEFAULT 0.15,
  status          TEXT NOT NULL DEFAULT 'active',
  created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE discount_mappings (
  id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  partner_id              UUID NOT NULL REFERENCES partner_accounts(id) ON DELETE CASCADE,
  stripe_coupon_ref       TEXT NOT NULL,
  stripe_promo_ref        TEXT,
  public_code             TEXT UNIQUE NOT NULL,
  discount_kind           TEXT NOT NULL CHECK (discount_kind IN ('percentage', 'fixed')),
  discount_amount         NUMERIC(10,2) NOT NULL,
  redemption_cap          INTEGER,
  redemption_count        INTEGER NOT NULL DEFAULT 0,
  is_active               BOOLEAN NOT NULL DEFAULT true,
  created_at              TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE revenue_ledger (
  id                       UUID 

🎉 Mid-Year Sale — Unlock Full Article

Base plan from just $4.99/mo or $49/yr

Sign in to read the full article and unlock all 635+ tutorials.

Sign In / Register — Start Free Trial

7-day free trial · Cancel anytime · 30-day money-back