Welcome to the Android Pay codelab! In this codelab you will learn how to set up your Android app to collect payment information from users with just a few clicks. No more flaky credit card forms, this is how your users should be shopping in your application.

Create an Android Studio Project

Open Android Studio and create a new project. Enter your Application Name and Company Domain. You can enter any value in these fields for the purposes of this codelab.

After picking a location to save your project, click Next. The next screen will ask you to select form factors and SDKs, just click Next again to accept the default setting.

On the Add Activity screen, select Empty Activity and click Next. The next screen will ask for an activity name, layout name, and more. Leave the default values and select Finish.

Add Dependencies

The Android Pay API is part of Google Play Services, so let's add a dependency on Google Play Services to the project:

Open the build.gradle file in the app module and add the following to the dependencies block:

dependencies {  
    compile 'com.google.android.gms:play-services-wallet:9.6.1'
    compile 'com.android.support:support-v4:23.3.0'
    compile 'com.android.support:appcompat-v7:23.3.0'
}

Next, click the "Gradle sync" button in the top toolbar. If this button is grayed out, wait for Android studio to finish loading the project (you can see progress in the lower right):

to download the dependencies you just added.

Add the Android Pay Tag

In the ‘Android' project view, open app > manifests > AndroidManifest.xml:

Inside the <application> tag, add a tag to enable the Android Pay API in your app:

<application

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

</application>

Congrats, you've finished setup! Next up, let's create the Buy with Google Button and Masked Wallet Request.

Add a FrameLayout to hold the Android Pay Button

In this step you will modify the layout created by Android Studio and add a placeholder where you will add the Android Pay button.

Open the file activity_main.xml file in the Android view:

This will open into the ‘Design' view where you can see a preview of your layout. At the bottom of the screen, click Text to open the text view where you can edit the XML directly. Replace the content of the file with the following code:

    <?xml version="1.0" encoding="utf-8"?>    
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

       <!-- We will add more here -->

    </LinearLayout>

Add a FrameLayout inside the LinearLayout. This will hold the Android Pay button. We'll make it 200dp x 48dp with 10dp margins:

<LinearLayout
    ...

    <FrameLayout
        android:id="@+id/wallet_button_holder"
        android:layout_height="48dp"
        android:layout_width="200dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp" />
</LinearLayout>

That's all the layout editing we need to do for now. It's time to get into the code!

The Transaction Flow

From here on you will begin implementing the steps of the transaction flow. Getting payment information through Android Pay takes place in a four steps:

  1. Request a Masked Wallet object: this is where you will define the details of the charge you would like to make and the information you will require (address, phone number, etc).
  2. Get a Masked Wallet object: this happens after the user has clicked the Buy button and provided all the information you asked for. At this point the user should be done entering information and you should display a confirmation screen.
  3. Request a Full Wallet object: once you have used the Masked Wallet object to display a confirmation screen, you can request a full wallet object. This confirms the details of the Masked Wallet request and asks for the network or gateway token.
  4. Get a Full Wallet object: at this point you are ready to make the charge. The Full Wallet object contains either a network or gateway token that you can use to charge the user for the transaction.

Create a MaskedWalletRequest Helper Function

Now you'll create the Masked Wallet request which defines the information that we're requesting from the user and some estimated information about their purchase.

Open the MainActivity.java file from the Android view:

Inside the MainActivity class, define a helper function called generateMaskedWalletRequest() to create the Masked Wallet Request. The Masked Wallet Request defines the information about the order and what you're requesting from the customer:

    private MaskedWalletRequest generateMaskedWalletRequest(){
      // We will implement this in the next step
      return null;
    }

Instead of return null; let's replace it with a MaskedWalletRequest which we can create using the Builder pattern. Here we're defining that we want the user's phone number, shipping address and that they're purchasing a Google I/O sticker for approximately $15 (including tax and shipping):

