Billing
Fixed-price subscriptions with Checkout

Create fixed-price subscriptions with Checkout

Learn how to offer multiple pricing options to your customers and charge them a fixed amount each month using Stripe Checkout.

What you're building

This guide shows you how to:

  1. Add a subscription button to your webpage that sends your customer to a Stripe-hosted checkout page.
  2. Create an order success page to show your customer after the payment.

1 Set up Stripe Server-side

First, make sure that you have set up an account name on the Stripe Dashboard.

Then install the libraries for access to the Stripe API from your application:

# Available as a gem gem install stripe
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Or install via NuGet PM> Install-Package Stripe.net

2 Create the business model Stripe CLI or Dashboard

You create your products and their pricing options with the Stripe CLI or in the Dashboard. A service with two different options needs a product and a price for each option.

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

Create the product object for the premium service:

stripe products create \ --name="Billing Guide: Premium Service" \ --description="Premium service with extra features"

The response looks like:

{ "id": "prod_H94k5odtwJXMtQ",
See all 21 lines "object": "product", "active": true, "attributes": [ ], "created": 1587577341, "description": "Premium service with extra features", "images": [ ], "livemode": false, "metadata": { }, "name": "Billing Guide: Premium Service", "statement_descriptor": null, "type": "service", "unit_label": null, "updated": 1587577341 }

Create the price for the premium product, passing the product ID from the response:

stripe prices create \ -d product=prod_H94k5odtwJXMtQ \ -d unit_amount=1500 \ -d currency=usd \ -d "recurring[interval]"=month

Create the product object for the basic service:

stripe products create \ --name="Billing Guide: Basic Service" \ --description="Basic service with minimum features"

Create the price object for the basic product, passing the product ID from the response:

stripe prices create \ -d product=prod_HGd6W1VUqqXGvr \ -d unit_amount=500 \ -d currency=usd \ -d "recurring[interval]"=month

The response to the calls to the Prices API look like:

{ "id": "price_HGd7M3DV3IMXkC", "object": "price", "product": "prod_HGd6W1VUqqXGvr", "type": "recurring", "currency": "usd", "recurring": { "aggregate_usage": null, "interval": "month", "interval_count": 1, "trial_period_days": null, "usage_type": "licensed" }, "active": true,
See all 27 lines "billing_scheme": "per_unit", "created": 1589319695, "livemode": false, "lookup_key": null, "metadata": { }, "nickname": null, "unit_amount": 1500, "unit_amount_decimal": "1500", "tiers": null, "tiers_mode": null, "transform_quantity": null }

Record the price IDs from each call to the Prices API so they can be used in subsequent steps.

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

  • Basic product
    • Price: 5.00 USD
  • Premium product
    • Price: 15.00 USD

After you create the prices, record the price IDs so they can be used in subsequent steps. Each ID is displayed in the Pricing section of the product and should look 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.

3 Add a checkout button on your webpage Client-side

Create a checkout button on your website that takes your customer to Stripe Checkout, a Stripe-hosted payment form where they can complete their payment.

Add a button

Start by adding a checkout button to your page:

<html> <head> <title>Buy cool new product</title> </head> <body> <button id="checkout-button">Checkout</button> </body> </html>

Add the Stripe.js library to your page

Adding Stripe.js to your page automatically includes advanced fraud detection whenever your customers use Checkout.

Add the library to your page:

<html> <head> <title>Buy cool new product</title> <script src="https://js.stripe.com/v3/"></script> </head> <body> <button id="checkout-button">Checkout</button> </body> </html>

Add a button

Start by adding a checkout button to your page:

import React from 'react'; import ReactDOM from 'react-dom'; function App() { return ( <button role="link"> Checkout </button> ); } ReactDOM.render(<App />, document.getElementById('root'));

Install Stripe.js

When you add Stripe.js to your page, you automatically get advanced fraud detection included whenever your customers use Checkout.

First, install the Stripe.js ES Module:

npm install @stripe/stripe-js

Initialize Stripe.js

Call loadStripe with your publishable API key. It returns a Promise that resolves with the Stripe object as soon as Stripe.js loads.

import React from 'react'; import ReactDOM from 'react-dom'; import { loadStripe } from '@stripe/stripe-js'; // Make sure to call `loadStripe` outside of a component’s render to avoid // recreating the `Stripe` object on every render. const stripePromise = loadStripe('pk_test_TYooMQauvdEDq54NiTphI7jx'); function App() { return ( <button role="link"> Checkout </button> ); } ReactDOM.render(<App />, document.getElementById('root'));

4 Redirect your customer to Stripe Checkout

Set up the new button to redirect your customer to a Stripe Checkout-hosted payment form. After being redirected, your customer can enter their payment details in the form.

The redirect to Stripe Checkout cuts down on development time and maintenance and gives you added security. It also reduces the amount of private customer information you handle on your site, gives you immediate access to digital wallets in addition to card payments, and allows you to customize the styling of the Checkout form to match your branding.

Create a Checkout Session

A Checkout Session is the programmatic representation of what your customer sees when they’re redirected to the payment form. You can configure it with options such as:

You also need to specify:

  • A success_url, a page on your website to redirect your customer after they complete the payment.
  • A cancel_url, a page on your website to redirect your customer if they click on your logo in Checkout.

For a full list of configuration options, see Checkout Session.

You need a server-side endpoint to create the Checkout Session. Creating the Checkout Session server-side prevents malicious customers from being able to choose their own prices.

Use the Price ID representing your subscription product you created in step 2 when creating your Checkout Session.

