Skip to content
Create account
or
Sign in
The Stripe Docs logo
/
Ask AI
Create account
Sign in
Get started
Payments
Finance automation
Platforms and marketplaces
Money management
Developer tools
Get started
Payments
Finance automation
Get started
Payments
Finance automation
Platforms and marketplaces
Money management

Android basic integrationDeprecated

Accept cards with the Android SDK's prebuilt UI.

Note

We created an improved payments UI for mobile apps with support for additional payment methods. We recommend using it for your integration instead of this one.

If you want to migrate but are unable to, please let us know.

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
PaymentMethodsActivity

PaymentMethodsActivity

AddPaymentMethodActivity

AddPaymentMethodActivity

PaymentFlowActivity

PaymentFlowActivity

These activities can also be used individually. This integration requires both server and client-side steps to implement.

Note

Check out the example Basic Integration app and backend for a full implementation of this guide.

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:

Command Line
Ruby
# Available as a gem sudo gem install stripe
Gemfile
Ruby
# If you use bundler, you can add this line to your Gemfile gem 'stripe'

Client-side

The Stripe 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:

build.gradle.kts
Kotlin
plugins { id("com.android.application") } android { ... } dependencies { // ... // Stripe Android SDK implementation("com.stripe:stripe-android:21.15.0") // Include the financial connections SDK to support US bank account as a payment method implementation("com.stripe:financial-connections:21.15.0") }

Note

For details on the latest SDK release and past versions, see the Releases page on GitHub. To receive notifications when a new release is published, watch releases for the repository.

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

Kotlin
import com.stripe.android.PaymentConfiguration class MyApp : Application() { override fun onCreate() { super.onCreate() PaymentConfiguration.init( applicationContext,
"pk_test_TYooMQauvdEDq54NiTphI7jx"
) } }

Note

Use your test keys while you test and develop, and your live mode keys when you publish your app.

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.

Command Line
curl
curl https://api.stripe.com/v1/ephemeral_keys \ -u
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:
\ -d "customer"="{{CUSTOMER_ID}}" \ -H "Stripe-Version: {{API_VERSION}}"

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. Refer to our example app to see this in practice.

ExampleEphemeralKeyProvider.java
Java
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) { } })); } }

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.

StoreActivity.java
Java
public class StoreActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); CustomerSession.initCustomerSession( this, new 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 by calling CustomerSession.endCustomerSession(). 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.

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

PaymentSessionConfig

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.

StoreActivity.java
Java
public StoreActivity extends AppCompatActivity { @NonNull private PaymentSessionConfig 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" ) ); } } }

PaymentSessionListener

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

MyPaymentSessionListener.java
Java
public class MyPaymentSessionListener implements PaymentSession.PaymentSessionListener { // Called whenever the PaymentSession's data changes, // For example, 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) { } }

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

MyPaymentSessionListener.java
Java
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 } } }

void onError(int errorCode, @Nullable String errorMessage)

This method is called whenever an error occurs when connecting to the Stripe API. Make sure users can see the error messages, so display them in an alert dialog.

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.

HostActivity.java
Java
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(); } }

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()

PaymentMethodsActivity

PaymentMethodsActivity

AddPaymentMethodActivity

AddPaymentMethodActivity

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()

PaymentFlowActivity

PaymentFlowActivity

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

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.

Command Line
curl
curl https://api.stripe.com/v1/payment_intents \ -u
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:
\ -d "amount"=1099 \ -d "currency"="usd"

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.
CheckoutActivity.java
Java
public class CheckoutActivity extends Activity { private void confirmPayment( @NonNull String clientSecret, @NonNull String paymentMethodId ) { stripe.confirmPayment( this, ConfirmPaymentIntentParams.createWithPaymentMethodId( paymentMethodId, clientSecret ) ); } }

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().

Manage PaymentSession in a host Activity
Client-side

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()

HostActivity.java
Java
public class HostActivity extends Activity { @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 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 are reported to the PaymentSessionListener argument to PaymentSession#init().

HostActivity.java
Java
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(); } }

Test the integration

​​Several test cards are available for you to use in a sandbox to make sure this integration is ready. Use them with any CVC and an expiration date in the future.

NumberDescription
Succeeds and immediately processes the payment.
Requires authentication. Stripe triggers a modal asking for the customer to authenticate.
Always fails with a decline code of insufficient_funds.

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

OptionalHandle post-payment events

OptionalUse individual activities
Client-side

OptionalCustomize the UI
Client-side

See also

  • About Stripe payments
  • The Payment Intents API
  • Stripe Android SDK Reference
Was this page helpful?
YesNo
Need help? Contact Support.
Join our early access program.
Check out our changelog.
Questions? Contact Sales.
LLM? Read llms.txt.
Powered by Markdoc