Android Integration

    Our Android libraries let you easily accept mobile payments inside any Android app. If you need help after reading this, search our documentation or check out answers to common questions. You can even chat live with other developers in #stripe on freenode.

    Stripe has created a Java library for Android, allowing you to easily submit payments from an Android app. With our mobile library, we shoulder the burden of PCI compliance by eliminating the need to send card data directly to your server. Instead, our libraries send the card data directly to our servers, where we can convert them to tokens.

    Your app will receive the token back, and can then send the token to an endpoint on your server, where it can be used to process a payment, establish recurring billing, or merely saved for later use.

    We support Android back to version 11, and the library has no external dependencies.

    Installation

    Installing the Stripe Android library is similar using Android Studio and IntelliJ. You don’t need to clone a repo or download any files. Just add the following to your project’s build.gradle file, inside the dependencies section.

    compile 'com.stripe:stripe-android:4.0.1'

    ## Collecting credit card information

    At some point in the flow of your app, you’ll want to obtain payment details from the user. There are a couple ways to do this:

    Instructions for each route follows, although you may want to write your app to offer support for both.

    Using Android Pay Preview

    Through Android Pay, you can access payment information stored in your customers’ Google accounts.

    Setting up your app

    You’ll first need to obtain credentials and a client ID for your app, as explained in the Android Pay API Tutorial. You will also need to set up the latest version of Google Play Services, version 8.4 or higher.

    Collecting payment information through Android Pay

    To use Android Pay in your app, first enable the Android Pay API by adding the following to the <application> tag of your AndroidManifest.xml:

    <application
      ...
      <meta-data
        android:name="com.google.android.gms.wallet.api.enabled"
        android:value="true" />
    </application>

    Your application will need a SupportWalletFragment in the activity’s layout. The fragment is the placeholder for the Android Pay purchase button. You can create this either programmatically or add it to an activity layout via XML:

    <!-- You will need to add the wallet namespace to your enclosing Layout -->
    xmlns:wallet="http://schemas.android.com/apk/res-auto"
    
    <fragment
      android:id="@+id/wallet_fragment"
      android:name="com.google.android.gms.wallet.fragment.SupportWalletFragment"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      wallet:environment="test"
      wallet:fragmentMode="buyButton" />

    With the fragment in place, back in the activity, you need to:

    1. Grab a reference to that fragment.
    2. Create a MaskedWalletRequest.
    3. Initialize the fragment.

    In the MaskedWalletRequest, you are able to specify the amount to charge and what additional information you’d like to collect (e.g., the shipping address). This is also where you specify you are using Stripe as the processor. Doing so will let the application request a Stripe token directly from the wallet.

    Before starting the Android Pay flow, use the isReadyToPay() method to check whether the user has the Android Pay app installed and is ready to pay through it. Also see Google’s documentation for information on their UI and branding requirements.

    public class PaymentActivity extends FragmentActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
    
      // You will need to use your live API key even while testing
      public static final String PUBLISHABLE_KEY = "pk_live_XXX";
    
      // Unique identifiers for asynchronous requests:
      private static final int LOAD_MASKED_WALLET_REQUEST_CODE = 1000;
      private static final int LOAD_FULL_WALLET_REQUEST_CODE = 1001;
    
      private SupportWalletFragment walletFragment;
    
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Wallet.Payments.isReadyToPay(googleApiClient).setResultCallback(
          new ResultCallback<BooleanResult>() {
            @Override
            public void onResult(@NonNull BooleanResult booleanResult) {
              if (booleanResult.getStatus().isSuccess()) {
                if (booleanResult.getValue()) {
                  showAndroidPay();
                } else {
                  // Hide Android Pay buttons, show a message that Android Pay
                  // cannot be used yet, and display a traditional checkout button
                }
              } else {
                // Error making isReadyToPay call
                Log.e(TAG, "isReadyToPay:" + booleanResult.getStatus());
              }
            }
          }
        );
        ...
      }
    
      public void showAndroidPay() {
        setContentView(R.layout.payment_activity);
    
        walletFragment =
          (SupportWalletFragment) getSupportFragmentManager().findFragmentById(R.id.wallet_fragment);
    
        MaskedWalletRequest maskedWalletRequest = MaskedWalletRequest.newBuilder()
    
          // Request credit card tokenization with Stripe by specifying tokenization parameters:
          .setPaymentMethodTokenizationParameters(PaymentMethodTokenizationParameters.newBuilder()
            .setPaymentMethodTokenizationType(PaymentMethodTokenizationType.PAYMENT_GATEWAY)
            .addParameter("gateway", "stripe")
            .addParameter("stripe:publishableKey", PUBLISHABLE_KEY)
            .addParameter("stripe:version", com.stripe.android.BuildConfig.VERSION_NAME)
            .build())
    
          // You want the shipping address:
          .setShippingAddressRequired(true)
    
          // Price set as a decimal:
          .setEstimatedTotalPrice("20.00")
          .setCurrencyCode("USD")
          .build();
    
        // Set the parameters:
        WalletFragmentInitParams initParams = WalletFragmentInitParams.newBuilder()
          .setMaskedWalletRequest(maskedWalletRequest)
          .setMaskedWalletRequestCode(LOAD_MASKED_WALLET_REQUEST_CODE)
          .build();
    
        // Initialize the fragment:
        walletFragment.initialize(initParams);
    
        ...
      }
    
      public void onStart() {
        ...
      }
    
      public void onStop() {
        ...
      }
    
      @Override
      protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        ...
      }
    
      @Override
      public void onConnectionFailed(ConnectionResult connectionResult) {}
    
      @Override
      public void onConnected(Bundle bundle) {}
    
      @Override
      public void onConnectionSuspended(int i) {}
    }

    Note that the price set within the Android app is written as a decimal. This is for the Android app only. The token received back will be sent to your server, and the charge request will be made of the Stripe API from there. The actual amount to be charged is requested at that point, and is set as an integer.

    The last part of the setup process for the app is to connect to the Google Wallet API. You will need this connection in place for when the user presses the Android Pay purchase button just created.

    public class PaymentActivity extends FragmentActivity {
      ...
    
      private GoogleApiClient googleApiClient;
    
      public void onCreate(Bundle savedInstanceState) {
        ...
    
        googleApiClient = new GoogleApiClient.Builder(this)
          .addConnectionCallbacks(this)
          .addOnConnectionFailedListener(this)
          .addApi(Wallet.API, new Wallet.WalletOptions.Builder()
            .setEnvironment(WalletConstants.ENVIRONMENT_TEST)
            .setTheme(WalletConstants.THEME_LIGHT)
            .build())
          .build();
      }
    
      public void onStart() {
        super.onStart();
        googleApiClient.connect();
      }
    
      public void onStop() {
        super.onStop();
        googleApiClient.disconnect();
      }
    }

    When your customer confirms their purchase, the application then needs to create a FullWalletRequest. This will allow the application to request access to the customer’s FullWallet, which is where the payment information comes from. To do this, implement onActivityResult.

    public class PaymentActivity extends FragmentActivity {
      ...
    
      @Override
      protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    
        if (requestCode == LOAD_MASKED_WALLET_REQUEST_CODE) { // Unique, identifying constant
          if (resultCode == Activity.RESULT_OK) {
            MaskedWallet maskedWallet = data.getParcelableExtra(WalletConstants.EXTRA_MASKED_WALLET);
            FullWalletRequest fullWalletRequest = FullWalletRequest.newBuilder()
              .setCart(Cart.newBuilder()
                .setCurrencyCode("USD")
                .setTotalPrice("20.00")
                .addLineItem(LineItem.newBuilder() // Identify item being purchased
                  .setCurrencyCode("USD")
                  .setQuantity("1")
                  .setDescription("Premium Llama Food")
                  .setTotalPrice("20.00")
                  .setUnitPrice("20.00")
                  .build())
                .build())
              .setGoogleTransactionId(maskedWallet.getGoogleTransactionId())
              .build();
            Wallet.Payments.loadFullWallet(googleApiClient, fullWalletRequest, LOAD_FULL_WALLET_REQUEST_CODE);
          }
        } else if (requestCode == LOAD_FULL_WALLET_REQUEST_CODE) { // Unique, identifying constant
          ...
        }
      }
    }

    You can see from this example that you can add line items to the FullWalletRequest’s cart. The above only has one–Premium Llama Food, but if your customer is purchasing multiple items, you can add multiple line items by calling addLineItem additional times.

    Creating tokens from Android Pay

    Finally, when the customer allows access to their wallet for payment, the application will be given back a Stripe token. You will want to send this token to your server for use through the API.

    public class PaymentActivity extends FragmentActivity {
      ...
      //keep track of your current environment,
      //change to WalletConstants.ENVIRONMENT_PRODUCTION when you're ready to go live
      public static final int mEnvironment = WalletConstants.ENVIRONMENT_TEST;
    
      @Override
      protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == LOAD_MASKED_WALLET_REQUEST_CODE) {
          ...
        } else if (requestCode == LOAD_FULL_WALLET_REQUEST_CODE) {
          if (resultCode == Activity.RESULT_OK) {
            FullWallet fullWallet = data.getParcelableExtra(WalletConstants.EXTRA_FULL_WALLET);
            String tokenJSON = fullWallet.getPaymentMethodToken().getToken();
    
            //A token will only be returned in production mode,
            //i.e. WalletConstants.ENVIRONMENT_PRODUCTION
            if (mEnvironment == WalletConstants.ENVIRONMENT_PRODUCTION) {
              try {
                Token token = TokenParser.parseToken(tokenJSON);
                // TODO: send token to your server
              } catch (JSONException jsonException) {
                // Log the error and notify Stripe helpß
              }
            }
          }
        } else {
          super.onActivityResult(requestCode, resultCode, data);
        }
      }
    }

    Testing and Deploying with Android Pay

    To test your Android Pay flow, use your live Stripe API key, not your test key, in conjunction with the Android Pay test environment, specified by WalletConstants.ENVIRONMENT_TEST. In test mode, fullWallet.getPaymentMethodToken().getToken() will return the String "TEST_GATEWAY_TOKEN" in place of a JSON string representing a token.

    If you want to test your application on a physical device, the device will need to support NFC. You will also need to add a supported credit card to your Android Pay account.

    When you are ready to go, register your app to access Android Pay for live transactions.

    Using the card input widget

    To collect card data from your customers directly, you can use Stripe’s CardInputWidget in your application.

    Android Card Input Widget

    The different states of the CardInputWidget

    You can include it in any view’s layout file. Note that it has a minimum width of 320px.

    <com.stripe.android.view.CardInputWidget
      android:id="@+id/card_input_widget"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      />

    This will allow your customers to input all of the required data for their card: the number, the expiration date, and the CVC code.

    To access the card reader in your application code, first import the class.

    import com.stripe.android.view.CardInputWidget;

    Next, find the CardInputWidget in your layout as you would any other view.

    CardInputWidget mCardInputWidget = (CardInputWidget) findViewById(R.id.card_input_widget);

    You can then directly access the widget and ask for a card object. Note that if the data in the widget is either incomplete or fails client-side validity checks, the Card object will be null.

    Card cardToSave = mCardInputWidget.getCard();
    if (cardToSave == null) {
        mErrorDialogHandler.showError("Invalid Card Data");
    }

    If you have any other data that you would like to associate with the card, such as name, address, or zip code, you can put additional input controls on your layout and add them directly to the Card object.

    cardToSave.setName("Customer Name");
    cardToSave.setAddressZip("12345");

    Styling the card widget

    The CardInputWidget is a custom view which contains several customized Android EditText elements. If you set values for EditText attributes in your Theme, they will be picked up by the CardInputWidget.

    There are also two special parameters that you can set: the card tint and the error text color. To set these values, make sure to add the res/auto namespace in the layout file where you have created the CardInputWidget. You can use any legal value for the namespace, such as stripe.

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:stripe="http://schemas.android.com/apk/res-auto"
        ...
        >
    </LinearLayout>

    You can then apply your preferred values for the color of the empty card hint item (before a brand has been detected) and as a text error color. By default, the card tint is identical to the hint text color of your theme, and the Stripe library selects a text error color from two different shades of red, depending on how dark the background of your theme is.

    To apply your custom colors, simply add them to the CardInputWidget’s XML declaration.

    <com.stripe.android.view.CardInputWidget
      android:id="@+id/card_input_widget"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      stripe:cardTint="@android:color/holo_purple"
      stripe:cardTextErrorColor="@android:color/holo_orange_dark"
      />

    Building your own form

    If you build your own payment form, you’ll need to collect at least your customers’ card numbers and expiration dates. Stripe strongly recommends collecting the CVC. You can optionally collect the user’s name and billing address for additional fraud protection.

    Once you’ve collected a customer’s information, you will need to exchange the information for a Stripe token.

    Creating & validating cards from a custom form

    To create a Card object from data you’ve collected from other forms, you can create the object with its constructor.

    import com.stripe.android.model.Card;
    
    //...
    //...
    public void onClickSomething(String cardNumber, String cardExpMonth, String cardExpYear, String cardCVC) {
      Card card = new Card(
        cardNumber,
        cardExpMonth,
        cardExpYear,
        cardCVC
      );
    
      card.validateNumber();
      card.validateCVC();
    }

    As you can see in the example above, the Card instance contains some helpers to validate that the card number passes the Luhn check, that the expiration date is the future, and that the CVC looks valid. You’ll probably want to validate these three things at once, so we’ve included a validateCard function that does so.

    Card card = new Card("4242-4242-4242-4242", 12, 2018, "123");
    
    if (!card.validateCard()) {
      // Show errors
    }

    Creating tokens

    The next step is to pass off that sensitive payment information securely to Stripe, where you’ll exchange it for a token. You can create tokens using the Stripe instance method createToken, passing in a Card instance, and completion callbacks. An asynchronous network request will be executed, and the appropriate callback invoked when it completes.

    Note that you will need access to a Context object, which can be the Activity or Fragment in which you are operating, or can be retrieved from any View via the View#getContext() method.

    First, import the Stripe object and the Token object.

    import com.stripe.android.Stripe;
    import com.stripe.android.model.Token;

    Then, get your Card object, which can be done from a custom form.

    Card card = new Card("4242424242424242", 12, 2018, "123");
    // Remember to validate the card object before you use it to save time.
    if (!card.validateCard()) {
      // Do not continue token creation.
    }

    You can also simply take the data from a CardInputWidget.

    // Remember that the card object will be null if the user inputs invalid data.
    Card card = mCardInputWidget.getCard();
    if (card == null) {
      // Do not continue token creation.
    }

    However you create your Card object, you can now move on and convert it into a token. You should not store the object locally.

    Stripe stripe = new Stripe(mContext, "pk_test_6pRNASCoBOKtIshFeQd4XMUh");
    stripe.createToken(
      card,
      new TokenCallback() {
        public void onSuccess(Token token) {
          // Send token to your server
        }
        public void onError(Exception error) {
          // Show localized error message
          Toast.makeText(getContext(),
            error.getLocalizedString(getContext()),
            Toast.LENGTH_LONG
          ).show();
        }
      }
    )

    We’ve placed your test publishable API key as the second argument to Stripe. You’ll need to swap it out with your live publishable key in production. You can see all your keys in your dashboard.

    Using tokens

    Using the payment token, however it was obtained, requires an API call from your server using your secret API key. (For security purposes, you should never embed your secret API key in your app.)

    Set up an endpoint on your server that can receive an HTTP POST call for the token. In the onActivityResult method (for Android Pay) or the onSuccess callback (when using your own form), you’ll need to POST the supplied token to your server. Make sure any communication with your server is SSL secured to prevent eavesdropping.

    Take a look at the full example application to see everything put together.

    Next Up

    Once you’ve retrieved a token from Stripe on your server, you’re going to want to do something with the payment details you just collected. This is usually one of two things: