# How to create coupon codes in Stripe

- Tool: Stripe
- Difficulty: Beginner
- Time required: 15 minutes
- Compatibility: Stripe API v2024-12+, Node.js 18+, Stripe Dashboard
- Last updated: March 2026

## TL;DR

Create coupons in Stripe via the API with stripe.coupons.create() specifying percent_off or amount_off, duration (once, repeating, or forever), and optional limits. Then create promotion codes with stripe.promotionCodes.create() to give customers a shareable code like SAVE20. Apply coupons to Checkout Sessions, subscriptions, or invoices.

## Coupons and Promotion Codes in Stripe

Stripe uses a two-layer discount system. A Coupon defines the discount logic (20% off, $10 off, etc.) and duration. A Promotion Code wraps a coupon in a customer-facing code string (like SAVE20) that customers enter at checkout. You can create coupons without promotion codes for internal use (applied directly via API), or create promotion codes for self-service discounts. Both work with Checkout Sessions, subscriptions, and invoices.

## Before you start

- A Stripe account with API keys from Dashboard → Developers → API keys
- Node.js 18 or newer installed
- The stripe npm package installed (npm install stripe)
- A product or Checkout Session to apply the coupon to

## Step-by-step guide

### 1. Create a percentage coupon

Create a coupon that gives a percentage discount. Set percent_off and duration. Duration options: 'once' (single use), 'repeating' (N months), or 'forever' (applies indefinitely for subscriptions).

```
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

const coupon = await stripe.coupons.create({
  percent_off: 20,
  duration: 'once',
  name: '20% Off First Purchase',
  max_redemptions: 100, // optional: limit total uses
  redeem_by: Math.floor(new Date('2026-12-31').getTime() / 1000), // optional: expiry
});

console.log('Coupon ID:', coupon.id);
```

**Expected result:** A coupon is created that gives 20% off for one payment, limited to 100 uses.

### 2. Create a fixed-amount coupon

Use amount_off instead of percent_off for a fixed discount. You must also specify the currency.

```
const coupon = await stripe.coupons.create({
  amount_off: 1000, // $10.00 in cents
  currency: 'usd',
  duration: 'repeating',
  duration_in_months: 3, // applies for 3 months of a subscription
  name: '$10 Off for 3 Months',
});
```

**Expected result:** A coupon is created that gives $10 off each month for 3 months.

### 3. Create a promotion code

Wrap the coupon in a promotion code that customers can enter. The code string is what customers type at checkout.

```
const promoCode = await stripe.promotionCodes.create({
  coupon: coupon.id,
  code: 'SAVE20',
  max_redemptions: 50,
  restrictions: {
    first_time_transaction: true, // only for new customers
    minimum_amount: 5000, // minimum $50.00 order
    minimum_amount_currency: 'usd',
  },
});

console.log('Promo code:', promoCode.code);
```

**Expected result:** A promotion code SAVE20 is created that applies the 20% off coupon with restrictions.

### 4. Apply a coupon to a Checkout Session

Enable promotion codes in your Checkout Session so customers can enter the code, or apply a coupon directly without requiring customer input.

```
// Option 1: Let customers enter promo codes at checkout
const session = await stripe.checkout.sessions.create({
  mode: 'payment',
  allow_promotion_codes: true, // shows promo code input field
  line_items: [{
    price_data: {
      currency: 'usd',
      product_data: { name: 'Annual Plan' },
      unit_amount: 9900, // $99.00
    },
    quantity: 1,
  }],
  success_url: 'https://yoursite.com/success',
  cancel_url: 'https://yoursite.com/cancel',
});

// Option 2: Apply coupon directly (no code needed from customer)
const session2 = await stripe.checkout.sessions.create({
  mode: 'payment',
  discounts: [{ coupon: coupon.id }],
  line_items: [{ /* ... */ }],
  success_url: 'https://yoursite.com/success',
  cancel_url: 'https://yoursite.com/cancel',
});
```

**Expected result:** Option 1: Checkout page shows a promo code input field. Option 2: Discount is pre-applied.

## Complete code example

File: `coupon-system.js`

```javascript
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

const app = express();
app.use(express.json());

// Create a coupon
app.post('/api/coupons', async (req, res) => {
  try {
    const { percent_off, amount_off, currency, duration, duration_in_months, name, max_redemptions } = req.body;

    const params = { duration, name };
    if (percent_off) params.percent_off = percent_off;
    if (amount_off) {
      params.amount_off = amount_off;
      params.currency = currency || 'usd';
    }
    if (duration === 'repeating') params.duration_in_months = duration_in_months;
    if (max_redemptions) params.max_redemptions = max_redemptions;

    const coupon = await stripe.coupons.create(params);
    res.json({ id: coupon.id, name: coupon.name });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Create a promotion code for a coupon
app.post('/api/promo-codes', async (req, res) => {
  try {
    const { couponId, code, max_redemptions, first_time_only } = req.body;

    const params = { coupon: couponId };
    if (code) params.code = code;
    if (max_redemptions) params.max_redemptions = max_redemptions;
    if (first_time_only) {
      params.restrictions = { first_time_transaction: true };
    }

    const promoCode = await stripe.promotionCodes.create(params);
    res.json({ id: promoCode.id, code: promoCode.code });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Create checkout with promo code support
app.post('/api/checkout', async (req, res) => {
  try {
    const session = await stripe.checkout.sessions.create({
      mode: 'payment',
      allow_promotion_codes: true,
      line_items: [{
        price_data: {
          currency: 'usd',
          product_data: { name: 'Pro Plan' },
          unit_amount: 4900,
        },
        quantity: 1,
      }],
      success_url: `${req.headers.origin}/success`,
      cancel_url: `${req.headers.origin}/cancel`,
    });

    res.json({ url: session.url });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server on port ${PORT}`));
```

## Common mistakes

- **Confusing coupons with promotion codes** — undefined Fix: A coupon is the discount logic (20% off). A promotion code is a customer-facing code string (SAVE20) linked to a coupon. Create the coupon first, then optionally create promotion codes for it.
- **Using both allow_promotion_codes and discounts in the same Checkout Session** — undefined Fix: These are mutually exclusive. Use allow_promotion_codes to let customers enter codes, OR use discounts to pre-apply a coupon. You cannot use both.
- **Setting amount_off without specifying currency** — undefined Fix: Fixed-amount coupons require a currency field. Without it, the API returns an error.
- **Forgetting to set max_redemptions or redeem_by limits** — undefined Fix: Without limits, coupons can be used unlimited times forever. Always set max_redemptions and/or redeem_by for marketing campaigns.

## Best practices

- Create coupons with descriptive names (e.g., '20% Off First Purchase Q1 2026') for easy Dashboard identification
- Set max_redemptions and redeem_by dates on coupons to prevent unlimited usage
- Use promotion codes for customer-facing discounts and direct coupon application for internal/automatic discounts
- Test coupons with card 4242424242424242 in test mode to verify the discount is applied correctly
- Use first_time_transaction restriction on promotion codes to limit discounts to new customers
- Track coupon performance in Dashboard → Coupons to see redemption counts and revenue impact
- Delete or deactivate expired coupons to keep your catalog clean

## Frequently asked questions

### What is the difference between a coupon and a promotion code?

A coupon defines the discount rules (percentage, amount, duration). A promotion code is a shareable string (like SAVE20) linked to a coupon that customers can enter at checkout. You need a coupon first, then optionally create promotion codes.

### Can I apply multiple coupons to one payment?

No. Stripe allows only one coupon per Checkout Session, subscription, or invoice. For stacking discounts, create a single coupon that represents the combined discount.

### Can I edit a coupon after creating it?

You can update the name and metadata of a coupon, but not the discount amount, duration, or other terms. To change the discount, create a new coupon.

### How do coupons work with subscriptions?

For subscriptions, 'once' applies the discount to the first invoice only, 'repeating' applies for a set number of months, and 'forever' applies to every invoice for the life of the subscription.

### Can customers use a promotion code more than once?

By default, each customer can use a promotion code once. Set max_redemptions on the promotion code to limit total uses across all customers.

### What if I need a complex discount system with tiered pricing and referral codes?

For advanced discount logic like volume-based pricing, referral programs, and multi-tier coupon systems, the RapidDev team can help design and implement a custom solution on top of Stripe's coupon API.

---

Source: https://www.rapidevelopers.com/stripe-guide/how-to-create-coupon-codes-in-stripe-api
© RapidDev — https://www.rapidevelopers.com/stripe-guide/how-to-create-coupon-codes-in-stripe-api