private MaskedWalletRequest generateMaskedWalletRequest() {
        // This is just an example publicKey for the purpose of this codelab. 
        // To learn how to generate your own visit:
        // https://github.com/android-pay/androidpay-quickstart
        String publicKey = "BO39Rh43UGXMQy5PAWWe7UGWd2a9YRjNLPEEVe+zWIbdIgALcDcnYCuHbmrrzl7h8FZjl6RCzoi5/cDrqXNRVSo=";
        PaymentMethodTokenizationParameters parameters =
                PaymentMethodTokenizationParameters.newBuilder()
                        .setPaymentMethodTokenizationType(
                             PaymentMethodTokenizationType.NETWORK_TOKEN)
                        .addParameter("publicKey", publicKey)
                        .build();

        MaskedWalletRequest maskedWalletRequest =
                MaskedWalletRequest.newBuilder()
                        .setMerchantName("Google I/O Codelab")
                        .setPhoneNumberRequired(true)
                        .setShippingAddressRequired(true)
                        .setCurrencyCode("USD")
                        .setCart(Cart.newBuilder()
                                .setCurrencyCode("USD")
                                .setTotalPrice("10.00")
                                .addLineItem(LineItem.newBuilder()
                                        .setCurrencyCode("USD")
                                        .setDescription("Google I/O Sticker")
                                        .setQuantity("1")
                                        .setUnitPrice("10.00")
                                        .setTotalPrice("10.00")
                                        .build())
                                .build())
                        .setEstimatedTotalPrice("15.00")
                        .setPaymentMethodTokenizationParameters(parameters)
                        .build();
        return maskedWalletRequest;

    }

Note: Android Studio will underline classes in red when imports are not met. You can import classes by placing the cursor over the class and hitting the Alt + Enter buttons on your keyboard.

Now you have created a request describing what your user would like to buy in this app. In the next steps we will configure the Android Pay button to execute this request and learn how to actually get the user's payment information.

Add the WalletFragment

We will create the Android Pay button using the WalletFragment class, which is essential to showing this button and other standard UI elements.

In MainActivity, add a SupportWalletFragment instance variable and a request code constant so that we can use them later:

    private SupportWalletFragment mWalletFragment;
    public static final int MASKED_WALLET_REQUEST_CODE = 888;

In the onCreate method define the style the WalletFragment will have:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // Wallet fragment style
        WalletFragmentStyle walletFragmentStyle = new WalletFragmentStyle()
                .setBuyButtonText(WalletFragmentStyle.BuyButtonText.BUY_WITH)
                .setBuyButtonWidth(WalletFragmentStyle.Dimension.MATCH_PARENT);
    }

Also in onCreate, define the WalletFragmentOptions that will control the WalletFragment:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       
        // ...

        // Wallet fragment options
        WalletFragmentOptions walletFragmentOptions = WalletFragmentOptions.newBuilder()
                .setEnvironment(WalletConstants.ENVIRONMENT_TEST)
                .setFragmentStyle(walletFragmentStyle)
                .setTheme(WalletConstants.THEME_LIGHT)
                .setMode(WalletFragmentMode.BUY_BUTTON)
                .build();
    }

Finally, still in onCreate, instantiate and initialize the WalletFragment:

@Override
protected void onCreate(Bundle savedInstanceState) {

   // ...

   // Initialize the WalletFragment
   WalletFragmentInitParams.Builder startParamsBuilder =
           WalletFragmentInitParams.newBuilder()
                   .setMaskedWalletRequest(generateMaskedWalletRequest())
                   .setMaskedWalletRequestCode(MASKED_WALLET_REQUEST_CODE)
                   .setAccountName("Google I/O Codelab");
   mWalletFragment = SupportWalletFragment.newInstance(walletFragmentOptions);
   mWalletFragment.initialize(startParamsBuilder.build());
}

Now the WalletFragment object is fully instantiated. In the next step we will add it to the UI so that your app can display the Android Pay buy button.

Insert the Fragment into the UI

In MainActivity, create a class constant to contain the WalletFragment's tag:

  public static final String WALLET_FRAGMENT_ID = "wallet_fragment";

Next, initialize the FrameLayout we added earlier with the WalletFragment in the onCreate method:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       
        // ...

        // Add the WalletFragment to the UI
        getSupportFragmentManager().beginTransaction()
            .replace(R.id.wallet_button_holder, mWalletFragment, WALLET_FRAGMENT_ID)
            .commit();
    }

We don't want to add WalletFragment twice, so we should surround all of the initialization code we have written so far with a check to see if it already exists in the Activity. Your onCreate should now look like this:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Check if WalletFragment exists
        mWalletFragment = (SupportWalletFragment) getSupportFragmentManager()
                .findFragmentByTag(WALLET_FRAGMENT_ID);

        if (mWalletFragment == null) {
            // Wallet fragment style
            WalletFragmentStyle walletFragmentStyle = new WalletFragmentStyle()
                    .setBuyButtonText(WalletFragmentStyle.BuyButtonText.BUY_WITH)
                    .setBuyButtonWidth(WalletFragmentStyle.Dimension.MATCH_PARENT);

            // Wallet fragment options
            WalletFragmentOptions walletFragmentOptions = WalletFragmentOptions.newBuilder()
                    .setEnvironment(WalletConstants.ENVIRONMENT_TEST)
                    .setFragmentStyle(walletFragmentStyle)
                    .setTheme(WalletConstants.THEME_LIGHT)
                    .setMode(WalletFragmentMode.BUY_BUTTON)
                    .build();

            // Initialize the WalletFragment
            WalletFragmentInitParams.Builder startParamsBuilder =
                    WalletFragmentInitParams.newBuilder()
                            .setMaskedWalletRequest(generateMaskedWalletRequest())
                            .setMaskedWalletRequestCode(MASKED_WALLET_REQUEST_CODE)
                            .setAccountName("Google I/O Codelab");
            mWalletFragment = SupportWalletFragment.newInstance(walletFragmentOptions);
            mWalletFragment.initialize(startParamsBuilder.build());

            // Add the WalletFragment to the UI
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.wallet_button_holder, mWalletFragment, WALLET_FRAGMENT_ID)
                    .commit();
        }

    }

Now your Android Pay buy button will be added to the UI at the right time and with the correct style!

Handling Masked Wallet Request Intents

When you click the Android Pay button, you will kick off a request for a Masked Wallet. In this section you will learn how to handle the result of that request.

Add the following instance variable to MainActivity to store the MaskedWallet:

    private MaskedWallet mMaskedWallet;

Then, add the following function to override onActivityResult:

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);
    }

In onActivityResult, check to see if the request code is for your Masked Wallet request and store the resulting response:

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case MASKED_WALLET_REQUEST_CODE:
                switch (resultCode) {
                    case RESULT_OK:
                        mMaskedWallet =  data
                                .getParcelableExtra(WalletConstants.EXTRA_MASKED_WALLET);
                        Toast.makeText(this, "Got Masked Wallet", Toast.LENGTH_SHORT).show();
                        break;
                    case RESULT_CANCELED:
                        // The user canceled the operation
                        break;
                    case WalletConstants.RESULT_ERROR:
                        Toast.makeText(this, "An Error Occurred", Toast.LENGTH_SHORT).show();
                        break;
                }
                break;
        }
    }

With that code in place, you will be able to receive the result of a MaskedWallet request. Now is a good time to run your app and make sure everything is working well.

You should see a Buy button. Clicking the button will bring up a payment method screen where you can select a credit card and billing address. If you proceed through the buy flow you should see a message that says "Got Masked Wallet".

Initiate the Full Wallet Request

In the last step you got Masked Wallet information representing the user's desired purchase. When you request a Full Wallet, you get a one-time card from Google and the user's full billing and shipping information. This card is backed by the user's secured payment credentials.

First, re-open the activity_main.xml file. We will add a button inside the LinearLayout (below the FrameLayout). When clicked, this button will call the requestFullWallet function which we will create later:

    <Button
        android:id="@+id/confirm_button"
        android:layout_width="200dp"
        android:layout_height="48dp"
        android:layout_gravity="center_horizontal"
        android:text="Confirm"
        android:onClick="requestFullWallet" />

That's enough layout work for now, we will make this button do something in the next few steps.

Requesting the Full Wallet

Just like with Masked Wallet, you need to create a request object to get a Full Wallet. Open MainActivity.java again. Create a helper function to generate the Full Wallet Request. It should take the Google Transaction ID as a parameter. The Google Transaction ID can be found in the Masked Wallet Response. You'll also want to add information about the purchase including the exact amount you will be charging for. Here we have a $10.00 sticker with $0.10 tax:

    private FullWalletRequest generateFullWalletRequest(String googleTransactionId) {
      FullWalletRequest fullWalletRequest = FullWalletRequest.newBuilder()
          .setGoogleTransactionId(googleTransactionId)
          .setCart(Cart.newBuilder()
              .setCurrencyCode("USD")
              .setTotalPrice("10.10")
              .addLineItem(LineItem.newBuilder()
                  .setCurrencyCode("USD")
                  .setDescription("Google I/O Sticker")
                  .setQuantity("1")
                  .setUnitPrice("10.00")
                  .setTotalPrice("10.00")
                  .build())
              .addLineItem(LineItem.newBuilder()
                  .setCurrencyCode("USD")
                  .setDescription("Tax")
                  .setRole(LineItem.Role.TAX)
                  .setTotalPrice(".10")
                  .build())
              .build())
          .build();
      return fullWalletRequest;
    }

You can see that this is very similar to the Masked Wallet request, except that we now have a transaction ID. In the next steps we will use this request to get the Full Wallet object which will enable us to actually get chargeable payment information.

Managing a GoogleApiClient

If you have ever used a Google API in your Android app, you may be familiar with GoogleApiClient. GoogleApiClient is your key to accessing Google Play Services from your Android application. In this step we will configure a GoogleApiClient, which we will need before we can continue.

First, add the following class variable to MainActivity:

    private GoogleApiClient mGoogleApiClient;

At the end of the onCreate method, add the following code to build the client:

    mGoogleApiClient = new GoogleApiClient.Builder(this)
        .addOnConnectionFailedListener(this)
        .enableAutoManage(this, 0, this)
        .addApi(Wallet.API, new Wallet.WalletOptions.Builder()
            .setEnvironment(WalletConstants.ENVIRONMENT_TEST)
            .setTheme(WalletConstants.THEME_LIGHT)
            .build())
        .build();

Then change the class definition of MainActivity to implement this interface. After you do this Android Studio will complain that we have not implemented the required methods. We will do that next:

public class MainActivity extends AppCompatActivity implements 
        GoogleApiClient.OnConnectionFailedListener {

    // ....

}

Add the following code to your activity to implement the required method for GoogleApiClient:

   @Override
   public void onConnectionFailed(ConnectionResult result) {
      // GoogleApiClient failed to connect, we should log the error and retry
    }

Now you have the basic setup for GoogleApiClient, and we can use it to request the Full Wallet.

Add a Function to Request the Full Wallet

Here we'll create a function that handles the button click, calls our helper function and requests the Full Wallet.

Define a class constant request code for the Full Wallet Request and an instance variable to store the Full Wallet response. This should look familiar as it is similar to what we did for Masked Wallet:

    public static final int FULL_WALLET_REQUEST_CODE = 889;
    private FullWallet mFullWallet;

Define a function to generate the Full Wallet Request using the function we defined earlier and make the request to load the Full Wallet. This function is defined by the button we defined a few steps ago:

    public void requestFullWallet(View view) {
      if (mMaskedWallet == null) {
        Toast.makeText(this, "No masked wallet, can't confirm", Toast.LENGTH_SHORT).show();
        return;
      } 
      Wallet.Payments.loadFullWallet(mGoogleApiClient,
          generateFullWalletRequest(mMaskedWallet.getGoogleTransactionId()),
          FULL_WALLET_REQUEST_CODE);
    }

Add a case to onActivityResult to handle the Full Wallet response:

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case MASKED_WALLET_REQUEST_CODE:
                // Previous code here
                // ...
            case FULL_WALLET_REQUEST_CODE:
                switch (resultCode) {
                    case RESULT_OK:
                        mFullWallet = data
                                .getParcelableExtra(WalletConstants.EXTRA_FULL_WALLET);
                        // Show the credit card number
                        Toast.makeText(this,
                                "Got Full Wallet, Done!",
                                Toast.LENGTH_SHORT).show();
                        break;
                    case WalletConstants.RESULT_ERROR:
                        Toast.makeText(this, "An Error Occurred", Toast.LENGTH_SHORT).show();
                        break;
                }
                break;
        }
    }

Once you have the Full Wallet object, you have the one-time payment credentials. You can access the payment method token with the following method call:

    mFullWallet.getPaymentMethodToken();

Now that you have the Full Wallet you are almost done! You have a payment token which you can use to process the transaction. Pat yourself on the back, you just did a complete integration with Android Pay! It's time to run your app again and see how it went.

That's it, you've integrated Android Pay in 30 minutes! Run your app again, it should look like this:

Tap the Buy button and select the fake Visa credit card from before. Feel free to enter any valid shipping/billing address, this is all sandboxed. Once you've selected your payment credentials, click the Confirm button. After a short delay you will see a Toast message with the result of the operation.

If you are interested in using Android Pay in your own app, please visit our developer site to get started.