## TL;DR

Stripe promotion codes are customer-facing wrappers around coupons: the code the customer types resolves to a coupon with its own duration and restrictions. On invoices, the underlying coupon's rules drive the discount, including expiry and redemption limits. Set max_redemptions and an expires_at on the code so a leaked code cannot discount forever. Expired or fully-redeemed codes fail at redemption time with a clear error; handle that in your checkout or portal UI instead of letting it 500.

## The query

```text
Stripe promotion codes on invoices: how they apply and expire
```

## Use this when

- Customers redeeming promo codes for subscription discounts
- A promo code leaking and getting overused
- Codes that should stop working after a campaign ends

## Not for

- Manual discounts applied by your team (use coupons directly)
- Usage-based credits (different mechanism)

## Steps

1. Create the coupon with the intended discount and duration first.
   Expected output: The discount behavior is defined once.
2. Create the promotion code on top of it with expires_at and max_redemptions set.
   Expected output: The code has a bounded blast radius even if it leaks.
3. Allow redemption in Checkout, the portal, or your API update path.
   Expected output: Customers can actually apply the code.
4. Handle redemption errors in the UI with a friendly message.
   Expected output: Bad or expired codes fail gracefully.
5. Monitor redemptions during the campaign and deactivate the code when it ends.
   Expected output: The campaign closes cleanly.

## Variant phrasings

### Stripe promotion codes expire

### promo code redemption limit Stripe

### how do Stripe promotion codes apply to invoices

## Root cause

Codes wrap coupons because the customer-facing string (WELCOME20) needs to be rotatable and restrictable independently of the discount math. Expiry and redemption caps live on the code so marketing can run campaigns without touching the underlying coupon.

## Edge cases

- Archived codes cannot be reactivated; create a new one for a rerun
- A code restricted to specific products silently fails on others; check restrictions first

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_e0XLO3OovHgELLAvnfNibg