# This example sets up an endpoint using the Sinatra framework. # Watch this video to get started: https://youtu.be/8aA9Enb8NVc. # 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' require 'json' require 'sinatra' post '/create-checkout-session' do session = Stripe::Checkout::Session.create({ payment_method_types: ['card'], line_items: [{ # Replace `price_...` with the actual price ID for your subscription # you created in step 2 of this guide. price: 'price_...', quantity: 1, }], mode: 'subscription', # For now leave these URLs as placeholder values. # # Later on in the guide, you'll create a real success page, but no need to # do it yet. success_url: 'https://example.com/success', cancel_url: 'https://example.com/cancel', }) { id: session.id }.to_json end
# This example sets up an endpoint using the Flask framework. # Watch this video to get started: https://youtu.be/7Ul1vfmsDck. import os import stripe from flask import Flask, jsonify app = Flask(__name__) # 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' @app.route('/create-checkout-session', methods=['POST']) def create_checkout_session(): session = stripe.checkout.Session.create( payment_method_types=['card'], line_items=[{ # Replace `price_...` with the actual price ID for your subscription # you created in step 2 of this guide. 'price': 'price_...' 'quantity': 1, }], mode='subscription', success_url='https://example.com/success', cancel_url='https://example.com/cancel', ) return jsonify(id=session.id) if __name__== '__main__': app.run(port=4242)
<?php // This example sets up an endpoint using the Slim framework. // Watch this video to get started: https://youtu.be/sGcNPFX1Ph4. use Slim\Http\Request; use Slim\Http\Response; use Stripe\Stripe; require 'vendor/autoload.php'; $app = new \Slim\App; $app->add(function ($request, $response, $next) { // 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'); return $next($request, $response); }); $app->post('/create-checkout-session', function (Request $request, Response $response) { $session = \Stripe\Checkout\Session::create([ 'payment_method_types' => ['card'], 'line_items' => [[ // Replace `price_...` with the actual price ID for your subscription // you created in step 2 of this guide. 'price' => 'price_...', 'quantity' => 1, ]], 'mode' => 'subscription', 'success_url' => 'https://example.com/success', 'cancel_url' => 'https://example.com/cancel', ]); return $response->withJson([ 'id' => $session->id ])->withStatus(200); }); $app->run();
import java.util.HashMap; import java.util.Map; import static spark.Spark.get; import static spark.Spark.post; import static spark.Spark.port; import static spark.Spark.staticFiles; import com.google.gson.Gson; import com.stripe.Stripe; import com.stripe.model.checkout.Session; import com.stripe.param.checkout.SessionCreateParams; public class Server { private static Gson gson = new Gson(); public static void main(String[] args) { port(4242); // 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"; post("/create-checkout-session", (request, response) -> { response.type("application/json"); SessionCreateParams params = SessionCreateParams.builder() .addPaymentMethodType(SessionCreateParams.PaymentMethodType.CARD) .setMode(SessionCreateParams.Mode.SUBSCRIPTION) .setSuccessUrl("https://example.com/success") .setCancelUrl("https://example.com/cancel") .addLineItem( SessionCreateParams.LineItem.builder() .setQuantity(1L) // Replace `price_...` with the actual price ID for your subscription // you created in step 2 of this guide. .setPrice("price_...") .build()) .build(); Session session = Session.create(params); Map<String, String> responseData = new HashMap(); responseData.put("id", session.getId()); return gson.toJson(responseData); }); } }
// This example sets up an endpoint using the Express framework. // Watch this video to get started: https://youtu.be/rPR2aJ6XnAc. const express = require('express'); const app = express(); // 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'); app.post('/create-checkout-session', async (req, res) => { const session = await stripe.checkout.sessions.create({ payment_method_types: ['card'], line_items: [ { // Replace `price_...` with the actual price ID for your subscription // you created in step 2 of this guide. price: 'price_...', quantity: 1, }, ], mode: 'subscription', success_url: 'https://example.com/success', cancel_url: 'https://example.com/cancel', }); res.json({ id: session.id }); }); app.listen(4242, () => console.log(`Listening on port ${4242}!`));
package main import ( "net/http" "github.com/labstack/echo" "github.com/labstack/echo/middleware" "github.com/stripe/stripe-go/v71" "github.com/stripe/stripe-go/v71/checkout/session" ) // This example sets up an endpoint using the Echo framework. // Watch this video to get started: https://youtu.be/ePmEVBu8w6Y. func main() { // 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" e := echo.New() e.Use(middleware.Logger()) e.Use(middleware.Recover()) e.POST("/create-checkout-session", createCheckoutSession) e.Logger.Fatal(e.Start("localhost:4242")) } type CreateCheckoutSessionResponse struct { SessionID string `json:"id"` } func createCheckoutSession(c echo.Context) (err error) { params := &stripe.CheckoutSessionParams{ PaymentMethodTypes: stripe.StringSlice([]string{ "card", }), Mode: stripe.String(string(stripe.CheckoutSessionModeSubscription)), LineItems: []*stripe.CheckoutSessionLineItemParams{ &stripe.CheckoutSessionLineItemParams{ // Replace `price_...` with the actual price ID for your subscription // you created in step 2 of this guide. Price: stripe.String("price_..."), Quantity: stripe.Int64(1), }, }, SuccessURL: stripe.String("https://example.com/success"), CancelURL: stripe.String("https://example.com/cancel"), } session, _ := session.New(params) if err != nil { return err } data := CreateCheckoutSessionResponse{ SessionID: session.ID, } return c.JSON(http.StatusOK, data) }
// This example sets up an endpoint using the ASP.NET MVC framework. // Watch this video to get started: https://youtu.be/2-mMOB8MhmE. using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Stripe; using Stripe.Checkout; namespace server.Controllers { public class PaymentsController : Controller { public PaymentsController() { // 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"; } [HttpPost("create-checkout-session")] public ActionResult CreateCheckoutSession() { var options = new SessionCreateOptions { PaymentMethodTypes = new List<string> { "card", }, LineItems = new List<SessionLineItemOptions> { new SessionLineItemOptions { // Replace `price_...` with the actual price ID for your subscription // you created in step 2 of this guide. Price = "price_...", Quantity = 1, }, }, Mode = "subscription", SuccessUrl = "https://example.com/success", CancelUrl = "https://example.com/cancel", }; var service = new SessionService(); Session session = service.Create(options); return Json(new { id = session.Id }); } } }

Test your endpoint by starting your web server (e.g., localhost:4242) and running the following command:

curl -X POST -is "http://localhost:4242/create-checkout-session" -d ""

You should see a response in your terminal that looks like this:

HTTP/1.1 200 OK Content-Type: application/json { id: "cs_test_.........." }

Add an event handler to the checkout button

Now that you have a <button> and an endpoint to create a Checkout Session, modify the <button> to redirect to Stripe Checkout when clicked, using redirectToCheckout and providing the Checkout Session ID:

<html> <head> <title>Buy cool new product</title> </head> <body> <button id="checkout-button">Checkout</button> <script type="text/javascript"> // Create an instance of the Stripe object with your publishable API key var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx'); var checkoutButton = document.getElementById('checkout-button'); checkoutButton.addEventListener('click', function() { // Create a new Checkout Session using the server-side endpoint you // created in step 3. fetch('/create-checkout-session', { method: 'POST', }) .then(function(response) { return response.json(); }) .then(function(session) { return stripe.redirectToCheckout({ sessionId: session.id }); }) .then(function(result) { // If `redirectToCheckout` fails due to a browser or network // error, you should display the localized error message to your // customer using `error.message`. if (result.error) { alert(result.error.message); } }) .catch(function(error) { console.error('Error:', error); }); }); </script> </body> </html>
import React from 'react'; import ReactDOM from 'react-dom'; import { loadStripe } from '@stripe/stripe-js'; // Make sure to call `loadStripe` outside of a component’s render to avoid // recreating the `Stripe` object on every render. const stripePromise = loadStripe('pk_test_TYooMQauvdEDq54NiTphI7jx'); function App() { const handleClick = async (event) => { // Get Stripe.js instance const stripe = await stripePromise; // Call your backend to create the Checkout Session const response = await fetch('/create-checkout-session', { method: 'POST' }); const session = await response.json(); // When the customer clicks on the button, redirect them to Checkout. const result = await stripe.redirectToCheckout({ sessionId: session.id, }); if (result.error) { // If `redirectToCheckout` fails due to a browser or network // error, display the localized error message to your customer // using `result.error.message`. } }; return ( <button role="link" onClick={handleClick}> Checkout </button> ); } ReactDOM.render(<App />, document.getElementById('root'));

Testing

You should now have a working checkout button that redirects your customer to Stripe Checkout.

  • Click the checkout button.
  • You’re redirected to a Stripe Checkout payment form.

If your integration isn’t working:

  • Open the Network tab in your browser’s developer tools.
  • Click the checkout button and see if an XHR request is made to your server-side endpoint (POST /create-checkout-session).
  • Verify the request is returning a 200 status.
  • Use console.log(session) inside your button click listener to confirm the correct data is returned.

4 Show a success page Client and server

It’s important for your customer to see a success page after they successfully submit the payment form. This success page is hosted on your site.

Create a minimal success page:

<html> <head><title>Thanks for your order!</title></head> <body> <h1>Thanks for your order!</h1> <p> We appreciate your business! If you have any questions, please email <a href="mailto:orders@example.com">orders@example.com</a>. </p> </body> </html>

Next, update the Checkout Session creation endpoint to use this new page:

# This example sets up an endpoint using the Sinatra framework. # Watch this video to get started: https://youtu.be/8aA9Enb8NVc. # 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' require 'json' require 'sinatra' post '/create-checkout-session' do session = Stripe::Checkout::Session.create({ payment_method_types: ['card'], line_items: [{ # Replace `price_...` with the actual price ID for your subscription # you created in step 2 of this guide. price: 'price_...', quantity: 1, }], mode: 'subscription', success_url: "https://yoursite.com/success.html", cancel_url: 'https://example.com/cancel', }) { id: session.id }.to_json end
# This example sets up an endpoint using the Flask framework. # Watch this video to get started: https://youtu.be/7Ul1vfmsDck. import os import stripe from flask import Flask, jsonify app = Flask(__name__) # 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' @app.route('/create-checkout-session', methods=['POST']) def create_checkout_session(): session = stripe.checkout.Session.create( payment_method_types=['card'], line_items=[{ # Replace `price_...` with the actual price ID for your subscription # you created in step 2 of this guide. 'price': 'price_...' 'quantity': 1, }], mode='subscription', success_url= "https://yoursite.com/success.html", cancel_url='https://example.com/cancel', ) return jsonify(id=session.id) if __name__== '__main__': app.run(port=4242)
<?php // This example sets up an endpoint using the Slim framework. // Watch this video to get started: https://youtu.be/sGcNPFX1Ph4. use Slim\Http\Request; use Slim\Http\Response; use Stripe\Stripe; require 'vendor/autoload.php'; $app = new \Slim\App; $app->add(function ($request, $response, $next) { // 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'); return $next($request, $response); }); $app->post('/create-checkout-session', function (Request $request, Response $response) { $session = \Stripe\Checkout\Session::create([ 'payment_method_types' => ['card'], 'line_items' => [[ // Replace `price_...` with the actual price ID for your subscription // you created in step 2 of this guide. 'price' => 'price_...', 'quantity' => 1, ]], 'mode' => 'subscription', 'success_url' => 'https://yoursite.com/success.html', 'cancel_url' => 'https://example.com/cancel', ]); return $response->withJson([ 'id' => $session->id ])->withStatus(200); }); $app->run();
import java.util.HashMap; import java.util.Map; import static spark.Spark.get; import static spark.Spark.post; import static spark.Spark.port; import static spark.Spark.staticFiles; import com.google.gson.Gson; import com.stripe.Stripe; import com.stripe.model.checkout.Session; import com.stripe.param.checkout.SessionCreateParams; public class Server { private static Gson gson = new Gson(); public static void main(String[] args) { port(4242); // 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"; post("/create-checkout-session", (request, response) -> { response.type("application/json"); SessionCreateParams params = SessionCreateParams.builder() .addPaymentMethodType(SessionCreateParams.PaymentMethodType.CARD) .setMode(SessionCreateParams.Mode.SUBSCRIPTION) .setSuccessUrl("https://yoursite.com/success.html") .setCancelUrl("https://example.com/cancel") .addLineItem( SessionCreateParams.LineItem.builder() .setQuantity(1L) // Replace `price_...` with the actual price ID for your subscription // you created in step 2 of this guide. .setPrice("price_...") .build()) .build(); Session session = Session.create(params); Map<String, String> responseData = new HashMap(); responseData.put("id", session.getId()); return gson.toJson(responseData); }); } }
// This example sets up an endpoint using the Express framework. // Watch this video to get started: https://youtu.be/rPR2aJ6XnAc. const express = require('express'); const app = express(); // 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'); app.post('/create-checkout-session', async (req, res) => { const session = await stripe.checkout.sessions.create({ payment_method_types: ['card'], line_items: [ { // Replace `price_...` with the actual price ID for your subscription // you created in step 2 of this guide. price: 'price_...', quantity: 1, }, ], mode: 'subscription', success_url: 'https://yoursite.com/success.html', cancel_url: 'https://example.com/cancel', }); res.json({ id: session.id }); }); app.listen(4242, () => console.log(`Listening on port ${4242}!`));
package main import ( "net/http" "github.com/labstack/echo" "github.com/labstack/echo/middleware" "github.com/stripe/stripe-go/v71" "github.com/stripe/stripe-go/v71/checkout/session" ) // This example sets up an endpoint using the Echo framework. // Watch this video to get started: https://youtu.be/ePmEVBu8w6Y. func main() { // 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" e := echo.New() e.Use(middleware.Logger()) e.Use(middleware.Recover()) e.POST("/create-checkout-session", createCheckoutSession) e.Logger.Fatal(e.Start("localhost:4242")) } type CreateCheckoutSessionResponse struct { SessionID string `json:"id"` } func createCheckoutSession(c echo.Context) (err error) { params := &stripe.CheckoutSessionParams{ PaymentMethodTypes: stripe.StringSlice([]string{ "card", }), Mode: stripe.String(string(stripe.CheckoutSessionModeSubscription)), LineItems: []*stripe.CheckoutSessionLineItemParams{ &stripe.CheckoutSessionLineItemParams{ // Replace `price_...` with the actual price ID for your subscription // you created in step 2 of this guide. Price: stripe.String("price_..."), Quantity: stripe.Int64(1), }, }, SuccessURL: stripe.String("https://yoursite.com/success.html"), CancelURL: stripe.String("https://example.com/cancel"), } session, _ := session.New(params) if err != nil { return err } data := CreateCheckoutSessionResponse{ SessionID: session.ID, } return c.JSON(http.StatusOK, data) }
// This example sets up an endpoint using the ASP.NET MVC framework. // Watch this video to get started: https://youtu.be/2-mMOB8MhmE. using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Stripe; using Stripe.Checkout; namespace server.Controllers { public class PaymentsController : Controller { public PaymentsController() { // 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"; } [HttpPost("create-checkout-session")] public ActionResult CreateCheckoutSession() { var options = new SessionCreateOptions { PaymentMethodTypes = new List<string> { "card", }, LineItems = new List<SessionLineItemOptions> { new SessionLineItemOptions { // Replace `price_...` with the actual price ID for your subscription // you created in step 2 of this guide. Price = "price_...", Quantity = 1, }, }, Mode = "subscription", SuccessUrl = "https://yoursite.com/success.html", CancelUrl = "https://example.com/cancel", }; var service = new SessionService(); Session session = service.Create(options); return Json(new { id = session.Id }); } } }

Testing

  • Click your checkout button
  • Fill out the payment details with the test card information:
    • Enter 4242 4242 4242 4242 as the card number.
    • Enter any future date for card expiry.
    • Enter any 3-digit number for CVC.
    • Enter any billing ZIP code.
  • Click Pay.
  • You’re redirected to your new success page.

Next, find the new payment in the Stripe Dashboard. Successful payments appear in the Dashboard’s list of payments. When you click a payment, it takes you to the payment detail page. The Checkout summary section contains billing information and the list of items purchased, which you can use to manually fulfill the order.

Additional testing resources

There are several test cards you can use to make sure your integration is ready for production. Use them with any CVC, postal code, and future expiration date.

Number Description
4242424242424242 Succeeds and immediately processes the payment.
4000000000003220 3D Secure 2 authentication must be completed for a successful payment.
4000000000009995 Always fails with a decline code of insufficient_funds.

For the full list of test cards see our guide on testing.

Apple Pay and Google Pay

No configuration or integration changes are required to enable Apple Pay or Google Pay in Stripe Checkout. These payments are handled the same way as other card payments.

The Apple Pay button is displayed in a given Checkout Session if all of the following apply:

  • Apple Pay is enabled for Checkout in your Stripe Dashboard.
  • The customer’s device is running macOS 10.14.1+ or iOS 12.1+.
  • The customer is using the Safari browser.
  • The customer has a valid card registered with Apple Pay.

This ensures that Checkout only displays the Apple Pay button to customers who are able to use it.

The Google Pay button is displayed in a given Checkout Session if all of the following apply:

  • Google Pay is enabled for Checkout in your Stripe Dashboard.
  • The customer is using Google Chrome or Safari.
  • The customer has a valid card registered with Google Pay.

This ensures that Checkout only displays the Google Pay button to customers who are able to use it.

5 Fulfill your subscriptions Server-side

To fulfill new subscriptions and keep track of whether a subscription is paid up and active each month with Checkout, you need to listen for a few events that we send to your server.

The core Checkout fulfillment guide describes how to listen for the basic checkout.session.completed event and explains how to set up a basic webhook endpoint, as well as use Stripe CLI to quickly test and iterate during development.

To use subscriptions, you need to listen for a few more invoice events, so you can be alerted every month if a subscription is successfully charged or has a payment failure that needs fixing.

Event name Description
checkout.session.completed This event is sent when a customer clicks the Pay button in Checkout, informing you of a new purchase.
invoice.paid This event is sent each billing interval when a payment succeeds.
invoice.payment_failed This event is sent each billing interval if there is an issue with your customer’s payment method.

Handle new subscriptions

Following the template from the core fulfillment guide, create an event handler that listens for checkout.session.completed. You should create your customer and subscription here, and fulfill it as needed:

# 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' require 'sinatra' # You can find your endpoint's secret in the output of the `stripe listen` # command you ran earlier endpoint_secret = 'whsec_...' post '/webhook' do event = nil # Verify webhook signature and extract the event # See https://stripe.com/docs/webhooks/signatures for more information. begin sig_header = request.env['HTTP_STRIPE_SIGNATURE'] payload = request.body.read event = Stripe::Webhook.construct_event(payload, sig_header, endpoint_secret) rescue JSON::ParserError => e # Invalid payload return status 400 rescue Stripe::SignatureVerificationError => e # Invalid signature return status 400 end case event['type'] when 'checkout.session.completed' checkout_session = event['data']['object'] subscription_id = checkout_session.subscription customer_email = checkout_session.customer_email # Create a new subscription and mark it as paid this month. subscription = sign_up_customer(customer_email, subscription_id) mark_paid!(subscription) end status 200 end def mark_paid!(subscription) # TODO mark your subscription as paid for this month # # Store the customer's subscription status in your database to reference # when a user accesses your service to avoid hitting rate limits. end def sign_up_customer(customer_email, subscription_id) # TODO sign the customer up for your subscription in your database end
# 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' # Using Django from django.http import HttpResponse # You can find your endpoint's secret in your webhook settings endpoint_secret = 'whsec_...' @csrf_exempt def my_webhook_view(request): payload = request.body sig_header = request.META['HTTP_STRIPE_SIGNATURE'] event = None try: event = stripe.Webhook.construct_event( payload, sig_header, endpoint_secret ) except ValueError as e: # Invalid payload return HttpResponse(status=400) except stripe.error.SignatureVerificationError as e: # Invalid signature return HttpResponse(status=400) # Handle the checkout.session.completed event if event['type'] == 'checkout.session.completed': checkout_session = event['data']['object'] subscription_id = checkout_session.subscription customer_email = checkout_session.customer_email # Create a new subscription and mark it as paid this month. subscription = sign_up_customer(customer_email, subscription_id) mark_paid(subscription) end # Passed signature verification return HttpResponse(status=200) def mark_paid(subscription): # TODO mark your subscription as paid for this month # # Store the customer's subscription status in your database to reference # when a user accesses your service to avoid hitting rate limits. def sign_up_customer(customer_email, subscription_id): # TODO sign the customer up for your subscription in your database
// 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'); // You can find your endpoint's secret in your webhook settings $endpoint_secret = 'whsec_...'; $payload = @file_get_contents('php://input'); $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE']; $event = null; try { $event = \Stripe\Webhook::constructEvent( $payload, $sig_header, $endpoint_secret ); } catch(\UnexpectedValueException $e) { // Invalid payload http_response_code(400); exit(); } catch(\Stripe\Exception\SignatureVerificationException $e) { // Invalid signature http_response_code(400); exit(); } function mark_paid($subscription) { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } function sign_up_customer($customer_email, $subscription_id) { // TODO sign the customer up for your subscription in your database } // Handle the checkout.session.completed event switch ($event->type) { case 'checkout.session.completed': $checkout_session = $event->data->object; $subscription_id = $checkout_session->subscription; $customer_email = $checkout_session->customer_email; // Create a new subscription and mark it as paid this month. $subscription = sign_up_customer($customer_email, $subscription_id); mark_paid($subscription); break; } http_response_code(200);
// 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"; // You can find your endpoint's secret in your webhook settings String endpointSecret = "whsec_..."; public CustomerSignup signUpCustomer(String customerEmail, String subscriptionId) { // TODO sign the customer up for your subscription in your database // // `CustomerSignup` is a placeholder type for however you represent // customer signups in your Java application. } public void markPaid(CustomerSignup subscription) { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } // Using the Spark framework (http://sparkjava.com) public Object handle(Request request, Response response) { String payload = request.body(); String sigHeader = request.headers("Stripe-Signature"); Event event = null; try { event = Webhook.constructEvent(payload, sigHeader, endpointSecret); } catch (JsonSyntaxException e) { // Invalid payload response.status(400); return ""; } catch (SignatureVerificationException e) { // Invalid signature response.status(400); return ""; } switch (event.getType()) { case "checkout.session.completed": Session checkoutSession = (Session) event.getDataObjectDeserializer().getObject(); String subscriptionId = checkoutSession.getSubscription(); String customerEmail = checkoutSession.getCustomerEmail(); // Create a new subscription and mark it as paid this month. CustomerSignup subscription = signUpCustomer(customerEmail, subscriptionId); markPaid(subscription); break; } response.status(200); return ""; }
// 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'); // Find your endpoint's secret in your Dashboard's webhook settings const endpointSecret = 'whsec_...'; // Using Express const app = require('express')(); // Use body-parser to retrieve the raw body as a buffer const bodyParser = require('body-parser'); const signUpCustomer = (customerEmail, subscriptionId) => { // TODO sign the customer up for your subscription in your database } const markPaid = (subscription) => { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => { const payload = request.body; const sig = request.headers['stripe-signature']; let event; try { event = stripe.webhooks.constructEvent(payload, sig, endpointSecret); } catch (err) { return response.status(400).send(`Webhook Error: ${err.message}`); } switch (event.type) { case 'checkout.session.completed': { const session = event.data.object; const subscriptionId = session.subscription; const customerEmail = session.customer_email; // Create a new subscription and mark it as paid this month. const subscription = signUpCustomer(customerEmail, subscriptionId); markPaid(subscription); break; } } response.status(200); }); app.listen(4242, () => console.log('Running on port 4242'));
// 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" func SignUpCustomer(customerEmail string, subscriptionId string) CustomerSignup { // TODO sign the customer up for your subscription in your database // // `CustomerSignup` is a placeholder type for however you represent // customer signups in your Go application. } func MarkPaid(subscription CustomerSignup) { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } http.HandleFunc("/webhook", func(w http.ResponseWriter, req *http.Request) { const MaxBodyBytes = int64(65536) req.Body = http.MaxBytesReader(w, req.Body, MaxBodyBytes) body, err := ioutil.ReadAll(req.Body) if err != nil { fmt.Fprintf(os.Stderr, "Error reading request body: %v\n", err) w.WriteHeader(http.StatusServiceUnavailable) return } // Pass the request body and Stripe-Signature header to ConstructEvent, along with the webhook signing key // You can find your endpoint's secret in your webhook settings endpointSecret := "whsec_..."; event, err := webhook.ConstructEvent(body, req.Header.Get("Stripe-Signature"), endpointSecret) if err != nil { fmt.Fprintf(os.Stderr, "Error verifying webhook signature: %v\n", err) w.WriteHeader(http.StatusBadRequest) // Return a 400 error on a bad signature return } // Handle the checkout.session.completed event switch event.Type { case "checkout.session.completed": var session stripe.CheckoutSession err := json.Unmarshal(event.Data.Raw, &session) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err) w.WriteHeader(http.StatusBadRequest) return } var string subscriptionId = session.Subscription var string customerEmail = session.CustomerEmail // Create a new subscription and mark it as paid this month. CustomerSignup subscription = SignUpCustomer(customerEmail, subscriptionId) MarkPaid(subscription) } w.WriteHeader(http.StatusOK) })
// 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"; using System; using System.IO; using Microsoft.AspNetCore.Mvc; using Stripe; namespace workspace.Controllers { [Route("api/[controller]")] public class StripeWebHook : Controller { // You can find your endpoint's secret in your webhook settings const string secret = "whsec_..."; [HttpPost] public async Task<IActionResult> Index() { var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); try { var stripeEvent = EventUtility.ConstructEvent( json, Request.Headers["Stripe-Signature"], secret ); // Handle the checkout.session.completed event if (stripeEvent.Type == Events.CheckoutSessionCompleted) { var session = stripeEvent.Data.Object as Checkout.Session; String subscriptionId = session.SubscriptionId; String customerEmail = session.CustomerEmail; // Create a new subscription and mark it as paid this month. CustomerSignup subscription = SignUpCustomer(customerEmail, subscriptionId); MarkPaid(subscription); } return Ok(); } catch (StripeException e) { return BadRequest(); } } private CustomerSignup SignUpCustomer(String customerEmail, String subscriptionId) { // TODO sign the customer up for your subscription in your database // // `CustomerSignup` is a placeholder type for however you represent // customer signups in your .NET application. } private void MarkPaid(CustomerSignup subscription) { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } } }

Handle future successful invoices

Part of managing your customer subscriptions is keeping track of whether they are paid up for the current billing interval. To do this, add an invoice.paid event handler to your endpoint. This handler should make sure that all invoices after the first one are marked as paid on your end:

# 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' require 'sinatra' # You can find your endpoint's secret in the output of the `stripe listen` # command you ran earlier endpoint_secret = 'whsec_...' post '/webhook' do event = nil # Verify webhook signature and extract the event # See https://stripe.com/docs/webhooks/signatures for more information. begin sig_header = request.env['HTTP_STRIPE_SIGNATURE'] payload = request.body.read event = Stripe::Webhook.construct_event(payload, sig_header, endpoint_secret) rescue JSON::ParserError => e # Invalid payload return status 400 rescue Stripe::SignatureVerificationError => e # Invalid signature return status 400 end case event['type'] when 'checkout.session.completed' checkout_session = event['data']['object'] subscription_id = checkout_session.subscription customer_email = checkout_session.customer_email # Create a new subscription and mark it as paid this month. subscription = sign_up_customer(customer_email, subscription_id) mark_paid!(subscription) when 'invoice.paid' invoice = event.object subscription_id = invoice.subscription subscription = find_customer_signup(subscription_id) # Check if this is the first invoice or a later invoice in the # subscription lifecycle. first_invoice = invoice.billing_reason == 'subscription_create' # You already handle marking the first invoice as paid in the # `checkout.session.completed` handler. # # Only use this for the 2nd invoice and later, so it doesn't conflict. unless first_invoice # Mark the subscription as paid. mark_paid!(subscription) end end status 200 end def mark_paid!(subscription) # TODO mark your subscription as paid for this month # # Store the customer's subscription status in your database to reference # when a user accesses your service to avoid hitting rate limits. end def find_customer_signup(subscription_id) # TODO find your customer's signup record by Stripe subscription ID here end def sign_up_customer(customer_email, subscription_id) # TODO sign the customer up for your subscription in your database end
# 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' # Using Django from django.http import HttpResponse # You can find your endpoint's secret in your webhook settings endpoint_secret = 'whsec_...' @csrf_exempt def my_webhook_view(request): payload = request.body sig_header = request.META['HTTP_STRIPE_SIGNATURE'] event = None try: event = stripe.Webhook.construct_event( payload, sig_header, endpoint_secret ) except ValueError as e: # Invalid payload return HttpResponse(status=400) except stripe.error.SignatureVerificationError as e: # Invalid signature return HttpResponse(status=400) # Handle the checkout.session.completed event if event['type'] == 'checkout.session.completed': checkout_session = event['data']['object'] subscription_id = checkout_session.subscription customer_email = checkout_session.customer_email # Create a new subscription and mark it as paid this month. subscription = sign_up_customer(customer_email, subscription_id) mark_paid(subscription) elif event['type'] == 'invoice.paid': invoice = event['data']['object'] subscription_id = invoice.subscription subscription = find_customer_signup(subscription_id) # Check if this is the first invoice or a later invoice in the # subscription lifecycle. first_invoice = invoice.billing_reason == 'subscription_create' # You already handle marking the first invoice as paid in the # `checkout.session.completed` handler. # # Only use this for the 2nd invoice and later, so it doesn't conflict. if not first_invoice: # Mark the subscription as paid. mark_paid(subscription) end # Passed signature verification return HttpResponse(status=200) def mark_paid(subscription): # TODO mark your subscription as paid for this month # # Store the customer's subscription status in your database to reference # when a user accesses your service to avoid hitting rate limits. def find_customer_signup(subscription_id): # TODO find your customer's signup record by Stripe subscription ID here def sign_up_customer(customer_email, subscription_id): # TODO sign the customer up for your subscription in your database
// 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'); // You can find your endpoint's secret in your webhook settings $endpoint_secret = 'whsec_...'; $payload = @file_get_contents('php://input'); $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE']; $event = null; try { $event = \Stripe\Webhook::constructEvent( $payload, $sig_header, $endpoint_secret ); } catch(\UnexpectedValueException $e) { // Invalid payload http_response_code(400); exit(); } catch(\Stripe\Exception\SignatureVerificationException $e) { // Invalid signature http_response_code(400); exit(); } function mark_paid($subscription) { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } function find_customer_signup($subscription_id) { // TODO find your customer's signup record by Stripe subscription ID here } function sign_up_customer($customer_email, $subscription_id) { // TODO sign the customer up for your subscription in your database } // Handle the checkout.session.completed event switch ($event->type) { case 'checkout.session.completed': $checkout_session = $event->data->object; $subscription_id = $checkout_session->subscription; $customer_email = $checkout_session->customer_email; // Create a new subscription and mark it as paid this month. $subscription = sign_up_customer($customer_email, $subscription_id); mark_paid($subscription); break; case 'invoice.paid': $invoice = $event->data->object; $subscription_id = $invoice->subscription; $subscription = find_customer_signup($subscription_id) // Check if this is the first invoice or a later invoice in the // subscription lifecycle. $first_invoice = $invoice->billing_reason == 'subscription_create'; // You already handle marking the first invoice as paid in the // `checkout.session.completed` handler. // // Only use this for the 2nd invoice and later, so it doesn't conflict. if (!$first_invoice) { // Mark the subscription as paid. mark_paid($subscription); } break; } http_response_code(200);
// 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"; // You can find your endpoint's secret in your webhook settings String endpointSecret = "whsec_..."; public CustomerSignup signUpCustomer(String customerEmail, String subscriptionId) { // TODO sign the customer up for your subscription in your database // // `CustomerSignup` is a placeholder type for however you represent // customer signups in your Java application. } public CustomerSignup findCustomerSignup(String subscriptionId) { // TODO find your customer's signup record by Stripe subscription ID here } public void markPaid(CustomerSignup subscription) { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } // Using the Spark framework (http://sparkjava.com) public Object handle(Request request, Response response) { String payload = request.body(); String sigHeader = request.headers("Stripe-Signature"); Event event = null; try { event = Webhook.constructEvent(payload, sigHeader, endpointSecret); } catch (JsonSyntaxException e) { // Invalid payload response.status(400); return ""; } catch (SignatureVerificationException e) { // Invalid signature response.status(400); return ""; } switch (event.getType()) { case "checkout.session.completed": Session checkoutSession = (Session) event.getDataObjectDeserializer().getObject(); String subscriptionId = checkoutSession.getSubscription(); String customerEmail = checkoutSession.getCustomerEmail(); // Create a new subscription and mark it as paid this month. CustomerSignup subscription = signUpCustomer(customerEmail, subscriptionId); markPaid(subscription); break; case "invoice.paid": Invoice invoice = (Invoice) event.getDataObjectDeserializer().getObject(); String subscriptionId = invoice.getSubscription(); CustomerSignup subscription = findCustomerSignup(subscriptionId); // Check if this is the first invoice or a later invoice in the // subscription lifecycle. boolean firstInvoice = invoice.getBillingReason().equals("subscription_create"); // You already handle marking the first invoice as paid in the // `checkout.session.completed` handler. // // Only use this for the 2nd invoice and later, so it doesn't conflict. if (!firstInvoice) { // Mark the subscription as paid. markPaid(subscription); } break; } response.status(200); return ""; }
// 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'); // Find your endpoint's secret in your Dashboard's webhook settings const endpointSecret = 'whsec_...'; // Using Express const app = require('express')(); // Use body-parser to retrieve the raw body as a buffer const bodyParser = require('body-parser'); const signUpCustomer = (customerEmail, subscriptionId) => { // TODO sign the customer up for your subscription in your database } const findCustomerSignup = (subscriptionId) => { // TODO find your customer's signup record by Stripe subscription ID here } const markPaid = (subscription) => { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => { const payload = request.body; const sig = request.headers['stripe-signature']; let event; try { event = stripe.webhooks.constructEvent(payload, sig, endpointSecret); } catch (err) { return response.status(400).send(`Webhook Error: ${err.message}`); } switch (event.type) { case 'checkout.session.completed': { const session = event.data.object; const subscriptionId = session.subscription; const customerEmail = session.customer_email; // Create a new subscription and mark it as paid this month. const subscription = signUpCustomer(customerEmail, subscriptionId); markPaid(subscription); break; } case 'invoice.paid': { const invoice = event.data.object; const subscriptionId = invoice.subscription; const subscription = findCustomerSignup(subscriptionId); // Check if this is the first invoice or a later invoice in the // subscription lifecycle. const firstInvoice = invoice.billing_reason === 'subscription_create'; // You already handle marking the first invoice as paid in the // `checkout.session.completed` handler. // // Only use this for the 2nd invoice and later, so it doesn't conflict. if (!firstInvoice) { markPaid(subscription); } break; } } response.status(200); }); app.listen(4242, () => console.log('Running on port 4242'));
// 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" func SignUpCustomer(customerEmail string, subscriptionId string) CustomerSignup { // TODO sign the customer up for your subscription in your database // // `CustomerSignup` is a placeholder type for however you represent // customer signups in your Go application. } func FindCustomerSignup(subscriptionId string) CustomerSignup { // TODO find your customer's signup record by Stripe subscription ID here } func MarkPaid(subscription CustomerSignup) { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } http.HandleFunc("/webhook", func(w http.ResponseWriter, req *http.Request) { const MaxBodyBytes = int64(65536) req.Body = http.MaxBytesReader(w, req.Body, MaxBodyBytes) body, err := ioutil.ReadAll(req.Body) if err != nil { fmt.Fprintf(os.Stderr, "Error reading request body: %v\n", err) w.WriteHeader(http.StatusServiceUnavailable) return } // Pass the request body and Stripe-Signature header to ConstructEvent, along with the webhook signing key // You can find your endpoint's secret in your webhook settings endpointSecret := "whsec_..."; event, err := webhook.ConstructEvent(body, req.Header.Get("Stripe-Signature"), endpointSecret) if err != nil { fmt.Fprintf(os.Stderr, "Error verifying webhook signature: %v\n", err) w.WriteHeader(http.StatusBadRequest) // Return a 400 error on a bad signature return } // Handle the checkout.session.completed event switch event.Type { case "checkout.session.completed": var session stripe.CheckoutSession err := json.Unmarshal(event.Data.Raw, &session) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err) w.WriteHeader(http.StatusBadRequest) return } var string subscriptionId = session.Subscription var string customerEmail = session.CustomerEmail // Create a new subscription and mark it as paid this month. CustomerSignup subscription = SignUpCustomer(customerEmail, subscriptionId) MarkPaid(subscription) case "invoice.paid": var invoice stripe.Invoice err := json.Unmarshal(event.Data.Raw, &invoice) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err) w.WriteHeader(http.StatusBadRequest) return } var string subscriptionId = invoice.Subscription var CustomerSignup subscription = FindCustomerSignup(subscriptionId); // Check if this is the first invoice or a later invoice in the // subscription lifecycle. var bool firstInvoice = invoice.BillingReason == "subscription_create" // You already handle marking the first invoice as paid in the // `checkout.session.completed` handler. // // Only use this for the 2nd invoice and later, so it doesn't conflict. if !firstInvoice { // Mark the subscription as paid. MarkPaid(subscription); } } w.WriteHeader(http.StatusOK) })
// 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"; using System; using System.IO; using Microsoft.AspNetCore.Mvc; using Stripe; namespace workspace.Controllers { [Route("api/[controller]")] public class StripeWebHook : Controller { // You can find your endpoint's secret in your webhook settings const string secret = "whsec_..."; [HttpPost] public async Task<IActionResult> Index() { var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); try { var stripeEvent = EventUtility.ConstructEvent( json, Request.Headers["Stripe-Signature"], secret ); // Handle the checkout.session.completed event if (stripeEvent.Type == Events.CheckoutSessionCompleted) { var session = stripeEvent.Data.Object as Checkout.Session; String subscriptionId = session.SubscriptionId; String customerEmail = session.CustomerEmail; // Create a new subscription and mark it as paid this month. CustomerSignup subscription = SignUpCustomer(customerEmail, subscriptionId); MarkPaid(subscription); } else if (stripeEvent.Type == Events.InvoicePaid) { var invoice = stripeEvent.Data.Object as Invoice; String subscriptionId = invoice.SubscriptionId; CustomerSignup subscription = FindCustomerSignup(subscriptionId); // Check if this is the first invoice or a later invoice in the // subscription lifecycle. bool firstInvoice = invoice.BillingReason.Equals("subscription_create"); // You already handle marking the first invoice as paid in the // `checkout.session.completed` handler. // // Only use this for the 2nd invoice and later, so it doesn't conflict. if (!firstInvoice) { // Mark the subscription as paid. MarkPaid(subscription); } } return Ok(); } catch (StripeException e) { return BadRequest(); } } private CustomerSignup SignUpCustomer(String customerEmail, String subscriptionId) { // TODO sign the customer up for your subscription in your database // // `CustomerSignup` is a placeholder type for however you represent // customer signups in your .NET application. } private CustomerSignup FindCustomerSignup(String subscriptionId) { // TODO find your customer's signup record by Stripe subscription ID here } private void MarkPaid(CustomerSignup subscription) { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } } }

Handle invoice payment failures

At some point, one of your customer’s invoices will fail to be charged. Maybe your customer’s credit card expired, or they canceled the card and got a new one without updating the card on file. These are all common scenarios, and can be handled by listening to invoice.payment_failed:

# 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' require 'sinatra' # You can find your endpoint's secret in the output of the `stripe listen` # command you ran earlier endpoint_secret = 'whsec_...' post '/webhook' do event = nil # Verify webhook signature and extract the event # See https://stripe.com/docs/webhooks/signatures for more information. begin sig_header = request.env['HTTP_STRIPE_SIGNATURE'] payload = request.body.read event = Stripe::Webhook.construct_event(payload, sig_header, endpoint_secret) rescue JSON::ParserError => e # Invalid payload return status 400 rescue Stripe::SignatureVerificationError => e # Invalid signature return status 400 end case event['type'] when 'checkout.session.completed' checkout_session = event['data']['object'] subscription_id = checkout_session.subscription customer_email = checkout_session.customer_email # Create a new subscription and mark it as paid this month. subscription = sign_up_customer(customer_email, subscription_id) mark_paid!(subscription) when 'invoice.paid' invoice = event.object subscription_id = invoice.subscription subscription = find_customer_signup(subscription_id) # Check if this is the first invoice or a later invoice in the # subscription lifecycle. first_invoice = invoice.billing_reason == 'subscription_create' # You already handle marking the first invoice as paid in the # `checkout.session.completed` handler. # # Only use this for the 2nd invoice and later, so it doesn't conflict. unless first_invoice # Mark the subscription as paid. mark_paid!(subscription) end when 'invoice.payment_failed' invoice = event.object subscription_id = invoice.subscription subscription = find_customer_signup(subscription_id) mark_past_due!(subscription) end status 200 end def mark_past_due!(subscription) # TODO mark your subscription as past due end def mark_paid!(subscription) # TODO mark your subscription as paid for this month # # Store the customer's subscription status in your database to reference # when a user accesses your service to avoid hitting rate limits. end def find_customer_signup(subscription_id) # TODO find your customer's signup record by Stripe subscription ID here end def sign_up_customer(customer_email, subscription_id) # TODO sign the customer up for your subscription in your database end
# 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' # Using Django from django.http import HttpResponse # You can find your endpoint's secret in your webhook settings endpoint_secret = 'whsec_...' @csrf_exempt def my_webhook_view(request): payload = request.body sig_header = request.META['HTTP_STRIPE_SIGNATURE'] event = None try: event = stripe.Webhook.construct_event( payload, sig_header, endpoint_secret ) except ValueError as e: # Invalid payload return HttpResponse(status=400) except stripe.error.SignatureVerificationError as e: # Invalid signature return HttpResponse(status=400) # Handle the checkout.session.completed event if event['type'] == 'checkout.session.completed': checkout_session = event['data']['object'] subscription_id = checkout_session.subscription customer_email = checkout_session.customer_email # Create a new subscription and mark it as paid this month. subscription = sign_up_customer(customer_email, subscription_id) mark_paid(subscription) elif event['type'] == 'invoice.paid': invoice = event['data']['object'] subscription_id = invoice.subscription subscription = find_customer_signup(subscription_id) # Check if this is the first invoice or a later invoice in the # subscription lifecycle. first_invoice = invoice.billing_reason == 'subscription_create' # You already handle marking the first invoice as paid in the # `checkout.session.completed` handler. # # Only use this for the 2nd invoice and later, so it doesn't conflict. if not first_invoice: # Mark the subscription as paid. mark_paid(subscription) elif event['type'] == 'invoice.payment_failed': invoice = event['data']['object'] subscription_id = invoice.subscription subscription = find_customer_signup(subscription_id) mark_past_due!(subscription) end # Passed signature verification return HttpResponse(status=200) def mark_past_due(subscription): # TODO mark your subscription as past due def mark_paid(subscription): # TODO mark your subscription as paid for this month # # Store the customer's subscription status in your database to reference # when a user accesses your service to avoid hitting rate limits. def find_customer_signup(subscription_id): # TODO find your customer's signup record by Stripe subscription ID here def sign_up_customer(customer_email, subscription_id): # TODO sign the customer up for your subscription in your database
// 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'); // You can find your endpoint's secret in your webhook settings $endpoint_secret = 'whsec_...'; $payload = @file_get_contents('php://input'); $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE']; $event = null; try { $event = \Stripe\Webhook::constructEvent( $payload, $sig_header, $endpoint_secret ); } catch(\UnexpectedValueException $e) { // Invalid payload http_response_code(400); exit(); } catch(\Stripe\Exception\SignatureVerificationException $e) { // Invalid signature http_response_code(400); exit(); } function mark_past_due!($subscription) { // TODO mark your subscription as past due } function mark_paid($subscription) { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } function find_customer_signup($subscription_id) { // TODO find your customer's signup record by Stripe subscription ID here } function sign_up_customer($customer_email, $subscription_id) { // TODO sign the customer up for your subscription in your database } // Handle the checkout.session.completed event switch ($event->type) { case 'checkout.session.completed': $checkout_session = $event->data->object; $subscription_id = $checkout_session->subscription; $customer_email = $checkout_session->customer_email; // Create a new subscription and mark it as paid this month. $subscription = sign_up_customer($customer_email, $subscription_id); mark_paid($subscription); break; case 'invoice.paid': $invoice = $event->data->object; $subscription_id = $invoice->subscription; $subscription = find_customer_signup($subscription_id) // Check if this is the first invoice or a later invoice in the // subscription lifecycle. $first_invoice = $invoice->billing_reason == 'subscription_create'; // You already handle marking the first invoice as paid in the // `checkout.session.completed` handler. // // Only use this for the 2nd invoice and later, so it doesn't conflict. if (!$first_invoice) { // Mark the subscription as paid. mark_paid($subscription); } break; case 'invoice.payment_failed': $invoice = $event->data->object; $subscription_id = $invoice->subscription; $subscription = find_customer_signup($subscription_id); mark_past_due($subscription); break; } http_response_code(200);
// 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"; // You can find your endpoint's secret in your webhook settings String endpointSecret = "whsec_..."; public CustomerSignup signUpCustomer(String customerEmail, String subscriptionId) { // TODO sign the customer up for your subscription in your database // // `CustomerSignup` is a placeholder type for however you represent // customer signups in your Java application. } public CustomerSignup findCustomerSignup(String subscriptionId) { // TODO find your customer's signup record by Stripe subscription ID here } public void markPaid(CustomerSignup subscription) { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } public void markPastDue(CustomerSignup subscription) { // TODO mark your subscription as past due } // Using the Spark framework (http://sparkjava.com) public Object handle(Request request, Response response) { String payload = request.body(); String sigHeader = request.headers("Stripe-Signature"); Event event = null; try { event = Webhook.constructEvent(payload, sigHeader, endpointSecret); } catch (JsonSyntaxException e) { // Invalid payload response.status(400); return ""; } catch (SignatureVerificationException e) { // Invalid signature response.status(400); return ""; } switch (event.getType()) { case "checkout.session.completed": Session checkoutSession = (Session) event.getDataObjectDeserializer().getObject(); String subscriptionId = checkoutSession.getSubscription(); String customerEmail = checkoutSession.getCustomerEmail(); // Create a new subscription and mark it as paid this month. CustomerSignup subscription = signUpCustomer(customerEmail, subscriptionId); markPaid(subscription); break; case "invoice.paid": Invoice invoice = (Invoice) event.getDataObjectDeserializer().getObject(); String subscriptionId = invoice.getSubscription(); CustomerSignup subscription = findCustomerSignup(subscriptionId); // Check if this is the first invoice or a later invoice in the // subscription lifecycle. boolean firstInvoice = invoice.getBillingReason().equals("subscription_create"); // You already handle marking the first invoice as paid in the // `checkout.session.completed` handler. // // Only use this for the 2nd invoice and later, so it doesn't conflict. if (!firstInvoice) { // Mark the subscription as paid. markPaid(subscription); } break; case "invoice.payment_failed": Invoice invoice = (Invoice) event.getDataObjectDeserializer().getObject(); String subscriptionId = invoice.getSubscription(); CustomerSignup subscription = findCustomerSignup(subscriptionId); markPastDue(subscription); break; } response.status(200); return ""; }
// 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'); // Find your endpoint's secret in your Dashboard's webhook settings const endpointSecret = 'whsec_...'; // Using Express const app = require('express')(); // Use body-parser to retrieve the raw body as a buffer const bodyParser = require('body-parser'); const signUpCustomer = (customerEmail, subscriptionId) => { // TODO sign the customer up for your subscription in your database } const findCustomerSignup = (subscriptionId) => { // TODO find your customer's signup record by Stripe subscription ID here } const markPaid = (subscription) => { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } const markPastDue = (subscription) => { // TODO mark your subscription as past due } app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => { const payload = request.body; const sig = request.headers['stripe-signature']; let event; try { event = stripe.webhooks.constructEvent(payload, sig, endpointSecret); } catch (err) { return response.status(400).send(`Webhook Error: ${err.message}`); } switch (event.type) { case 'checkout.session.completed': { const session = event.data.object; const subscriptionId = session.subscription; const customerEmail = session.customer_email; // Create a new subscription and mark it as paid this month. const subscription = signUpCustomer(customerEmail, subscriptionId); markPaid(subscription); break; } case 'invoice.paid': { const invoice = event.data.object; const subscriptionId = invoice.subscription; const subscription = findCustomerSignup(subscriptionId); // Check if this is the first invoice or a later invoice in the // subscription lifecycle. const firstInvoice = invoice.billing_reason === 'subscription_create'; // You already handle marking the first invoice as paid in the // `checkout.session.completed` handler. // // Only use this for the 2nd invoice and later, so it doesn't conflict. if (!firstInvoice) { markPaid(subscription); } break; } case 'invoice.payment_failed': { const invoice = event.data.object; const subscriptionId = invoice.subscription; const subscription = findCustomerSignup(subscriptionId); markPastDue(subscription); break; } } response.status(200); }); app.listen(4242, () => console.log('Running on port 4242'));
// 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" func SignUpCustomer(customerEmail string, subscriptionId string) CustomerSignup { // TODO sign the customer up for your subscription in your database // // `CustomerSignup` is a placeholder type for however you represent // customer signups in your Go application. } func FindCustomerSignup(subscriptionId string) CustomerSignup { // TODO find your customer's signup record by Stripe subscription ID here } func MarkPaid(subscription CustomerSignup) { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } func MarkPastDue(subscription CustomerSignup) { // TODO mark your subscription as past due } http.HandleFunc("/webhook", func(w http.ResponseWriter, req *http.Request) { const MaxBodyBytes = int64(65536) req.Body = http.MaxBytesReader(w, req.Body, MaxBodyBytes) body, err := ioutil.ReadAll(req.Body) if err != nil { fmt.Fprintf(os.Stderr, "Error reading request body: %v\n", err) w.WriteHeader(http.StatusServiceUnavailable) return } // Pass the request body and Stripe-Signature header to ConstructEvent, along with the webhook signing key // You can find your endpoint's secret in your webhook settings endpointSecret := "whsec_..."; event, err := webhook.ConstructEvent(body, req.Header.Get("Stripe-Signature"), endpointSecret) if err != nil { fmt.Fprintf(os.Stderr, "Error verifying webhook signature: %v\n", err) w.WriteHeader(http.StatusBadRequest) // Return a 400 error on a bad signature return } // Handle the checkout.session.completed event switch event.Type { case "checkout.session.completed": var session stripe.CheckoutSession err := json.Unmarshal(event.Data.Raw, &session) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err) w.WriteHeader(http.StatusBadRequest) return } var string subscriptionId = session.Subscription var string customerEmail = session.CustomerEmail // Create a new subscription and mark it as paid this month. CustomerSignup subscription = SignUpCustomer(customerEmail, subscriptionId) MarkPaid(subscription) case "invoice.paid": var invoice stripe.Invoice err := json.Unmarshal(event.Data.Raw, &invoice) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err) w.WriteHeader(http.StatusBadRequest) return } var string subscriptionId = invoice.Subscription var CustomerSignup subscription = FindCustomerSignup(subscriptionId); // Check if this is the first invoice or a later invoice in the // subscription lifecycle. var bool firstInvoice = invoice.BillingReason == "subscription_create" // You already handle marking the first invoice as paid in the // `checkout.session.completed` handler. // // Only use this for the 2nd invoice and later, so it doesn't conflict. if !firstInvoice { // Mark the subscription as paid. MarkPaid(subscription); } case "invoice.payment_failed": var invoice stripe.Invoice err := json.Unmarshal(event.Data.Raw, &invoice) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err) w.WriteHeader(http.StatusBadRequest) return } var string subscriptionId = invoice.Subscription var CustomerSignup subscription = FindCustomerSignup(subscriptionId); MarkPastDue(subscription); } w.WriteHeader(http.StatusOK) })
// 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"; using System; using System.IO; using Microsoft.AspNetCore.Mvc; using Stripe; namespace workspace.Controllers { [Route("api/[controller]")] public class StripeWebHook : Controller { // You can find your endpoint's secret in your webhook settings const string secret = "whsec_..."; [HttpPost] public async Task<IActionResult> Index() { var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); try { var stripeEvent = EventUtility.ConstructEvent( json, Request.Headers["Stripe-Signature"], secret ); // Handle the checkout.session.completed event if (stripeEvent.Type == Events.CheckoutSessionCompleted) { var session = stripeEvent.Data.Object as Checkout.Session; String subscriptionId = session.SubscriptionId; String customerEmail = session.CustomerEmail; // Create a new subscription and mark it as paid this month. CustomerSignup subscription = SignUpCustomer(customerEmail, subscriptionId); MarkPaid(subscription); } else if (stripeEvent.Type == Events.InvoicePaid) { var invoice = stripeEvent.Data.Object as Invoice; String subscriptionId = invoice.SubscriptionId; CustomerSignup subscription = FindCustomerSignup(subscriptionId); // Check if this is the first invoice or a later invoice in the // subscription lifecycle. bool firstInvoice = invoice.BillingReason.Equals("subscription_create"); // You already handle marking the first invoice as paid in the // `checkout.session.completed` handler. // // Only use this for the 2nd invoice and later, so it doesn't conflict. if (!firstInvoice) { // Mark the subscription as paid. MarkPaid(subscription); } } else if (stripeEvent.Type == Events.InvoicePaymentFailed) { var invoice = stripeEvent.Data.Object as Invoice; String subscriptionId = invoice.SubscriptionId; CustomerSignup subscription = FindCustomerSignup(subscriptionId); MarkPastDue(subscription); } return Ok(); } catch (StripeException e) { return BadRequest(); } } private CustomerSignup SignUpCustomer(String customerEmail, String subscriptionId) { // TODO sign the customer up for your subscription in your database // // `CustomerSignup` is a placeholder type for however you represent // customer signups in your .NET application. } private CustomerSignup FindCustomerSignup(String subscriptionId) { // TODO find your customer's signup record by Stripe subscription ID here } private void MarkPaid(CustomerSignup subscription) { // TODO mark your subscription as paid for this month // // Store the customer's subscription status in your database to reference // when a user accesses your service to avoid hitting rate limits. } private void MarkPastDue(CustomerSignup subscription) { // TODO mark your subscription as past due } } }

At this point, you should be listening for new subscriptions, future invoice payments, and handling any payment failures that come your way. Your basic webhook endpoint is now complete and ready to handle subscriptions.

Was this page helpful?
Questions? Contact us.
Developer tutorials on YouTube.