Fulfill your orders
Learn how to fulfill orders after a customer pays with Stripe Checkout.
You wrote the code to display a beautiful payment form on your website. Now, when a customer pays, you need notification that you can fulfill their order.
In this guide, you’ll learn how to:
- Receive an event notification when a customer pays you.
- Handle the event.
- Use Stripe CLI to quickly test your new event handler.
- Optionally handle additional payment methods.
- Turn on your event handler in production.
1 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:
stripe status âś” All services are online.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.
2 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/account/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# 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' 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
# 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 @csrf_exempt def my_webhook_view(request): payload = request.body # For now, you only need to print out the webhook payload so you can see # the structure. print(payload) return HttpResponse(status=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.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' # Using Django from django.http import HttpResponse @csrf_exempt def my_webhook_view(request): payload = request.body # For now, you only need to print out the webhook payload so you can see # the structure. print(payload) return HttpResponse(status=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\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); function log($val) { return file_put_contents('php://stderr', print_r($val, TRUE)); } $payload = @file_get_contents('php://input'); // For now, you only need to log the webhook payload so you can see // the structure. log($payload);// 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'); function log($val) { return file_put_contents('php://stderr', print_r($val, TRUE)); } $payload = @file_get_contents('php://input'); // For now, you only need to log the webhook payload so you can see // the structure. log($payload);
// 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"; // Using the Spark framework (http://sparkjava.com) public Object handle(Request request, Response response) { String payload = request.body(); System.out.println("Got payload: " + payload); 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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; // Using the Spark framework (http://sparkjava.com) public Object handle(Request request, Response response) { String payload = request.body(); System.out.println("Got payload: " + payload); 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); // Using Express const app = require('express')(); // Use body-parser to retrieve the raw body as a buffer const bodyParser = require('body-parser'); app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => { const payload = request.body; console.log("Got payload: " + payload); 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 const Stripe = require('stripe'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); // Using Express const app = require('express')(); // Use body-parser to retrieve the raw body as a buffer const bodyParser = require('body-parser'); app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => { const payload = request.body; console.log("Got payload: " + payload); 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" 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 } fmt.Fprintf(os.Stdout, "Got body: %s\n", body) 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" 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 } fmt.Fprintf(os.Stdout, "Got body: %s\n", body) 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; namespace workspace.Controllers { [Route("api/[controller]")] public class StripeWebHook : Controller { [HttpPost] public async Task<IActionResult> Index() { var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); Console.WriteLine(json); return Ok(); } } }// 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; namespace workspace.Controllers { [Route("api/[controller]")] public class StripeWebHook : Controller { [HttpPost] public async Task<IActionResult> Index() { var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); Console.WriteLine(json); return Ok(); } } }
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:
stripe listen --forward-to localhost:4242/webhook Ready! Your webhook signing secret is 'whsec_<REDACTED>' (^C to quit)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 ZIP code (
90210
)
- Enter
- Click the Pay button
You should see:
- A
checkout.session.completed
in thestripe 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/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 # Print out the event so you can look at it puts event.inspect status 200 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 # Print out the event so you can look at it puts event.inspect status 200 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) # Passed signature verification return HttpResponse(status=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.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) # Passed signature verification return HttpResponse(status=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\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); function log($val) { return file_put_contents('php://stderr', print_r($val, TRUE)); } // 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(); } log("Passed signature verification!"); 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\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); function log($val) { return file_put_contents('php://stderr', print_r($val, TRUE)); } // 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(); } log("Passed signature verification!"); 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_..."; // 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 ""; } 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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; // You can find your endpoint's secret in your webhook settings String endpointSecret = "whsec_..."; // 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 ""; } 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'); const stripe = 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'); 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}`); } 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 const Stripe = require('stripe'); const stripe = 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'); 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}`); } 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" 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 } 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" 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 } 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 ); return Ok(); } catch (StripeException e) { return BadRequest(); } } } }// 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 ); return Ok(); } catch (StripeException e) { return BadRequest(); } } } }
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:
curl -X POST \ -H "Content-Type: application/json" \ --data '{ fake: "unsigned request" }' \ -is http://localhost:4242/webhook HTTP/1.1 400 Bad Request ... more headerscurl -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.
3 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 (e.g., 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.
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/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 if event['type'] == 'checkout.session.completed' checkout_session = event['data']['object'] fulfill_order(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# 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 if event['type'] == 'checkout.session.completed' checkout_session = event['data']['object'] fulfill_order(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
# 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': session = event['data']['object'] # Fulfill the purchase... fulfill_order(session) # Passed signature verification return HttpResponse(status=200) def fulfill_order(session): # TODO: fill me in print("Fulfilling order")# 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': session = event['data']['object'] # Fulfill the purchase... fulfill_order(session) # Passed signature verification return HttpResponse(status=200) def fulfill_order(session): # TODO: fill me in print("Fulfilling order")
// 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'); function log($val) { return file_put_contents('php://stderr', print_r($val, TRUE)); } // 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 fulfill_order($session) { // TODO: fill me in log("Fulfilling order..."); log($session); } // Handle the checkout.session.completed event if ($event->type == 'checkout.session.completed') { $session = $event->data->object; // Fulfill the purchase... fulfill_order($session); } 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\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); function log($val) { return file_put_contents('php://stderr', print_r($val, TRUE)); } // 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 fulfill_order($session) { // TODO: fill me in log("Fulfilling order..."); log($session); } // Handle the checkout.session.completed event if ($event->type == 'checkout.session.completed') { $session = $event->data->object; // Fulfill the purchase... fulfill_order($session); } 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 void fulfillOrder(Session session) { // TODO: fill me in System.out.println("Fulfilling order..."); } // 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 ""; } // Handle the checkout.session.completed event if ("checkout.session.completed".equals(event.getType())) { Session session = (Session) event.getDataObjectDeserializer().getObject(); // Fulfill the purchase... fulfillOrder(session); } 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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; // You can find your endpoint's secret in your webhook settings String endpointSecret = "whsec_..."; public void fulfillOrder(Session session) { // TODO: fill me in System.out.println("Fulfilling order..."); } // 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 ""; } // Handle the checkout.session.completed event if ("checkout.session.completed".equals(event.getType())) { Session session = (Session) event.getDataObjectDeserializer().getObject(); // Fulfill the purchase... fulfillOrder(session); } 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'); const stripe = 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 fulfillOrder = (session) => { // TODO: fill me in console.log("Fulfilling order", session); } 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}`); } // Handle the checkout.session.completed event if (event.type === 'checkout.session.completed') { const session = event.data.object; // Fulfill the purchase... fulfillOrder(session); } 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 const Stripe = require('stripe'); const stripe = 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 fulfillOrder = (session) => { // TODO: fill me in console.log("Fulfilling order", session); } 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}`); } // Handle the checkout.session.completed event if (event.type === 'checkout.session.completed') { const session = event.data.object; // Fulfill the purchase... fulfillOrder(session); } 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 FulfillOrder(session stripe.CheckoutSession) { // TODO: fill me in } 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 if event.Type == "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 } // Fulfill the purchase... FulfillOrder(session) } 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" func FulfillOrder(session stripe.CheckoutSession) { // TODO: fill me in } 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 if event.Type == "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 } // Fulfill the purchase... FulfillOrder(session) } 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; // Fulfill the purchase... this.FulfillOrder(session); } return Ok(); } catch (StripeException e) { return BadRequest(); } } private void FulfillOrder(Checkout.Session session) { // TODO: fill me in } } }// 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; // Fulfill the purchase... this.FulfillOrder(session); } return Ok(); } catch (StripeException e) { return BadRequest(); } } private void FulfillOrder(Checkout.Session session) { // TODO: fill me in } } }
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.
4 Optional Handle delayed notification payment methods Server-side
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 PaymentIntentThe Payment Intents API is a new way to build dynamic payment flows. It tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods. status changes from processing
to succeeded
.
You’ll need to handle the following Checkout events:
Event Name | Description | Next steps |
---|---|---|
checkout.session.completed | The customer has successfully authorized the debit payment by submitting the Checkout form. | Wait for the payment to succeed or fail. |
checkout.session.async_payment_succeeded | The customer’s payment succeeded. | Fulfill the purchased goods or services. |
checkout.session.async_payment_failed | The 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/account/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 (e.g., 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# 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' # 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 (e.g., 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
# 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': session = event['data']['object'] # Fulfill the purchase... fulfill_order(session) # Handle the checkout.session.completed event if event['type'] == 'checkout.session.completed': session = event['data']['object'] # Save an order in your database, marked as 'awaiting payment' create_order(session) # Check if the order is already paid (e.g., 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 session.payment_status == "paid": # Fulfill the purchase fulfill_order(session) elif event['type'] == 'checkout.session.async_payment_succeeded': session = event['data']['object'] # Fulfill the purchase fulfill_order(session) elif event['type'] == '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(session) # Passed signature verification return HttpResponse(status=200) def fulfill_order(session): # TODO: fill me in print("Fulfilling order") def create_order(session): # TODO: fill me in print("Creating order") def email_customer_about_failed_payment(session): # TODO: fill me in print("Emailing customer")# 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': session = event['data']['object'] # Fulfill the purchase... fulfill_order(session) # Handle the checkout.session.completed event if event['type'] == 'checkout.session.completed': session = event['data']['object'] # Save an order in your database, marked as 'awaiting payment' create_order(session) # Check if the order is already paid (e.g., 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 session.payment_status == "paid": # Fulfill the purchase fulfill_order(session) elif event['type'] == 'checkout.session.async_payment_succeeded': session = event['data']['object'] # Fulfill the purchase fulfill_order(session) elif event['type'] == '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(session) # Passed signature verification return HttpResponse(status=200) def fulfill_order(session): # TODO: fill me in print("Fulfilling order") def create_order(session): # TODO: fill me in print("Creating order") def email_customer_about_failed_payment(session): # TODO: fill me in print("Emailing customer")
// 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'); function log($val) { return file_put_contents('php://stderr', print_r($val, TRUE)); } // 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 fulfill_order($session) { // TODO: fill me in log("Fulfilling order..."); log($session); } function create_order($session) { // TODO fill me in } function email_customer_about_failed_payment($session) { // TODO fill me in } // Handle the checkout.session.completed event if ($event->type == 'checkout.session.completed') { $session = $event->data->object; // Fulfill the purchase... fulfill_order($session); } switch ($event->type) { case 'checkout.session.completed': $session = $event->data->object; // Save an order in your database, marked as 'awaiting payment' create_order($session); // Check if the order is paid (e.g., 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 ($session->payment_status == 'paid') { // Fulfill the purchase fulfill_order($session); } break; case 'checkout.session.async_payment_succeeded': $session = $event->data->object; // Fulfill the purchase fulfill_order($session); break; case '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($session); 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\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); function log($val) { return file_put_contents('php://stderr', print_r($val, TRUE)); } // 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 fulfill_order($session) { // TODO: fill me in log("Fulfilling order..."); log($session); } function create_order($session) { // TODO fill me in } function email_customer_about_failed_payment($session) { // TODO fill me in } // Handle the checkout.session.completed event if ($event->type == 'checkout.session.completed') { $session = $event->data->object; // Fulfill the purchase... fulfill_order($session); } switch ($event->type) { case 'checkout.session.completed': $session = $event->data->object; // Save an order in your database, marked as 'awaiting payment' create_order($session); // Check if the order is paid (e.g., 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 ($session->payment_status == 'paid') { // Fulfill the purchase fulfill_order($session); } break; case 'checkout.session.async_payment_succeeded': $session = $event->data->object; // Fulfill the purchase fulfill_order($session); break; case '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($session); 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 void fulfillOrder(Session session) { // TODO: fill me in System.out.println("Fulfilling order..."); } public void createOrder(Session session) { // TODO: fill me in System.out.println("Creating order..."); } public void emailCustomerAboutFailedPayment(Session session) { // TODO: fill me in System.out.println("Emailing customer..."); } // 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 ""; } // Handle the checkout.session.completed event if ("checkout.session.completed".equals(event.getType())) { Session session = (Session) event.getDataObjectDeserializer().getObject(); // Fulfill the purchase... fulfillOrder(session); } switch (event.getType()) { case "checkout.session.completed": Session session = (Session) event.getDataObjectDeserializer().getObject(); // Save an order in your database, marked as 'awaiting payment' createOrder(session); // Check if the order is paid (e.g., 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 (session.getPaymentStatus() == "paid") { // Fulfill the purchase fulfillOrder(session); } break; case "checkout.session.async_payment_succeeded": Session session = (Session) event.getDataObjectDeserializer().getObject(); // Fulfill the purchase fulfillOrder(session); break; case "checkout.session.async_payment_failed": Session session = (Session) event.getDataObjectDeserializer().getObject(); // Send an email to the customer asking them to retry their order emailCustomerAboutFailedPayment(session); 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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; // You can find your endpoint's secret in your webhook settings String endpointSecret = "whsec_..."; public void fulfillOrder(Session session) { // TODO: fill me in System.out.println("Fulfilling order..."); } public void createOrder(Session session) { // TODO: fill me in System.out.println("Creating order..."); } public void emailCustomerAboutFailedPayment(Session session) { // TODO: fill me in System.out.println("Emailing customer..."); } // 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 ""; } // Handle the checkout.session.completed event if ("checkout.session.completed".equals(event.getType())) { Session session = (Session) event.getDataObjectDeserializer().getObject(); // Fulfill the purchase... fulfillOrder(session); } switch (event.getType()) { case "checkout.session.completed": Session session = (Session) event.getDataObjectDeserializer().getObject(); // Save an order in your database, marked as 'awaiting payment' createOrder(session); // Check if the order is paid (e.g., 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 (session.getPaymentStatus() == "paid") { // Fulfill the purchase fulfillOrder(session); } break; case "checkout.session.async_payment_succeeded": Session session = (Session) event.getDataObjectDeserializer().getObject(); // Fulfill the purchase fulfillOrder(session); break; case "checkout.session.async_payment_failed": Session session = (Session) event.getDataObjectDeserializer().getObject(); // Send an email to the customer asking them to retry their order emailCustomerAboutFailedPayment(session); 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'); const stripe = 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 fulfillOrder = (session) => { // TODO: fill me in console.log("Fulfilling order", session); } const createOrder = (session) => { // TODO: fill me in console.log("Creating order", session); } const emailCustomerAboutFailedPayment = (session) => { // TODO: fill me in console.log("Emailing customer", session); } 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}`); } // Handle the checkout.session.completed event if (event.type === 'checkout.session.completed') { const session = event.data.object; // Fulfill the purchase... fulfillOrder(session); } switch (event.type) { case 'checkout.session.completed': { const session = event.data.object; // Save an order in your database, marked as 'awaiting payment' createOrder(session); // Check if the order is paid (e.g., 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 (session.payment_status === 'paid') { fulfillOrder(session); } break; } case 'checkout.session.async_payment_succeeded': { const session = event.data.object; // Fulfill the purchase... fulfillOrder(session); break; } case 'checkout.session.async_payment_failed': { const session = event.data.object; // Send an email to the customer asking them to retry their order emailCustomerAboutFailedPayment(session); 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 const Stripe = require('stripe'); const stripe = 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 fulfillOrder = (session) => { // TODO: fill me in console.log("Fulfilling order", session); } const createOrder = (session) => { // TODO: fill me in console.log("Creating order", session); } const emailCustomerAboutFailedPayment = (session) => { // TODO: fill me in console.log("Emailing customer", session); } 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}`); } // Handle the checkout.session.completed event if (event.type === 'checkout.session.completed') { const session = event.data.object; // Fulfill the purchase... fulfillOrder(session); } switch (event.type) { case 'checkout.session.completed': { const session = event.data.object; // Save an order in your database, marked as 'awaiting payment' createOrder(session); // Check if the order is paid (e.g., 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 (session.payment_status === 'paid') { fulfillOrder(session); } break; } case 'checkout.session.async_payment_succeeded': { const session = event.data.object; // Fulfill the purchase... fulfillOrder(session); break; } case 'checkout.session.async_payment_failed': { const session = event.data.object; // Send an email to the customer asking them to retry their order emailCustomerAboutFailedPayment(session); 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 FulfillOrder(session stripe.CheckoutSession) { // TODO: fill me in } func CreateOrder(session stripe.CheckoutSession) { // TODO: fill me in } func EmailCustomerAboutFailedPayment(session stripe.CheckoutSession) { // TODO: fill me in } 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 if event.Type == "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 } // Fulfill the purchase... FulfillOrder(session) } 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 } // Save an order in your database, marked as 'awaiting payment' CreateOrder(session) // Check if the order is paid (e.g., 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. orderPaid := session.PaymentStatus == stripe.CheckoutSessionPaymentStatusPaid if orderPaid { // Fulfill the purchase FulfillOrder(session) } case "checkout.session.async_payment_succeeded": 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 } // Fulfill the purchase FulfillOrder(session) case "checkout.session.async_payment_failed": 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 } // Send an email to the customer asking them to retry their order EmailCustomerAboutFailedPayment(session) } 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" func FulfillOrder(session stripe.CheckoutSession) { // TODO: fill me in } func CreateOrder(session stripe.CheckoutSession) { // TODO: fill me in } func EmailCustomerAboutFailedPayment(session stripe.CheckoutSession) { // TODO: fill me in } 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 if event.Type == "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 } // Fulfill the purchase... FulfillOrder(session) } 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 } // Save an order in your database, marked as 'awaiting payment' CreateOrder(session) // Check if the order is paid (e.g., 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. orderPaid := session.PaymentStatus == stripe.CheckoutSessionPaymentStatusPaid if orderPaid { // Fulfill the purchase FulfillOrder(session) } case "checkout.session.async_payment_succeeded": 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 } // Fulfill the purchase FulfillOrder(session) case "checkout.session.async_payment_failed": 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 } // Send an email to the customer asking them to retry their order EmailCustomerAboutFailedPayment(session) } 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; // Fulfill the purchase... FulfillOrder(session); } Checkout.Session session = null; switch (stripeEvent.Type) { case Events.CheckoutSessionCompleted: session = stripeEvent.Data.Object as Checkout.Session; // Save an order in your database, marked as 'awaiting payment' CreateOrder(session); // Check if the order is paid (e.g., 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. var orderPaid = session.PaymentStatus == "paid" if (orderPaid) { // Fulfill the purchase FulfillOrder(session); } break; case Events.CheckoutSessionAsyncPaymentSucceeded: session = stripeEvent.Data.Object as Checkout.Session; // Fulfill the purchase FulfillOrder(session); break; case Events.CheckoutSessionAsyncPaymentFailed: session = stripeEvent.Data.Object as Checkout.Session; // Send an email to the customer asking them to retry their order EmailCustomerAboutFailedPayment(session); break; } return Ok(); } catch (StripeException e) { return BadRequest(); } } private void FulfillOrder(Checkout.Session session) { // TODO: fill me in } private void CreateOrder(Checkout.Session session) { // TODO: fill me in } private void EmailCustomerAboutFailedPayment(Checkout.Session session) { // TODO: fill me in } } }// 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; // Fulfill the purchase... FulfillOrder(session); } Checkout.Session session = null; switch (stripeEvent.Type) { case Events.CheckoutSessionCompleted: session = stripeEvent.Data.Object as Checkout.Session; // Save an order in your database, marked as 'awaiting payment' CreateOrder(session); // Check if the order is paid (e.g., 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. var orderPaid = session.PaymentStatus == "paid" if (orderPaid) { // Fulfill the purchase FulfillOrder(session); } break; case Events.CheckoutSessionAsyncPaymentSucceeded: session = stripeEvent.Data.Object as Checkout.Session; // Fulfill the purchase FulfillOrder(session); break; case Events.CheckoutSessionAsyncPaymentFailed: session = stripeEvent.Data.Object as Checkout.Session; // Send an email to the customer asking them to retry their order EmailCustomerAboutFailedPayment(session); break; } return Ok(); } catch (StripeException e) { return BadRequest(); } } private void FulfillOrder(Checkout.Session session) { // TODO: fill me in } private void CreateOrder(Checkout.Session session) { // TODO: fill me in } private void EmailCustomerAboutFailedPayment(Checkout.Session session) { // TODO: fill me in } } }
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.
5 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.