Android Pay Integration Guide

    Android Pay is available to users and customers in the U.S., Australia, Singapore, and the U.K. You can use it to access payment information stored in your customers' Google accounts.

    To set up your app, you’ll first need to obtain credentials and a client ID for it, 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>

    You can use Stripe in two different ways to process your transactions with Android Pay.

    1. Use Stripe only as a processor
    2. Use the Stripe Android Pay Wrapper

    Using Stripe only as a processor

    To use Stripe only as a processor, you can follow Google’s tutorial using stripe as your gateway.

    PaymentMethodTokenizationParameters parameters =
        PaymentMethodTokenizationParameters.newBuilder()
            .setPaymentMethodTokenizationType(PaymentMethodTokenizationType.PAYMENT_GATEWAY)
                .addParameter("gateway", "stripe")
                .addParameter("stripe:publishableKey", publishableKey)
                .addParameter("stripe:version", version)
                .build();

    Stripe Android Pay Wrapper

    Add the following lines to your build.gradle file to use the Stripe Android Pay Wrapper.

    /* This line is all you need for the stripe-android bindings */
    compile 'com.stripe:stripe-android:4.1.1'
    
    /* To use the Stripe android pay wrapper, you need these two lines */
    compile 'com.stripe:stripe-android-pay:4.1.1'
    compile 'com.google.android.gms:play-services-wallet:10.2.6'

    Before doing anything with Android Pay in your application (preferably on startup), make sure to initialize the AndroidPayConfiguration.

    AndroidPayConfiguration payConfiguration = AndroidPayConfiguration.init(PUBLISHABLE_KEY, "USD");

    A Stripe publishable key and valid currency code are required. The AndroidPayConfiguration object is a singleton. It can be accessed from anywhere in code, so long as it has already been initialized.

    AndroidPayConfiguration.getInstance().setShippingAddressRequired(true);

    As the user selects items for purchase in the application, those items can be added to a CartManager.

    CartManager cartManager = new CartManager();
    // Add an item called "Llama Food", priced at $50.00
    cartManager.addLineItem("Llama Food", 5000L);
    // Add a line item "Llama Shoes" with 4 units, at a unit price of $20.00 per item. Total price is automatically calculated.
    cartManager.addLineItem("Llama Shoes", 4, 2000L);
    // Add a shipping line item
    cartManager.addShippingLineItem("Domestic shipping estimate", 1000L);
    // Set the tax line item - there can be only one
    cartManager.setTaxLineItem("Tax", 100L);

    You can add line items via the CartManager methods, or you can construct them via Google’s LineItem.newBuilder().build() functions, and add them directly to the CartManager:

    LineItem item = getLineItemFromSomewhere();
    cartManager.addLineItem(item);

    Note: in any given cart, the Google API allows only one item with role LineItem.Roles.TAX. Adding another such item will replace any currently existing tax item in the CartManager.

    You can also remove items from the CartManager. Every call to add an item returns a unique item ID, which you can hold on to.

    // Every added non-tax item gets a UUID.
    String bicycleId = cartManager.addItem("Bicycle", 9900L);
    // You get access to the removed item when you take it out of the cart.
    LineItem removedItem = cartManager.removeItem(bicycleId);
    // If you add the item back, it will have a new, unique ID.
    String otherId = cartManager.addItem(removedItem);

    You can also create a CartManager from an existing Cart, which can be useful when passing Cart objects via Intent and Bundle.

    CartManager copiedCartManager = new CartManager(cart);

    By default, this only copies items with role LineItem.Role.REGULAR, not shipping or tax items. To copy the other categories, use:

    // Args: Cart, keepShipping, keepTax
    CartManager copiedCartManager = new CartManager(cart, true, true);

    When you are ready to check out, you’ll need to create a Google Play cart. This is wrapped in a try-catch statement to verify client-side whether or not there are any problems with the Cart content that would prevent its processing via Android Pay. If you use only the CartManager.addItem methods to add items to a cart, no exceptions will occur.

    try {
        Cart cart = cartManager.buildCart();
    } catch (CartContentException unexpected) {
        // Bad line items detected or bad total price string for the cart
    }

    You can manually set the total price of the CartManager. If you do not, it is calculated automatically as the sum of the prices of the line items, unless those items have invalid prices, or are of mixed currencies. If a CartManager does not have a total price that can be assigned to it, the buildCart() function will throw an exception.

    You may also use the Cart created from Stripe’s CartManager directly with the Android Pay API. This is the same Cart object passed into the MaskedWalletRequest in the tutorial.

    Adding the SupportWalletFragment

    To show a “Buy With Android Pay” button, you must display a SupportWalletFragment on the purchase page.

    The Stripe Android Pay Wrapper streamlines this process. To use the wrapper, create a purchase page Activity that extends StripeAndroidPayActivity.

    Note: StripeAndroidPayActivity extends AppCompatActivity, the preferred Activity type for most situations. To launch a StripeAndroidPayActivity, you must give it an Intent with either a Cart or a MaskedWallet:

    try {
        Cart cart = cartManager.buildCart();
        Intent intent = new Intent(this, AndroidPayActivity.class)
                .putExtra(StripeAndroidPayActivity.EXTRA_CART, cart);
        startActivity(intent);
    } catch (CartContentException unexpected) {
        // ...
    }

    There are only two required overrides:

    protected abstract void onAndroidPayAvailable();
    protected abstract void onAndroidPayNotAvailable();

    When the Activity is launched, it will check to see if Android Play is available. Upon result, it will call the appropriate method. If you wish to display a Buy Button, override the addBuyButtonWalletFragment method:

    /*
     * Here is where you tell your activity where to display the Buy Button Fragment. You can
     * override this to be a no-op or ignore this method entirely if you don't intend to show
     * the buy button.
     */
    @Override
    protected void addBuyButtonWalletFragment(@NonNull SupportWalletFragment walletFragment) {
        FragmentTransaction buttonTransaction = getSupportFragmentManager().beginTransaction();
        buttonTransaction.add(R.id.android_pay_button_container, walletFragment).commit();
    }

    You can customize the appearance of the Buy Button by overriding getWalletFragmentButtonStyle. For instance, the default configuration is given by

    /**
     * Creates the {@link WalletFragmentStyle} for the buy button for this Activity.
     * Override to change the appearance of the button. The results of this method
     * are used to build the {@link WalletFragmentOptions}.
     *
     * @return a {@link WalletFragmentStyle} used to display Android Pay options to the user
     */
    @NonNull
    protected WalletFragmentStyle getWalletFragmentButtonStyle() {
        return new WalletFragmentStyle()
                .setBuyButtonText(WalletFragmentStyle.BuyButtonText.BUY_WITH)
                .setBuyButtonAppearance(WalletFragmentStyle.BuyButtonAppearance.ANDROID_PAY_DARK)
                .setBuyButtonWidth(WalletFragmentStyle.Dimension.MATCH_PARENT);
    }

    When the users presses the Android Pay button, the Android Pay dialog fragment (controlled by Google Play Services) will appear. To handle the result of the user’s selection and get a MaskedWallet, override onMaskedWalletRetrieved:

    @Override
    protected void onMaskedWalletRetrieved(@Nullable MaskedWallet maskedWallet) {
        super.onMaskedWalletRetrieved(maskedWallet);
        if (maskedWallet != null) {
            mPossibleConfirmedMaskedWallet = maskedWallet;
    
            // This is a good place to update your displayed information
            updatePaymentInformation(mPossibleConfirmedMaskedWallet);
        }
    }

    Requesting the Full Wallet

    At this point, if you have a non-null MaskedWallet, you can proceed to request the full wallet. Remember to use the shipping address and billing address details from the MaskedWallet to update the activity’s Cart using setCart(Cart cart). The copy-constructor CartManager is useful here.

    Best practices suggest that you should allow the user to confirm the details of the purchase. You can display a SupportWalletFragment with the confirmation details in the same activity, or launch a new StripeAndroidPayActivity with the received MaskedWallet.

    // Launching a confirmation activity
    Intent intent = new Intent(this, MyAndroidPayActivity.class)
            .putExtra(StripeAndroidPayActivity.EXTRA_MASKED_WALLET, maskedWallet)
            .putExtra(StripeAndroidPayActivity.EXTRA_CART, cart);
    startActivity(intent);

    To display the confirmation fragment in the original activity or a new one, override the addConfirmationWalletFragment method.

    @Override
    protected void addConfirmationWalletFragment(@NonNull SupportWalletFragment walletFragment) {
        // If the container was previously hidden, show it
        mChangeDetailsContainer.setVisibility(View.VISIBLE);
        // Make the fragment transaction
        FragmentTransaction confirmationTransaction =
                getSupportFragmentManager().beginTransaction();
        confirmationTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        confirmationTransaction.replace(R.id.container_fragment_confirmation, walletFragment).commit();
    }

    Whenever you want to display the confirmation fragment, simply call createAndAddConfirmationWalletFragment with the MaskedWallet instance that the user should confirm. You can change the styling of the confirmation fragment by overriding getWalletFragmentConfirmationStyle:

    @NonNull
    @Override
    protected WalletFragmentStyle getWalletFragmentConfirmationStyle() {
        return new WalletFragmentStyle()
            .setMaskedWalletDetailsLogoImageType(WalletFragmentStyle.LogoImageType.ANDROID_PAY)
            .setStyleResourceId(R.style.SampleTheme)
            .setMaskedWalletDetailsTextAppearance(android.R.style.TextAppearance_DeviceDefault_Medium)
            .setMaskedWalletDetailsHeaderTextAppearance(android.R.style.TextAppearance_DeviceDefault_Large);
    }

    The user may choose to change details about payment methods or address while interacting with the confirmation fragment. To handle these changes, override onChangedMaskedWalletRetrieved.

    @Override
    protected void onChangedMaskedWalletRetrieved(@Nullable MaskedWallet maskedWallet) {
        super.onChangedMaskedWalletRetrieved(maskedWallet);
        if (maskedWallet == null) {
            // If the value is null, no changes were made.
            return;
        }
    
        mPossibleConfirmedMaskedWallet = maskedWallet;
        updatePaymentInformation(mPossibleConfirmedMaskedWallet);
    }

    When you are ready to proceed with a MaskedWallet, use the AndroidPayConfiguration to create a full wallet request. Built-in functions to access the current cart (getCart()) and load the full wallet (loadFullWallet(FullWalletRequest request)) streamline the process.

    // Create the final FullWalletRequest
    FullWalletRequest walletRequest =
        AndroidPayConfiguration.generateFullWalletRequest(
                maskedWallet.getGoogleTransactionId(),
                getCart());
    // This is a good place to display a progress dialog
    mProgressDialogController.startProgress();
    // This method starts the final communication with Google's servers.
    loadFullWallet(walletRequest);

    This makes a call to the Android Pay api to make a token request through Stripe. To get the result of this request, override onStripePaymentSourceReturned, as well as handleError.

    @Override
    protected void onStripePaymentSourceReturned(
            FullWallet wallet,
            StripePaymentSource paymentSource) {
        super.onStripePaymentSourceReturned(wallet, paymentSource);
        // If a progress dialog was launched, cancel it now.
        mProgressDialogController.finishProgress();
    
        // You can use the details in the returned wallet to confirm
        // to the user which card was used. You can also ignore it, as
        // there is nothing else needed.
        String cardDetails = null;
        if (wallet.getInstrumentInfos() != null && wallet.getInstrumentInfos().length > 0) {
            InstrumentInfo info = wallet.getInstrumentInfos()[0];
            cardDetails = info.getInstrumentDetails();
        }
    
        // This ID is what you would send to your server to create a charge.
        String id = paymentSource.getId();
        sendTokenIdToMyServer(id);
    }

    The StripePaymentSource returned will be a Token or a Source. To make a charge, send the ID retrieved from the token to your servers. In the event that an error occurs, use the Android Pay documentation to best determine how to resolve the problem. The argument to the handleError(int errorCode) method is returned directly from the Android Pay API.