Subscriptions with multiple products

Create subscriptions with multiple products, all billed in a single invoice.

If you offer multiple products or if want to charge charge different amounts for the same product, you can attach multiple products to a subscription. This generates a single invoice each billing period that combines every price. Only a single payment for that invoice is required, reducing your costs and the number of charges your customer sees.

Creating multiple-product subscriptions

Create multiple-product subscriptions on a customer using the items parameter. Provide the price and, optionally, a quantity (when using a value other than 1), for each product:

curl https://api.stripe.com/v1/subscriptions \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d customer=cus_4fdAW5ftNQow1a \ -d "items[0][price]"=price_CBXbz9i7AIOTzr \ -d "items[1][price]"=price_IFuCu48Snc02bc \ -d "items[1][quantity]"=2
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' subscription = Stripe::Subscription.create({ customer: 'cus_4fdAW5ftNQow1a', items: [ { price: 'price_CBXbz9i7AIOTzr', }, { price: 'price_IFuCu48Snc02bc', quantity: 2, }, ], })
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' subscription = stripe.Subscription.create( customer='cus_4fdAW5ftNQow1a', items=[ { 'price': 'price_CBXbz9i7AIOTzr', }, { 'price': 'price_IFuCu48Snc02bc', 'quantity': 2, }, ], )
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $subscription = \Stripe\Subscription::create([ 'customer' => 'cus_4fdAW5ftNQow1a', 'items' => [ [ 'price' => 'price_CBXbz9i7AIOTzr', ], [ 'price' => 'price_IFuCu48Snc02bc', 'quantity' => 2, ], ], ]);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; SubscriptionCreateParams params = SubscriptionCreateParams.builder() .addItem( SubscriptionCreateParams.Item.builder() .setPrice("price_CBXbz9i7AIOTzr") .build()) .addItem( SubscriptionCreateParams.Item.builder() .setPrice("price_IFuCu48Snc02bc") .setQuantity(2L) .build()) .setCustomer("cus_4fdAW5ftNQow1a") .build(); Subscription subscription = Subscription.create(params);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const subscription = await stripe.subscriptions.create({ customer: 'cus_4fdAW5ftNQow1a', items: [ { price: 'price_CBXbz9i7AIOTzr', }, { price: 'price_IFuCu48Snc02bc', quantity: 2, }, ], });
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" items := []*stripe.SubscriptionItemsParams{ { Price: stripe.String("price_CBXbz9i7AIOTzr"), }, { Price: stripe.String("price_IFuCu48Snc02bc"), Quantity: stripe.Int64(2), }, } params := &stripe.SubscriptionParams{ Customer: stripe.String("cus_4fdAW5ftNQow1a"), Items: items, } s, _ := sub.New(params)
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var items = new List<SubscriptionItemOptions> { new SubscriptionItemOptions { Price = "price_CBXbz9i7AIOTzr" }, new SubscriptionItemOptions { Price = "price_IFuCu48Snc02bc", Quantity = 2, }, }; var options = new SubscriptionCreateOptions { Customer = "cus_4fdAW5ftNQow1a", Items = items, }; var service = new SubscriptionService(); Subscription subscription = service.Create(options);

The response includes a list of all the subscription items, prices, and quantities:

{ "id": "sub_CZEpS1Zt9QLxdo", "object": "subscription", ... "items": { "object": "list", "data": [ { "id": "si_H1yPnAVzP9vDRW", "object": "subscription_item", "billing_thresholds": null, "created": 1585939321, "metadata": { }, "price": { "id": "price_H1c8v1liEvrfcd", "object": "price", "active": true, "billing_scheme": "per_unit", "created": 1585856460, "currency": "usd", "livemode": false, "lookup_key": null, "metadata": { }, "nickname": null, "product": "prod_H1c7exjJHbC4sr", "recurring": { "aggregate_usage": null, "interval": "month", "interval_count": 1, "trial_period_days": null, "usage_type": "licensed" }, "tiers": null, "tiers_mode": null, "transform_quantity": null, "type": "recurring", "unit_amount": 1000, "unit_amount_decimal": "1000" }, "quantity": 1, "subscription": "sub_H1yPRslJXa4TUt", "tax_rates": [ ]
See all 89 lines }, { "id": "si_H1yPu4fSjq3oqM", "object": "subscription_item", "billing_thresholds": null, "created": 1585939321, "metadata": { }, "price": { "id": "price_H1yCssogQ6gtx1", "object": "price", "active": true, "billing_scheme": "per_unit", "created": 1585938535, "currency": "usd", "livemode": false, "lookup_key": null, "metadata": { }, "nickname": null, "product": "prod_H1c7exjJHbC4sr", "recurring": { "aggregate_usage": null, "interval": "month", "interval_count": 1, "trial_period_days": null, "usage_type": "licensed" }, "tiers": null, "tiers_mode": null, "transform_quantity": null, "type": "recurring", "unit_amount": 2000, "unit_amount_decimal": "2000" }, "quantity": 2, "subscription": "sub_H1yPRslJXa4TUt", "tax_rates": [ ] } ], }, ... }

Billing periods with multiple prices

