Sign in
An image of the Stripe logo
Create account
Sign in
Home
Payments
Business operations
Financial services
Developer tools
No-code
All products
Home
Payments
Business operations
Home
Payments
Business operations
Financial services
Developer tools
Overview
Online payments
Products and prices
Invoicing
Subscriptions
Quotes
In-person payments
Multiparty payments
After the payment
Add payment methods
Payment Links
Stripe Checkout
    Overview
    How Checkout works
    Quickstart
    Fulfill your orders
    Migrate payment methods to the Dashboard
    Migrate from legacy Checkout
    Migrate Checkout to use Prices
    Customize your integration
    Customize branding
    Collect taxes
    Collect tax IDs
    Collect phone numbers
    Post-payment invoices
    Make line item quantities adjustable
    Add custom fields
    Let customers decide what to pay
    Boost revenue
    Present local currencies
    Configure subscription upsells
    Configure cross-sells
    Recover abandoned carts
    Collect consent for promotional emails
    Analyze conversion funnel
    Additional features
    Add discounts
    Add shipping
    Start a free trial without collecting payment details
    Manage limited inventory
    Guest customers
Stripe Elements
About the APIs
Regulation support
Implementation guides
Testing
Checkout
·
HomePayments

Fulfill orders with Checkout

Learn how to fulfill orders after a customer pays with Stripe Checkout or Stripe Payment Links.

After you integrate Stripe Checkout or create a Stripe Payment Link to take your customers to a payment form, you need notification that you can fulfill their order after they pay.

In this guide, you’ll learn how to:

  1. Receive an event notification when a customer pays you.
  2. Handle the event.
  3. Use Stripe CLI to quickly test your new event handler.
  4. Optionally, handle additional payment methods.
  5. Turn on your event handler in production.

Install Stripe CLI

The quickest way to develop and test webhooks locally is with the Stripe CLI.

As a first step, follow the install guide for Stripe CLI.

After you finish the install guide, run the following command to test that your Stripe CLI installation works:

Command Line
stripe status ✔ All services are online.

If you see a success message after running stripe status, you’re ready to move on to the next step.

Create your event handler
Server-side

In this section, you’ll create a small event handler so Stripe can send you a checkout.session.completed event when a customer completes checkout.

First, create a new route for your event handler. Start by printing out the event you receive. You’ll verify that delivery is working in the next step:

# Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys Stripe.api_key =
'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
require 'sinatra' post '/webhook' do payload = request.body.read # For now, you only need to print out the webhook payload so you can see # the structure. puts payload.inspect status 200 end

Testing

Run your server (for example, on localhost:4242). Next, set up Stripe CLI to forward events to your local server, so you can test your event handler locally:

Command Line
stripe listen --forward-to localhost:4242/webhook Ready! Your webhook signing secret is 'whsec_<REDACTED>' (^C to quit)

Next, go through Checkout as a customer:

  • Click your checkout button (you probably set this up in the Accept a payment guide)
  • Fill out your payment form with test data
    • Enter 4242 4242 4242 4242 as the card number
    • Enter any future date for card expiry
    • Enter any 3-digit number for CVV
    • Enter any billing postal code (90210)
  • Click the Pay button

You should see:

  • A checkout.session.completed in the stripe listen output
  • A print statement from your server’s event logs with the checkout.session.completed event

Now that you’ve verified event delivery, you can add a bit of security to make sure that events are only coming from Stripe.

Verify events came from Stripe

Anyone can POST data to your event handler. Before processing an event, always verify that it came from Stripe before trusting it. The official Stripe library has built-in support for verifying webhook events, which you’ll update your event handler with:

# Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/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 # Print out the event so you can look at it puts event.inspect status 200 end

Testing

Go through the testing flow from the previous step. You should still see the checkout.session.completed event being printed out successfully.

Next, try hitting the endpoint with an unsigned request:

Command Line
curl -X POST \ -H "Content-Type: application/json" \ --data '{ "fake": "unsigned request" }' \ -is http://localhost:4242/webhook HTTP/1.1 400 Bad Request ... more headers

You should get a 400 Bad Request error, because you tried to send an unsigned request to your endpoint.

Now that the basics of the event handler are set up, you can move on to fulfilling the order.

Fulfill the order
Server-side

To fulfill the order, you’ll need to handle the checkout.session.completed event. Depending on which payment methods you accept (for example, cards, mobile wallets), you’ll also optionally handle a few extra events after this basic step.

Handle the checkout.session.completed event

Now that you have the basic structure and security in place to make sure any event you process came from Stripe, you can handle the checkout.session.completed event. This event includes the Checkout Session object, which contains details about your customer and their payment.

