Stripe Coupons and Promotions — Discounts and Special Offers
In this tutorial, you will learn about Stripe Coupons and Promotions. We cover key concepts, practical examples, and best practices to help you master this topic.
Stripe coupons and promotion codes let you offer discounts on subscriptions and one-time purchases, with support for percentage and fixed amounts, duration limits, and usage restrictions.
What You'll Learn
By the end of this lesson you will understand how to create coupons, generate promotion codes, apply discounts to subscriptions and invoices, and enforce usage limits.
Why It Matters
Promotions drive customer acquisition and retention. Stripe handles discount application, proration, and expiration automatically, ensuring consistent pricing across your customer base.
Real-World Use
DodaZIP offers a 20% annual discount coupon and first-month-free promotion codes. Stripe applies these automatically during Checkout and handles the billing adjustments.
flowchart LR
C[Create Coupon] --> PC[Generate Promotion Code]
PC --> APP[Apply at Checkout]
APP --> S[Discount Applied]
S --> R[Recurring Discount]
R -->|Duration ends| F[Full Price]
style C fill:#6772e5,color:#fff
Creating Coupons
Coupons define the discount. They can be percentage-based or fixed-amount.
import stripe
import os
stripe.api_key = os.environ.get("STRIPE_SECRET_KEY", "sk_test_placeholder")
def create_percent_coupon(percent, duration="forever", name=None):
coupon = stripe.Coupon.create(
percent_off=percent,
duration=duration,
name=name or f"{percent}% off",
)
return coupon.id
def create_amount_coupon(amount_cents, currency="usd", duration="once"):
coupon = stripe.Coupon.create(
amount_off=amount_cents,
currency=currency,
duration=duration,
)
return coupon.id
stripe.api_key = "sk_test_placeholder"
class MockCoupon:
id = "coupon_test"
stripe.Coupon.create = lambda **kwargs: MockCoupon()
percent_off = create_percent_coupon(20, "forever", "20% annual discount")
amount_off = create_amount_coupon(1000, "usd", "once")
print(f"Coupons created:")
print(f" Percent: {percent_off} (20% off forever)")
print(f" Amount: {amount_off} ($10.00 off once)")
Expected output:
Coupons created:
Percent: coupon_test (20% off forever)
Amount: coupon_test ($10.00 off once)
Promotion Codes
Promotion codes are customer-facing codes that customers enter at checkout.
# promotion_codes.py
# Creating promotion codes
def create_promotion_code(coupon_id, code, max_redemptions=None, expires_at=None):
restrictions = {}
if max_redemptions:
restrictions["max_redemptions"] = max_redemptions
if expires_at:
restrictions["expires_at"] = expires_at
promo = {
"id": f"promo_{code.lower()}",
"code": code,
"coupon": coupon_id,
"restrictions": restrictions,
"active": True
}
print(f"Promotion code: {code}")
print(f" Coupon: {coupon_id}")
print(f" Max redemptions: {max_redemptions or 'Unlimited'}")
print(f" Active: True")
return promo
codes = [
("SAVE20", "coupon_20percent", 100),
("WELCOME10", "coupon_10dollar", 500),
("FREEMONTH", "coupon_firstmonth", None),
]
for code, coupon, max_redemptions in codes:
create_promotion_code(coupon, code, max_redemptions)
print()
Applying to Subscriptions
Coupons are applied at subscription creation or invoice generation.
# apply_discount.py
# Applying coupons to subscriptions
def apply_coupon(subscription_id, coupon_id):
print(f"Applying coupon {coupon_id} to subscription {subscription_id}")
print(f" -> Coupon applied successfully")
if coupon_id.startswith("coupon_20"):
print(" -> 20% discount on all future renewals")
elif coupon_id == "coupon_firstmonth":
print(" -> 100% off first month only")
def calculate_discounted_price(original_price, coupon):
if coupon["type"] == "percent":
discounted = original_price * (1 - coupon["value"] / 100)
else:
discounted = max(0, original_price - coupon["value"])
savings = original_price - discounted
print(f"Original: ${original_price/100:.2f}")
print(f"Discounted: ${discounted/100:.2f}")
print(f"You save: ${savings/100:.2f}")
return discounted
calculate_discounted_price(2999, {"type": "percent", "value": 20})
Common Mistakes
Not setting duration limitations: Forever coupons discount every renewal. Use duration=once or duration=repeating with duration_in_months for limited discounts.
Creating too many promotion codes: Each code has separate tracking. Clean up expired codes regularly.
Not testing coupon stacking: By default only one coupon applies. Configure promotion code stackability if needed.
Forgetting max redemptions: Without limits, a promotion code can be used infinitely. Set max_redemptions for limited offers.
Applying coupons after subscription creation: Coupons applied mid-cycle are prorated. Apply at subscription creation for clean billing.
Practice Questions
What is the difference between a coupon and a promotion code? A coupon defines the discount. A promotion code is a customer-facing code that applies a coupon.
What duration options are available for coupons? once (single billing cycle), repeating (specified number of cycles), forever (all cycles).
How do you limit promotion code usage? Set max_redemptions on the promotion code when creating it.
Can a customer use multiple coupons? By default, no. Only one coupon applies per subscription or invoice.
Challenge: Design a promotion system with referral codes, first-purchase discount, and seasonal promotions with expiration dates.
FAQ
Mini Project
Create a promotion code endpoint that generates a coupon and promotion code with configurable discount and restrictions.
import json
import random
import string
def lambda_handler(event, context):
body = json.loads(event.get("body", "{}"))
discount_percent = body.get("discount_percent", 10)
duration = body.get("duration", "once")
max_uses = body.get("max_uses", 100)
coupon_id = f"coupon_{discount_percent}pct"
code = "".join(random.choices(string.ascii_uppercase + string.digits, k=8))
print(f"Created {discount_percent}% off coupon ({coupon_id})")
print(f"Promotion code: {code}")
print(f"Duration: {duration}")
print(f"Max uses: {max_uses}")
return {
"statusCode": 201,
"body": json.dumps({
"coupon_id": coupon_id,
"promotion_code": code,
"discount_percent": discount_percent,
"duration": duration,
"max_uses": max_uses
})
}
print(json.loads(lambda_handler({"body": json.dumps({"discount_percent": 25, "duration": "repeating", "max_uses": 50})}, None)["body"]))
What's Next
Next: Testing Stripe for test cards and scenarios.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro