Payment Intents API Quickstart

    Learn how to accept card payments with Elements and the Payment Intents API to make one-time payments.

    There are two ways to accept card payments with the Payment Intents API depending on how you want to complete the payment: automatic confirmation and manual confirmation. See the Payments Intents API Overview for a detailed comparison.

    • The automatic confirmation flow confirms the payment on the client and uses webhooks to inform you when the payment is successful.
    • The manual confirmation flow confirms the payment on the server and lets you fulfill on your server immediately after the payment succeeds.

    Automatic confirmation quickstart

    Automatic confirmation is the fastest way to get started with the Payment Intents API. Because some payment methods have asynchronous flows, such as 3D Secure3D Secure provides an additional layer of authentication for credit card transactions that protects merchants from liability for fraudulent card payments. During a transaction that incorporates the 3D Secure authorization process, the customer is prompted to supply a separate password or code to validate their purchase. authentication for cards, the automatic confirmation integration requires using webhooks to handle any post-payment logic.

    A diagram showing the auto confirmation server to client to webhooks flow

    1. Create a PaymentIntent on the server
    2. Pass the PaymentIntent’s client secret to the client
    3. Collect payment method details on the client
    4. Submit the payment to Stripe from the client
    5. Asynchronously fulfill the customer’s order

    Step 1: Create a PaymentIntent on the server

    A PaymentIntent is an object that represents your intent to collect payment from a customer, tracking the lifecycle of the payment process through each stage. When you create a PaymentIntent, specify the amount of money that you wish to collect and the currency. The following example shows how to create a PaymentIntent on your server:

    curl https://api.stripe.com/v1/payment_intents \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d amount=1099 \
      -d currency=usd
    
    # 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',
    })
    
    # 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',
    )
    
    // 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 = \Stripe\PaymentIntent::create([
      'amount' => 1099,
      'currency' => 'usd',
    ]);
    
    // 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");
    
    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',
      });
    })();
    
    // 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)),
    }
    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",
    };
    paymentIntents.Create(createOptions);
    

    We recommend creating a PaymentIntent as soon as the amount is known, such as when the customer begins the checkout process. If the amount changes, for example when the cart subtotal updates or when shipping fees and taxes are calculated, you can update the amount.

    curl https://api.stripe.com/v1/payment_intents/pi_1DRuHnHgsMRlo4MtwuIAUe6u \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d amount=1499
    
    # 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.update(
      'pi_1DRuHnHgsMRlo4MtwuIAUe6u',
      {
        amount: 1499,
      }
    )
    
    # 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.modify(
      'pi_1DRuHnHgsMRlo4MtwuIAUe6u',
      amount=1499
    )
    
    // 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::update(
      'pi_1DRuHnHgsMRlo4MtwuIAUe6u',
      [
        'amount' => 1499,
      ]
    );
    
    // 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_1DRuHnHgsMRlo4MtwuIAUe6u");
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("amount", 1499);
    intent.update(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.update("pi_1DRuHnHgsMRlo4MtwuIAUe6u", {
        amount: 1499
      })
    })();
    
    // 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(1499),
    }
    intent, err := paymentintent.Update("pi_1DRuHnHgsMRlo4MtwuIAUe6u", 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 service = new PaymentIntentService();
    var options = new PaymentIntentUpdateOptions {
      Amount = 1499,
    };
    service.Update("pi_1DRuHnHgsMRlo4MtwuIAUe6u", options);
    

    Step 2: Pass the PaymentIntent’s client secret to the client

    The PaymentIntent object contains a client secret, a unique key that you need to pass to Stripe.js on the client side in order to create a charge. If your application uses server-side rendering, use your template framework to embed the client secret in the page using a data attribute or a hidden HTML element.

    <input id="cardholder-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="cardholder-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="cardholder-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="cardholder-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<String, String> map = new HashMap();
            map.put("client_secret", intent.clientSecret);
    
            return new ModelAndView(map, "checkout.hbs");
          }, new HandlebarsTemplateEngine());
        }
    }
    <input id="cardholder-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.client_secret });
    });
    
    app.listen(3000, () => {
      console.log('Running on port 3000')
    });
    <input id="cardholder-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="cardholder-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();
            }
        }
    }

    Each PaymentIntent typically correlates with a single “cart” or customer session in your application. You can create the PaymentIntent during checkout and store its ID on the user’s cart in your application’s data model, retrieving it again as necessary.

    Step 3: Collect payment method details on the client

    The Payment Intents API is fully integrated with Stripe.js, using Elements to securely collect payment information on the client side and submitting it to Stripe to create a charge. To get started with Elements, include the following script on your pages. This script must always load directly from js.stripe.com in order to remain PCI compliantAny party involved in processing, transmitting, or storing credit card data must comply with the rules specified in the the Payment Card Industry (PCI) Data Security Standards. PCI compliance is a shared responsibility and applies to both Stripe and your business.—you can’t include it in a bundle or host a copy of it yourself.

    <script src="https://js.stripe.com/v3/"></script>
    

    To best leverage Stripe’s advanced fraud functionality, include this script on every page on your site, not just the checkout page. Including the script on every page allows Stripe to detect anomalous behavior that may be indicative of fraud as users browse your website.

    Next, create an instance of the Stripe object, providing your publishable API key as the first parameter. Afterwards, create an instance of the Elements object and use it to mount a Card element in the relevant placeholder in the page.

    var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
    
    var elements = stripe.elements();
    var cardElement = elements.create('card');
    cardElement.mount('#card-element');
    const stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
    
    const elements = stripe.elements();
    const cardElement = elements.create('card');
    cardElement.mount('#card-element');

    Step 4: Submit the payment to Stripe from the client

    To complete the payment, retrieve the client secret made available in the second step. After obtaining the client secret from the data attribute, use stripe.handleCardPayment to complete the payment:

    var cardholderName = document.getElementById('cardholder-name');
    var cardButton = document.getElementById('card-button');
    var clientSecret = cardButton.dataset.secret;
    
    cardButton.addEventListener('click', function(ev) {
      stripe.handleCardPayment(
        clientSecret, cardElement, {
          payment_method_data: {
            billing_details: {name: cardholderName.value}
          }
        }
      ).then(function(result) {
        if (result.error) {
          // Display error.message in your UI.
        } else {
          // The payment has succeeded. Display a success message.
        }
      });
    });
    const cardholderName = document.getElementById('cardholder-name');
    const cardButton = document.getElementById('card-button');
    const clientSecret = cardButton.dataset.secret;
    
    cardButton.addEventListener('click', async (ev) => {
      const {paymentIntent, error} = await stripe.handleCardPayment(
        clientSecret, cardElement, {
          payment_method_data: {
            billing_details: {name: cardholderName.value}
          }
        }
      );
    
      if (error) {
        // Display error.message in your UI.
      } else {
        // The payment has succeeded. Display a success message.
      }
    });

    If the customer must perform additional steps to complete the payment, such as authentication, Stripe.js walks them through that process. When the payment completes successfully, the value of the returned PaymentIntent’s status property is succeeded. If the payment was not successful, you can inspect the returned error to determine the cause.

    Step 5: Asynchronously fulfill the customer’s order

    You can use the PaymentIntent returned by Stripe.js to provide immediate feedback to your customers when the payment completes on the client. However, your integration should not attempt to handle order fulfillmentFulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected. on the client side because it is possible for customers to leave the page after payment is complete but before the fulfillment process initiates. Instead, you will need to handle asynchronous events in order to be notified and drive fulfillment when the payment succeeds.

    There are several ways you can fulfill an order:

    When a PaymentIntent attempts to collect payment from a customer, it creates a charge object. You can inspect a PaymentIntent’s charges property to obtain its complete list of attempted charges:

    # 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")
    charges = intent.charges.data
    
    # 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")
    charges = intent['charges']['data']
    
    // 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 = \Stripe\PaymentIntent::retrieve("pi_Aabcxyz01aDfoo");
    $charges = $intent->charges->data;
    
    // 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<Charge> charges = intent.charges.getData();
    
    // 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 charges = intent.charges.data;
     })();
    
    // 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)
    charges := intent.Charges.Data
    
     // 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 intent = paymentIntents.Get("pi_Aabcxyz01aDfoo");
     var charges = intent.Charges.Data;
    

    The charges are listed in reverse chronological order, so the most recent charge is first in the array. Note that the array includes any unsuccessful charges created during the payment process in addition to the final successful charge.

    Next, test your integration to make sure you’re correctly handling cards that require additional authentication.

    Manual confirmation quickstart

    Manual confirmation lets you confirm the PaymentIntent on the server. It’s the easiest way to migrate from the Charges API, but requires extra trips to your server to manually handle authentication flows like 3D Secure3D Secure provides an additional layer of authentication for credit card transactions that protects merchants from liability for fraudulent card payments. During a transaction that incorporates the 3D Secure authorization process, the customer is prompted to supply a separate password or code to validate their purchase..

    A diagram showing the manual confirmation client to server with optional arrow pointing back to client

    1. Collect payment method details on the client
    2. Create a PaymentIntent on the server
    3. Handle any next actions on the client
    4. Confirm the PaymentIntent again on the server

    Step 1: Collect payment method details on the client

    The Payment Intents API works with Stripe.js & Elements to securely collect payment information on the client side. To get started with Elements, include the following script on your pages. This script must always load directly from js.stripe.com in order to remain PCI compliantAny party involved in processing, transmitting, or storing credit card data must comply with the rules specified in the the Payment Card Industry (PCI) Data Security Standards. PCI compliance is a shared responsibility and applies to both Stripe and your business.—you can’t include it in a bundle or host a copy of it yourself.

    <script src="https://js.stripe.com/v3/"></script>
    

    To securely collect card details from your customers, Elements creates UI components for you that are hosted by Stripe. They are then placed into your payment form, rather than you creating them directly. To determine where to insert these components, create empty DOM elements (containers) with unique IDs within your payment form.

    <input id="cardholder-name" type="text">
    <!-- placeholder for Elements -->
    <div id="card-element"></div>
    <button id="card-button">Submit Payment</button>
    

    Next, create an instance of the Stripe object, providing your publishable API key as the first parameter. Afterwards, create an instance of the Elements object and use it to mount a Card element in the relevant placeholder in the page.

    var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
    
    var elements = stripe.elements();
    var cardElement = elements.create('card');
    cardElement.mount('#card-element');
    const stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
    
    const elements = stripe.elements();
    const cardElement = elements.create('card');
    cardElement.mount('#card-element');

    Finally, use stripe.createPaymentMethod on your client to collect the card details and create a PaymentMethod when the user clicks the submit button.

    var cardholderName = document.getElementById('cardholder-name');
    var cardButton = document.getElementById('card-button');
    
    cardButton.addEventListener('click', function(ev) {
      stripe.createPaymentMethod('card', cardElement, {
        billing_details: {name: cardholderName.value}
      }).then(function(result) {
        if (result.error) {
          // Show error in payment form
        } else {
          // Otherwise send paymentMethod.id to your server (see Step 2)
          fetch('/ajax/confirm_payment', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ payment_method_id: result.paymentMethod.id })
          }).then(function(result) {
            // Handle server response (see Step 3)
            result.json().then(function(json) {
              handleServerResponse(json);
            })
          });
        }
      });
    });
    
    
    const cardholderName = document.getElementById('cardholder-name');
    const cardButton = document.getElementById('card-button');
    
    cardButton.addEventListener('click', async (ev) => {
      const {paymentMethod, error} =
        await stripe.createPaymentMethod('card', cardElement, {
          billing_details: {name: cardholderName.value}
        });
      if (error) {
        // Show error in payment form
      } else {
        // Send paymentMethod.id to your server (see Step 2)
        const response = await fetch('/ajax/confirm_payment', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ payment_method_id: paymentMethod.id })
        });
    
        const json = await response.json();
    
        // Handle server response (see Step 3)
        handleServerResponse(json);
      }
    });
    
    

    Step 2: Create a PaymentIntent on the server

    Set up an endpoint on your server to receive the request. This endpoint will also be used in Step 4 to handle cards that require an extra step of authentication.

    Create a new PaymentIntent with the ID of the PaymentMethod created on your client. You can confirmConfirming a PaymentIntent indicates that the customer intends to pay with the current or provided payment method. Upon confirmation, the PaymentIntent attempts to initiate a payment. the PaymentIntent by setting confirm property to true when the PaymentIntent is created or by calling confirm after creation.

    If the payment requires additional actions such as 3D Secure authentication, the PaymentIntent’s status will be set to requires_action. If the payment failed, the status is set back to requires_payment_method and you should show an error to your user. If the payment doesn’t require any additional authentication then a charge is created and the PaymentIntent status is set to succeeded.

    curl https://api.stripe.com/v1/payment_intents \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d payment_method="{{PAYMENT_METHOD_ID}}" \
      -d amount=1099 \
      -d currency=usd \
      -d confirmation_method=manual \
      -d confirm=true
    
    # AJAX endpoint when `/ajax/confirm_payment` is called from client
    post '/ajax/confirm_payment' do
      data = JSON.parse(request.body.read.to_s)
    
      begin
        if data['payment_method_id']
          # Create the PaymentIntent
          intent = Stripe::PaymentIntent.create(
            payment_method: data['payment_method_id'],
            amount: 1099,
            currency: 'usd',
            confirmation_method: 'manual',
            confirm: true,
          )
        elsif data['payment_intent_id']
          intent = Stripe::PaymentIntent.confirm(data['payment_intent_id'])
        end
      rescue Stripe::CardError => e
        # Display error on client
        return [200, { error: e.message }.to_json]
      end
    
      return generate_payment_response(intent)
    end
    
    def generate_payment_response(intent)
      if intent.status == 'requires_action' &&
          intent.next_action.type == 'use_stripe_sdk'
        # Tell the client to handle the action
        [
          200,
          {
            requires_action: true,
            payment_intent_client_secret: intent.client_secret
          }.to_json
        ]
      elsif intent.status == 'succeeded'
        # The payment didn’t need any additional actions and completed!
        # Handle post-payment fulfillment
        [200, { success: true }.to_json]
      else
        # Invalid status
        return [500, { error: 'Invalid PaymentIntent status' }.to_json]
      end
    end
    
    # AJAX endpoint when `/ajax/confirm_payment` is called from client
    @app.route('/ajax/confirm_payment', methods=['POST'])
    def confirm_payment():
      data = request.get_json()
      intent = None
    
      try:
        if 'payment_method_id' in data:
          # Create the PaymentIntent
          intent = stripe.PaymentIntent.create(
            payment_method = data['payment_method_id'],
            amount = 1099,
            currency = 'usd',
            confirmation_method = 'manual',
            confirm = True,
          )
        elif 'payment_intent_id' in data:
          intent = stripe.PaymentIntent.confirm(data['payment_intent_id'])
      except stripe.error.CardError as e:
        # Display error on client
        return json.dumps({'error': e.message}), 200
    
      return generate_payment_response(intent)
    
    def generate_payment_response(intent):
      if intent.status == 'requires_action' and intent.next_action.type == 'use_stripe_sdk':
        # Tell the client to handle the action
        return json.dumps({
          'requires_action': True,
          'payment_intent_client_secret': intent.client_secret,
        }), 200
      elif intent.status == 'succeeded':
        # The payment didn’t need any additional actions and completed!
        # Handle post-payment fulfillment
        return json.dumps({'success': True}), 200
      else:
        # Invalid status
        return json.dumps({'error': 'Invalid PaymentIntent status'}), 500
    
    <?php
      # vendor using composer
      require_once('vendor/autoload.php');
    
      \Stripe\Stripe::setApiKey(getenv('STRIPE_SECRET_KEY'));
    
      header('Content-Type: application/json');
    
      # retrieve json from POST body
      $json_str = file_get_contents('php://input');
      $json_obj = json_decode($json_str);
    
      $intent = null;
      try {
        if (isset($json_obj->payment_method_id)) {
          # Create the PaymentIntent
          $intent = \Stripe\PaymentIntent::create([
            'payment_method' => $json_obj->payment_method_id,
            'amount' => 1099,
            'currency' => 'usd',
            'confirmation_method' => 'manual',
            'confirm' => true,
          ]);
        }
        if (isset($json_obj->payment_intent_id)) {
          $intent = \Stripe\PaymentIntent::retrieve(
            $json_obj->payment_intent_id
          );
          $intent->confirm();
        }
        generatePaymentResponse($intent);
      } catch (\Stripe\Error\Base $e) {
        # Display error on client
        echo json_encode([
          'error' => $e->getMessage()
        ]);
      }
    
      function generatePaymentResponse($intent) {
        if ($intent->status == 'requires_action' &&
            $intent->next_action->type == 'use_stripe_sdk') {
          # Tell the client to handle the action
          echo json_encode([
            'requires_action' => true,
            'payment_intent_client_secret' => $intent->client_secret
          ]);
        } else if ($intent->status == 'succeeded') {
          # The payment didn’t need any additional actions and completed!
          # Handle post-payment fulfillment
          echo json_encode([
            "success" => true
          ]);
        } else {
          # Invalid status
          http_response_code(500);
          echo json_encode(['error' => 'Invalid PaymentIntent status']);
        }
      }
    ?>
    
    package com.stripe.generator;
    
    import static spark.Spark.post;
    
    import com.google.gson.Gson;
    import com.google.gson.annotations.SerializedName;
    import com.stripe.Stripe;
    import com.stripe.model.PaymentIntent;
    
    import com.stripe.param.PaymentIntentCreateParams;
    import java.util.HashMap;
    import java.util.Map;
    import spark.Request;
    import spark.Response;
    import spark.Route;
    
    public class MyApp {
    
      static class ConfirmPaymentRequest {
        @SerializedName("payment_method_id")
        String paymentMethodId;
        @SerializedName("payment_intent_id")
        String paymentIntentId;
    
        public String getPaymentMethodId() {
          return paymentMethodId;
        }
    
        public String getPaymentIntentId() {
          return paymentIntentId;
        }
      }
    
      /**
       * Your application.
       */
      public static void main(String[] args) {
    
        Stripe.apiKey = System.getenv("STRIPE_SECRET_KEY");
    
        post(new Route("/ajax/confirm_payment") {
    
          @Override
          public Object handle(Request request, Response response) {
            Gson gson = new Gson();
            ConfirmPaymentRequest confirmRequest =
                gson.fromJson(request.body(), ConfirmPaymentRequest.class);
            PaymentIntent intent;
            try {
              if (confirmRequest.getPaymentMethodId() != null) {
                PaymentIntentCreateParams createParams = PaymentIntentCreateParams.builder()
                    .setAmount(1099)
                    .setCurrency("usd")
                    .setConfirm(true)
                    .setPaymentMethod(confirmRequest.paymentMethodId)
                    .setConfirmationMethod(PaymentIntentCreateParams.ConfirmationMethod.MANUAL)
                    .build();
                intent = PaymentIntent.create(createParams);
              } else if (confirmRequest.getPaymentIntentId() != null) {
                intent = PaymentIntent.retrieve(confirmRequest.getPaymentIntentId());
                intent.confirm();
              }
    
              Map<String, Object> responseData = generatePaymentResponse(response, intent);
    
              return gson.toJson(responseData);
            } catch (Exception e) {
              response.status(500);
              Map<String, Object> errorResponse = new HashMap<>();
              errorResponse.put("message", e.getMessage());
              return gson.toJson(errorResponse);
            }
          }
    
          private Map<String, Object> generatePaymentResponse(Response response,
                                                              PaymentIntent intent) {
            response.type("application/json");
            Map<String, Object> responseData = new HashMap<>();
            if (intent.getStatus().equals("requires_action")
                && intent.getNextAction().getType().equals("use_stripe_sdk")) {
              responseData.put("requires_action", true);
              responseData.put("payment_intent_client_secret", intent.getClientSecret());
            } else if (intent.getStatus().equals("succeeded")) {
              responseData.put("success", true);
            } else {
              // invalid status
              responseData.put("Error", "Invalid status");
              response.status(500);
              return responseData;
            }
            response.status(200);
            return responseData;
          }
        });
      }
    }
    
    // Using Express
    const express = require('express');
    const app = express();
    app.use(express.json());
    
    app.post('/ajax/confirm_payment', async (request, response) => {
      try {
        let intent;
        if (request.body.payment_method_id) {
          // Create the PaymentIntent
          intent = await stripe.paymentIntents.create({
            payment_method: request.body.payment_method_id,
            amount: 1099,
            currency: 'usd',
            confirmation_method: 'manual',
            confirm: true
          });
        } else if (request.body.payment_intent_id) {
          intent = await stripe.paymentIntents.confirm(
            request.body.payment_intent_id
          );
        }
        // Send the response to the client
        response.send(generate_payment_response(intent));
      } catch (e) {
        // Display error on client
        return response.send({ error: e.message });
      }
    });
    
    const generate_payment_response = (intent) => {
      if (
        intent.status === 'requires_action' &&
        intent.next_action.type === 'use_stripe_sdk'
      ) {
        // Tell the client to handle the action
        return {
          requires_action: true,
          payment_intent_client_secret: intent.client_secret
        };
      } else if (intent.status === 'succeeded') {
        // The payment didn’t need any additional actions and completed!
        // Handle post-payment fulfillment
        return {
          success: true
        };
      } else {
        // Invalid status
        return {
          error: "Invalid PaymentIntent status"
        }
      }
    };
    
    package main
    
    import (
      "encoding/json"
      "net/http"
      "os"
      "fmt"
    
      stripe "github.com/stripe/stripe-go"
      paymentintent "github.com/stripe/stripe-go/paymentintent"
    )
    
    // Structs to handle request and response JSON serializations
    type ConfirmPaymentRequest struct {
      PaymentMethodId *string `json:"payment_method_id"`
      PaymentIntentId *string `json:"payment_intent_id"`
    }
    
    type ErrorResponse struct {
      Message string `json:"error"`
    }
    
    type ConfirmPaymentResponse struct {
      RequiresAction bool `json:"requires_action"`
      PaymentIntentClientSecret *string `json:"payment_intent_client_secret"`
    }
    
    func generatePaymentResponse(intent *stripe.PaymentIntent, w http.ResponseWriter) {
      if intent.Status == stripe.PaymentIntentStatusRequiresAction &&
        intent.NextAction.Type == "use_stripe_sdk" {
    
        // Tell the client to handle the action
        w.WriteHeader(http.StatusOK)
        json.NewEncoder(w).Encode(ConfirmPaymentResponse{
          RequiresAction: true,
          PaymentIntentClientSecret: &intent.ClientSecret,
        })
    
      } else if intent.Status == stripe.PaymentIntentStatusSucceeded {
    
        // The payment didn’t need any additional actions and completed!
        // Handle post-payment fulfillment
        w.WriteHeader(http.StatusOK)
        fmt.Fprint(w, "Success")
    
      } else {
    
        // Invalid status
        w.WriteHeader(http.StatusInternalServerError)
        fmt.Fprintf(w, "Invalid Payment Intent status: %s", intent.Status)
    
      }
    }
    
    func main() {
      // Initialize stripe-go with Stripe secret key from environment
      stripe.Key = os.Getenv("STRIPE_SECRET_KEY")
    
      // AJAX endpoint when `/ajax/confirm_payment` is called from client
      http.HandleFunc("/ajax/confirm_payment",
        func(w http.ResponseWriter, req *http.Request) {
          var confirmPaymentRequest ConfirmPaymentRequest
          json.NewDecoder(req.Body).Decode(&confirmPaymentRequest)
    
          var intent *stripe.PaymentIntent
          var err error
    
          if confirmPaymentRequest.PaymentMethodId != nil {
            // Create the PaymentIntent
            params := &stripe.PaymentIntentParams{
              PaymentMethod: stripe.String(*confirmPaymentRequest.PaymentMethodId),
              Amount: stripe.Int64(1099),
              Currency: stripe.String(string(stripe.CurrencyUSD)),
              ConfirmationMethod: stripe.String(string(
                stripe.PaymentIntentConfirmationMethodManual,
              )),
              Confirm: stripe.Bool(true),
            }
            intent, err = paymentintent.New(params)
          }
    
          if confirmPaymentRequest.PaymentIntentId != nil {
            intent, err = paymentintent.Confirm(
              *confirmPaymentRequest.PaymentIntentId, nil,
            )
          }
    
          if err != nil {
            if stripeErr, ok := err.(*stripe.Error); ok {
              // Display error on client
              w.WriteHeader(http.StatusOK)
              json.NewEncoder(w).Encode(ErrorResponse{Message: stripeErr.Msg})
            } else {
              w.WriteHeader(http.StatusInternalServerError)
              fmt.Fprintf(w, "Other error occurred, %v\n", err.Error())
            }
          } else {
            generatePaymentResponse(intent, w)
          }
        })
    
      http.ListenAndServe(":"+os.Getenv("PORT"), nil)
    }
    
    using System;
    using System.Collections.Generic;
    using Microsoft.AspNetCore.Mvc;
    
    using Stripe;
    using Newtonsoft.Json;
    
    namespace Controllers
    {
        public class ConfirmPaymentRequest
        {
            [JsonProperty("payment_method_id")]
            public string PaymentMethodId { get; set; }
    
            [JsonProperty("payment_intent_id")]
            public string PaymentIntentId { get; set; }
        }
    
        // AJAX endpoint when `/ajax/confirm_payment` is called from client
        [Route("/ajax/confirm_payment")]
        public class ConfirmPaymentController : Controller
        {
            public IActionResult Index([FromBody] ConfirmPaymentRequest request)
            {
                var paymentIntentService = new PaymentIntentService();
                PaymentIntent paymentIntent = null;
    
                try
                {
                    if (request.PaymentMethodId != null)
                    {
                        // Create the PaymentIntent
                        var createOptions = new PaymentIntentCreateOptions
                        {
                            PaymentMethodId = request.PaymentMethodId,
                            Amount = 1099,
                            Currency = "usd",
                            ConfirmationMethod = "manual",
                            Confirm = true,
                        };
                        paymentIntent = paymentIntentService.Create(createOptions);
                    }
                    if (request.PaymentIntentId != null)
                    {
                        var confirmOptions = new PaymentIntentConfirmOptions{};
                        paymentIntent = paymentIntentService.Confirm(
                            request.PaymentIntentId,
                            confirmOptions
                        );
                    }
                }
                catch (StripeException e)
                {
                    return Json(new { error = e.StripeError.Message });
                }
                return generatePaymentResponse(paymentIntent);
              }
    
              private IActionResult generatePaymentResponse(PaymentIntent intent)
              {
                if (intent.Status == "requires_action" &&
                    intent.NextAction.Type == "use_stripe_sdk")
                {
                    // Tell the client to handle the action
                    return Json(new
                    {
                        requires_action = true,
                        payment_intent_client_secret = intent.ClientSecret
                    });
                }
                else if (intent.Status == "succeeded")
                {
                    // The payment didn’t need any additional actions and completed!
                    // Handle post-payment fulfillment
                    return Json(new { success = true });
                }
                else
                {
                    // Invalid status
                    return StatusCode(500, new { error = "Invalid PaymentIntent status" });
                }
            }
        }
    }
    

    Step 3: Handle any next actions on the client

    If the PaymentIntent requires additional action from the customer, such as authenticating with 3D Secure, you can use stripe.handleCardAction to trigger the UI to handle the action. If successful, the PaymentIntent will have a status of requires_confirmation and you will need to confirm the PaymentIntent again on your server to finish the payment.

    While testing, you can use our 3D Secure required test card number (4000000000003063) to force this flow. Using the 3D Secure not required card (4242424242424242) will skip this part of the flow and complete at Step 2 because it does not require authentication.

    function handleServerResponse(response) {
      if (response.error) {
        // Show error from server on payment form
      } else if (response.requires_action) {
        // Use Stripe.js to handle required card action
        stripe.handleCardAction(
          response.payment_intent_client_secret
        ).then(function(result) {
          if (result.error) {
            // Show error in payment form
          } else {
            // The card action has been handled
            // The PaymentIntent can be confirmed again on the server
            fetch('/ajax/confirm_payment', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ payment_intent_id: result.paymentIntent.id })
            }).then(function(confirmResult) {
              return confirmResult.json();
            }).then(handleServerResponse);
          }
        });
      } else {
        // Show success message
      }
    }
    
    const handleServerResponse = async (response) => {
      if (response.error) {
        // Show error from server on payment form
      } else if (response.requires_action) {
        // Use Stripe.js to handle the required card action
        const { error: errorAction, paymentIntent } =
          await stripe.handleCardAction(response.payment_intent_client_secret);
    
        if (errorAction) {
          // Show error from Stripe.js in payment form
        } else {
          // The card action has been handled
          // The PaymentIntent can be confirmed again on the server
          const serverResponse = await fetch('/ajax/confirm_payment', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ payment_intent_id: paymentIntent.id })
          });
          handleServerResponse(await serverResponse.json());
        }
      } else {
        // Show success message
      }
    }
    

    Step 4: Confirm the PaymentIntent again on the server

    Using the same endpoint you set up in Step 2, confirm the PaymentIntent again to finalize the payment and fulfillFulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected. the order.

    curl https://api.stripe.com/v1/payment_intents/{{PAYMENT_INTENT_ID}}/confirm \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -X POST
    
    # AJAX endpoint when `/ajax/confirm_payment` is called from client
    post '/ajax/confirm_payment' do
      data = JSON.parse(request.body.read.to_s)
    
      begin
        if data['payment_method_id']
          # Create the PaymentIntent
          intent = Stripe::PaymentIntent.create(
            payment_method: data['payment_method_id'],
            amount: 1099,
            currency: 'usd',
            confirmation_method: 'manual',
            confirm: true,
          )
        elsif data['payment_intent_id']
          intent = Stripe::PaymentIntent.confirm(data['payment_intent_id'])
        end
      rescue Stripe::CardError => e
        # Display error on client
        return [200, { error: e.message }.to_json]
      end
    
      return generate_payment_response(intent)
    end
    
    def generate_payment_response(intent)
      if intent.status == 'requires_action' &&
          intent.next_action.type == 'use_stripe_sdk'
        # Tell the client to handle the action
        [
          200,
          {
            requires_action: true,
            payment_intent_client_secret: intent.client_secret
          }.to_json
        ]
      elsif intent.status == 'succeeded'
        # The payment didn’t need any additional actions and completed!
        # Handle post-payment fulfillment
        [200, { success: true }.to_json]
      else
        # Invalid status
        return [500, { error: 'Invalid PaymentIntent status' }.to_json]
      end
    end
    
    # AJAX endpoint when `/ajax/confirm_payment` is called from client
    @app.route('/ajax/confirm_payment', methods=['POST'])
    def confirm_payment():
      data = request.get_json()
      intent = None
    
      try:
        if 'payment_method_id' in data:
          # Create the PaymentIntent
          intent = stripe.PaymentIntent.create(
            payment_method = data['payment_method_id'],
            amount = 1099,
            currency = 'usd',
            confirmation_method = 'manual',
            confirm = True,
          )
        elif 'payment_intent_id' in data:
          intent = stripe.PaymentIntent.confirm(data['payment_intent_id'])
      except stripe.error.CardError as e:
        # Display error on client
        return json.dumps({'error': e.message}), 200
    
      return generate_payment_response(intent)
    
    def generate_payment_response(intent):
      if intent.status == 'requires_action' and intent.next_action.type == 'use_stripe_sdk':
        # Tell the client to handle the action
        return json.dumps({
          'requires_action': True,
          'payment_intent_client_secret': intent.client_secret,
        }), 200
      elif intent.status == 'succeeded':
        # The payment didn’t need any additional actions and completed!
        # Handle post-payment fulfillment
        return json.dumps({'success': True}), 200
      else:
        # Invalid status
        return json.dumps({'error': 'Invalid PaymentIntent status'}), 500
    
    <?php
      # vendor using composer
      require_once('vendor/autoload.php');
    
      \Stripe\Stripe::setApiKey(getenv('STRIPE_SECRET_KEY'));
    
      header('Content-Type: application/json');
    
      # retrieve json from POST body
      $json_str = file_get_contents('php://input');
      $json_obj = json_decode($json_str);
    
      $intent = null;
      try {
        if (isset($json_obj->payment_method_id)) {
          # Create the PaymentIntent
          $intent = \Stripe\PaymentIntent::create([
            'payment_method' => $json_obj->payment_method_id,
            'amount' => 1099,
            'currency' => 'usd',
            'confirmation_method' => 'manual',
            'confirm' => true,
          ]);
        }
        if (isset($json_obj->payment_intent_id)) {
          $intent = \Stripe\PaymentIntent::retrieve(
            $json_obj->payment_intent_id
          );
          $intent->confirm();
        }
        generatePaymentResponse($intent);
      } catch (\Stripe\Error\Base $e) {
        # Display error on client
        echo json_encode([
          'error' => $e->getMessage()
        ]);
      }
    
      function generatePaymentResponse($intent) {
        if ($intent->status == 'requires_action' &&
            $intent->next_action->type == 'use_stripe_sdk') {
          # Tell the client to handle the action
          echo json_encode([
            'requires_action' => true,
            'payment_intent_client_secret' => $intent->client_secret
          ]);
        } else if ($intent->status == 'succeeded') {
          # The payment didn’t need any additional actions and completed!
          # Handle post-payment fulfillment
          echo json_encode([
            "success" => true
          ]);
        } else {
          # Invalid status
          http_response_code(500);
          echo json_encode(['error' => 'Invalid PaymentIntent status']);
        }
      }
    ?>
    
    package com.stripe.generator;
    
    import static spark.Spark.post;
    
    import com.google.gson.Gson;
    import com.google.gson.annotations.SerializedName;
    import com.stripe.Stripe;
    import com.stripe.model.PaymentIntent;
    
    import com.stripe.param.PaymentIntentCreateParams;
    import java.util.HashMap;
    import java.util.Map;
    import spark.Request;
    import spark.Response;
    import spark.Route;
    
    public class MyApp {
    
      static class ConfirmPaymentRequest {
        @SerializedName("payment_method_id")
        String paymentMethodId;
        @SerializedName("payment_intent_id")
        String paymentIntentId;
    
        public String getPaymentMethodId() {
          return paymentMethodId;
        }
    
        public String getPaymentIntentId() {
          return paymentIntentId;
        }
      }
    
      /**
       * Your application.
       */
      public static void main(String[] args) {
    
        Stripe.apiKey = System.getenv("STRIPE_SECRET_KEY");
    
        post(new Route("/ajax/confirm_payment") {
    
          @Override
          public Object handle(Request request, Response response) {
            Gson gson = new Gson();
            ConfirmPaymentRequest confirmRequest =
                gson.fromJson(request.body(), ConfirmPaymentRequest.class);
            PaymentIntent intent;
            try {
              if (confirmRequest.getPaymentMethodId() != null) {
                PaymentIntentCreateParams createParams = PaymentIntentCreateParams.builder()
                    .setAmount(1099)
                    .setCurrency("usd")
                    .setConfirm(true)
                    .setPaymentMethod(confirmRequest.paymentMethodId)
                    .setConfirmationMethod(PaymentIntentCreateParams.ConfirmationMethod.MANUAL)
                    .build();
                intent = PaymentIntent.create(createParams);
              } else if (confirmRequest.getPaymentIntentId() != null) {
                intent = PaymentIntent.retrieve(confirmRequest.getPaymentIntentId());
                intent.confirm();
              }
    
              Map<String, Object> responseData = generatePaymentResponse(response, intent);
    
              return gson.toJson(responseData);
            } catch (Exception e) {
              response.status(500);
              Map<String, Object> errorResponse = new HashMap<>();
              errorResponse.put("message", e.getMessage());
              return gson.toJson(errorResponse);
            }
          }
    
          private Map<String, Object> generatePaymentResponse(Response response,
                                                              PaymentIntent intent) {
            response.type("application/json");
            Map<String, Object> responseData = new HashMap<>();
            if (intent.getStatus().equals("requires_action")
                && intent.getNextAction().getType().equals("use_stripe_sdk")) {
              responseData.put("requires_action", true);
              responseData.put("payment_intent_client_secret", intent.getClientSecret());
            } else if (intent.getStatus().equals("succeeded")) {
              responseData.put("success", true);
            } else {
              // invalid status
              responseData.put("Error", "Invalid status");
              response.status(500);
              return responseData;
            }
            response.status(200);
            return responseData;
          }
        });
      }
    }
    
    // Using Express
    const express = require('express');
    const app = express();
    app.use(express.json());
    
    app.post('/ajax/confirm_payment', async (request, response) => {
      try {
        let intent;
        if (request.body.payment_method_id) {
          // Create the PaymentIntent
          intent = await stripe.paymentIntents.create({
            payment_method: request.body.payment_method_id,
            amount: 1099,
            currency: 'usd',
            confirmation_method: 'manual',
            confirm: true
          });
        } else if (request.body.payment_intent_id) {
          intent = await stripe.paymentIntents.confirm(
            request.body.payment_intent_id
          );
        }
        // Send the response to the client
        response.send(generate_payment_response(intent));
      } catch (e) {
        // Display error on client
        return response.send({ error: e.message });
      }
    });
    
    const generate_payment_response = (intent) => {
      if (
        intent.status === 'requires_action' &&
        intent.next_action.type === 'use_stripe_sdk'
      ) {
        // Tell the client to handle the action
        return {
          requires_action: true,
          payment_intent_client_secret: intent.client_secret
        };
      } else if (intent.status === 'succeeded') {
        // The payment didn’t need any additional actions and completed!
        // Handle post-payment fulfillment
        return {
          success: true
        };
      } else {
        // Invalid status
        return {
          error: "Invalid PaymentIntent status"
        }
      }
    };
    
    package main
    
    import (
      "encoding/json"
      "net/http"
      "os"
      "fmt"
    
      stripe "github.com/stripe/stripe-go"
      paymentintent "github.com/stripe/stripe-go/paymentintent"
    )
    
    // Structs to handle request and response JSON serializations
    type ConfirmPaymentRequest struct {
      PaymentMethodId *string `json:"payment_method_id"`
      PaymentIntentId *string `json:"payment_intent_id"`
    }
    
    type ErrorResponse struct {
      Message string `json:"error"`
    }
    
    type ConfirmPaymentResponse struct {
      RequiresAction bool `json:"requires_action"`
      PaymentIntentClientSecret *string `json:"payment_intent_client_secret"`
    }
    
    func generatePaymentResponse(intent *stripe.PaymentIntent, w http.ResponseWriter) {
      if intent.Status == stripe.PaymentIntentStatusRequiresAction &&
        intent.NextAction.Type == "use_stripe_sdk" {
    
        // Tell the client to handle the action
        w.WriteHeader(http.StatusOK)
        json.NewEncoder(w).Encode(ConfirmPaymentResponse{
          RequiresAction: true,
          PaymentIntentClientSecret: &intent.ClientSecret,
        })
    
      } else if intent.Status == stripe.PaymentIntentStatusSucceeded {
    
        // The payment didn’t need any additional actions and completed!
        // Handle post-payment fulfillment
        w.WriteHeader(http.StatusOK)
        fmt.Fprint(w, "Success")
    
      } else {
    
        // Invalid status
        w.WriteHeader(http.StatusInternalServerError)
        fmt.Fprintf(w, "Invalid Payment Intent status: %s", intent.Status)
    
      }
    }
    
    func main() {
      // Initialize stripe-go with Stripe secret key from environment
      stripe.Key = os.Getenv("STRIPE_SECRET_KEY")
    
      // AJAX endpoint when `/ajax/confirm_payment` is called from client
      http.HandleFunc("/ajax/confirm_payment",
        func(w http.ResponseWriter, req *http.Request) {
          var confirmPaymentRequest ConfirmPaymentRequest
          json.NewDecoder(req.Body).Decode(&confirmPaymentRequest)
    
          var intent *stripe.PaymentIntent
          var err error
    
          if confirmPaymentRequest.PaymentMethodId != nil {
            // Create the PaymentIntent
            params := &stripe.PaymentIntentParams{
              PaymentMethod: stripe.String(*confirmPaymentRequest.PaymentMethodId),
              Amount: stripe.Int64(1099),
              Currency: stripe.String(string(stripe.CurrencyUSD)),
              ConfirmationMethod: stripe.String(string(
                stripe.PaymentIntentConfirmationMethodManual,
              )),
              Confirm: stripe.Bool(true),
            }
            intent, err = paymentintent.New(params)
          }
    
          if confirmPaymentRequest.PaymentIntentId != nil {
            intent, err = paymentintent.Confirm(
              *confirmPaymentRequest.PaymentIntentId, nil,
            )
          }
    
          if err != nil {
            if stripeErr, ok := err.(*stripe.Error); ok {
              // Display error on client
              w.WriteHeader(http.StatusOK)
              json.NewEncoder(w).Encode(ErrorResponse{Message: stripeErr.Msg})
            } else {
              w.WriteHeader(http.StatusInternalServerError)
              fmt.Fprintf(w, "Other error occurred, %v\n", err.Error())
            }
          } else {
            generatePaymentResponse(intent, w)
          }
        })
    
      http.ListenAndServe(":"+os.Getenv("PORT"), nil)
    }
    
    using System;
    using System.Collections.Generic;
    using Microsoft.AspNetCore.Mvc;
    
    using Stripe;
    using Newtonsoft.Json;
    
    namespace Controllers
    {
        public class ConfirmPaymentRequest
        {
            [JsonProperty("payment_method_id")]
            public string PaymentMethodId { get; set; }
    
            [JsonProperty("payment_intent_id")]
            public string PaymentIntentId { get; set; }
        }
    
        // AJAX endpoint when `/ajax/confirm_payment` is called from client
        [Route("/ajax/confirm_payment")]
        public class ConfirmPaymentController : Controller
        {
            public IActionResult Index([FromBody] ConfirmPaymentRequest request)
            {
                var paymentIntentService = new PaymentIntentService();
                PaymentIntent paymentIntent = null;
    
                try
                {
                    if (request.PaymentMethodId != null)
                    {
                        // Create the PaymentIntent
                        var createOptions = new PaymentIntentCreateOptions
                        {
                            PaymentMethodId = request.PaymentMethodId,
                            Amount = 1099,
                            Currency = "usd",
                            ConfirmationMethod = "manual",
                            Confirm = true,
                        };
                        paymentIntent = paymentIntentService.Create(createOptions);
                    }
                    if (request.PaymentIntentId != null)
                    {
                        var confirmOptions = new PaymentIntentConfirmOptions{};
                        paymentIntent = paymentIntentService.Confirm(
                            request.PaymentIntentId,
                            confirmOptions
                        );
                    }
                }
                catch (StripeException e)
                {
                    return Json(new { error = e.StripeError.Message });
                }
                return generatePaymentResponse(paymentIntent);
              }
    
              private IActionResult generatePaymentResponse(PaymentIntent intent)
              {
                if (intent.Status == "requires_action" &&
                    intent.NextAction.Type == "use_stripe_sdk")
                {
                    // Tell the client to handle the action
                    return Json(new
                    {
                        requires_action = true,
                        payment_intent_client_secret = intent.ClientSecret
                    });
                }
                else if (intent.Status == "succeeded")
                {
                    // The payment didn’t need any additional actions and completed!
                    // Handle post-payment fulfillment
                    return Json(new { success = true });
                }
                else
                {
                    // Invalid status
                    return StatusCode(500, new { error = "Invalid PaymentIntent status" });
                }
            }
        }
    }
    

    Test the integration

    It’s important to thoroughly test your Payment Intents API integration to make sure you’re correctly handling cards that require additional authentication and cards that don’t to ensure the smoothest checkout experience for your users. We have test cards you can use in test mode to simulate different types of cards.

    The two test cards you should use to validate your integration are 3D Secure required and 3D Secure not required cards. You can use these card numbers with any expiration date in the future and any three digit CVC code.

    Card Number 3D Secure usage Description
    4000000000003220 Required This test card requires 3D Secure 2 on all transactions and will trigger 3D Secure 2 in test mode.
    4000000000003055 Supported This test card supports but does not require 3D Secure 2 and will not require additional authentication steps in test mode.

    Use these cards in your application or the payments demo to see the different behavior.

    Viewport meta tag requirements

    In order to provide a great user experience for 3D Secure3D Secure provides an additional layer of authentication for credit card transactions that protects merchants from liability for fraudulent card payments. During a transaction that incorporates the 3D Secure authorization process, the customer is prompted to supply a separate password or code to validate their purchase. on all devices, you should set your page’s viewport width to device-width with the the viewport meta tag. There are several other viewport settings, and you can configure those based on your needs. Just make sure you include width=device-width somewhere in your configuration.

    For instance the following will work with Elements:

    <meta name="viewport" content="width=device-width, initial-scale=1" />

    If you already have this tag on your page and it includes width=device-width, then you are all set.

    Next steps

    Now you have a complete Payment Intents API integration suitable for accepting card payments. To learn more, continue reading:

    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.

    On this page