Conventional prices that charge a fixed amount on an interval are billed at the start of each billing cycle. With each invoice, the customer effectively pays for the next interval of service. With metered billing, the amount paid by the customer varies based on consumption during the billing cycle, so the customer pays for their usage at the end.

When a subscription combines a fixed rate with metered billing, metered usage from the previous billing cycle is charged alongside the fixed rate for the new billing cycle at the start of each interval. The metered billing and fixed rate are combined in a single invoice.

Restrictions

Since using multiple products with a subscription results in a single invoice and payment, all of the prices for those products must use the same currency and have the same billing interval. You are also limited to 20 products in a single subscription.

Discounts, taxes, and trial periods

When using multiple products, you can also create discounts, charge taxes, and use trial periods as you would with a single-product subscription. Provide these as top-level arguments to the create or update subscription call, as they apply to the subscription at large:

curl https://api.stripe.com/v1/subscriptions \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d customer=cus_4fdAW5ftNQow1a \ -d coupon=free-period \ -d "default_tax_rates[0]"=txr_1EO66sClCIKljWvs98IiVfHW \ -d trial_end=1595061472 \ -d "items[0][price]"=price_CBXbz9i7AIOTzr \ -d "items[1][price]"=price_IFuCu48Snc02bc \ -d "items[1][quantity]"=2
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' subscription = Stripe::Subscription.create({ customer: 'cus_4fdAW5ftNQow1a', coupon: 'free-period', default_tax_rates: ['txr_1EO66sClCIKljWvs98IiVfHW'], trial_end: 1595061472, items: [ { price: 'price_CBXbz9i7AIOTzr', }, { price: 'price_IFuCu48Snc02bc', quantity: 2, }, ], })
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' subscription = stripe.Subscription.create( customer='cus_4fdAW5ftNQow1a', coupon='free-period', default_tax_rates=['txr_1EO66sClCIKljWvs98IiVfHW'], trial_end=1595061472, items=[ { 'price': 'price_CBXbz9i7AIOTzr', }, { 'price': 'price_IFuCu48Snc02bc', 'quantity': 2, }, ], )
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $subscription = \Stripe\Subscription::create([ 'customer' => 'cus_4fdAW5ftNQow1a', 'coupon' => 'free-period', 'default_tax_rates' => ['txr_1EO66sClCIKljWvs98IiVfHW'], 'trial_end' => 1595061472, 'items' => [ [ 'price' => 'price_CBXbz9i7AIOTzr', ], [ 'price' => 'price_IFuCu48Snc02bc', 'quantity' => 2, ], ], ]);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; SubscriptionCreateParams params = SubscriptionCreateParams.builder() .addItem( SubscriptionCreateParams.Item.builder() .setPrice("price_CBXbz9i7AIOTzr") .build()) .addItem( SubscriptionCreateParams.Item.builder() .setPrice("price_IFuCu48Snc02bc") .build()) .setCustomer("cus_4fdAW5ftNQow1a") .setCoupon("free-period") .addDefaultTaxRate("card") .addTrialEnd(1595061472) .build(); Subscription subscription = Subscription.create(params);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const subscription = await stripe.subscriptions.create({ customer: 'cus_4fdAW5ftNQow1a', coupon: 'free-period', default_tax_rates: ['txr_1EO66sClCIKljWvs98IiVfHW'], trial_end: 1595061472, items: [ { price: 'price_CBXbz9i7AIOTzr', }, { price: 'price_IFuCu48Snc02bc', quantity: 2, }, ], });
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.SubscriptionParams{ Customer: stripe.String("cus_4fdAW5ftNQow1a"), Coupon: stripe.String("free-period"), DefaultTaxRates: stripe.StringSlice([]string{ "txr_1EO66sClCIKljWvs98IiVfHW", }), TrialEnd: stripe.Int64(1595061472), Items: []*stripe.SubscriptionItemsParams{ { Price: stripe.String("price_CBXbz9i7AIOTzr"), }, { Price: stripe.String("price_IFuCu48Snc02bc"), Quantity: stripe.Int64(2), }, }, } s, _ := sub.New(params)
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var options = new SubscriptionCreateOptions { Customer = "cus_4fdAW5ftNQow1a", Coupon = "free-period", DefaultTaxRates = new List<string> { "txr_1EO66sClCIKljWvs98IiVfHW", }, TrialEnd = DateTimeOffset.FromUnixTimeSeconds(1595061472).UtcDateTime, Items = new List<SubscriptionItemOptions> { new SubscriptionItemOptions { Price = "price_CBXbz9i7AIOTzr" }, new SubscriptionItemOptions { Price = "price_IFuCu48Snc02bc", Quantity = 2, }, }, }; var service = new SubscriptionService(); Subscription subscription = service.Create(options);

When you create a subscription by passing prices into the items attribute, it will ignore any trial period that is specified on the individual prices. The trial period is only respected if you create a subscription with a single price via the legacy plan attribute.

Next steps

Now that you understand how to use multiple prices on a single subscription, you may want to check out:

Was this page helpful?
Questions? Contact us.
Developer tutorials on YouTube.
You can unsubscribe at any time. Read our privacy policy.