Stripe Products and Prices — Building Your Product Catalog
In this tutorial, you will learn about Stripe Products and Prices. We cover key concepts, practical examples, and best practices to help you master this topic.
Stripe Products represent what you sell, and Prices define how much and how often customers pay, supporting one-time, recurring, tiered, and usage-based pricing models.
What You'll Learn
By the end of this lesson you will understand how to create and manage products and prices, configure different pricing models, handle multi-currency pricing, and organize your catalog.
Why It Matters
Well-organized products and prices make subscription management, reporting, and plan changes straightforward. Poor organization leads to confusing plans and difficult maintenance.
Real-World Use
DodaZIP maintains three products (Free, Basic, Pro) with monthly and annual prices for each. The pricing IDs are stored in configuration and used across all subscription operations.
flowchart LR
P1[Free Product] -->|Price $0| FR[Free Tier]
P2[Basic Product] -->|Monthly $9.99| BM[Basic Monthly]
P2 -->|Annual $99.99| BA[Basic Annual]
P3[Pro Product] -->|Monthly $29.99| PM[Pro Monthly]
P3 -->|Annual $299.99| PA[Pro Annual]
P4[Add-on Product] -->|Usage-based| UB[Per API Call]
style P2 fill:#6772e5,color:#fff
Creating Products and Prices
Products define the item. Prices define the cost and billing frequency.
import stripe
import os
stripe.api_key = os.environ.get("STRIPE_SECRET_KEY", "sk_test_placeholder")
def create_product(name, description):
product = stripe.Product.create(name=name, description=description)
return product.id
def create_price(product_id, amount_cents, currency="usd", interval=None):
price_data = {
"product": product_id,
"unit_amount": amount_cents,
"currency": currency,
}
if interval:
price_data["recurring"] = {"interval": interval}
price = stripe.Price.create(**price_data)
return price.id
stripe.api_key = "sk_test_placeholder"
class MockP:
id = "prod_test"
class MockPrice:
id = "price_test"
stripe.Product.create = lambda **kwargs: MockP()
stripe.Price.create = lambda **kwargs: MockPrice()
prod = create_product("Basic Plan", "Basic features for individuals")
monthly = create_price(prod, 999, interval="month")
annual = create_price(prod, 9999, interval="year")
print(f"Product: {prod}")
print(f"Monthly Price: {monthly} ($9.99/month)")
print(f"Annual Price: {annual} ($99.99/year)")
Expected output:
Product: prod_test
Monthly Price: price_test ($9.99/month)
Annual Price: price_test ($99.99/year)
Pricing Models
Stripe supports standard, tiered, volume, graduated, and package pricing.
# pricing_models.py
# Different pricing models
def standard_pricing(amount_cents, interval):
return {"type": "standard", "amount": amount_cents, "interval": interval}
def tiered_pricing():
tiers = [
{"up_to": 1000, "unit_amount": 5},
{"up_to": 10000, "unit_amount": 3},
{"up_to": "inf", "unit_amount": 1},
]
return {"type": "tiered", "tiers": tiers}
def package_pricing(amount_cents, package_size):
return {"type": "package", "amount": amount_cents, "package_size": package_size}
models = {
"Standard": standard_pricing(999, "month"),
"Tiered (API calls)": tiered_pricing(),
"Package (seats)": package_pricing(5000, 10),
}
for name, config in models.items():
print(f"\n{name}:")
for key, value in config.items():
print(f" {key}: {value}")
Multi-Currency Pricing
Create prices in multiple currencies for the same product.
# multi_currency.py
# Multi-currency pricing
def create_multi_currency_prices(product_id, base_amount_usd):
rates = {
"usd": base_amount_usd,
"eur": round(base_amount_usd * 0.92),
"gbp": round(base_amount_usd * 0.79),
"cad": round(base_amount_usd * 1.36),
}
prices = {}
for currency, amount in rates.items():
price_id = f"price_{product_id}_{currency}"
prices[currency] = {"id": price_id, "amount": amount}
print(f" {currency.upper()}: ${amount/100:.2f} -> {price_id}")
return prices
print("Multi-currency prices for Basic Plan ($9.99 USD):")
create_multi_currency_prices("prod_basic", 999)
Common Mistakes
Not using price IDs in code: Hardcoding amounts instead of price IDs means updating prices requires code changes.
Creating duplicate products: Check if a product exists before creating. Use metadata to deduplicate.
Forgetting active/inactive status: Deactivate old prices instead of deleting them. Deleted prices break existing subscriptions.
Mixing payment and subscription prices: One-time prices and subscription prices are different Price objects. Do not mix modes.
Not testing price changes: Changing a price affects all future subscriptions. Test price changes in test mode first.
Practice Questions
What is the difference between a Product and a Price? A Product is what you sell. A Price defines the cost, currency, and billing frequency for a product.
How do you create a subscription price? Create a Price with recurring parameter: recurring={"interval": "month"}.
What happens if you delete a Price that has active subscriptions? Existing subscriptions continue at that price but new subscriptions cannot use it.
How do you handle multi-currency pricing? Create separate Price objects for each currency for the same product.
Challenge: Design a product catalog for a SaaS platform with three tiers, monthly and annual pricing, add-ons, and usage-based pricing.
FAQ
Mini Project
Create a product catalog management endpoint that creates products with multiple pricing options.
import json
def lambda_handler(event, context):
body = json.loads(event.get("body", "{}"))
product_name = body.get("name", "New Product")
prices_config = body.get("prices", [{"amount": 999, "interval": "month"}])
product_id = f"prod_{hash(product_name)}"
print(f"Product: {product_name} ({product_id})")
price_ids = []
for price in prices_config:
pid = f"price_{product_id}_{price['interval']}_{price['amount']}"
price_ids.append(pid)
print(f" Price: ${price['amount']/100:.2f}/{price.get('interval', 'one-time')} -> {pid}")
return {
"statusCode": 201,
"body": json.dumps({
"product_id": product_id,
"product_name": product_name,
"prices": price_ids
})
}
event = {"body": json.dumps({"name": "Premium Plan", "prices": [{"amount": 1999, "interval": "month"}, {"amount": 19999, "interval": "year"}]})}
print(json.loads(lambda_handler(event, None)["body"]))
What's Next
Next: Coupons and Promotions for discounts.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro