Using Payment Intents on Android

    Learn how to build an Android application that uses the Payment Intents API to accept card payments.

    Accepting a card payment with the Payment Intents API and the Android SDK is a three-step process, with server-side and client-side actions:

    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

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

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

    Stripe uses a PaymentIntent object to represent your intent to collect payment from a customer, tracking charge attempts and payment state changes throughout the process.

    Create a PaymentIntent on your server with an amount and currency. Always decide how much to charge on the server side, a trusted environment, as opposed to the client. This prevents malicious customers from being able to choose their own prices.

    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";
    
    import com.stripe.model.PaymentIntent;
    import com.stripe.param.PaymentIntentCreateParams;
    
    PaymentIntentCreateParams createParams = new PaymentIntentCreateParams.Builder()
            .setCurrency("usd").setAmount(new Long(1099))
            .build();
    
    PaymentIntent intent = PaymentIntent.create(createParams);
    
    // 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);
    

    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(). 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);
    
            // Optional: customize the payment authentication experience.
            // PaymentAuthConfig.init() must be called before Stripe object
            // is instantiated.
            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());
    
            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) {
            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)
    
            // Optional: customize the payment authentication experience.
            // PaymentAuthConfig.init() must be called before Stripe object
            // is instantiated.
            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())
    
            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) {
            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. You can inspect the PaymentIntent’s charges.data property to obtain the latest 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
    Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
    
    intent = Stripe::PaymentIntent.retrieve('{{PAYMENT_INTENT_ID}}')
    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('{{PAYMENT_INTENT_ID}}')
    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('{{PAYMENT_INTENT_ID}}');
    $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("{{PAYMENT_INTENT_ID}}");
    List<Charge> charges = intent.getCharges().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
    const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
    
    (async () => {
      const intent = await stripe.paymentIntents.retrieve('{{PAYMENT_INTENT_ID}}');
      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("{{PAYMENT_INTENT_ID}}", 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.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    var service = new PaymentIntentService();
    var intent = service.Get("{{PAYMENT_INTENT_ID}}");
    var charges = intent.Charges.Data;
    

    A PaymentIntent’s Charge property only contains the most recent charge. To view all of the charges associated with a PaymentIntent, list all charges with the payment_intent​ parameter specified. Note that in addition to the final successful charge, the list includes any unsuccessful charges created during the payment process.

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

    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?

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

    On this page