1

Create subscriptions with Checkout

 3 years ago
source link: https://stripe.com/docs/billing/subscriptions/checkout
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Create subscriptions with Checkout

Learn how to offer and manage subscriptions with different pricing options.
Complexity:

Customize logo, images, and colors.

Use prebuilt hosted forms to collect payments and manage subscriptions.

Clone a sample integration from GitHub or learn how to accept payments manually in the Dashboard if you aren’t ready to build an integration.

View the demo to see a hosted example.

We support metered and tiered prices as part of Checkout. We welome any feedback on this or any other parts of Checkout at [email protected].

This use case demonstrates how to:

  • Model a fixed-price subscription with Products and Prices. Checkout also supports metered billing and tiers.
  • Configure the customer portal
  • Collect payment information and create the subscription with Checkout
  • Integrate the customer portal to allow customers to manage their billing settings

This guide shows you how to accept card payments only. You can add payment methods to Checkout, but the customer portal supports only cards. To handle payment updates for non-card payments, you can integrate the hosted invoice page, or use Checkout in setup mode.

Install the Stripe client of your choice:

Terminal
# Available as a gem sudo gem install stripe
Gemfile
# If you use bundler, you can add this line to your Gemfile gem 'stripe'

Install the Stripe CLI (optional). The CLI provides webhook testing, and you can run it to create your products and prices.

To install the Stripe CLI with homebrew, run:

Terminal
brew install stripe/stripe-cli/stripe

To run the Stripe CLI, you must also pair it with your Stripe account. Run stripe login and follow the prompts. For more information, see the Stripe CLI documentation page.

Product and prices objects

A product represents the item your customer subscribes to. The price represents how much and how often to charge for the product.

Create your products and their prices in the Dashboard or with the Stripe CLI.

The following is an example where the product is a fixed-price service with two different options (Basic and Premium), and each option needs a product and a price.

In this sample, each product bills at monthly intervals. The price for one product is 5 USD, and the other is 15 USD.

Navigate to the Create a product page, and create two products. Add one price for each product, each with a monthly billing interval:

  • Basic option—Price 5.00 USD
  • Premium option—Price 15.00 USD

After you create the prices, record the price IDs so you can use them in subsequent steps. The Pricing section of the product displays each ID and the ID looks similar to this: price_G0FvDp6vZvdwRZ.

The Copy to live mode button at the top right of the page lets you clone your product from test mode to live mode when you’re ready.

For other business models, see Billing examples.

Use the the Dashboard to configure the portal. At a minimum, make sure to configure it so that customers can update their payment methods. See Integrating the customer portal for information about other settings you can configure.

On the backend of your application, define an endpoint that creates the session for your frontend to call. You need these values:

  • The price ID of the subscription the customer is signing up for—your frontend passes this value
  • Your success_url, a page on your website that Checkout returns your customer to after they complete the payment
  • Your cancel_url, a page on your website that Checkout returns your customer to if they cancel the payment process
# 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"
post '/create-checkout-session' do content_type 'application/json' data = JSON.parse(request.body.read) # See https://stripe.com/docs/api/checkout/sessions/create # for additional parameters to pass. # {CHECKOUT_SESSION_ID} is a string literal; do not change it! # the actual Session ID is returned in the query parameter when your customer # is redirected to the success page. begin session = Stripe::Checkout::Session.create( success_url: 'https://example.com/success.html?session_id={CHECKOUT_SESSION_ID}', cancel_url: 'https://example.com/canceled.html', payment_method_types: ['card'], mode: 'subscription', line_items: [{ # For metered billing, do not pass quantity quantity: 1, price: data['priceId'], }], ) rescue => e halt 400, { 'Content-Type' => 'application/json' }, { 'error': { message: e.error.message } }.to_json end { sessionId: session.id }.to_json end

The response object includes an id value you need in subsequent calls to the Checkout Sessions endpoint.

This example customizes the success_url by appending the Session ID. For more information about this approach, see the documentation on how to Customize your success page.

On the frontend of your application, add a button that takes your customer to Checkout to complete their payment. You also need to include Stripe.js:

<head> <title>Checkout</title> <script src="https://js.stripe.com/v3/"></script> </head> <body> <button id="checkout">Subscribe</button> </body>

Always load Stripe.js directly from https://js.stripe.com. You can’t include it in a bundle or self-host it.

Pass the price ID of your customer’s selection to the backend endpoint that creates the Checkout Session:

var createCheckoutSession = function(priceId) { return fetch("/create-checkout-session", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ priceId: priceId }) }).then(function(result) { return result.json(); }); };

Add an event handler to the button to redirect to Checkout, calling redirectToCheckout and passing the Checkout Session ID:

document .getElementById("checkout") .addEventListener("click", function(evt) { createCheckoutSession(PriceId).then(function(data) { // Call Stripe.js method to redirect to the new Checkout page stripe .redirectToCheckout({ sessionId: data.sessionId }) .then(handleResult); }); });

After the subscription signup succeeds, the customer returns to your website at the success_url, which initiates a checkout.session.completed event. You can check for this event in the Dashboard or with a webhook endpoint and the Stripe CLI.

On your frontend, add a button to the page at the success_url that provides a link to the customer portal:

success.html
<head> <script src="./success.js" defer></script> </head> <body> <form id="manage-billing-form"> <button>Manage Billing</button> </form> </body>

Pass the Checkout Session ID to a backend endpoint that retrieves the Checkout Session and gets the customer ID from the response:

const urlParams = new URLSearchParams(window.location.search); const sessionId = urlParams.get("session_id") let customerId; if (sessionId) { fetch("/checkout-session?sessionId=" + sessionId) .then(function(result){ return result.json() }) .then(function(session){ // We store the customer ID here so that we can pass to the // server and redirect to customer portal. Note that, in practice // this ID should be stored in your database when you receive // the checkout.session.completed event. This demo does not have // a database, so this is the workaround. This is *not* secure. // You should use the Stripe Customer ID from the authenticated // user on the server. customerId = session.customer; var sessionJSON = JSON.stringify(session, null, 2); document.querySelector("pre").textContent = sessionJSON; }) .catch(function(err){ console.log('Error when fetching Checkout session', err); });

Add an event handler to the button to redirect to the portal, passing the customer ID:

// In production, this should check CSRF, and not pass the session ID. // The customer ID for the portal should be pulled from the // authenticated user on the server. const manageBillingForm = document.querySelector('#manage-billing-form'); manageBillingForm.addEventListener('submit', function(e) { e.preventDefault(); fetch('/customer-portal', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ sessionId: sessionId }), }) .then((response) => response.json()) .then((data) => { window.location.href = data.url; }) .catch((error) => { console.error('Error:', error); }); });

On the backend, define the endpoint that retrieves the Checkout session for your frontend to call. The response object includes the customer ID that you pass to the portal session.

# 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"
get '/checkout-session' do content_type 'application/json' session_id = params[:sessionId] session = Stripe::Checkout::Session.retrieve(session_id) session.to_json end

You can also create the Stripe Customer object separately and pass the customer ID to the initial call to the Checkout Session endpoint.

Define the endpoint that creates the customer portal session for your frontend to call, passing the customer ID from the frontend. You can also pass an optional return_url value for the page on your site to redirect your customer to after they finish managing their subscription:

# 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"
post '/customer-portal' do content_type 'application/json' data = JSON.parse(request.body.read) # For demonstration purposes, we're using the Checkout session to retrieve the customer ID. # Typically this is stored alongside the authenticated user in your database. checkout_session_id = data['sessionId'] checkout_session = Stripe::Checkout::Session.retrieve(checkout_session_id) # This is the URL to which users will be redirected after they are done # managing their billing. return_url = ENV['DOMAIN'] session = Stripe::BillingPortal::Session.create({ customer: checkout_session['customer'], return_url: return_url }) { url: session.url }.to_json end

You can also set a default redirect link for the portal in the Dashboard.

When your customer completes the Checkout form and you receive a checkout.session.completed event, you can provision the subscription. Continue to provision each month (if billing monthly) as you receive invoice.paid events. If you receive an invoice.payment_failed event, notify your customer and send them to the customer portal to update their payment method.

For testing purposes, you can monitor events in the Dashboard. For production, set up a webhook endpoint and subscribe to appropriate event types:

# 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"
post '/webhook' do webhook_secret = {{'STRIPE_WEBHOOK_SECRET'}} payload = request.body.read if !webhook_secret.empty? # Retrieve the event by verifying the signature using the raw body and secret if webhook signing is configured. sig_header = request.env['HTTP_STRIPE_SIGNATURE'] event = nil begin event = Stripe::Webhook.construct_event( payload, sig_header, webhook_secret ) rescue JSON::ParserError => e # Invalid payload status 400 return rescue Stripe::SignatureVerificationError => e # Invalid signature puts '⚠️ Webhook signature verification failed.' status 400 return end else data = JSON.parse(payload, symbolize_names: true) event = Stripe::Event.construct_from(data) end # Get the type of webhook event sent event_type = event['type'] data = event['data'] data_object = data['object'] case event.type when 'checkout.session.completed' # Payment is successful and the subscription is created. # You should provision the subscription. when 'invoice.paid' # Continue to provision the subscription as payments continue to be made. # Store the status in your database and check when a user accesses your service. # This approach helps you avoid hitting rate limits. when 'invoice.payment_failed' # The payment failed or the customer does not have a valid payment method. # The subscription becomes past_due. Notify your customer and send them to the # customer portal to update their payment information. else puts "Unhandled event type: \#{event.type}" end status 200 end

The minimum event types to monitor:

Event nameDescriptioncheckout.session.completedSent when a customer clicks the Pay or Subscribe button in Checkout, informing you of a new purchase.invoice.paidSent each billing interval when a payment succeeds.invoice.payment_failedSent each billing interval if there is an issue with your customer’s payment method.

If you configure the customer portal to allow more actions, such as canceling a subscription, see Integrating the customer portal for events to monitor.

For even more events to monitor, see Subscription webhooks.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK