PaymentIntents Usage Guide Beta

    Learn how to use all of the features of PaymentIntents.

    A Stripe integration built with PaymentIntents typically follows these four steps:

    1. Create a new PaymentIntent on your server when the customer begins the checkout process.
    2. Make the PaymentIntent’s client secret available on the the client side.
    3. Use Stripe.js on the client side to collect payment information, initiate the charge, and handle required source actions.
    4. Use webhooks to monitor and handle the success or failure of the charge as needed.

    When using PaymentIntents, charge creation is initiated on the client side and takes place on Stripe’s servers. In a sense, the process is asynchronous–you don’t immediately receive a response on your server that indicates success or failure. As such, you should rely on webhooks to determine when you should fulfill the goods and services purchased by the customer.

    The approach required by PaymentIntents is an inversion of the more traditional Stripe model, where you use Stripe.js to tokenize the customer’s payment information on the client side and then send it to your server to create the charge. For more details about how PaymentIntents differ from direct charge creation, please refer to the migration guide.

    For step-by-step instructions that demonstrate how to build a complete Stripe integration with PaymentIntents, refer to the payments quickstart.

    Creating a PaymentIntent

    Each PaymentIntent typically correlates with a single “cart” or customer session in your application. On the server side, applications generally create a PaymentIntent when the customer begins their checkout process.

    The PaymentIntent encapsulates certain details about the transaction, such as the supported payment methods, the amount to collect, and the desired currency. You can specify these options when you create the PaymentIntent:

    curl https://api.stripe.com/v1/payment_intents \
       -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
       -d amount=1099 \
       -d currency=usd \
       -d allowed_source_types[]=card
    
    # 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"
    
    intent = Stripe::PaymentIntent.create({
      amount: 1099,
      currency: 'usd',
      allowed_source_types: ['card']
    })
    
    # 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"
    
    stripe.PaymentIntent.create(
      amount=1099,
      currency='usd',
      allowed_source_types=['card']
    )
    
    // 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");
    
    \Stripe\PaymentIntent::create([
      "amount" => 1099,
      "currency" => "usd",
      "allowed_source_types" => ["card"],
    ]);
    
    // 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";
    
    Map<String, Object> paymentintentParams = new HashMap<String, Object>();
    paymentintentParams.put("amount", 1099);
    paymentintentParams.put("currency", "usd");
    ArrayList allowed_source_types = new ArrayList();
    allowed_source_types.add("card");
    paymentintentParams.put("allowed_source_types", allowed_source_types);
    
    PaymentIntent.create(paymentintentParams);
    
    // 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
    var stripe = require("stripe")("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    (async () => {
      const paymentIntent = await stripe.paymentIntents.create({
        amount: 1099,
        currency: 'usd',
        allowed_source_types: ['card'],
      });
    })();
    
    // 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.PaymentIntentParams{
      Amount: stripe.Int64(1099),
      Currency: stripe.String(string(stripe.CurrencyUSD)),
      AllowedSourceTypes: []*string{
        stripe.String("card"),
      },
    }
    paymentIntent.New(params)
    
    // 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.SetApiKey("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    var paymentIntents = new PaymentIntentService();
    var createOptions = new PaymentIntentCreateOptions {
      Amount = 1099,
      Currency = "usd",
      AllowedSourceTypes = new List<string> { "card" }
    };
    paymentIntents.Create(createOptions);
    

    If the checkout process is interrupted and resumes later, you should attempt to reuse the same PaymentIntent instead of creating a new one. Each PaymentIntent has a unique ID that you can use to retrieve it if you need it again. In your application’s data model, you can store the PaymentIntent’s ID on the customer’s shopping cart or session in order to facilitate retrieval.

    When reusing an existing PaymentIntent, you can update its amount if the desired value has changed since the PaymentIntent was created. For example, if your customer backs out of the checkout process and adds new items to their cart, you may need to update the amount accordingly when they start the checkout process again.

    You should also provide an idempotency key–typically based on the ID that you associate with the cart or customer session in your application–when creating the PaymentIntent to avoid erroneously creating duplicate PaymentIntents for the same purchase.

    Passing the client secret to the client side

    The PaymentIntent contains a client secret, a key that is unique to the individual PaymentIntent. On the client side of your application, the client secret is used as a parameter when invoking Stripe.js functions (such as stripe.handleCardPayment) to complete the payment.

    To use the client secret, you must obtain it from the PaymentIntent on your server and pass it the client side. There are different approaches that you can use to get the client secret to the client side–choose the approach that best suits the architecture of your application.

    Server-side rendering

    If your application uses server-side rendering, you may wish to use your template framework to embed the client secret in the HTML output of your checkout page during rendering. You can embed it in a data attribute or hidden HTML element and then extract it with JavaScript in order to use it to complete payment.

    <input id="card-name" type="text">
    <!-- placeholder for Elements -->
    <div id="card-element"></div>
    <button id="card-button" data-secret="<%= @intent.client_secret %>">Submit Payment</button>
    get '/checkout' do
      @intent = # ... Fetch or create the PaymentIntent
      erb :checkout
    end
    <input id="card-name" type="text">
    <!-- placeholder for Elements -->
    <div id="card-element"></div>
    <button id="card-button" data-secret="{{ client_secret }}">
      Submit Payment
    </button>
    @app.route('/checkout')
    def checkout():
      intent = # ... Fetch or create the PaymentIntent
      return render_template('checkout.html', client_secret=intent.client_secret)
    <?php
      $intent = # ... Fetch or create the PaymentIntent;
    ?>
    ...
    <input id="card-name" type="text">
    <!-- placeholder for Elements -->
    <div id="card-element"></div>
    <button id="card-button" data-secret="<?= $intent->client_secret ?>">
      Submit Payment
    </button>
    ...
    <input id="card-name" type="text">
    <!-- placeholder for Elements -->
    <div id="card-element"></div>
    <button id="card-button" data-secret="{{ client_secret }}">
      Submit Payment
    </button>
    import java.util.HashMap;
    import java.util.Map;
    
    import com.stripe.model.PaymentIntent;
    
    import spark.ModelAndView;
    
    import static spark.Spark.get;
    
    public class StripeJavaQuickStart {
        public static void main(String[] args) {
            get("/checkout", (request, response) -> {
              PaymentIntent intent = // ... Fetch or create the PaymentIntent
    
              Map map = new HashMap();
              map.put("client_secret", intent.clientSecret);
    
              return new ModelAndView(map, "checkout.hbs");
            }, new HandlebarsTemplateEngine());
        }
    }
    <input id="card-name" type="text">
    <!-- placeholder for Elements -->
    <div id="card-element"></div>
    <button id="card-button" data-secret="{{ client_secret }}">
      Submit Payment
    </button>
    const express = require('express');
    const expressHandlebars = require('express-handlebars');
    const app = express();
    
    app.engine('.hbs', expressHandlebars(extname: '.hbs'));
    app.set('view engine', '.hbs');
    app.set('views', './views');
    
    app.get('/checkout', async (req, res) => {
      const intent = // ... Fetch or create the PaymentIntent
      res.render('checkout', { client_secret: intent.clientSecret });
    });
    
    app.listen(3000, () => {
      console.log('Running on port 3000')
    });
    <input id="card-name" type="text">
    <!-- placeholder for Elements -->
    <div id="card-element"></div>
    <button id="card-button" data-secret="{{ .ClientSecret }}">
      Submit Payment
    </button>
    package main
    
    import (
      "html/template"
      "net/http"
    
      stripe "github.com/stripe/stripe-go"
    )
    
    type CheckoutData struct {
      ClientSecret string
    }
    
    func main() {
      checkoutTmpl := template.Must(template.ParseFiles("views/checkout.html"))
    
      http.HandleFunc("/checkout", func(w http.ResponseWriter, r *http.Request) {
        intent := // ... Fetch or create the PaymentIntent
        data := CheckoutData{
          ClientSecret: intent.ClientSecret,
        }
        checkoutTmpl.Execute(w, data)
      })
    
      http.ListenAndServe(":3000", nil)
    }
    <input id="card-name" type="text">
    <!-- placeholder for Elements -->
    <div id="card-element"></div>
    <button id="card-button" data-secret="@ViewData["ClientSecret"]">
      Submit Payment
    </button>
    using System;
    using Microsoft.AspNetCore.Mvc;
    using Stripe;
    
    namespace StripeExampleApi.Controllers
    {
        [Route("/[controller]")]
        public class CheckoutController : Controller
        {
            public IActionResult Index()
            {
              var intent = // ... Fetch or create the PaymentIntent
              ViewData["ClientSecret"] = intent.ClientSecret;
              return View();
            }
        }
    }

    Single-page application

    Alternately, you can retrieve the client secret from an endpoint on your server using the browser’s fetch function on the client side. This approach is generally most suitable when your client side is a single-page application, particularly one built with a modern frontend framework such as React or Vue. This example shows how to create the server endpoint that serves the client secret:

    get '/secret' do
      intent = # ... Create or retrieve the PaymentIntent
      {client_secret: intent.client_secret}.to_json
    end
    from flask import jsonify
    
    @app.route('/secret')
    def secret():
      intent = # ... Create or retrieve the PaymentIntent
      return jsonify(client_secret=intent.client_secret)
    <?php
      $intent = # ... Create or retrieve the PaymentIntent
      echo json_encode(array('client_secret' => $intent->client_secret));
    ?>
    import java.util.HashMap;
    import java.util.Map;
    
    import com.stripe.model.PaymentIntent;
    
    import com.google.gson.Gson;
    
    import static spark.Spark.get;
    
    public class StripeJavaQuickStart {
        public static void main(String[] args) {
          Gson gson = new Gson();
    
          get("/secret", (request, response) -> {
            PaymentIntent intent = // ... Fetch or create the PaymentIntent
    
            Map<String, String> map = new HashMap();
            map.put("client_secret", intent.clientSecret);
    
            return map;
          }, gson::toJson);
        }
    }
    const express = require('express');
    const app = express();
    
    app.get("/secret", function(req, res) {
      const intent = //... Create or retrieve the PaymentIntent
    
      res.json({client_secret: intent.client_secret});
    });
    
    app.listen(3000, () => {
      console.log('Running on port 3000')
    });
    package main
    
    import (
      "encoding/json"
      "net/http"
    
      stripe "github.com/stripe/stripe-go"
    )
    
    type CheckoutData struct {
      ClientSecret string `json:"client_secret"`
    }
    
    func main() {
      http.HandleFunc("/secret", func(w http.ResponseWriter, r *http.Request) {
        intent := // ... Fetch or create the PaymentIntent
        data := CheckoutData{
          ClientSecret: intent.ClientSecret,
        }
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusOK)
        json.NewEncoder(w).Encode(data)
      })
    
      http.ListenAndServe(":3000", nil)
    }
    using System;
    using Microsoft.AspNetCore.Mvc;
    using Stripe;
    
    namespace StripeExampleApi.Controllers
    {
        [Route("secret")]
        [ApiController]
        public class CheckoutApiController : Controller
        {
            [HttpGet]
            public ActionResult Get()
            {
                var intent = // ... Fetch or create the PaymentIntent
                return Json(new {client_secret = intent.ClientSecret});
            }
        }
    }

    This example demonstrates how to fetch the client secret with JavaScript on the client side:

    var response = fetch('/secret').then(function(response) {
      return response.json();
    }).then(function(responseJson) {
      var clientSecret = responseJson.client_secret;
      // Call stripe.handleCardPayment() with the client secret.
    });
    (async () => {
      const response = await fetch('/secret');
      const {client_secret: clientSecret} = await response.json();
      // Call stripe.handleCardPayment() with the client secret.
    })();

    Completing payment on the client side

    PaymentIntents integrate with Stripe.js, using Elements to securely collect payment information on the client side. To complete a purchase, retrieve the PaymentIntent’s client secret and pass it to the stripe.handleCardPayment function in Stripe.js:

    stripe.handleCardPayment(clientSecret, cardElement).then(function(result) {
      if (result.error) {
        // Display error.message in your UI.
      } else {
        // The payment has succeeded. Display a success message.
      }
    });
    (async () => {
      const {paymentIntent, error} = await stripe.handleCardPayment(clientSecret, cardElement);
      if (error) {
        // Display error.message in your UI.
      } else {
        // The payment has succeeded. Display a success message.
      }
    })();

    When you call the function, it collects the customer’s payment information from the desired element and securely transmits it to Stripe to complete the transaction. If it completes successfully, the result object’s error property is null.

    Some payment methods require additional steps, such as authentication. When you use one of the handle functions in Stripe.js to complete the payment, it automatically ushers the customer through the necessary source actions. It also confirms the PaymentIntent for you.

    Handling source actions

    If you choose to handle source actions yourself instead of using a Stripe.js function like handleCardPayment, that handles them for you, inspect the PaymentIntent’s next_source_action property to determine what steps are necessary. The type of possible source actions can differ between various payment methods. The following is a list of source action types and the payment methods that may require them:

    Type Payment Methods What it means How to handle
    authorize_with_url Cards with 3DS The customer needs to be sent to the provided URL to authenticate the payment. top.location = sourceAction.authorize_with_url.url

    You can refer to the documentation for individual payment methods for more details about how to handle their required source actions.

    Monitoring a PaymentIntent with webhooks

    Stripe can send webhook events to your server to notify you when the status of a PaymentIntent changes. This is useful for purposes like determining when to fulfill the goods and services purchased by the customer. Your customers can leave your website while the payment is still processing, so we strongly recommend using webhooks to monitor the payment_intent.succeeded event instead of attempting to initiate fulfillment on the client side.

    To handle a webhook event, create a route on your server and configure a corresponding webhook endpoint in the Dashboard. Stripe sends the payment_intent.succeeded event when payment is successful and the payment_intent.payment_failed event when payment isn’t successful. The webhook payload includes the PaymentIntent object. The following example shows how to handle both events:

    require 'sinatra'
    require 'stripe'
    
    post '/webhook' do
      payload = request.body.read
      sig_header = request.env['HTTP_STRIPE_SIGNATURE']
      event = nil
    
      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
    
      case event['type']
      when 'payment_intent.succeeded'
        intent = event['data']['object']
        puts "Succeeded:", intent['id']
        # Fulfill the customer's purchase
      when 'payment_intent.payment_failed'
        intent = event['data']['object']
        error_message = intent['last_payment_error'] && intent['last_payment_error']['message']
        puts "Failed:", intent['id'], error_message
        # Notify the customer that payment failed
      end
    
      status 200
    end
    
    # You can find your endpoint's secret in your webhook settings
    endpoint_secret = 'whsec_...'
    
    @app.route("/webhook", methods=['POST'])
    def webhook():
      payload = request.get_data()
      sig_header = request.headers.get('STRIPE_SIGNATURE')
      event = None
    
      try:
        event = stripe.Webhook.construct_event(
          payload, sig_header, endpoint_secret
        )
      except ValueError as e:
        # invalid payload
        return "Invalid payload", 400
      except stripe.error.SignatureVerificationError as e:
        # invalid signature
        return "Invalid signature", 400
    
      event_dict = event.to_dict()
      if event_dict['type'] == "payment_intent.succeeded":
        intent = event_dict['data']['object']
        print "Suceeded: ", intent['id']
        # Fulfill the customer's purchase
      elif event_dict['type'] == "payment_intent.payment_failed":
        intent = event_dict['data']['object']
        error_message = intent['last_payment_error']['message'] if intent.get('last_payment_error') else None
        print "Failed: ", intent['id'], error_message
        # Notify the customer that payment failed
    
      return "OK", 200
    
    // 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); // PHP 5.4 or greater
      exit();
    } catch(\\Stripe\\Error\\SignatureVerification $e) {
      // Invalid signature
      http_response_code(400); // PHP 5.4 or greater
      exit();
    }
    
    if ($event->type == "payment_intent.succeeded") {
      $intent = $event->data->object;
      printf("Succeeded: %s", $intent->id);
      http_response_code(200);
      exit();
    } elseif ($event->type == "payment_intent.payment_failed") {
      $intent = $event->data->object;
      $error_message = $intent->last_payment_error ? $intent->last_payment_error->message : "";
      printf("Failed: %s, %s", $intent->id, $error_message);
      http_response_code(200);
      exit();
    }
    
    import com.stripe.model.PaymentIntent;
    import com.stripe.model.Event;
    import com.stripe.net.Webhook;
    
    import static spark.Spark.post;
    
    public class StripeJavaQuickStart {
        public static void main(String[] args) {
          String endpointSecret = "whsec_...";
    
          post("/webhook", (request, 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 "Invalid payload";
            } catch (SignatureVerificationException e) {
              // Invalid signature
              response.status(400);
              return "Invalid signature";
            }
    
            PaymentIntent intent = null;
            switch(event.getType()) {
              case "payment_intent.succeeded":
                intent = (PaymentIntent) event.getData().getObject();
                System.out.println("Succeeded: " + intent.getId());
                break;
                // Fulfil the customer's purchase
    
              case "payment_intent.payment_failed":
                intent = (PaymentIntent) event.getData().getObject();
                System.out.println("Failed: " + intent.getId());
                break;
                // Notify the customer that payment failed
    
              default:
                // Handle other event types
                break;
            }
    
            response.status(200);
            return "OK";
          });
        }
    }
    
    app.use(require('body-parser').text({type: '*/*'}));
    
    const endpointSecret = "whsec_...";
    
    app.post('/webhook', function(request, response) {
      const sig = request.headers["stripe-signature"];
      const body = request.body;
    
      let event = null;
    
      try {
        event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
      }
      catch (err) {
        // invalid signature
        response.status(400).end();
        return;
      }
    
      let intent = null;
      switch (event['type']) {
        case 'payment_intent.succeeded':
          intent = event.data.object;
          console.log("Succeeded:", intent.id);
          break;
        case 'payment_intent.payment_failed':
          intent = event.data.object;
          const message = intent.last_payment_error && intent.last_payment_error.message;
          console.log("Failed:", intent.id, message);
          break;
      }
    
      response.sendStatus(200);
    });
    
    package main
    
    import (
      "encoding/json"
      "net/http"
    
      stripe "github.com/stripe/stripe-go"
      webhook "github.com/stripe/stripe-go/webhook"
    )
    
    func main() {
      http.HandleFunc("/webhook", func(w http.ResponseWriter, req *http.Request) {
        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
        }
    
        if event.Type == "payment_intent.succeeded" {
          var paymentIntent stripe.PaymentIntent
          err := json.Unmarshal(event.Data.Raw, &paymentIntent)
          if err != nil {
            fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err)
            w.WriteHeader(http.StatusBadRequest)
            return
          }
          fmt.Sprintf("Succeeded: %v\n", paymentIntent)
          // Fulfil the customer's purchase
    
        } else if event.Type == "payment_intent.payment_failed" {
          var paymentIntent stripe.PaymentIntent
            err := json.Unmarshal(event.Data.Raw, &paymentIntent)
            if err != nil {
              fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err)
              w.WriteHeader(http.StatusBadRequest)
              return
            }
            fmt.Sprintf("Failed: %v, %v\n", paymentIntent, paymentIntent.LastPaymentError)
            // Notify the customer that payment failed
        }
        w.WriteHeader(http.StatusOK)
      })
    
      http.ListenAndServe(":3000", nil)
    }
    using System;
    using System.IO;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Logging;
    using Stripe;
    
    namespace StripeExampleApi.Controllers
    {
        [Route("webhook")]
        [ApiController]
        public class WebhookController : Controller
        {
            private readonly ILogger<WebhookController> _logger;
    
            public WebhookController(ILogger<WebhookController> logger)
            {
                _logger = logger;
            }
    
            const string secret = "whsec_...";
    
            [HttpPost]
            public ActionResult Post()
            {
                try {
                    var json = new StreamReader(HttpContext.Request.Body).ReadToEnd();
                    var stripeEvent = EventUtility.ConstructEvent(json,
                        Request.Headers["Stripe-Signature"], secret);
    
                    PaymentIntent intent = null;
    
                    switch (stripeEvent.Type)
                    {
                      case "payment_intent.succeeded":
                        intent = (PaymentIntent)stripeEvent.Data.Object;
                        _logger.LogInformation("Succeeded: {ID}", intent.Id);
    
                        // Fulfil the customer's purchase
    
                        break;
                      case "payment_intent.payment_failed":
                        intent = (PaymentIntent)stripeEvent.Data.Object;
                        _logger.LogInformation("Failure: {ID}", intent.Id);
    
                        // Notify the customer that payment failed
    
                        break;
                      default:
                        // Handle other event types
    
                        break;
                    }
                    return new EmptyResult();
    
                }
                catch (StripeException e) {
                    // Invalid Signature
                    return BadRequest();
                }
            }
        }
    }
    

    When payment is unsuccessful, you can find more details by inspecting the PaymentIntent’s last_payment_error property. You can notify the customer that their payment didn’t complete and encourage them to try again with a different payment method. When creating a new charge for the user to help them complete their purchase, you can reuse the existing PaymentIntent because the value of its status property has not transitioned to succeeded yet.

    The following list describes how to handle webhook events and responses returned by stripe.handleCardPayment:

    • On your checkout page, when stripe.handleCardPayment resolves with a PaymentIntent:
      • What happened: your customer completed a payment while still on your checkout page.
      • Expected integration: while your customer is still on your page, tell them that their payment has succeeded. Note that it is possible for your customer to pay successfully, but leave before this promise resolves.
    • On your checkout page, when stripe.handleCardPayment resolves with an error:
      • What happened: your customer’s payment failed while still on your checkout page.
      • Expected integration: while your customer is still on your page, display the error and tell them to provide another card and try again.
    • Your webhook endpoint receives a payment_intent.succeeded event:
      • What happened: you received a successful payment.
      • Expected integration: if you wish to fulfill goods or services once payment succeeds, you should perform those actions when you receive this event. This is the only way to guarantee that your integration is aware that a payment has completed successfully for asynchronous payment flows such as 3DS.
    • Your webhook endpoint receives a payment_intent.requires_capture event:
      • What happened: a payment has been authorized and is ready to be captured.
      • Expected integration: if you use separate auth and capture, you’ll receive this event prior to payment_intent.succeeded . If you wish to capture the funds for a payment once the payment has been authorized, you should do that here.
    • Your webhook endpoint receives a payment_intent.payment_failed event:
      • What happened: a payment was declined by the card network or otherwise expired.
      • Expected integration: optionally reach out to your customer via email or push notification to prompt them to provide another payment method to complete their payment.

    Canceling a PaymentIntent

    You can cancel a PaymentIntent if you no longer intend to use it to collect payment from the customer:

    curl https://api.stripe.com/v1/payment_intents/pi_Aabcxyz01aDfoo/cancel \
       -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
       -X POST
    
    # 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"
    
    intent = Stripe::PaymentIntent.retrieve("pi_Aabcxyz01aDfoo")
    intent.cancel
    
    # 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"
    
    intent = stripe.PaymentIntent.retrieve("pi_Aabcxyz01aDfoo")
    intent.cancel()
    
    // 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");
    
    $intent = StripePaymentIntent::retrieve("pi_Aabcxyz01aDfoo");
    $intent->cancel();
    
    // 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";
    
    PaymentIntent intent = PaymentIntent.retrieve("pi_Aabcxyz01aDfoo");
    intent.cancel();
    
    // 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
    var stripe = require("stripe")("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    (async () => {
      await stripe.paymentIntents.cancel("pi_Aabcxyz01aDfoo")
    })();
    
    // 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"
    
    intent, err := paymentintent.Cancel("pi_Aabcxyz01aDfoo", nil)
    
    // 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.SetApiKey("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    var paymentIntents = new PaymentIntentService();
    var cancelOptions = new PaymentIntentCancelOptions {};
    paymentIntents.Cancel("pi_Aabcxyz01aDfoo", cancelOptions);
    

    A PaymentIntent can only be canceled when it has one of the following statuses: requires_source, requires_capture, requires_confirmation, or requires_source_action–a PaymentIntent can’t be canceled while it is actively processing or once it has succeeded.

    When a PaymentIntent is canceled, you can no longer use it to perform additional charges. Any operations that your application attempts to perform on a canceled PaymentIntent will fail with an error.

    Separate authorization and capture

    PaymentIntents optionally support separating the authorization and capture steps. To indicate that you want separate authorization and capture, you must set the value of capture_method option to “manual” when creating the PaymentIntent.

    When a customer completes the payment process on a PaymentIntent with manual capture, it triggers the payment_intent.amount_capturable_updated event. You can inspect the PaymentIntent’s amount_capturable property to see the total amount that can be captured from the PaymentIntent.

    To capture the authorized funds, call the PaymentIntent’s capture function and specify the desired amount. If you do not provide an explicit value with the amount_to_capture option, it defaults to the full amount that can be captured from the PaymentIntent.

    The following example demonstrates how to capture funds from an authorized payment:

    curl https://api.stripe.com/v1/payment_intents/pi_Aabcxyz01aDfoo/capture \
       -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
       -d amount_to_capture=750
    
    # 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"
    
    intent = Stripe::PaymentIntent.retrieve("pi_Aabcxyz01aDfoo")
    intent.capture(amount_to_capture: 750)
    
    # 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"
    
    intent = stripe.PaymentIntent.retrieve("pi_Aabcxyz01aDfoo")
    intent.capture({amount_to_capture: 750})
    
    // 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");
    
    $intent = StripePaymentIntent::retrieve("pi_Aabcxyz01aDfoo");
    $intent->capture(["amount" => 750]);
    
    // 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";
    
    PaymentIntent intent = PaymentIntent.retrieve("pi_Aabcxyz01aDfoo");
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("amount_to_capture", 750);
    intent.capture(params);
    
    // 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
    var stripe = require("stripe")("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    (async () => {
      await stripe.paymentIntents.capture("pi_Aabcxyz01aDfoo", {
        amount: 750
      })
    })();
    
    // 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.PaymentIntentCaptureParams{
      AmountToCapture: stripe.Int64(750),
    }
    intent, err := paymentintent.Capture("pi_Aabcxyz01aDfoo", params)
    
    // 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.SetApiKey("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    var paymentIntents = new PaymentIntentService();
    var captureOptions = new PaymentIntentCaptureOptions {
      AmountToCapture = 750
    };
    paymentIntents.Capture("pi_Aabcxyz01aDfoo", captureOptions);
    

    Once authorized, the amount is held on the customer’s card for up to seven days. If the funds are not captured within this time, the authorization is canceled and the funds are released.

    Refunds

    When you use a PaymentIntent to collect payment from a customer, Stripe creates a charge behind the scenes. To refund the customer’s payment after the PaymentIntent has succeeded, create a refund on the payment object. You can also optionally refund part of their payment by specifying an amount. You can perform refunds with the API or through the Dashboard.

    Note that this does not apply if you are using separate authorization and capture, and you wish to refund a PaymentIntent that has a status of requires_capture. In this case, the charge attached to the PaymentIntent is still uncaptured and cannot be refunded directly. You should cancel the PaymentIntent instead.

    To find the charge object, you can inspect the PaymentIntent’s charges property, which has an array of all the charges created by the PaymentIntent. The first charge in the array is the one that completed successfully.

    The following example shows to refund a PaymentIntent’s charge:

    curl https://api.stripe.com/v1/payment_intents/pi_Aabcxyz01aDfoo \
     -u sk_test_4eC39HqLyjWDarjtT1zdp7dc:
    # Retrieve the ID of the first charge in PaymentIntent
    # charges array returned from the previous request and use
    # it to create a refund.
    curl https://api.stripe.com/v1/refunds \
     -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
     -d charge="{CHARGE_ID}"
    # 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"
    
    intent = Stripe::PaymentIntent.retrieve("pi_Aabcxyz01aDfoo")
    intent['charges']['data'].first.refund
    
    # 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"
    
    intent = stripe.PaymentIntent.retrieve("pi_Aabcxyz01aDfoo")
    intent['charges']['data'][0].refund()
    
    // 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");
    
    $intent = StripePaymentIntent::retrieve("pi_Aabcxyz01aDfoo");
    $intent->charges->data[0].refund();
    
    // 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";
    
    PaymentIntent intent = PaymentIntent.retrieve("pi_Aabcxyz01aDfoo");
    intent.charges.getData().get(0).refund(new Map());
    
    // 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
    var stripe = require("stripe")("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    (async () => {
      const intent = await stripe.paymentIntents.retrieve("pi_Aabcxyz01aDfoo");
      const refund = await stripe.refunds.create({
        charge: intent.charges.data[0].id
      });
    })();
    
    // 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"
    
    intent, err := paymentintent.Get("pi_Aabcxyz01aDfoo", nil)
    refundParams := &stripe.RefundParams{
      Charge: stripe.String(intent.Charges.Data[0].ID),
    }
    r, err := refund.New(refundParams)
    
    // 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.SetApiKey("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    var paymentIntents = new PaymentIntentService();
    var refunds = new RefundService();
    var intent = paymentIntents.Get("pi_Aabcxyz01aDfoo");
    var refundOptions = new RefundCreateOptions {
      ChargeId = intent.Charges.Data[0].Id
    };
    var refund = refunds.Create(refundOptions);
    

    Sources and customers

    Collected payment information can be saved and associated with a customer for later reuse. When you create a PaymentIntent, set the value of save_source_to_customer to true and specify the desired customer and source. The following example demonstrates how to create a PaymentIntent that saves the customer’s payment information:

    curl https://api.stripe.com/v1/payment_intents \
       -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
       -d amount=1099 \
       -d currency=usd \
       -d allowed_source_types[]=card \
       -d save_source_to_customer=true \
       -d customer=cus_DyA7gideDWXNOX \
       -d source=src_1DWaarHgsMRXo4MtXrvYGoMZ
    
    # 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"
    
    intent = Stripe::PaymentIntent.create({
      amount: 1099,
      currency: 'usd',
      allowed_source_types: ['card'],
      save_source_to_customer: true,
      customer: 'cus_DyA7gideDWXNOX',
      source: 'src_1DWaarHgsMRXo4MtXrvYGoMZ',
    })
    
    # 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"
    
    stripe.PaymentIntent.create(
      amount=1099,
      currency='usd',
      allowed_source_types=['card'],
      save_source_to_customer=true,
      customer='cus_DyA7gideDWXNOX',
      source='src_1DWaarHgsMRXo4MtXrvYGoMZ'
    )
    
    // 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");
    
    \Stripe\PaymentIntent::create([
      "amount" => 1099,
      "currency" => "usd",
      "allowed_source_types" => ["card"],
      "save_source_to_customer" => true,
      "customer" => "cus_DyA7gideDWXNOX",
      "source" => "src_1DWaarHgsMRXo4MtXrvYGoMZ",
    ]);
    
    // 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";
    
    Map<String, Object> paymentintentParams = new HashMap<String, Object>();
    paymentintentParams.put("amount", 1099);
    paymentintentParams.put("currency", "usd");
    ArrayList allowed_source_types = new ArrayList();
    allowed_source_types.add("card");
    paymentintentParams.put("allowed_source_types", allowed_source_types);
    paymentintentsParams.put("save_source_to_customer", true);
    paymentintentsParams.put("customer", "cus_DyA7gideDWXNOX");
    paymentintentsParams.put("source", "src_1DWaarHgsMRXo4MtXrvYGoMZ");
    
    PaymentIntent.create(paymentintentParams);
    
    // 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
    var stripe = require("stripe")("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    (async () => {
      const paymentIntent = await stripe.paymentIntents.create({
        amount: 1099,
        currency: 'usd',
        allowed_source_types: ['card'],
        save_source_to_customer: true,
        customer: "cus_DyA7gideDWXNOX",
        source: "src_1DWaarHgsMRXo4MtXrvYGoMZ",
      });
    })();
    
    // 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.PaymentIntentParams{
      Amount: stripe.Int64(1099),
      Currency: stripe.String(string(stripe.CurrencyUSD)),
      AllowedSourceTypes: []*string{
        stripe.String("card"),
      },
      SaveSourceToCustomer: stripe.Boolean(true),
      Customer: stripe.String("cus_DyA7gideDWXNOX"),
      Source: stripe.String("src_1DWaarHgsMRXo4MtXrvYGoMZ")
    }
    paymentIntent.New(params)
    
    // 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.SetApiKey("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    var paymentIntents = new PaymentIntentService();
    var createOptions = new PaymentIntentCreateOptions {
      Amount = 1099,
      Currency = "usd",
      AllowedSourceTypes = new List<string> { "card" },
      SaveSourceToCustomer = true,
      Customer = "cus_DyA7gideDWXNOX",
      Source = "src_1DWaarHgsMRXo4MtXrvYGoMZ"
    };
    paymentIntents.Create(createOptions);
    

    The client-side handleCardPayment function also has a save_source_to_customer parameter, which you can optionally use to save the payment information provided by the customer. This is useful in cases where you would like to give the customer the option to choose whether their payment information is saved during the checkout process. This parameter only applies to the currently provided payment information. If the customer later provides different payment information, save_source_to_customer must be specified again to save the new information.

    Using a saved source

    When creating a PaymentIntent, you can instruct it to use a saved source by providing the relevant IDs with the customer and source parameters.

    When a PaymentIntent is confirmed with the source parameter, it immediately attempts to complete payment with the specified source. If the PaymentIntent can successfully complete payment with the saved source, you do not need to pass the PaymentIntent to the client side and collect billing information. The following example demonstrates how to create a PaymentIntent that uses a saved source:

    curl https://api.stripe.com/v1/payment_intents \
       -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
       -d amount=1099 \
       -d currency=usd \
       -d allowed_source_types[]=card \
       -d source=src_1DWDnvHgsMRlo4Mtl1GO25qW \
       -d customer=cus_DyA7gideDWXNOX \
       -d confirm=true
    
    # 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"
    
    intent = Stripe::PaymentIntent.create({
      amount: 1099,
      currency: 'usd',
      allowed_source_types: ['card'],
      source: 'src_1DWDnvHgsMRlo4Mtl1GO25qW',
      customer: 'cus_DyA7gideDWXNOX',
      confirm: true,
    })
    
    # 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"
    
    stripe.PaymentIntent.create(
      amount=1099,
      currency='usd',
      allowed_source_types=['card'],
      source='src_1DWDnvHgsMRlo4Mtl1GO25qW',
      customer='cus_DyA7gideDWXNOX',
      confirm=True
    )
    
    // 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");
    
    \Stripe\PaymentIntent::create([
      "amount" => 1099,
      "currency" => "usd",
      "allowed_source_types" => ["card"],
      "source" => "src_1DWDnvHgsMRlo4Mtl1GO25qW",
      "customer" => "cus_DyA7gideDWXNOX",
      "confirm" => true,
    ]);
    
    // 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";
    
    Map<String, Object> paymentintentParams = new HashMap<String, Object>();
    paymentintentParams.put("amount", 1099);
    paymentintentParams.put("currency", "usd");
    ArrayList allowed_source_types = new ArrayList();
    allowed_source_types.add("card");
    paymentintentParams.put("allowed_source_types", allowed_source_types);
    paymentintentsParams.put("source", "src_1DWDnvHgsMRlo4Mtl1GO25qW");
    paymentintentsParams.put("customer", "cus_DyA7gideDWXNOX");
    paymentIntentsParams.put("confirm", true);
    
    PaymentIntent.create(paymentintentParams);
    
    // 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
    var stripe = require("stripe")("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    (async () => {
      const paymentIntent = await stripe.paymentIntents.create({
        amount: 1099,
        currency: 'usd',
        allowed_source_types: ['card'],
        source: "src_1DWDnvHgsMRlo4Mtl1GO25qW",
        customer: "cus_DyA7gideDWXNOX",
        confirm: true,
      });
    })();
    
    // 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.PaymentIntentParams{
      Amount: stripe.Int64(1099),
      Currency: stripe.String(string(stripe.CurrencyUSD)),
      AllowedSourceTypes: []*string{
        stripe.String("card"),
      },
      Source: stripe.String("src_1DWDnvHgsMRlo4Mtl1GO25qW"),
      Customer: stripe.String("cus_DyA7gideDWXNOX"),
      Confirm: true
    }
    paymentIntent.New(params)
    
    // 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.SetApiKey("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    var paymentIntents = new PaymentIntentService();
    var createOptions = new PaymentIntentCreateOptions {
      Amount = 1099,
      Currency = "usd",
      AllowedSourceTypes = new List<string> { "card" },
      Source = "src_1DWDnvHgsMRlo4Mtl1GO25qW",
      Customer = "cus_DyA7gideDWXNOX",
      Confirm = true
    };
    paymentIntents.Create(createOptions);
    

    Using Connect

    Stripe supports several approaches for creating charges on behalf of a connected account. One approach is a direct charge, in which the connected account is responsible for the cost of the Stripe fees, refunds, and chargebacks. To perform a direct charge with a PaymentIntent, supply the ID of the connected account while creating the PaymentIntent:

    curl https://api.stripe.com/v1/payment_intents \
       -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
       -d amount=1099 \
       -d currency=usd \
       -d allowed_source_types[]=card \
       -H "Stripe-Account: {CONNECTED_ACCOUNT_ID}"
    
    # 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"
    
    Stripe::PaymentIntent.create({
      amount: 1099,
      currency: 'usd',
      allowed_source_types: ['card'],
    }, stripe_account: "{CONNECTED_ACCOUNT_ID}")
    
    # 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"
    
    stripe.PaymentIntent.create(
      amount=1099,
      currency='usd',
      allowed_source_types=['card'],
      stripe_account='{CONNECTED_ACCOUNT_ID}'
    )
    
    // 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");
    
    \Stripe\PaymentIntent::create([
      "amount" => 1099,
      "currency" => "usd",
      "allowed_source_types" => ["card"],
    ], ["stripe_account" => "{CONNECTED_ACCOUNT_ID}"]);
    
    // 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";
    
    Map<String, Object> paymentintentParams = new HashMap<String, Object>();
    paymentintentParams.put("amount", 1099);
    paymentintentParams.put("currency", "usd");
    ArrayList allowed_source_types = new ArrayList();
    allowed_source_types.add("card");
    paymentintentParams.put("allowed_source_types", allowed_source_types);
    
    RequestOptions requestOptions = RequestOptions.builder().setStripeAccount("{CONNECTED_ACCOUNT_ID}").build();
    
    PaymentIntent.create(paymentintentParams, requestOptions);
    
    // 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
    var stripe = require("stripe")("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    (async () => {
      const paymentIntent = await stripe.paymentIntents.create({
        amount: 1099,
        currency: 'usd',
        allowed_source_types: ['card']
      }, {
        stripe_account: "{CONNECTED_ACCOUNT_ID}"
      });
    })();
    
    // 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.PaymentIntentParams{
      Amount: stripe.Int64(1099),
      Currency: stripe.String(string(stripe.CurrencyUSD)),
      AllowedSourceTypes: []*string{
        stripe.String("card"),
      },
    }
    params.SetStripeAccount("{CONNECTED_ACCOUNT_ID}")
    paymentIntent.New(params)
    
    // 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.SetApiKey("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    var paymentIntents = new PaymentIntentService();
    var createOptions = new PaymentIntentCreateOptions {
      Amount = 1099,
      Currency = "usd",
      AllowedSourceTypes = new List<string> { "card" }
    };
    var requestOptions = new RequestOptions {
      StripeConnectAccountId = "{CONNECTED_ACCOUNT_ID}"
    }
    paymentIntents.Create(createOptions, requestOptions);
    

    PaymentIntents also support destination charges, in which the platform is responsible for the cost of Stripe fees, refunds, and chargebacks. Destination charges are also settled in the country of the connected account, which confers a number of advantages. To create a destination charge with PaymentIntents, provide the ID of the connected account with the on_behalf_of parameter and also provide transfer_data to indicate the destination of the funds to transfer to the connected account’s user. Optionally, you may withhold an application fee by providing the application_fee_amount parameter:

    curl https://api.stripe.com/v1/payment_intents \
       -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
       -d amount=1099 \
       -d currency=usd \
       -d allowed_source_types[]=card \
       -d application_fee_amount=200 \
       -d on_behalf_of="{CONNECTED_ACCOUNT_ID}" \
       -d transfer_data[destination]="{CONNECTED_ACCOUNT_ID}"
    
    # 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"
    
    intent = Stripe::PaymentIntent.create({
      amount: 1099,
      currency: 'usd',
      allowed_source_types: ['card'],
      application_fee_amount: 200,
      on_behalf_of: '{CONNECTED_ACCOUNT_ID}',
      transfer_data: {destination: '{CONNECTED_ACCOUNT_ID}'}
    })
    
    # 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"
    
    stripe.PaymentIntent.create(
      amount=1099,
      currency='usd',
      application_fee_amount=200,
      allowed_source_types=['card'],
      on_behalf_of='{CONNECTED_ACCOUNT_ID}',
      transfer_data={'destination': '{CONNECTED_ACCOUNT_ID}'}
    )
    
    // 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");
    
    \Stripe\PaymentIntent::create([
      "amount" => 1099,
      "currency" => "usd",
      "application_fee_amount" => 200,
      "allowed_source_types" => ["card"],
      "on_behalf_of" => "{CONNECTED_ACCOUNT_ID}",
      "transfer_data" => [
        "destination" => "{CONNECTED_ACCOUNT_ID}"
      ]
    ]);
    
    // 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";
    
    Map<String, Object> paymentintentParams = new HashMap<String, Object>();
    paymentintentParams.put("amount", 1099);
    paymentintentParams.put("currency", "usd");
    ArrayList allowed_source_types = new ArrayList();
    allowed_source_types.add("card");
    paymentintentParams.put("allowed_source_types", allowed_source_types);
    paymentintentParams.put("on_behalf_of", "{CONNECTED_ACCOUNT_ID}");
    paymentintentParams.put("application_fee_amount", 200);
    
    Map<String, Object> transferDataParams = new HashMap<String, Object>();
    transferDataParams.put("destination", "{CONNECTED_ACCOUNT_ID}");
    
    paymentintentParams.put("transfer_data", transferDataParams);
    
    PaymentIntent.create(paymentintentParams);
    
    // 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
    var stripe = require("stripe")("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    (async () => {
      const paymentIntent = await stripe.paymentIntents.create({
        amount: 1099,
        currency: 'usd',
        allowed_source_types: ['card'],
        application_fee_amount: 200,
        on_behalf_of: '{CONNECTED_ACCOUNT_ID}',
        transfer_data: {
          destination: '{CONNECTED_ACCOUNT_ID}'
        }
      });
    })();
    
    // 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"
    
    transferData = &stripe.PaymentIntentTransferDataParams{
      Destination: stripe.String("{CONNECTED_ACCOUNT_ID}"),
    }
    
    params := &stripe.PaymentIntentParams{
      Amount: stripe.Int64(1099),
      Currency: stripe.String(string(stripe.CurrencyUSD)),
      AllowedSourceTypes: []*string{
        stripe.String("card"),
      },
      OnBehalfOf: stripe.String("{CONNECTED_ACCOUNT_ID}"),
      ApplicationFeeAmount: stripe.Int64(200),
      TransferData: transferData,
    }
    paymentIntent.New(params)
    
    // 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.SetApiKey("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    var paymentIntents = new PaymentIntentService();
    var createOptions = new PaymentIntentCreateOptions {
      Amount = 1099,
      Currency = "usd",
      AllowedSourceTypes = new List<string> { "card" },
      ApplicationFeeAmount = 200,
      OnBehalfOf = "{CONNECTED_ACCOUNT_ID}",
      TransferData = new PaymentIntentTransferDataOptions {
        DestinationId = "{CONNECTED_ACCOUNT_ID}",
      }
    };
    paymentIntents.Create(createOptions);
    

    The destination is the ID of the connected account that should receive the funds. You can optionally use the on_behalf_of parameter without providing transfer_data if you do not want an automatic transfer to occur. After creating the PaymentIntent, you may use it as normal to complete payment.

    Payment Request and Apple Pay

    Follow the Payment Request Button quickstart guide until step 4.

    Instead of creating a charge directly from a token in step 4, listen to the source event to receive a source object.

    Then, supply the source ID to stripe.confirmPaymentIntent and stripe.handleCardPayment to complete the payment.

    paymentRequest.on('source', function(ev) {
      stripe.confirmPaymentIntent(clientSecret, {
        source: ev.source.id,
        use_stripe_sdk: true
      }).then(function(confirmResult) {
        if (confirmResult.error) {
          // Report to the browser that the payment failed, prompting it to
          // re-show the payment interface, or show an error message and close
          // the payment interface.
          ev.complete('fail');
        } else {
          // Report to the browser that the confirmation was successful, prompting
          // it to close the browser payment method collection interface.
          ev.complete('success');
          if (confirmResult.paymentIntent.status === 'succeeded') {
            // The payment has succeeded. No additional steps are required.
          } else {
            // Let Stripe.js handle the rest of the payment flow.
            stripe.handleCardPayment(clientSecret).then(function(result) {
              if (result.error) {
                // The payment failed -- ask your customer for a new payment method.
              } else {
                // The payment has succeeded.
              }
            });
          }
        }
      });
    });
    paymentRequest.on('source', async (ev) => {
      const {
        error: confirmError,
        paymentIntent,
      } = stripe.confirmPaymentIntent(clientSecret, {
        source: ev.source.id,
        use_stripe_sdk: true,
      });
    
      if (confirmError) {
        // Report to the browser that the payment failed, prompting it to
        // re-show the payment interface, or show an error message and close
        // the payment interface.
        ev.complete('fail');
      } else {
        // Report to the browser that the confirmation was successful, prompting
        // it to close the browser payment method collection interface.
        ev.complete('success');
        if (paymentIntent.status === 'succeeded') {
          // The payment has succeeded. No additional steps are required.
        } else {
          // Let Stripe.js handle the rest of the payment flow.
          const {error} = await stripe.handleCardPayment(clientSecret);
          if (error) {
            // The payment failed -- ask your customer for a new payment method.
          } else {
            // The payment has succeeded.
          }
        }
      }
    });

    Next steps

    To learn more about PaymentIntents, read more:

    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.

    Was this page helpful? Yes No

    Send

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