DEV Community

Mihir kanzariya
Mihir kanzariya

Posted on

Coupons and affiliate commissions on Stripe: what do you actually pay?

Here is a case that quietly breaks a lot of affiliate programs: a customer uses a discount code and came through an affiliate link. Your $50/month plan gets a 40% coupon, they pay $30, and your affiliate earns 30%. Do you pay the affiliate 30% of $50 or 30% of $30? Get this wrong in the generous direction and a discounted sale can pay out more in commission than it brought in.

There are two separate questions hiding here, and Stripe gives you a clean answer to both.

Pay on what Stripe actually charged, not the sticker price

The single rule that keeps you solvent: commission is a percentage of amount_paid, which already has the discount subtracted. Never compute off the plan's list price.

// on invoice.paid
const charged = invoice.amount_paid; // already net of any coupon, in cents
const commission = Math.round(charged * 0.30);
Enter fullscreen mode Exit fullscreen mode

Why it matters: say you pay 30% on the sticker $50 ($15) for a sale where the customer only paid $30 after a coupon. Your gross on that sale is $30, you hand $15 to the affiliate, and after Stripe fees you are close to breaking even on a customer you are supposed to be profiting from. Do that across a launch-week coupon and the program bleeds. Paying on amount_paid ($9 here) keeps the math sane.

When the coupon IS the attribution

A lot of programs skip links entirely and give each affiliate a unique code: JANE20. The code does double duty, it discounts the customer and it tells you who to credit. You read it straight off the invoice.

// invoice.paid, attribute via the promotion code used
const lines = invoice.lines?.data ?? [];
const discount = invoice.discount || lines[0]?.discount;
const promoCode = discount?.promotion_code; // e.g. "promo_123"

if (promoCode) {
  const affiliateId = await affiliateForPromoCode(promoCode); // your mapping
  if (affiliateId) {
    await ledger.credit({
      affiliate_id: affiliateId,
      amount: Math.round(invoice.amount_paid * 0.30),
      idempotency_key: invoice.id,
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Map each Stripe promotion code to an affiliate when you create it, so this lookup is a single row read, not a guess.

The stacking trap: do not double-attribute

The mess happens when both signals fire: the customer clicked an affiliate link (cookie says affiliate A) and also entered a general storewide coupon that happens to map to nobody, or worse, to affiliate B. Now two systems each think they own the sale.

Pick one source of truth and write it down as a rule:

  • If a code maps to a specific affiliate, the code wins (it is explicit intent).
  • Otherwise fall back to the click attribution stored at signup.
  • Never let both credit the same invoice. Key every credit to invoice.id so even a double-fire can only ever write one row.
const affiliateId =
  (await affiliateForPromoCode(promoCode)) ||   // explicit code first
  invoice.customer_metadata?.affiliate_id;      // then click attribution
Enter fullscreen mode Exit fullscreen mode

One more: does the affiliate keep earning after the coupon expires?

Intro coupons end. The customer paid $30 for three months, then rolls to full $50. Because you are always paying a percentage of amount_paid, this just works: the commission rises to 30% of $50 on its own when the real charge goes up. No special case, as long as you never hardcoded the discounted number.

The whole thing reduces to one habit: treat amount_paid as the only number that decides commission, and treat the promotion code as an attribution signal, not a reason to change the math. We handle both in Referralful (affiliate software on Stripe, which I help build) so coupon stacking does not turn into an overpayment, but the rules above are the same whether you buy or build.

Top comments (0)