Using Android basic integration

    Accept cards with the Android SDK’s prebuilt UI.

    Use this integration if you want a prebuilt UI that:

    • Accepts credit cards and other payment methods
    • Saves and displays cards for reuse
    • Can be customized to fit your app’s look and feel using an Android theme
    • Launches full-screen activities to collect payment details, shipping address, and shipping method
    • Allows your customer to choose Google Pay as a payment method

    These activities can also be used individually.

    This integration requires both server and client-side steps to implement.

    1 Set up Stripe Client-side Server-side

    First, you need a Stripe account. Register now.

    Server-side

    This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:

    # Available as a gem sudo gem install stripe
    # If you use bundler, you can add this line to your Gemfile gem 'stripe'
    # Install through pip pip install --upgrade stripe
    # Or find the Stripe package on http://pypi.python.org/pypi/stripe/
    # Install the PHP library via Composer composer require stripe/stripe-php
    # Or download the source directly: https://github.com/stripe/stripe-php/releases
    /* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
    <!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
    # For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
    # Install via npm npm install --save stripe
    # Install via go go get github.com/stripe/stripe-go
    // Then import the package import ( "github.com/stripe/stripe-go" )
    # Install via dotnet dotnet add package Stripe.net dotnet restore
    # Or install via NuGet PM> Install-Package Stripe.net

    Client-side

    The Android SDK is open source and fully documented.

    To install the SDK, add stripe-android to the dependencies block of your app/build.gradle file:

    apply plugin: 'com.android.application' android { ... } dependencies { // ... // Stripe Android SDK implementation 'com.stripe:stripe-android:14.2.1' }

    Configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API, such as in your Application subclass:

    import com.stripe.android.PaymentConfiguration class MyApp: Application() { override fun onCreate() { super.onCreate() PaymentConfiguration.init( applicationContext, "pk_test_TYooMQauvdEDq54NiTphI7jx" ) } }
    import com.stripe.android.PaymentConfiguration; public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); PaymentConfiguration.init( getApplicationContext(), "pk_test_TYooMQauvdEDq54NiTphI7jx" ); } }

    2 Set up an ephemeral key Client-side Server-side

    In order for the SDK to save and retrieve credit cards for later use, create a single Stripe Customer object for each of your users. When you create a new user or account on your server, create a corresponding Customer object at the same time, even if you don’t collect payment information from your users when they sign up. This ensures that your application has a matching Customer for each user.

    For security, the Customer API is not directly accessible from the client. Instead, your server provides the SDK with an ephemeral key—a short-lived API key with restricted access to the Customer API. You can think of an ephemeral key as a session, authorizing the SDK to retrieve and update a specific Customer object s for the duration of the session.

    Server-side

    To provide an ephemeral key to the SDK, expose a new API endpoint on your backend. This endpoint should create an ephemeral key for the current Stripe customer, and return the key’s unmodified response as JSON. When the SDK requests an ephemeral key, it will specify the version of the Stripe API that it expects the response to come from. Your endpoint must accept an api_version parameter, and use the specified API version when creating the ephemeral key. This ensures that the SDK always receives the correct ephemeral key response from your backend. Consult our Example Backend to see this in practice.

    curl https://api.stripe.com/v1/ephemeral_keys \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d customer="{{CUSTOMER_ID}}" \ -H "Stripe-Version: {{API_VERSION}}"
    key = Stripe::EphemeralKey.create( {customer: '{{CUSTOMER_ID}}'}, {stripe_version: '{{API_VERSION}}'} )
    key = stripe.EphemeralKey.create(customer='{{CUSTOMER_ID}}', stripe_version='{{API_VERSION}}')
    $key = \Stripe\EphemeralKey::create( ['customer' => '{{CUSTOMER_ID}}'], ['stripe_version' => '{{API_VERSION}}'] );
    let key = await stripe.ephemeralKeys.create( {customer: '{{CUSTOMER_ID}}'}, {stripe_version: '{{API_VERSION}}'} );
    RequestOptions requestOptions = (new RequestOptions.RequestOptionsBuilder()) .setStripeVersion("{{API_VERSION}}") .build(); Map<String, Object> options = new HashMap<String, Object>(); options.put("customer", "{{CUSTOMER_ID}}"); EphemeralKey key = EphemeralKey.create(options, requestOptions);
    params := &stripe.EphemeralKeyParams{ Customer: stripe.String("{{CUSTOMER_ID}}"), StripeVersion: stripe.String("{{API_VERSION}}"), } key, _ := ephemeralkey.New(params)
    var options = new EphemeralKeyCreateOptions { Customer = "{{CUSTOMER_ID}}", StripeVersion = "{{API_VERSION}}" }; var service = new EphemeralKeyService(); service.Create(options);

    Client-side

    After you’ve added an ephemeral key endpoint to your backend, you’ll need a way for your Android app to communicate with this endpoint. In your app, make your API client class implement the EphemeralKeyProvider interface, which defines a single method, createEphemeralKey(). When implementing this method, pass the apiVersion parameter along to your ephemeral keys endpoint. Consult our Example App to see this in practice.

    public class ExampleEphemeralKeyProvider implements EphemeralKeyProvider { private final BackendApi backendApi = RetrofitFactory.instance.create(BackendApi.class); private final CompositeDisposable compositeDisposable = new CompositeDisposable(); @Override public void createEphemeralKey( @NonNull @Size(min = 4) String apiVersion, @NonNull final EphemeralKeyUpdateListener keyUpdateListener) { final Map<String, String> apiParamMap = new HashMap<>(); apiParamMap.put("api_version", apiVersion); // Using RxJava2 for handling asynchronous responses compositeDisposable.add(backendApi.createEphemeralKey(apiParamMap) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( response -> { try { final String rawKey = response.string(); keyUpdateListener.onKeyUpdate(rawKey); } catch (IOException ignored) { } })); } }
    class ExampleEphemeralKeyProvider : EphemeralKeyProvider { private val compositeDisposable: CompositeDisposable = CompositeDisposable() private val backendApi: BackendApi = RetrofitFactory.instance.create(BackendApi::class.java) override fun createEphemeralKey( @Size(min = 4) apiVersion: String, keyUpdateListener: EphemeralKeyUpdateListener ) { compositeDisposable.add( backendApi.createEphemeralKey(hashMapOf("api_version" to apiVersion)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { responseBody -> try { val ephemeralKeyJson = responseBody.string() keyUpdateListener.onKeyUpdate(ephemeralKeyJson) } catch (e: IOException) { keyUpdateListener .onKeyUpdateFailure(0, e.message ?: "") } }) } }

    3 Set up a CustomerSession Client-side

    After creating an EphemeralKeyProvider, initialize a CustomerSession. A CustomerSession talks to your backend to retrieve an ephemeral key for your Customer with its EphemeralKeyProvider, and uses that key to manage retrieving and updating the Customer’s payment methods on your behalf.

    You can also use CustomerSession with your own custom UI to retrieve or refresh the Customer, and list their payment methods, attach a payment method, or detach a payment method.

    public class StoreActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); CustomerSession.initCustomerSession( this, new ExampleEphemeralKeyProvider() ); } }
    class StoreActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) CustomerSession.initCustomerSession(this, ExampleEphemeralKeyProvider()) } }

    To reduce load times, preload your customer’s information by initializing CustomerSession before they enter your payment flow.

    If your current user logs out of the app, clear the current CustomerSession singleton using the clearInstance method. When a new user logs in, re-initialize the instance. On your backend, create and return a new ephemeral key for the Customer object associated with the new user.

    4 Set up a PaymentSession Client-side

    The core of this integration is the PaymentSession class. It uses CustomerSession to launch full-screen activities to collect and store payment information, and can also be used to collect shipping info. Think of it as the data source for your checkout activity—it handles asynchronously retrieving the data you need, and notifies its PaymentSessionListener when your UI should change.

    To work with PaymentSession, you’ll need to:

    1. Create a PaymentSessionConfig object
    2. Implement a PaymentSessionListener

    PaymentSessionConfg

    The PaymentSessionConfig object is created using a Builder. All of the Builder’s fields are optional. See the API reference for details on each method.

    public StoreActivity extends AppCompatActivity { @NonNull private PaymentSessionConfg createPaymentSessionConfig() { return PaymentSessionConfig.Builder() // hide the phone field on the shipping information form .setHiddenShippingInfoFields( ShippingInfoWidget.CustomizableShippingField.PHONE_FIELD ) // make the address line 2 field optional .setOptionalShippingInfoFields( ShippingInfoWidget.CustomizableShippingField.ADDRESS_LINE_TWO_FIELD ) // specify an address to pre-populate the shipping information form .setPrepopulatedShippingInfo(ShippingInformation( new Address.Builder() .setLine1("123 Market St") .setCity("San Francisco") .setState("CA") .setPostalCode("94107") .setCountry("US") .build(), "Jenny Rosen", "4158675309" )) // collect shipping information .setShippingInfoRequired(true) // collect shipping method .setShippingMethodsRequired(true) // specify the payment method types that the customer can use; // defaults to PaymentMethod.Type.Card .setPaymentMethodTypes( Arrays.asList(PaymentMethod.Type.Card) ) // only allow US and Canada shipping addresses .setAllowedShippingCountryCodes(new HashSet<>( Arrays.asList("US", "CA") )) // specify a layout to display under the payment collection form .setAddPaymentMethodFooter(R.layout.add_payment_method_footer) // specify the shipping information validation delegate .setShippingInformationValidator(new AppShippingInformationValidator()) // specify the shipping methods factory delegate .setShippingMethodsFactory(new AppShippingMethodsFactory()) // if `true`, will show "Google Pay" as an option on the // Payment Methods selection screen .setShouldShowGooglePay(true) .build(); } private static class AppShippingInformationValidator extends PaymentSessionConfig.ShippingInformationValidator { @Override public boolean isValid( @NonNull ShippingInformation shippingInformation ) { final Address address = shippingInformation.getAddress(); return address != null && Locale.US.country == address.getCountry(); } @NonNull public String getErrorMessage( @NonNull ShippingInformation shippingInformation ) { return "A US address is required"; } } private static class AppShippingMethodsFactory extends PaymentSessionConfig.ShippingMethodsFactory { @Override public List<ShippingMethod> create( @NonNull ShippingInformation shippingInformation ) { returns Arrays.asList( new ShippingMethod( "UPS Ground", "ups-ground", 0, "USD", "Arrives in 3-5 days" ), new ShippingMethod( "FedEx", "fedex", 599, "USD", "Arrives tomorrow" ) ); } } }
    class StoreActivity : AppCompatActivity() { private fun createPaymentSessionConfig(): PaymentSessionConfg { return PaymentSessionConfig.Builder() // hide the phone field on the shipping information form .setHiddenShippingInfoFields( ShippingInfoWidget.CustomizableShippingField.PHONE_FIELD ) // make the address line 2 field optional .setOptionalShippingInfoFields( ShippingInfoWidget.CustomizableShippingField.ADDRESS_LINE_TWO_FIELD ) // specify an address to pre-populate the shipping information form .setPrepopulatedShippingInfo(ShippingInformation( Address.Builder() .setLine1("123 Market St") .setCity("San Francisco") .setState("CA") .setPostalCode("94107") .setCountry("US") .build(), "Jenny Rosen", "4158675309" )) // collect shipping information .setShippingInfoRequired(true) // collect shipping method .setShippingMethodsRequired(true) // specify the payment method types that the customer can use; // defaults to PaymentMethod.Type.Card .setPaymentMethodTypes( listOf(PaymentMethod.Type.Card) ) // only allow US and Canada shipping addresses .setAllowedShippingCountryCodes( setOf("US", "CA") ) // specify a layout to display under the payment collection form .setAddPaymentMethodFooter(R.layout.add_payment_method_footer) // specify the shipping information validation delegate .setShippingInformationValidator(AppShippingInformationValidator()) // specify the shipping methods factory delegate .setShippingMethodsFactory(AppShippingMethodsFactory()) // if `true`, will show "Google Pay" as an option on the // Payment Methods selection screen .setShouldShowGooglePay(true) .build() } private class AppShippingInfoValidator : PaymentSessionConfig.ShippingInformationValidator { override fun getErrorMessage( shippingInformation: ShippingInformation ): String { return "A US address is required" } override fun isValid( shippingInformation: ShippingInformation ): Boolean { return Locale.US.country == shippingInformation.address?.country } } private class AppShippingMethodsFactory : PaymentSessionConfig.ShippingMethodsFactory { override fun create(shippingInformation: ShippingInformation): List<ShippingMethod> { return listOf( ShippingMethod( label = "UPS Ground", identifier = "ups-ground", detail = "Arrives in 3-5 days", amount = 0, currency = currency ), ShippingMethod( label = "FedEx", identifier = "fedex", detail = "Arrives tomorrow", amount = 599, currency = currency ) ) } } }

    PaymentSessionListener

    After creating the PaymentSessionConfig, you’ll need to implement PaymentSessionListener.

    public class MyPaymentSessionListener implements PaymentSession.PaymentSessionListener { // Called whenever the PaymentSession's data changes, // e.g. when the user selects a new `PaymentMethod` or enters shipping info. @Override public void onPaymentSessionDataChanged(@NonNull PaymentSessionData data) { if (data.getUseGooglePay()) { // customer intends to pay with Google Pay } else { final PaymentMethod paymentMethod = data.getPaymentMethod(); if (paymentMethod != null) { // Display information about the selected payment method } } // Update your UI here with other data if (data.isPaymentReadyToCharge()) { // Use the data to complete your charge - see below. } } @Override public void onCommunicatingStateChanged(boolean isCommunicating) { } @Override public void onError(int errorCode, @NotNull String errorMessage) { } }
    class MyPaymentSessionListener : PaymentSession.PaymentSessionListener { // Called whenever the PaymentSession's data changes, // e.g. when the user selects a new `PaymentMethod` or enters shipping info. override fun onPaymentSessionDataChanged(data: PaymentSessionData) { if (data.useGooglPay) { // customer intends to pay with Google Pay } else { data.paymentMethod?.let { paymentMethod -> // Display information about the selected payment method } } // Update your UI here with other data if (data.isPaymentReadyToCharge()) { // Use the data to complete your charge - see below. } } override fun onCommunicatingStateChanged(isCommunicating: Boolean) {} override fun onError(errorCode: Int, errorMessage: String) {} }

    This method should also check for whether or not the payment data is complete, according to the PaymentSessionConfig specified. If you receive an update for which PaymentSessionData#isPaymentReadyToCharge() returns true, you can immediately send a message to your server to complete the charge.

    void onCommunicatingStateChanged(boolean isCommunicating)

    This method is called whenever the network communication state has changed. We recommend showing a spinner or infinite progress bar when it is set to true

    public class MyPaymentSessionListener implements PaymentSession.PaymentSessionListener { @Override public void onCommunicatingStateChanged(boolean isCommunicating) { if (isCommunicating) { // update UI to indicate that network communication is in progress } else { // update UI to indicate that network communication has completed } } }
    class MyPaymentSessionListener : PaymentSession.PaymentSessionListener { override fun onCommunicatingStateChanged(isCommunicating: Boolean) { if (isCommunicating) { // update UI to indicate that network communication is in progress } else { // update UI to indicate that network communication has completed } } }

    void onError(int errorCode, @Nullable String errorMessage)

    This method is called whenever an error occurs when connecting to the Stripe API. The error messages should be user-surfaceable, so displaying them in an alert dialog is recommended.

    Initialize a PaymentSession

    Having created your PaymentSessionConfig and PaymentSessionListener, you can now initialize the PaymentSession. In the below example, we use anonymous classes to create our listener and config for simplicity.

    public class HostActivity extends Activity { private PaymentSession paymentSession; private Button startPaymentFlowButton; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); paymentSession = new PaymentSession( this, new PaymentSessionConfig.Builder() .setPrepopulatedShippingInfo(getDefaultShippingInfo()) .build() ); setupPaymentSession(); } private void setupPaymentSession() { paymentSession.init( new PaymentSession.PaymentSessionListener() { @Override public void onCommunicatingStateChanged( boolean isCommunicating ) { // update UI, such as hiding or showing a progress bar } @Override public void onError( int errorCode, @Nullable String errorMessage ) { // handle error } @Override public void onPaymentSessionDataChanged( @NonNull PaymentSessionData data ) { final PaymentMethod paymentMethod = data.getPaymentMethod(); // use paymentMethod } } ); startPaymentFlowButton.setEnabled(true); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (data != null) { paymentSession.handlePaymentData(requestCode, resultCode, data); } } @NonNull private ShippingInformation getDefaultShippingInfo() { // optionally specify default shipping address return new ShippingInformation(); } }
    class HostActivity : Activity() { private lateinit var paymentSession: PaymentSession private lateinit var startPaymentFlowButton: Button private val defaultShippingInfo: ShippingInformation // optionally specify default shipping address get() = ShippingInformation() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) paymentSession = PaymentSession( this, PaymentSessionConfig.Builder() .setPrepopulatedShippingInfo(defaultShippingInfo) .build() ) } private fun setupPaymentSession() { paymentSession.init( object : PaymentSession.PaymentSessionListener { override fun onCommunicatingStateChanged(isCommunicating: Boolean) { // update UI, such as hiding or showing a progress bar } override fun onError(errorCode: Int, errorMessage: String) { // handle error } override fun onPaymentSessionDataChanged(data: PaymentSessionData) { val paymentMethod: PaymentMethod? = data.paymentMethod // use paymentMethod } } ) startPaymentFlowButton.isEnabled = true } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (data != null) { paymentSession.handlePaymentData(requestCode, resultCode, data) } } }

    5 Collect the customer's payment and shipping details Client-side

    Once the PaymentSession has been initialized, you can use it to make the following calls.

    void presentPaymentMethodSelection()

    This method starts the PaymentMethodsActivity to allow the customer to choose a saved payment method, using CustomerSession as its data source. If the Add new card button is tapped, or there are no existing payment methods, AddPaymentMethodActivity is launched to add a credit card.

    void presentShippingFlow()

    This method presents the PaymentFlowActivity to allow the user to enter shipping information, if such information is required according to your PaymentSessionConfig.

    6 Complete the payment Client-side Server-side

    Once PaymentSession#isPaymentReadyToCharge() returns true, submit the payment to Stripe using a Payment Intent. Stripe uses this payment object to track and handle all the states of the payment—even when the bank requires customer intervention, like additional card authentication—until the payment completes.

    Server-side

    On your server, make an endpoint that creates a PaymentIntent with an amount and currency and returns its client secret to your client.

    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 switch 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', }) client_secret = payment_intent['client_secret'] # Pass the client secret to the client
    # Set your secret key. Remember to switch 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', ) client_secret = intent.client_secret # Pass the client secret to the client
    // Set your secret key. Remember to switch 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', ]); $client_secret = $intent->client_secret // Pass the client secret to the client
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("usd") .build(); PaymentIntent intent = PaymentIntent.create(params); String clientSecret = intent.getClientSecret(); // Pass the client secret to the client
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'usd', }); const clientSecret = paymentIntent.client_secret // Pass the client secret to the client
    // Set your secret key. Remember to switch 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)), } pi, _ := paymentintent.New(params) // Pass the client secret to the client
    // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var options = new PaymentIntentCreateOptions { Amount = 1099, Currency = "usd", }; var service = new PaymentIntentService(); var paymentIntent = service.Create(options); // Pass the client secret to the client

    Client-side

    1. Request a PaymentIntent from your server
    2. Assemble a ConfirmPaymentIntentParams object with the PaymentIntent client secret from your server and the id of PaymentSessionData#paymentMethod obtained from PaymentSessionListenerImpl#onPaymentSessionDataChanged().
    3. Call the Stripe confirmPayment method to confirm the payment.
    public class CheckoutActivity extends Activity { private static final String RETURN_URL = "..."; private void confirmPayment( @NonNull String clientSecret, @NonNull String paymentMethodId ) { stripe.confirmPayment(this, ConfirmPaymentIntentParams.createWithPaymentMethodId( paymentMethodId, clientSecret, RETURN_URL ) ); } }
    class CheckoutActivity : Activity() { private fun confirmPayment(clientSecret: String, paymentMethodId: String) { stripe.confirmPayment(this, ConfirmPaymentIntentParams.createWithPaymentMethodId( paymentMethodId, clientSecret, RETURN_URL ) ) } companion object { private const val RETURN_URL = "..." } }

    When the payment completes, onSuccess is called and the value of the returned PaymentIntent’s status is Succeeded. Any other value indicates the payment was not successful. Inspect lastPaymentError to determine the cause. End the payment session by calling PaymentSession#onCompleted().

    7 Manage PaymentSession in a host Activity Client-side

    In order to get updates for the PaymentSessionData object and to handle state during Activity lifecycle, you’ll need to hook up your PaymentSession instance to a few key parts of your host Activity lifecycle. The first is in onActivityResult()

    public class HostActivity extends Activity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); paymentSession.handlePaymentData(requestCode, resultCode, data); } }
    class HostActivity : Activity() { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) paymentSession.handlePaymentData(requestCode, resultCode, data) } }

    This is all you need to do to get updates from the various activities launched by PaymentSession. Any updates to the data will be reported to the PaymentSessionListener argument to PaymentSession#init().

    public class HostActivity extends Activity { private PaymentSession paymentSession; // Can also be re-initialized in onRestoreInstanceState @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // other onCreate logic // Create the PaymentSession paymentSession = new PaymentSession( this, createPaymentSessionConfig() ); // Attach your listener paymentSession.init(createPaymentSessionListener()); } @NonNull private PaymentSession.PaymentSessionListener createPaymentSessionListener() { return new PaymentSession.PaymentSessionListener() { @Override public void onCommunicatingStateChanged( boolean isCommunicating ) { // update UI, such as hiding or showing a progress bar } @Override public void onError( int errorCode, @NotNull String errorMessage ) { // handle error } @Override public void onPaymentSessionDataChanged( @NotNull PaymentSessionData data ) { data.getPaymentMethod(); } }; } @NonNull private PaymentSessionConfig createPaymentSessionConfig() { return new PaymentSessionConfig.Builder() .build(); } }
    class HostActivity : Activity() { private lateinit var paymentSession: PaymentSession // Can also be re-initialized in onRestoreInstanceState public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // other onCreate logic // Create the PaymentSession paymentSession = PaymentSession( this, createPaymentSessionConfig() ) // Attach your listener paymentSession.init(createPaymentSessionListener()) } private fun createPaymentSessionListener(): PaymentSession.PaymentSessionListener { return object : PaymentSession.PaymentSessionListener { override fun onCommunicatingStateChanged(isCommunicating: Boolean) {} override fun onError(errorCode: Int, errorMessage: String) {} override fun onPaymentSessionDataChanged(data: PaymentSessionData) {} } } private fun createPaymentSessionConfig(): PaymentSessionConfig { return PaymentSessionConfig.Builder() .build() } }

    8 Test the integration

    By this point you should have a basic card integration that collects card details and makes a payment.

    There are several test cards you can use in test mode to make sure this integration is ready. Use them with any CVC, postal code, and future expiration date.

    Number Description
    4242424242424242 Succeeds and immediately processes the payment.
    4000002500003155 Requires authentication. Stripe will trigger a modal asking for the customer to authenticate.
    4000000000009995 Always fails with a decline code of insufficient_funds.

    For the full list of test cards see our guide on testing.

    Optional Handle post-payment events

    Stripe sends a payment_intent.succeeded event when the payment completes. Use the Dashboard, a custom webhook, or a partner solution to receive these events and run actions, like sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.

    Listen for these events rather than waiting on a callback from the client. On the client, the customer could close the browser window or quit the app before the callback executes. Setting up your integration to listen for asynchronous events also makes it easier to accept more payment methods in the future. Check out our guide to payment methods to see the differences between all supported payment methods.

    Receive events and run business actions

    Manually

    Use the Stripe Dashboard to view all your Stripe payments, send email receipts, handle payouts, or retry failed payments.

    Custom code

    Build a webhook handler to listen for events and build custom asynchronous payment flows. Test and debug your webhook integration locally with the Stripe CLI.

    Prebuilt apps

    Handle common business events, like shipping and inventory management, by integrating a partner application.

    Optional Use individual activities Client-side

    The full-screen activities that Basic Integration displays are also available to use individually, without using PaymentSession.

    AddPaymentMethodActivity

    Use AddPaymentMethodActivity to collect card details from your customer. To launch the activity, use AddPaymentMethodActivityStarter#startForResult().

    PaymentMethodsActivity

    Use PaymentMethodsActivity to display the customer’s payment methods and allow them to select one or add new ones. First, follow the steps to set up an ephemeral key and CustomerSession. Then, use PaymentMethodsActivityStarter#startForResult() to launch the activity.

    public class HostActivity extends Activity { private void launchPaymentMethodsActivity() { new PaymentMethodsActivityStarter(this).startForResult(); } }
    class HostActivity : Activity() { private fun launchPaymentMethodsActivity() { PaymentMethodsActivityStarter(this).startForResult() } }

    In your host Activity or Fragment, implement Activity#onActivityResult and handle the result.

    public class HostActivity extends Activity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PaymentMethodsActivityStarter.REQUEST_CODE) { final PaymentMethodsActivityStarter.Result result = PaymentMethodsActivityStarter.Result.fromIntent(data); final PaymentMethod paymentMethod = result != null ? result.paymentMethod : null; // use paymentMethod } } }
    class HostActivity : Activity() { override fun onActivityResult( requestCode: Int, resultCode: Int, data: Intent? ) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == PaymentMethodsActivityStarter.REQUEST_CODE && data != null) { val result = PaymentMethodsActivityStarter.Result.fromIntent(data) val paymentMethod = result?.paymentMethod // use paymentMethod } } }

    Optional Customize the UI Client-side

    The full-screen activities can be customized to fit your app’s look and feel by defining a StripeDefaultTheme theme that extends StripeBaseTheme. See below for an example as well as explanation of how each theme attribute is applied.

    <?xml version="1.0" encoding="utf-8"?> <resources> <style name="StripeDefaultTheme" parent="StripeBaseTheme"> <!-- Applied to toolbar background --> <item name="colorPrimary">@color/primary</item> <!-- Applied to status bar --> <item name="colorPrimaryDark">@color/primaryDark</item> <!-- Applied to inactive Shipping Method name and amount --> <item name="android:textColorPrimary">@color/text</item> <!-- Applied to inactive Shipping Method description --> <item name="android:textColorSecondary">@color/textPrimary</item> <!-- Applied to Shipping Information drop-down items; add Payment Method footer text color --> <item name="android:textColor">@color/textPrimary</item> <!-- Applied to toolbar foreground/text --> <item name="titleTextColor">@color/title</item> <!-- Applied to selected Payment Method; active field; links; add new Payment Method call-to-action --> <item name="colorAccent">@color/accent</item> <!-- Applied to: Payment Method icon; inactive form field highlight --> <item name="colorControlNormal">@color/accent</item> <!-- Applied to form field text --> <item name="android:editTextColor">@color/textPrimary</item> <item name="editTextColor">@color/textPrimary</item> </style> </resources>

    For more information on Android themes, see the Android Styles and Themes guide.

    See also

    Was this page helpful?

    Feedback about this page?

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

    On this page