Skip to content

Stripe Products and Prices — Building Your Product Catalog

DodaTech Updated 2026-06-28 4 min read

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

  1. Not using price IDs in code: Hardcoding amounts instead of price IDs means updating prices requires code changes.

  2. Creating duplicate products: Check if a product exists before creating. Use metadata to deduplicate.

  3. Forgetting active/inactive status: Deactivate old prices instead of deleting them. Deleted prices break existing subscriptions.

  4. Mixing payment and subscription prices: One-time prices and subscription prices are different Price objects. Do not mix modes.

  5. Not testing price changes: Changing a price affects all future subscriptions. Test price changes in test mode first.

Practice Questions

  1. 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.

  2. How do you create a subscription price? Create a Price with recurring parameter: recurring={"interval": "month"}.

  3. What happens if you delete a Price that has active subscriptions? Existing subscriptions continue at that price but new subscriptions cannot use it.

  4. How do you handle multi-currency pricing? Create separate Price objects for each currency for the same product.

  5. Challenge: Design a product catalog for a SaaS platform with three tiers, monthly and annual pricing, add-ons, and usage-based pricing.

FAQ

Can I have multiple active prices for one product?

Yes. A product can have multiple active prices for different intervals or currencies.

How do I update a price?

Prices are immutable. Create a new price and deactivate the old one.

What is the maximum number of products?

No documented limit, but organize with metadata for easy management.

Can I use tax-inclusive pricing?

Yes. Set tax_behavior to inclusive or exclusive on each price.

How do usage-based prices work?

Use metered prices with recurring.usage_type=metered and report usage via the API.

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