Skip to main content
RapidDev - Software Development Agency
stripe-guide

How to create coupon codes in Stripe

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.

What you'll learn

  • The difference between Stripe coupons and promotion codes
  • How to create percentage and fixed-amount coupons via the API
  • How to generate customer-facing promotion codes
  • How to apply discounts to Checkout Sessions and subscriptions
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner6 min read15 minutesStripe API v2024-12+, Node.js 18+, Stripe DashboardMarch 2026RapidDev Engineering Team
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.

Prerequisites

  • 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).

typescript
1const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
2
3const coupon = await stripe.coupons.create({
4 percent_off: 20,
5 duration: 'once',
6 name: '20% Off First Purchase',
7 max_redemptions: 100, // optional: limit total uses
8 redeem_by: Math.floor(new Date('2026-12-31').getTime() / 1000), // optional: expiry
9});
10
11console.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.

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

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.

typescript
1const promoCode = await stripe.promotionCodes.create({
2 coupon: coupon.id,
3 code: 'SAVE20',
4 max_redemptions: 50,
5 restrictions: {
6 first_time_transaction: true, // only for new customers
7 minimum_amount: 5000, // minimum $50.00 order
8 minimum_amount_currency: 'usd',
9 },
10});
11
12console.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.

typescript
1// Option 1: Let customers enter promo codes at checkout
2const session = await stripe.checkout.sessions.create({
3 mode: 'payment',
4 allow_promotion_codes: true, // shows promo code input field
5 line_items: [{
6 price_data: {
7 currency: 'usd',
8 product_data: { name: 'Annual Plan' },
9 unit_amount: 9900, // $99.00
10 },
11 quantity: 1,
12 }],
13 success_url: 'https://yoursite.com/success',
14 cancel_url: 'https://yoursite.com/cancel',
15});
16
17// Option 2: Apply coupon directly (no code needed from customer)
18const session2 = await stripe.checkout.sessions.create({
19 mode: 'payment',
20 discounts: [{ coupon: coupon.id }],
21 line_items: [{ /* ... */ }],
22 success_url: 'https://yoursite.com/success',
23 cancel_url: 'https://yoursite.com/cancel',
24});

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

Complete working example

coupon-system.js
1const express = require('express');
2const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
3
4const app = express();
5app.use(express.json());
6
7// Create a coupon
8app.post('/api/coupons', async (req, res) => {
9 try {
10 const { percent_off, amount_off, currency, duration, duration_in_months, name, max_redemptions } = req.body;
11
12 const params = { duration, name };
13 if (percent_off) params.percent_off = percent_off;
14 if (amount_off) {
15 params.amount_off = amount_off;
16 params.currency = currency || 'usd';
17 }
18 if (duration === 'repeating') params.duration_in_months = duration_in_months;
19 if (max_redemptions) params.max_redemptions = max_redemptions;
20
21 const coupon = await stripe.coupons.create(params);
22 res.json({ id: coupon.id, name: coupon.name });
23 } catch (err) {
24 res.status(500).json({ error: err.message });
25 }
26});
27
28// Create a promotion code for a coupon
29app.post('/api/promo-codes', async (req, res) => {
30 try {
31 const { couponId, code, max_redemptions, first_time_only } = req.body;
32
33 const params = { coupon: couponId };
34 if (code) params.code = code;
35 if (max_redemptions) params.max_redemptions = max_redemptions;
36 if (first_time_only) {
37 params.restrictions = { first_time_transaction: true };
38 }
39
40 const promoCode = await stripe.promotionCodes.create(params);
41 res.json({ id: promoCode.id, code: promoCode.code });
42 } catch (err) {
43 res.status(500).json({ error: err.message });
44 }
45});
46
47// Create checkout with promo code support
48app.post('/api/checkout', async (req, res) => {
49 try {
50 const session = await stripe.checkout.sessions.create({
51 mode: 'payment',
52 allow_promotion_codes: true,
53 line_items: [{
54 price_data: {
55 currency: 'usd',
56 product_data: { name: 'Pro Plan' },
57 unit_amount: 4900,
58 },
59 quantity: 1,
60 }],
61 success_url: `${req.headers.origin}/success`,
62 cancel_url: `${req.headers.origin}/cancel`,
63 });
64
65 res.json({ url: session.url });
66 } catch (err) {
67 res.status(500).json({ error: err.message });
68 }
69});
70
71const PORT = process.env.PORT || 3000;
72app.listen(PORT, () => console.log(`Server on port ${PORT}`));

Common mistakes when creating coupon codes in Stripe

Why it's a problem: Confusing coupons with promotion codes

How to avoid: 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.

Why it's a problem: Using both allow_promotion_codes and discounts in the same Checkout Session

How to avoid: 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.

Why it's a problem: Setting amount_off without specifying currency

How to avoid: Fixed-amount coupons require a currency field. Without it, the API returns an error.

Why it's a problem: Forgetting to set max_redemptions or redeem_by limits

How to avoid: 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

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Write a Node.js Express API for Stripe coupon management. Include endpoints to create percentage and fixed-amount coupons, create promotion codes with restrictions, and create Checkout Sessions with allow_promotion_codes enabled. Use the stripe npm package.

Stripe Prompt

Add a discount system to my app using Stripe. Create coupons with percentage or fixed discounts, generate shareable promo codes with first-time customer restrictions, and integrate them into Checkout Sessions. Include an admin endpoint to list all active coupons.

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.