Using Payment Intents on Android

    Learn how to build an Android application that uses the Payment Intents API to accept card 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 Payment 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

    Automatic confirmation is the fastest way to get started with the Payment Intents API. Because some payment methods have asynchronous flows, such as 3D Secure 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 information and create ConfirmPaymentIntentParams
    4. Confirm the PaymentIntent
    5. Asynchronously fulfill the customer’s order

    Step 1: Create a PaymentIntent and make its client secret accessible to your application

    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
    const 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.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    var service = new PaymentIntentService();
    var options = new PaymentIntentCreateOptions
    {
        Amount = 1099,
        Currency = "usd",
    };
    service.Create(options);
    

    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. Separate authorization and capture is also supported for card payments.

    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
    const 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.ApiKey = "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 pass to your app to create a charge. This sample code assumes createPaymentIntent calls the server endpoint that you set up in step 1 to create a PaymentIntent.

    public class PaymentActivity extends Activity {
      private Stripe mStripe;
    
      @Override
      protected void onCreate(@Nullable Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          PaymentConfiguration.init("pk_test_TYooMQauvdEDq54NiTphI7jx");
          mStripe = new Stripe(this,
              PaymentConfiguration.getInstance().getPublishableKey());
    
          // Create the PaymentIntent on your backend
          myBackendApiClient.createPaymentIntent(100, "usd", "off_session",
              new ApiResultCallback<String>() {
                  @Override
                  public void onSuccess(@NonNull String clientSecret) {
                      // Hold onto the clientSecret for Step 4
                  }
    
                  @Override
                  public void onError(@NonNull Exception e) {
                  }
              });
    
      }
    }
    class PaymentActivity : Activity() {
      private lateinit var stripe: Stripe
    
      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          PaymentConfiguration.init("pk_test_TYooMQauvdEDq54NiTphI7jx")
          stripe = Stripe(this,
              PaymentConfiguration.getInstance().publishableKey)
    
          // create the PaymentIntent on your backend
          myBackendApiClient.createPaymentIntent(
              amount: 100, currency: "usd", setupFutureUsage: "off_session",
              object: ApiResultCallback<String> {
                  override fun onSuccess(clientSecret: String) {
                      // Hold onto the clientSecret for Step 4
                  }
    
                  override fun onError(e: Exception) {
                  }
              })
      }
    }

    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 the data model of your application, retrieving it again as necessary.

    You can use the client secret to complete the payment process with the amount specified on the PaymentIntent. Do not log it, embed it in URLs, or expose it to anyone other than the customer.

    Step 3: Collect payment information and create ConfirmPaymentIntentParams

    In your application, collect card details from the customer. There are a few ways to do this:

    Once you’ve collected the payment information, create a ConfirmPaymentIntentParams object with a new or existing PaymentMethod object.

    To create a ConfirmPaymentIntentParams object with a new PaymentMethod:

    1. Convert the Card object to a PaymentMethodCreateParams.Card using Card#toPaymentMethodParamsCard().

    2. Pass the PaymentMethodCreateParams.Card to PaymentMethodCreateParams.create() to create a new PaymentMethodCreateParams object.

    3. Call ConfirmPaymentIntentParams#createWithPaymentMethodCreateParams(), passing in the PaymentMethodCreateParams object, the PaymentIntent’s client secret, and a return URL, indicating where the PaymentIntent should return if it is redirected as part of the authentication process.

    @NonNull
    private ConfirmPaymentIntentParams createConfirmPaymentIntentParams(
            @NonNull String clientSecret,
            @NonNull PaymentMethod.BillingDetails billingDetails) {
        final PaymentMethodCreateParams.Card paymentMethodParamsCard =
                mCardInputWidget.getCard().toPaymentMethodParamsCard();
        final PaymentMethodCreateParams paymentMethodCreateParams =
                PaymentMethodCreateParams.create(paymentMethodParamsCard,
                    billingDetails);
        return ConfirmPaymentIntentParams.createWithPaymentMethodCreateParams(
                paymentMethodCreateParams, clientSecret,
                "yourapp://post-authentication-return-url");
    }
    private fun createPaymentIntentParams(
        clientSecret: String,
        billingDetails: PaymentMethod.BillingDetails): ConfirmPaymentIntentParams {
        val paymentMethodParamsCard = mCardInputWidget!!.card!!.toPaymentMethodParamsCard()
        val paymentMethodCreateParams =
            PaymentMethodCreateParams.create(paymentMethodParamsCard,
                billingDetails)
        return ConfirmPaymentIntentParams.createWithPaymentMethodCreateParams(
            paymentMethodCreateParams, clientSecret,
            "yourapp://post-authentication-return-url")
    }

    If you’re creating a ConfirmPaymentIntentParams object with an existing PaymentMethod, use createWithPaymentMethodId and pass in the ID of the existing PaymentMethod and the return URL, indicating where the PaymentIntent should return if it is redirected as part of the authentication process.

    @NonNull
    private ConfirmPaymentIntentParams createConfirmPaymentIntentParams(
            @NonNull String clientSecret,
            @NonNull String paymentMethodId) {
        return ConfirmPaymentIntentParams.createWithPaymentMethodId(
                paymentMethodId, clientSecret,
                "yourapp://post-authentication-return-url");
    }
    private fun createConfirmPaymentIntentParams(
        clientSecret: String,
        paymentMethodId: String): ConfirmPaymentIntentParams {
        return ConfirmPaymentIntentParams.createWithPaymentMethodId(
            paymentMethodId, clientSecret,
            "yourapp://post-authentication-return-url")
    }

    Extra parameters

    The parameter constructors are provided as convenience methods for the most common fields. To send additional data, use PaymentIntentParams#setExtraParams. Any extra parameters are included in the request to Stripe. For example, to send an e-mail to the customer on payment confirmation, use the receipt_email parameter:

    final Map<String, Object> extraParams = new HashMap();
    extraParams.put("receipt_email", "{{YOUR_RECEIPT_EMAIL}}");
    ConfirmPaymentIntentParams
                  .createWithPaymentMethodId(paymentMethodId, clientSecret,
                          returnUrl, false, extraParams);
    val extraParams = mapOf("receipt_email" to "{{YOUR_RECEIPT_EMAIL}}")
    ConfirmPaymentIntentParams
                  .createWithPaymentMethodId(paymentMethodId, clientSecret,
                          returnUrl, false, extraParams)

    Step 4: Confirm the PaymentIntent

    To initiate payment collection, you must confirm that your customer intends to pay with the provided payment details.

    To finish confirming the payment, pass the current Activity and ConfirmPaymentIntentParams object to Stripe#confirmPayment(). Some payment methods require additional authentication steps in order to complete payment. The SDK manages the payment confirmation and authentication flow, which may involve presenting additional screens required for authentication. For more information on 3D Secure authentication and customizing the authentication experience, see Supporting 3D Secure Authentication on Android.

    The result of the flow returns to your calling Activity via Activity#onActivityResult(). Handle the result by calling Stripe#onPaymentResult() within Activity#onActivityResult(). The PaymentIntentResult returned in ApiResultCallback#onSuccess() has two getters:

    • getIntent() - a PaymentIntent object that has been retrieved after confirmation/authentication
    • getOutcome() - a StripeIntentResult.Status value that indicates the outcome of payment authentication
      • SUCCEEDED - confirmation or payment authentication succeeded
      • FAILED - confirmation or payment authentication failed
      • CANCELED - the customer canceled required payment authentication
      • TIMEDOUT - the payment authentication attempt timed-out
    public class PaymentActivity extends Activity {
        private Stripe mStripe;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            PaymentConfiguration.init("pk_test_TYooMQauvdEDq54NiTphI7jx");
            mStripe = new Stripe(this,
                PaymentConfiguration.getInstance().getPublishableKey());
    
            // now retrieve the PaymentIntent that was created on your backend
        }
    
        private void confirmPayment(@NonNull ConfirmPaymentIntentParams params) {
            // Optional: customize the payment authentication experience
            final PaymentAuthConfig.Stripe3ds2UiCustomization uiCustomization =
                new PaymentAuthConfig.Stripe3ds2UiCustomization.Builder()
                    .build();
            PaymentAuthConfig.init(new PaymentAuthConfig.Builder()
                    .set3ds2Config(new PaymentAuthConfig.Stripe3ds2Config.Builder()
                            // set a 5 minute timeout for challenge flow
                            .setTimeout(5)
                            // customize the UI of the challenge flow
                            .setUiCustomization(uiCustomization)
                            .build())
                    .build());
    
            mStripe.confirmPayment(this, params);
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            mStripe.onPaymentResult(requestCode, data,
                new ApiResultCallback<PaymentIntentResult>() {
                    @Override
                    public void onSuccess(@NonNull PaymentIntentResult result) {
                        // If authentication succeeded, the PaymentIntent will
                        // have user actions resolved; otherwise, handle the
                        // PaymentIntent status as appropriate (e.g. the
                        // customer may need to choose a new payment method)
    
                        final PaymentIntent paymentIntent = result.getIntent();
                        final PaymentIntent.Status status =
                            paymentIntent.getStatus();
                        if (status == PaymentIntent.Status.Succeeded) {
                            // show success UI
                        } else if (PaymentIntent.Status.RequiresPaymentMethod
                                == status) {
                            // attempt authentication again or
                            // ask for a new Payment Method
                        }
                    }
    
                    @Override
                    public void onError(@NonNull Exception e) {
                        // handle error
                    }
                });
        }
    }
    class PaymentActivity : Activity() {
        private lateinit var stripe: Stripe
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            PaymentConfiguration.init("pk_test_TYooMQauvdEDq54NiTphI7jx")
            stripe = Stripe(this,
                PaymentConfiguration.getInstance().publishableKey)
    
            // now retrieve the PaymentIntent that was created on your backend
        }
    
        private fun confirmPayment(params: ConfirmPaymentIntentParams) {
            // Optional: customize the payment authentication experience
            val uiCustomization = PaymentAuthConfig.Stripe3ds2UiCustomization.Builder()
                .build()
            PaymentAuthConfig.init(PaymentAuthConfig.Builder()
                .set3ds2Config(PaymentAuthConfig.Stripe3ds2Config.Builder()
                    // set a 5 minute timeout for challenge flow
                    .setTimeout(5)
                    // customize the UI of the challenge flow
                    .setUiCustomization(uiCustomization)
                    .build())
                .build())
    
            stripe.confirmPayment(this, params)
        }
    
        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
            super.onActivityResult(requestCode, resultCode, data)
            stripe.onPaymentResult(requestCode, data,
                object : ApiResultCallback<PaymentIntentResult> {
                    override fun onSuccess(result: PaymentIntentResult) {
                        // If authentication succeeded, the PaymentIntent will have
                        // user actions resolved; otherwise, handle the PaymentIntent
                        // status as appropriate (e.g. the customer may need to choose
                        // a new payment method)
    
                        val paymentIntent = result.intent
                        val status = paymentIntent.status
                        if (status == StripeIntent.Status.Succeeded) {
                            // show success UI
                        } else if (PaymentIntent.Status.RequiresPaymentMethod == status) {
                            // attempt authentication again or
                            // ask for a new Payment Method
                        }
                    }
    
                    override fun onError(e: Exception) {
                        // handle error
                    }
                })
        }
    }

    Step 5: Asynchronously fulfill the customer’s order

    You can use the PaymentIntent returned in PaymentIntentResult to provide immediate feedback to your customers when the payment completes on the client. However, your integration should not attempt to handle order fulfillment on the client side because it is possible for customers to leave the app 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 you attempt to collect payment from a customer, the PaymentIntent creates a Charge object. You can inspect the PaymentIntent’s charges.data property to obtain the latest charge. For the complete list of attempted charges, use the Charges API to list all charges with the PaymentIntent’s 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'
    
    charges = Stripe::Charge.list({
      payment_intent: '{{PAYMENT_INTENT_ID}}',
      # Limit the number of objects to return (the default is 10)
      limit: 3,
    })
    
    # 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'
    
    charges = stripe.Charge.list(
      payment_intent = '{{PAYMENT_INTENT_ID}}',
      # Limit the number of objects to return (the default is 10)
      limit = 3,
    )
    
    // 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');
    
    $charges = \Stripe\Charge::all([
        'payment_intent' => '{{PAYMENT_INTENT_ID}}',
        // Limit the number of objects to return (the default is 10)
        'limit' => 3,
    ]);
    
    // 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> chargeParams = new HashMap<String, Object>();
    chargeParams.put("payment_intent", "{{PAYMENT_INTENT_ID}}");
    // Limit the number of objects to return (the default is 10)
    chargeParams.put("limit", "3");
    
    try {
      ChargeCollection charges = Charge.list(chargeParams);
    } catch (StripeException e) {
      // handle error from Stripe API
    }
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
    
    (async () => {
      const charges = stripe.charges.list({
        payment_intent: '{{PAYMENT_INTENT_ID}}',
        // Limit the number of objects to return (the default is 10)
        limit: 3,
      });
    })();
    
    // 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.ChargeListParams{}
    params.Filters.AddFilter("payment_intent", "", "{{PAYMENT_INTENT_ID}}")
    // Limit the number of objects to return (the default is 10)
    params.Filters.AddFilter("limit", "", "3")
    i := charge.List(params)
    for i.Next() {
      c := i.Charge()
    }
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    var service = new ChargeService();
    var options = new ChargeListOptions
    {
        PaymentIntentId = "{{PAYMENT_INTENT_ID}}",
        // Limit the number of objects to return (the default is 10)
        Limit = 3,
    };
    var charges = service.List(options);
    

    The charges are listed in reverse chronological order, so the most recent charge is first in the list. Note that the list 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

    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 Secure.

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

    1. Collect card information and create a PaymentMethod
    2. Create and confirm a PaymentIntent on the server
    3. Authenticate the payment
    4. Confirm the PaymentIntent again on the server

    Step 1: Collect card information and create a PaymentMethod

    If you already have a PaymentMethod that you would like to associate with the PaymentIntent, skip this step.

    To create a PaymentMethod object using the Payment Methods API with the Android SDK, use the appropriate PaymentMethodCreateParams creation method for your PaymentMethod.Type.

    PaymentMethodCreateParams.Card paymentMethodCreateParamsCard =
        mCardInputWidget.getCard().toPaymentMethodParamsCard();
    PaymentMethodCreateParams paymentMethodCreateParams =
        PaymentMethodCreateParams.create(paymentMethodCreateParamsCard);
    
    // create a PaymentMethod synchronously (don't call this on the UI thread)
    PaymentMethod paymentMethod =
        mStripe.createPaymentMethodSynchronous(paymentMethodCreateParams, null);
    val paymentMethodCreateParamsCard = cardInputWidget.getCard()
        .toPaymentMethodParamsCard()
    val paymentMethodCreateParams = PaymentMethodCreateParams
        .create(paymentMethodCreateParamsCard, null)
    
    // create a PaymentMethod synchronously (don't call this on the UI thread)
    val paymentMethod = stripe
        .createPaymentMethodSynchronous(paymentMethodCreateParams)

    Once you have a PaymentMethodCreateParams object, create a PaymentMethod with Stripe#createPaymentMethodSynchronous().

    Step 2: Create and confirm 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 confirm the PaymentIntent by setting confirm property to true when the PaymentIntent is created or by calling confirm after creation. Separate authorization and capture is also supported for card payments.

    The endpoint should accept the following parameters:

    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 \
      -d return_url="{{RETURN_URL}}" \
      -d use_stripe_sdk=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,
            return_url: data['return_url'],
            use_stripe_sdk: '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)
      # Note that if your API version is before 2019-02-11, 'requires_action'
      # appears as 'requires_source_action'.
      if intent.status == 'requires_action'
        # 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 is 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,
            return_url = data['return_url'],
            use_stripe_sdk = 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.user_message}), 200
    
      return generate_payment_response(intent)
    
    def generate_payment_response(intent):
      # Note that if your API version is before 2019-02-11, 'requires_action'
      # appears as 'requires_source_action'.
      if intent.status == 'requires_action':
        # 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,
            'return_url' => $json_obj->return_url,
            'use_stripe_sdk' => 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) {
        # Note that if your API version is before 2019-02-11, 'requires_action'
        # appears as 'requires_source_action'.
        if ($intent->status == 'requires_action') {
          # 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;
        @SerializedName("return_url")
        String returnURL;
    
        public String getPaymentMethodId() {
          return paymentMethodId;
        }
    
        public String getPaymentIntentId() {
          return paymentIntentId;
        }
    
        public String getReturnURL() {
          return returnURL;
        }
      }
    
      /**
       * 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)
                    .setReturnURL(confirmRequest.returnURL)
                    .setUseStripeSDK(true)
                    .build();
                intent = PaymentIntent.create(createParams);
              } else if (confirmRequest.getPaymentIntentId() != null) {
                intent = PaymentIntent.retrieve(confirmRequest.getPaymentIntentId());
                intent = 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<>();
            // Note that if your API version is before 2019-02-11, 'requires_action'
            // appears as 'requires_source_action'.
            if (intent.getStatus().equals("requires_action")) {
              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
            returnURL: request.body.return_url,
            use_stripe_sdk: 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) => {
      // Note that if your API version is before 2019-02-11, 'requires_action'
      // appears as 'requires_source_action'.
      if (
        intent.status === 'requires_action'
      ) {
        // 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"`
      ReturnURL *string `json:"return_url"`
    }
    
    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) {
      // Note that if your API version is before 2019-02-11, PaymentIntentStatusRequiresAction
      // appears as PaymentIntentStatusRequiresSourceAction.
      if intent.Status == stripe.PaymentIntentStatusRequiresAction {
    
        // 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),
              ReturnURL: stripe.String(*confirmPaymentRequest.ReturnURL),
              UseStripeSDK: 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; }
    
            [JsonProperty("return_url")]
            public string ReturnURL { 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,
                            ReturnURL = request.ReturnURL,
                            UseStripeSDK = 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)
              {
                // Note that if your API version is before 2019-02-11, 'requires_action'
                // appears as 'requires_source_action'.
                if (intent.Status == "requires_action")
                {
                    // 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: Authenticate the payment

    Some payment methods may require additional authentication steps in order to complete a payment. To determine whether further action is required from your customer, check the status of the PaymentIntent object returned from confirmation in the previous step.

    If the PaymentIntent’s status property is requires_action, further action is required from your customer in order to complete the payment. Pass the current Activity and PaymentIntent’s client_secret to Stripe#authenticatePayment(). The SDK manages the payment authentication flow, which may involve presenting additional screens required for authentication. For more information on 3D Secure authentication and customizing the authentication experience, see Supporting 3D Secure Authentication on Android.

    public class PaymentActivity extends Activity {
        private Stripe mStripe;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            PaymentConfiguration.init("pk_test_TYooMQauvdEDq54NiTphI7jx");
            mStripe = new Stripe(this,
                PaymentConfiguration.getInstance().getPublishableKey());
    
            // now retrieve the PaymentIntent that was created on your backend
        }
    
        private void handlePaymentIntent(@NonNull PaymentIntent paymentIntent) {
            if (PaymentIntent.Status.RequiresAction == paymentIntent.getStatus()) {
                mStripe.authenticatePayment(this, paymentIntent.getClientSecret());
            } else if (PaymentIntent.Status.Succeeded == paymentIntent.getStatus()) {
              // payment captured succeeded
            }
        }
    }
    class PaymentActivity : Activity() {
        private lateinit var stripe: Stripe
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            PaymentConfiguration.init("pk_test_TYooMQauvdEDq54NiTphI7jx")
            stripe = Stripe(this,
                PaymentConfiguration.getInstance().publishableKey)
    
            // now retrieve the PaymentIntent that was created on your backend
        }
    
        private fun handlePaymentIntent(paymentIntent: PaymentIntent) {
            if (PaymentIntent.Status.RequiresAction == paymentIntent.status) {
                stripe.authenticatePayment(this, paymentIntent.clientSecret)
            } else if (PaymentIntent.Status.Succeeded == paymentIntent.status) {
                // payment captured succeeded
            }
        }
    }

    The result of payment authentication returns to your calling Activity via Activity#onActivityResult(). Handle the result by calling Stripe#onPaymentResult() within Activity#onActivityResult(). The PaymentIntentResult returned in ApiResultCallback#onSuccess() has two getters:

    • getIntent() - a PaymentIntent object that has been retrieved after confirmation/authentication
    • getOutcome() - a StripeIntentResult.Status value that indicates the outcome of payment authentication
      • SUCCEEDED - payment authentication succeeded
      • FAILED - payment authentication failed
      • CANCELED - the customer canceled required payment authentication
      • TIMEDOUT - the payment authentication attempt timed-out
    public class PaymentActivity extends Activity {
        private Stripe mStripe;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            PaymentConfiguration.init("pk_test_TYooMQauvdEDq54NiTphI7jx");
            mStripe = new Stripe(this,
                PaymentConfiguration.getInstance().getPublishableKey());
    
            // now retrieve the PaymentIntent that was created on your backend
        }
    
        private void handlePaymentIntent(@NonNull PaymentIntent paymentIntent) {
            if (PaymentIntent.Status.RequiresAction == paymentIntent.getStatus()) {
            // Optional: customize the payment authentication experience
                final StripeUiCustomization uiCustomization =
                    new StripeUiCustomization();
                PaymentAuthConfig.init(new PaymentAuthConfig.Builder()
                        .set3ds2Config(new PaymentAuthConfig.Stripe3ds2Config.Builder()
                                // set a 5 minute timeout for challenge flow
                                .setTimeout(5)
                                // customize the UI of the challenge flow
                                .setUiCustomization(uiCustomization)
                                .build())
                        .build());
    
                mStripe.authenticatePayment(this, paymentIntent.getClientSecret());
            } else if (PaymentIntent.Status.Succeeded == paymentIntent.getStatus()) {
              // payment captured succeeded
            }
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            mStripe.onPaymentResult(requestCode, data,
                new ApiResultCallback<PaymentIntentResult>() {
                    @Override
                    public void onSuccess(@NonNull PaymentIntentResult result) {
                        // If authentication succeeded, the PaymentIntent will
                        // have user actions resolved; otherwise, handle the
                        // PaymentIntent status as appropriate (e.g. the
                        // customer may need to choose a new payment method)
    
                        final PaymentIntent.Status status =
                            result.getIntent().getStatus();
                        if (PaymentIntent.Status.RequiresPaymentMethod == status) {
                            // attempt authentication again or
                            // ask for a new Payment Method
                        } else if (PaymentIntent.Status.RequiresCapture == status) {
                            // capture payment on the server
                        } else if (PaymentIntent.Status.Succeeded == status) {
                            // capture succeeded
                        } else {
                            // optionally handle other PaymentIntent status values
                        }
                    }
    
                    @Override
                    public void onError(@NonNull Exception e) {
                        // handle error
                    }
                });
        }
    }
    class PaymentActivity : Activity() {
        private lateinit var stripe: Stripe
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            PaymentConfiguration.init("pk_test_TYooMQauvdEDq54NiTphI7jx")
            stripe = Stripe(this,
                PaymentConfiguration.getInstance().publishableKey)
    
            // now retrieve the PaymentIntent that was created on your backend
        }
    
        private fun handlePaymentIntent(paymentIntent: PaymentIntent) {
            if (PaymentIntent.Status.RequiresAction == paymentIntent.status) {
                // Optional: customize the payment authentication experience
                val uiCustomization = PaymentAuthConfig.Stripe3ds2UiCustomization.Builder()
                    .build()
                PaymentAuthConfig.init(PaymentAuthConfig.Builder()
                    .set3ds2Config(PaymentAuthConfig.Stripe3ds2Config.Builder()
                        // set a 5 minute timeout for challenge flow
                        .setTimeout(5)
                        // customize the UI of the challenge flow
                        .setUiCustomization(uiCustomization)
                        .build())
                    .build())
    
                stripe.authenticatePayment(this, paymentIntent.clientSecret)
            } else if (PaymentIntent.Status.Succeeded == paymentIntent.status) {
                // payment captured succeeded
            }
        }
    
        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
            super.onActivityResult(requestCode, resultCode, data)
            stripe.onPaymentResult(requestCode, data,
                object : ApiResultCallback<PaymentIntentResult> {
                    override fun onSuccess(result: PaymentIntentResult) {
                        // If authentication succeeded, the PaymentIntent will have
                        // user actions resolved; otherwise, handle the PaymentIntent
                        // status as appropriate (e.g. the customer may need to choose
                        // a new payment method)
    
                        val status = result.intent.status
                        if (PaymentIntent.Status.RequiresPaymentMethod == status) {
                            // attempt authentication again or
                            // ask for a new Payment Method
                        } else if (PaymentIntent.Status.RequiresCapture == status) {
                            // capture payment on the server
                        } else if (PaymentIntent.Status.Succeeded == status) {
                            // capture succeeded
                        } else {
                            // optionally handle other PaymentIntent status values
                        }
                    }
    
                    override fun onError(e: Exception) {
                        // handle error
                    }
                })
        }
    }

    Step 4: Confirm the PaymentIntent again on the server

    If handling next actions in step 3 was necessary, use the same endpoint you set up in Step 2 to confirm the PaymentIntent again to finalize the payment and fulfill 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,
            return_url: data['return_url'],
            use_stripe_sdk: '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)
      # Note that if your API version is before 2019-02-11, 'requires_action'
      # appears as 'requires_source_action'.
      if intent.status == 'requires_action'
        # 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 is 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,
            return_url = data['return_url'],
            use_stripe_sdk = 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.user_message}), 200
    
      return generate_payment_response(intent)
    
    def generate_payment_response(intent):
      # Note that if your API version is before 2019-02-11, 'requires_action'
      # appears as 'requires_source_action'.
      if intent.status == 'requires_action':
        # 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,
            'return_url' => $json_obj->return_url,
            'use_stripe_sdk' => 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) {
        # Note that if your API version is before 2019-02-11, 'requires_action'
        # appears as 'requires_source_action'.
        if ($intent->status == 'requires_action') {
          # 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;
        @SerializedName("return_url")
        String returnURL;
    
        public String getPaymentMethodId() {
          return paymentMethodId;
        }
    
        public String getPaymentIntentId() {
          return paymentIntentId;
        }
    
        public String getReturnURL() {
          return returnURL;
        }
      }
    
      /**
       * 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)
                    .setReturnURL(confirmRequest.returnURL)
                    .setUseStripeSDK(true)
                    .build();
                intent = PaymentIntent.create(createParams);
              } else if (confirmRequest.getPaymentIntentId() != null) {
                intent = PaymentIntent.retrieve(confirmRequest.getPaymentIntentId());
                intent = 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<>();
            // Note that if your API version is before 2019-02-11, 'requires_action'
            // appears as 'requires_source_action'.
            if (intent.getStatus().equals("requires_action")) {
              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
            returnURL: request.body.return_url,
            use_stripe_sdk: 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) => {
      // Note that if your API version is before 2019-02-11, 'requires_action'
      // appears as 'requires_source_action'.
      if (
        intent.status === 'requires_action'
      ) {
        // 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"`
      ReturnURL *string `json:"return_url"`
    }
    
    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) {
      // Note that if your API version is before 2019-02-11, PaymentIntentStatusRequiresAction
      // appears as PaymentIntentStatusRequiresSourceAction.
      if intent.Status == stripe.PaymentIntentStatusRequiresAction {
    
        // 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),
              ReturnURL: stripe.String(*confirmPaymentRequest.ReturnURL),
              UseStripeSDK: 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; }
    
            [JsonProperty("return_url")]
            public string ReturnURL { 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,
                            ReturnURL = request.ReturnURL,
                            UseStripeSDK = 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)
              {
                // Note that if your API version is before 2019-02-11, 'requires_action'
                // appears as 'requires_source_action'.
                if (intent.Status == "requires_action")
                {
                    // 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" });
                }
            }
        }
    }
    

    Using Google Pay with the Payment Intents API

    To integrate with Google Pay, use the token returned by your Google Pay integration to confirm the PaymentIntent. Create a PaymentMethodCreateParams object with PaymentMethodCreateParams.create(), passing in the ID of the token to PaymentMethodCreateParams.Card#create(). After that, you can confirm the PaymentIntent as normal.

    private void confirmPaymentIntent(
            @NonNull String paymentIntentClientSecret,
            @NonNull PaymentMethod.BillingDetails billingDetails) {
        PaymentMethodCreateParams paymentMethodCreateParams =
                PaymentMethodCreateParams.create(
                        PaymentMethodCreateParams.Card.create("tok_visa"),
                        billingDetails);
    
        // confirm PaymentIntent
    }
    private fun confirmPaymentIntent(paymentIntentClientSecret: String,
                                     billingDetails: PaymentMethod.BillingDetails) {
        val paymentMethodCreateParams =
            PaymentMethodCreateParams.create(
                PaymentMethodCreateParams.Card.create("tok_visa"),
                billingDetails)
    
        // confirm PaymentIntent
    }

    Test the integration

    It’s important to thoroughly test your integration to make sure you’re correctly handling cards that require additional authentication and cards that don’t. Use these card numbers in test mode with any expiration date in the future and any three digit CVC code to validate your integration when authentication is required and when it’s not required.

    Number Authentication Description
    4000002500003155 Required on setup or first transaction This test card requires authentication for one-time payments. However, if you set up this card using the Setup Intents API and use the saved card for subsequent payments, no further authentication is needed.
    4000002760003184 Required This test card requires authentication on all transactions.
    4000008260003178 Required This test card requires authentication, but payments will be declined with an insufficient_funds failure code after successful authentication.
    4000000000003055 Supported This test card supports authentication via 3D Secure 2, but does not require it. Payments using this card do not require additional authentication in test mode unless your test mode Radar rules request authentication.

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

    Next steps

    You now have an Android integration that can accept card payments with the Payment Intents API, prompting customers for authentication as needed.

    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.

    Questions?

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

    On this page