After the Payment

    Learn how to handle actions after your customer completes their payment.

    When your customer successfully completes their payment or initiates a subscription using Checkout, Stripe redirects them to the URL that you specified in the success_url parameter (or successUrl in the client-only integration). Typically, this is a page on your website that informs your customer that their payment was successful.

    At a later point, you may want to fulfill the customer’s purchase and deliver the goods or services they paid for. There are a few ways to do this:

    Fulfilling purchases with the Dashboard

    When a customer successfully completes a payment, it is recorded as a new entry in the Dashboard’s list of payments. When you click an entry in the list, it takes you to the payment detail page. In the Checkout summary section, you can find the customer’s billing information and the list of items that they purchased. You can use this information to manually fulfill the customer’s purchase.

    Dashboard fulfillment is only practical for digital goods that you can send to the customer via e-mail. If you intend to fulfill purchases that include physical goods, your application should collect shipping details.

    When a customer successfully pays for a recurring service, they are automatically subscribed to the plan you specified. Their subscription is recorded as a new entry in the Dashboard’s list of subscriptions.

    Fulfilling purchases with third-party plugins

    You can use plugins like Zapier to automate the process of updating your purchase fulfillment systems with information from Stripe payments.

    Examples of automations supported by plugins include:

    • Updating spreadsheets used for order tracking in response to successful payments
    • Updating inventory management systems in response to successful payments
    • Triggering notifications to internal customer service teams via email or chat applications

    Fulfilling purchases with webhooks

    Stripe can send webhook events to your server to notify you when a customer completes their payment. You can create a handler for the event and use it to execute any code that you need to fulfill the customer’s purchase. To handle a webhook event, create an HTTP endpoint on your server and configure the webhook endpoint in the Dashboard.

    Stripe sends the checkout.session.completed event when a Checkout payment is successful. The webhook payload includes the Checkout Session object, which contains information about the Customer, PaymentIntent, or Subscription, and optionally the client_reference_id if you provided it when calling redirectToCheckout on the client.

    The checkout.session.completed webhook is sent to your server before your customer is redirected. Your webhook acknowledgement (any 2xx status code) will trigger the customer’s redirect to the success_url. If Stripe does not receive successful acknowledgement after 10 seconds have passed from a successful payment, your customer will be automatically redirected.

    When your Connect platform makes the request on your behalf, Stripe will accept an acknowledged webhook on the endpoints registered by your platform.

    The guarantee that the webhook is sent prior to your customer’s redirect allows for use of the success_url to render a successful order. If you’d like to access the Checkout Session ID you can add the {CHECKOUT_SESSION_ID} template variable to the success_url (e.g. success_url=www.example.com/success?session_id={CHECKOUT_SESSION_ID}). When your customer is redirected on successful payment, the success_url will contain the Checkout Session ID in place of the {CHECKOUT_SESSION_ID} template variable.

    Learn more about setting up webhooks.

    # Set your secret key: remember to change this 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 your webhook settings
    endpoint_secret = 'whsec_...'
    
    # Using Sinatra
    post '/webhook' do
      payload = request.body.read
      event = nil
    
      # Verify webhook signature and extract the event
      # See https://stripe.com/docs/webhooks/signatures for more information.
      sig_header = request.env['HTTP_STRIPE_SIGNATURE']
      begin
        event = Stripe::Webhook.construct_event(
          payload, sig_header, endpoint_secret
        )
      rescue JSON::ParserError => e
        # Invalid payload
        status 400
        return
      rescue Stripe::SignatureVerificationError => e
        # Invalid signature
        status 400
        return
      end
    
      # Handle the checkout.session.completed event
      if event['type'] == 'checkout.session.completed'
        session = event['data']['object']
    
        # Fulfill the purchase...
        handle_checkout_session(session)
      end
    
      status 200
    end
    
    # Set your secret key: remember to change this 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...
        handle_checkout_session(session)
    
      return HttpResponse(status=200)
    
    // Set your secret key: remember to change this 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\Error\SignatureVerification $e) {
      // Invalid signature
      http_response_code(400);
      exit();
    }
    
    // Handle the checkout.session.completed event
    if ($event->type == 'checkout.session.completed') {
      $session = $event->data->object;
    
      // Fulfill the purchase...
      handle_checkout_session($session);
    }
    
    http_response_code(200);
    
    // Set your secret key: remember to change this 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 "";
      }
    
      // Handle the checkout.session.completed event
      if ("checkout.session.completed".equals(event.getType())) {
        Session session = (Session) event.getDataObjectDeserializer().getObject();
    
        // Fulfill the purchase...
        handleCheckoutSession(session);
      }
    
      response.status(200);
      return "";
    }
    
    // Set your secret key: remember to change this 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');
    
    // Match the raw body to content type application/json
    app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => {
      const sig = request.headers['stripe-signature'];
    
      let event;
    
      try {
        event = stripe.webhooks.constructEvent(request.body, 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...
        handleCheckoutSession(session);
      }
    
      // Return a response to acknowledge receipt of the event
      response.json({received: true});
    });
    
    app.listen(8000, () => console.log('Running on port 8000'));
    
    // Set your secret key: remember to change this 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 & 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...
            handleCheckoutSession(session)
        }
    
        w.WriteHeader(http.StatusOK)
    })
    
    // Set your secret key: remember to change this 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 void Index()
            {
                var json = new StreamReader(HttpContext.Request.Body).ReadToEnd();
    
                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...
                        HandleCheckoutSession(session);
                    }
    
                }
                catch (StripeException e)
                {
                    return BadRequest();
                }
            }
        }
    }
    

    You can obtain information about the customer, payment, or subscription by retrieving the Customer, PaymentIntent, or Subscription objects referenced by the customer, payment_intent, and subscription properties in the webhook payload.

    Fulfilling purchases by polling for events

    You can periodically check for new payments made via Checkout by polling the /v1/events endpoint for new checkout.session.completed events. You can then execute any code that you need to fulfill the customer’s purchase when you detect a new checkout.session.completed event.

    Stripe generates the checkout.session.completed event when a Checkout payment is successful. The event payload includes the Checkout Session object, which contains information about the Customer, PaymentIntent, or Subscription, and optionally the client_reference_id if you provided it when calling redirectToCheckout on the client.

    The following example demonstrates how to retrieve all checkout.session.completed events in the last 24 hours:

    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
    
    events = Stripe::Event.list(type: 'checkout.session.completed', created: {
      # Check for events created in the last 24 hours.
      gte: Time.now.utc.to_i - 24 * 60 * 60,
    })
    
    events.auto_paging_each do |event|
      session = event['data']['object']
    
      # Fulfill the purchase...
      handle_checkout_session(session)
    end
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
    
    events = stripe.Event.list(type = 'checkout.session.completed', created = {
      # Check for events created in the last 24 hours.
      'gte': int(time.time() - 24 * 60 * 60),
    })
    
    for event in events.auto_paging_iter():
      session = event['data']['object']
    
      # Fulfill the purchase...
      handle_checkout_session(session)
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
    
    $events = \Stripe\Event::all([
      'type' => 'checkout.session.completed',
      'created' => [
        // Check for events created in the last 24 hours.
        'gte' => time() - 24 * 60 * 60,
      ],
    ]);
    
    foreach ($events->autoPagingIterator() as $event) {
      $session = $event->data->object;
    
      // Fulfill the purchase...
      handle_checkout_session($session);
    }
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    HashMap<String, Object> params = new HashMap<>();
    params.put("type", "checkout.session.completed");
    HashMap<String, Object> createdParams = new HashMap<>();
    // Check for events created in the last 24 hours.
    createdParams.put("gte", (int) ((System.currentTimeMillis() / 1000) - 24 * 60 * 60));
    Iterable<Event> events = Event.list(params).autoPagingIterable();
    
    for (Event event : events) {
      EventDataObjectDeserializer deserializer = event.getDataObjectDeserializer();
      if (deserializer.getObject().isPresent()) {
        Session session = (Session) deserializer.getObject().get();
    
        // Fulfill the purchase...
        handleCheckoutSession(session);
      }
    }
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
    
    (async () => {
      const events = stripe.events.list({
        type: 'checkout.session.completed',
        created: {
          // Check for events created in the last 24 hours.
          gte: Date.now() - 24 * 60 * 60,
        },
      });
    
      // For older versions of Node, see https://github.com/stripe/stripe-node/#auto-pagination
      for await (const event of events) {
        const session = event.data.object;
    
        // Fulfill the purchase...
        handleCheckoutSession(session);
      }
    })();
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"
    
    params := &stripe.EventListParams{
      Type: stripe.String("checkout.session.completed),
      CreatedRange: &stripe.RangeQueryParams{
        // Check for events created in the last 24 hours.
        GreaterThan: stripe.Int64(time.Now().Unix() - 24 * 60 * 60),
      },
    }
    i := event.List(params)
    
    for i.Next() {
      event := i.Event()
      var session stripe.checkout.Session
      err := json.Unmarshal(event.Data.Raw, &session)
      if err == nil {
        // Fulfill the purchase...
        handleCheckoutSession(session)
      }
    }
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    var service = new EventService();
    var options = new EventListOptions {
      Type = "checkout.session.created",
      Created = new DateRangeOptions{
        // Check for events created in the last 24 hours.
        GreaterThan = DateTime.Now.Subtract(new TimeSpan(24, 0, 0)),
      }
    }
    
    foreach (var stripeEvent in service.ListAutoPaging(options)) {
      var session = stripeEvent.Data.Object as Session;
    
      // Fulfill the purchase...
      handleCheckoutSession(session);
    }
    

    You can obtain information about the customer, payment, or subscription by retrieving the Customer, PaymentIntent, or Subscription objects referenced by the customer, payment_intent, and subscription properties in the webhook payload.

    Was this page helpful?

    Thank you for helping improve Stripe's documentation. If you need help or have any questions, please consider contacting support.

    Questions?

    We're always happy to help with code or other questions you might have. Search our documentation, contact support, or connect with our sales team. You can also chat live with other developers in #stripe on freenode.

    On this page