When handling this event, you might also consider:

  • Saving a copy of the order in your own database.
  • Sending the customer a receipt email.
  • Reconciling the line items and quantity purchased by the customer if using line_item.adjustable_quantity. If the Checkout Session has many line items you can paginate through them with the line_items.

Add code to your event handler to fulfill the order:

# Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/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 if event['type'] == 'checkout.session.completed' # Retrieve the session. If you require line items in the response, you may include them by expanding line_items. session = Stripe::Checkout::Session.retrieve({ id: event['data']['object']['id'], expand: ['line_items'], }) line_items = session.line_items fulfill_order(line_items) end status 200 end def fulfill_order(line_items) # TODO: fill in with your own logic puts "Fulfilling order for #{line_items.inspect}" end

Your webhook endpoint redirects your customer to the success_url when you acknowledge you received the event. In scenarios where your endpoint is down or the event isn’t acknowledged properly, your handler redirects the customer to the success_url 10 seconds after a successful payment.

Testing

Ensure that stripe listen is still running. Go through Checkout as a test user, just like in the prior steps. Your event handler should receive a checkout.session.completed event, and you should have successfully handled it.

Handle delayed notification payment methods
Server-side

This step is only required if you plan to use any of the following payment methods: Bacs Direct Debit, Bank transfers, Boleto, Canadian pre-authorized debits, Konbini, OXXO, SEPA Direct Debit, SOFORT, or ACH Direct Debit.

When receiving payments with a delayed notification payment method, funds aren’t immediately available. It can take multiple days for funds to process so you should delay order fulfillment until the funds are available in your account. After the payment succeeds, the underlying PaymentIntent status changes from processing to succeeded.

You’ll need to handle the following Checkout events:

Event NameDescriptionNext steps
checkout.session.completedThe customer has successfully authorized the debit payment by submitting the Checkout form.Wait for the payment to succeed or fail.
checkout.session.async_payment_succeededThe customer’s payment succeeded.Fulfill the purchased goods or services.
checkout.session.async_payment_failedThe payment was declined, or failed for some other reason.Contact the customer via email and request that they place a new order.

These events all include the Checkout Session object.

Update your event handler to fulfill the order:

# Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys Stripe.api_key =
'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
# 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'] if event['type'] == 'checkout.session.completed' checkout_session = event['data']['object'] fulfill_order(checkout_session) end when 'checkout.session.completed' checkout_session = event['data']['object'] # Save an order in your database, marked as 'awaiting payment' create_order(checkout_session) # Check if the order is already paid (for example, from a card payment) # # A delayed notification payment will have an `unpaid` status, as # you're still waiting for funds to be transferred from the customer's # account. if checkout_session.payment_status == 'paid' fulfill_order(checkout_session) end when 'checkout.session.async_payment_succeeded' checkout_session = event['data']['object'] # Fulfill the purchase... fulfill_order(checkout_session) when 'checkout.session.async_payment_failed' session = event['data']['object'] # Send an email to the customer asking them to retry their order email_customer_about_failed_payment(checkout_session) end status 200 end def fulfill_order(checkout_session) # TODO: fill in with your own logic puts "Fulfilling order for #{checkout_session.inspect}" end def create_order(checkout_session) # TODO: fill in with your own logic puts "Creating order for #{checkout_session.inspect}" end def email_customer_about_failed_payment(checkout_session) # TODO: fill in with your own logic puts "Emailing customer about payment failure for: #{checkout_session.inspect}" end

Testing

Ensure that stripe listen is still running. Go through Checkout as a test user, like you did in the prior steps. Your event handler should receive a checkout.session.completed event, and you should have successfully handled it.

Now that you’ve completed these steps, you’re ready to go live in production whenever you decide to do so.

Go live in production

After you’ve deployed your event handler endpoint to production, you need to register your live URL with Stripe. Follow this quick guide to set that up.

Was this page helpful?
Need help? Contact Support.
Watch our developer tutorials.
Check out our product changelog.
Questions? Contact Sales.
Powered by Markdoc
You can unsubscribe at any time. Read our privacy policy.
On this page
Install Stripe CLI
Create your event handler
Fulfill the order
Handle delayed notification payment methods
Go live in production
Stripe Shell
Test mode
Welcome to the Stripe Shell! Stripe Shell is a browser-based shell with the Stripe CLI pre-installed. Login to your Stripe account and press Control + Backtick on your keyboard to start managing your Stripe resources in test mode. - View supported Stripe commands: - Find webhook events: - Listen for webhook events: - Call Stripe APIs: stripe [api resource] [operation] (e.g. )
The Stripe Shell is best experienced on desktop.
$