Google Wallet Instant Buy APIs

Notice
To get production access, sign up using the Instant Buy interest form. Then, integrate Instant Buy with your Android, mobile web, or web app as described in these pages.

Instant Buy Android API Tutorial

This tutorial guides you through integrating Instant Buy into a purchase flow, in the context of an example bike store. The tutorial provides details and complete source code to help you complete the the integration steps summarized in the integration overview, with the end goal of reproducing a flow similar to the Instant Buy API Process Flow. If you'd like to review the sample app first, you can download the APK file.

A significant portion of the Instant Buy API's functionality is provided as part of Google Play Services, the core services for developing on Android. For the purposes of this tutorial, we'll assume that you are familiar with the basic concepts and skills of application development for the Android platform. If you need to learn about Android development before getting started, work through some lessons in the Training for Android Developers.

Contents

Set up the tutorial

To get started with this tutorial, create a client ID for your project, download and review the source code, and set up Google Play services.

Obtain credentials and a client ID for your app

To access the Instant Buy Android API, you'll need to obtain a client ID for OAuth 2.0 authorization in the Google Developers Console. The client ID is generated automatically when you register your app.. You'll also need the SHA1 fingerprint in your developer's key to generate a client ID.

In a terminal, run the Keytool utility to get the SHA1 fingerprint for your digitally signed .apk file's public certificate.
keytool -exportcert -alias androiddebugkey -keystore path-to-debug-or-production-keystore -list -v

The Keytool prints the fingerprint to the shell. For example:

$ keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v
Enter keystore password: Type "android" if using debug.keystore
Alias name: androiddebugkey
Creation date: Aug 27, 2012
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 503bd581
Valid from: Mon Aug 27 13:16:01 PDT 2012 until: Wed Aug 20 13:16:01 PDT 2042
Certificate fingerprints:
   MD5:  1B:2B:2D:37:E1:CE:06:8B:A0:F0:73:05:3C:A3:63:DD
   SHA1: D8:AA:43:97:59:EE:C5:95:26:6A:07:EE:1C:37:8E:F4:F0:C8:05:C8
   SHA256: F3:6F:98:51:9A:DF:C3:15:4E:48:4B:0F:91:E3:3C:6A:A0:97:DC:0A:3F:B2:D2:E1:FE:23:57:F5:EB:AC:13:30
   Signature algorithm name: SHA1withRSA
   Version: 3

Copy the SHA1 fingerprint, which is highlighted in the example above.

  1. Go to the Google Developers Console.
  2. Select a project, or create a new one.
  3. In the sidebar on the left, select APIs & auth. Set the status to ON for any APIs you are using that appear in the list. Note that Google Wallet Instant Buy is not listed because it does not need to be activated.
  4. In the sidebar on the left, select Credentials.
  5. Select one of the following options:

    • If your application needs to submit authorized requests:

      1. Click Create New Client ID.
      2. Select Installed application and Android.
      3. In the Package name field, enter your Android's app's package name.
      4. Paste the SHA1 fingerprint into the form.
      5. Click Create Client ID.
    • If your application only needs to make API calls that do not require authorization:

      1. Click Create New Key.
      2. Select Android key.
      3. Paste the SHA1 fingerprint into the form.
      4. After the fingerprint, type a semicolon and then enter your Android's app's package name.
      5. Click Create.

Set up the sample and Google Play Services

Follow these setup instructions to import both the Google Play Services library as well as the Google Wallet Instant Buy sample. You can find the sample at android-sdk/extras/google/google_play_services/samples/wallet. If you don't already have it, you'll need to get the Android SDK.

Add a "Sign In with Google" Button to your app

You can improve the purchase and registration flow for many of your users by displaying a "Sign in with Google" button on your login page. If the user is already signed into a Google account on their device, then a single click on this button signs the user into your app with no need to enter a username or password.

To add this optional feature to your app, follow the steps described in Adding a sign-in button

Add Google Wallet buttons to your UI

Google Wallet provides Android SDK buttons for you to integrate into your app. The buttons are available in the following archives:

wallet_button_buy_with_google.zip Buy with Google button
wallet_button_book_now.zip Book Now button
wallet_button_buy_now.zip Buy Now button

Each archive contains 9-patch drawables in mdpi, hdpi and xhdpi as well as StateListDrawable XML for the background and foreground states for each button. Also, the archives contain buttons in disabled states, for cases such as over-limit cart total amounts.

To incorporate a button in your application, copy over the resources from the archive into the res/ folder of your application and add the following code to your Android layout file. Note that each button requires its unique contentDescription string and minWidth value in addition to the correct value for src.

Buy with Google

<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="48dp"
             android:minWidth="200dp"
             android:background="@drawable/wallet_button_background"
             android:contentDescription="@string/wallet_buy_with_google_wallet"
             android:src="@drawable/wallet_button_buy_with_foreground" />
  • Note that the contentDescription string resource value must be Buy with Google.
  • The layout_height for the button should always be 48dp and minWidth must be 200dp.

Book Now

<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="48dp"
             android:minWidth="144dp"
             android:background="@drawable/wallet_button_background"
             android:contentDescription="@string/wallet_book_now_button"
             android:src="@drawable/wallet_button_book_now_foreground" />
  • Note that the contentDescription string resource value must be Book Now.
  • The layout_height for the button should always be 48dp and minWidth must be 144dp.

Buy Now

<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="48dp"
             android:minWidth="134dp"
             android:background="@drawable/wallet_button_background"
             android:contentDescription="@string/wallet_buy_now"
             android:src="@drawable/wallet_button_buy_now_foreground" />
  • Note that the contentDescription string resource value must be Buy Now.
  • The layout_height for the button should always be 48dp and minWidth must be 134dp.

Initialize the Google API client

The GoogleApiClient object wraps a ServiceConnection to Google Play services and is used to communicate with the Instant Buy API. The GoogleApiClient becomes functional after the asynchronous connection has been established with the service, indicating that:

  • Google Play services is running on the device and your Activity successfully bound the service connection,
  • the user has selected an account that they wish to use with your app, and
  • the user's account has granted the permissions that your app is requesting.

Typically, you want to manage the GoogleApiClient lifecycle in your activity's (or fragment's) lifecycle:

The following code snippet (from a fragment) illustrates how an app can instantiate a Google API client for your Instant Buy app and then connect:

mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
        .addConnectionCallbacks(this)
        .addOnConnectionFailedListener(this)
        .setAccountName(accountName)
        .addApi(Wallet.API, new Wallet.WalletOptions.Builder()
            .setEnvironment(Constants.WALLET_ENVIRONMENT)
            .setTheme(WalletConstants.THEME_HOLO_LIGHT)
            .build())
        .build();

      ...

@Override
public void onStart() {
    super.onStart();

    // Connect to Google Play Services
    mGoogleApiClient.connect();
}
  • Environment is a parameter in WalletOptions whose value indicates the environment (production or sandbox) in which the server supporting the app is running. Its value may be WalletConstants.ENVIRONMENT_PRODUCTION or WalletConstants.ENVIRONMENT_SANDBOX For testing and development always use only the sandbox environment.
  • Theme is another WalletOptions parameter to set the UI theme for the app. A value of WalletConstants.THEME_HOLO_LIGHT optionally sets a light UI theme instead of the default dark theme (WalletConstants.THEME_HOLO_DARK).
  • accountName is an optional string containing the Google account name (email address).
  • connectionCallBacks is an object that implements the interface com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks. Android calls the methods of this object to notify the app when it connects and disconnects with Google Play Services.
  • connectionFailedListener is an object that implements the interface com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener. Android calls a method of this object to notify the app of a failed attempt to connect with Google Play Services.

This call results in a callback to one of two methods: OnConnectionCallbackListener.onConnected if the connection succeeded, or OnConnectionFailedListener.onConnectionFailed if the connection failed. The callback receives an error code which describes the reason for the failure.

Check for prior authorization

It is a good practice to determine whether users have previously authorized Google Wallet as a default payment method to use with your app. For preauthorized users, you can streamline your UI in these ways:

  • Highlight the Buy with Google button or move it to the top of a list of payment methods.
  • Make your "continue checkout" button default to Google Wallet and direct the user directly to the purchase confirmation page.

For more information, see Preauthorization.

To check for preauthorization, call mWalletClient.checkForPreAuthorization after you are connected to a Google API client. This method requires only one parameter: a request code used to uniquely identify this call in the onActivityResult() method

static final int REQUEST_CODE_PRE_AUTH = 1010;

@Override
public void onConnected(Bundle connectionHint) {
    Wallet.checkForPreAuthorization(GoogleApiClient,
        REQUEST_CODE_RESOLVE_PRE_AUTH);
}

public void onActivityResult(int requestCode, int resultCode,
        Intent data) {
    …
    switch (requestCode) {
        case REQUEST_CODE_RESOLVE_PRE_AUTH:
            switch (resultCode) {
                case Activity.RESULT_OK:
                    mIsPreAuthed = data.getBooleanExtra(

                            WalletConstants.EXTRA_IS_USER_PREAUTHORIZED, false);

                    break;
                case Activity.RESULT_CANCELED:
                    break;
                default:
                    break;
            }
    …
    }
}

Use the result of this check to default payment options to Google Wallet. For guidelines on how to make this work in your UI, see the UI Branding Guidelines

Create a masked wallet request

The next step in the payment transaction is to request the masked wallet (which contains a masked, or obfuscated, credit card number). You'll need to create an instance of MaskedWalletRequest to invoke the Instant Buy Android API to retrieve the masked wallet information. At this point you won't have the user's chosen shipping address, so you'll need to create an estimate of the shipping costs and tax. If you set the shopping cart as shown below (highly recommended), make sure the cart total matches the sum of the line items added to the cart.

Here is an example of creating the masked wallet request using the builder pattern:

MaskedWalletRequest maskedWalletRequest =
  MaskedWalletRequest.newBuilder()
  .setMerchantName(Constants.MERCHANT_NAME)
  .setPhoneNumberRequired(true)
  .setShippingAddressRequired(true)
  .setCurrencyCode("USD")
  .setShouldRetrieveWalletObjects(true)
  .setCart(Cart.newBuilder()
      .setCurrencyCode(Constants.CURRENCY_CODE_USD)
      .setTotalPrice("150.00")
      .addLineItem(LineItem.newBuilder()
              .setCurrencyCode(Constants.CURRENCY_CODE_USD)
              .setDescription(itemInfo.name)
              .setQuantity("1")
              .setUnitPrice(toDollars(context, itemInfo.priceMicros))
              .setTotalPrice("130.00")
              .build())
      .addLineItem(LineItem.newBuilder()
              .setCurrencyCode(Constants.CURRENCY_CODE_USD)
              .setDescription(Constants.DESCRIPTION_LINE_ITEM_SHIPPING)
              .setRole(LineItem.Role.SHIPPING)
              .setTotalPrice("13.00")
              .build())
      .addLineItem(LineItem.newBuilder()
              .setCurrencyCode(Constants.CURRENCY_CODE_USD)
              .setDescription(Constants.DESCRIPTION_LINE_ITEM_TAX)
              .setRole(LineItem.Role.TAX)
              .setTotalPrice("7.00")
              .build())
      .build())
  .setEstimatedTotalPrice("150.00")
  .build();

This masked wallet object is the first parameter taken by the loadMaskedWallet call described in the next section.

Retrieve the masked wallet

Retrieve the masked wallet when a user wants to make a purchase using Google Wallet. This is usually when the user clicks on the “Buy with Google” button, shown in this display from the sample app:

Requesting the masked wallet

At this point, call loadMaskedWallet method to get the masked wallet.

static final int REQUEST_CODE_RESOLVE_LOAD_MASKED_WALLET = 1001;

Wallet.loadMaskedWallet(mGoogleApiClient, createMaskedWalletRequest(),
        REQUEST_CODE_RESOLVE_LOAD_MASKED_WALLET);

public void onActivityResult(int requestCode, int resultCode,
        Intent data) {
   switch (requestCode) {
   ...
      case REQUEST_CODE_RESOLVE_LOAD_MASKED_WALLET:
          switch (resultCode) {
              case Activity.RESULT_OK:
                  MaskedWallet maskedWallet =
                          data.getParcelableExtra(WalletConstants.EXTRA_MASKED_WALLET);
                  launchConfirmationPage(maskedWallet);
                  break;
              case Activity.RESULT_CANCELED:
                  // The user cancelled the flow
                  break;
              default:
                  // Handle the error
                  int errorCode =
                      data.getIntExtra(
                          WalletConstants.EXTRA_ERROR_CODE, -1);
                  handleError(errorCode);
                  break;
          }
          break;
    }
  ...
}
  • maskedWalletRequest is the masked wallet request instance you created in the previous step.
  • REQUEST_CODE_RESOLVE_LOAD_MASKED_WALLET is the request code used to uniquely identify this call in the onActivityResult() method.

If the user has not preauthorized this app to retrieve Wallet information, Google Wallet presents a chooser dialog, handles preauthorization, and returns control to the app.

Confirm with the user and (optionally) change the masked wallet

After the app obtains the masked wallet, it typically presents a confirmation page which shows the total cost of the items purchased in the transaction. At this point the app has the shipping address and billing address, so it can calculate exact total purchase price and display it. This page should also allow the user to change the Google Wallet payment instrument and change the shipping address for the purchase, as shown in the following screen.

Reviewing order details

The “Change” buttons are particularly important for preauthorized users, who proceed directly to this confirmation page when they click the "Buy with Google" button. If the user clicks a “Change” button, the application must call the changeMaskedWallet method:

static final int REQUEST_CODE_RESOLVE_CHANGE_MASKED_WALLET = 1003;

Wallet.changeMaskedWallet(mGoogleApiClient, mMaskedWallet.getGoogleTransactionId(),
        mMaskedWallet.getMerchantTransactionId(),
        REQUEST_CODE_RESOLVE_CHANGE_MASKED_WALLET);
  • googleTransactionId is the ID returned by the previous call to loadMaskedWallet, that is, maskedWallet.getGoogleTransactionId()
  • merchantTransactionId is the app/merchant-specific transaction ID that is echoed back from MaskedWalletRequest back into the masked wallet, or maskedWallet.getMerchantTransactionId()
  • REQUEST_CODE_RESOLVE_CHANGE_MASKED_WALLET is the request code that is returned in this activity's onActivityResult(int requestCode, int resultCode, Intent data) callback.

public void onActivityResult(int requestCode, int resultCode,
        Intent data) {
    switch (requestCode) {
        case REQUEST_CODE_RESOLVE_CHANGE_MASKED_WALLET:
            switch (resultCode) {
                case Activity.RESULT_OK:
                    mMaskedWallet =
                    data.getParcelableExtra(
                        WalletConstants.EXTRA_MASKED_WALLET);
                    // Update UI with new Masked Wallet
                    break;
                case Activity.RESULT_CANCELED:
                    // nothing to do here
                    break;
                default:
                    int errorCode =
                        data.getIntExtra(
                            WalletConstants.EXTRA_ERROR_CODE, -1);
                        handleError(errorCode);
                        break;
            }
            break;
    }
  …
}

This changeMaskedWallet request should result in displaying the chooser for wallet selection. When the user is done selecting the payment instrument or address of choice, control comes back to app with the invocation of onActivityResult. When the masked wallet is available,the app can proceed to the confirmation page.

Request the full wallet

When the user confirms the order, you are ready to request the full wallet. The full wallet Request should have the total charge that you are requesting including exact shipping, handling and tax. You must include the GoogleTransactionId that you received in the masked wallet response.

Create a FullWalletRequest object that contains the various line items that include tax and shipping if necessary and a Cart object.

FullWalletRequest fullWalletRequest = FullWalletRequest.newBuilder()
  .setGoogleTransactionId(googleTransactionId)
  .setCart(Cart.newBuilder()
          .setCurrencyCode(Constants.CURRENCY_CODE_USD)
          .setTotalPrice(toDollars(context, itemInfo.getTotalPrice()))
          .addLineItem(LineItem.newBuilder()
                  .setCurrencyCode(Constants.CURRENCY_CODE_USD)
                  .setDescription(itemInfo.name)
                  .setQuantity("1")
                  .setUnitPrice(toDollars(context, itemInfo.priceMicros))
                  .setTotalPrice(toDollars(context, itemInfo.priceMicros))
                  .build())
          .addLineItem(LineItem.newBuilder()
                  .setCurrencyCode(Constants.CURRENCY_CODE_USD)
                  .setDescription(Constants.DESCRIPTION_LINE_ITEM_SHIPPING)
                  .setRole(LineItem.Role.SHIPPING)
                  .setTotalPrice(toDollars(context, itemInfo.shippingPriceMicros))
                  .build())
          .addLineItem(LineItem.newBuilder()
                  .setCurrencyCode(Constants.CURRENCY_CODE_USD)
                  .setDescription(Constants.DESCRIPTION_LINE_ITEM_TAX)
                  .setRole(LineItem.Role.TAX)
                  .setTotalPrice(toDollars(context, itemInfo.taxMicros))
                  .build())
          .build())
  .build();

Retrieve the full wallet

Once you have constructed the full wallet request instance, call the loadFullWallet method. This method takes two parameters: the fullWalletRequest, and the requestCode that is returned as part of the call to this activity's onActivityResult(int requestCode, int resultCode, Intent data).

static final int REQUEST_CODE_RESOLVE_LOAD_FULL_WALLET = 1002;

Wallet.loadFullWallet(mGoogleApiClient, WalletUtil.createFullWalletRequest(mItemInfo,
        mMaskedWallet.getGoogleTransactionId()), REQUEST_CODE_RESOLVE_LOAD_FULL_WALLET);

public void onActivityResult(int requestCode, int resultCode,
        Intent data) {
    switch (requestCode) {
    …
        case REQUEST_CODE_RESOLVE_LOAD_FULL_WALLET:
            switch (resultCode) {
                case Activity.RESULT_OK:
                    mFullWallet  =
                    data.getParcelableExtra(
                        WalletConstants.EXTRA_FULL_WALLET);
                    // the full wallet can now be used to
                    // process the customer's payment. Send the
                    // wallet info up to server to process, and to
                    // get the result for sending
                    // transaction status
                    break;
                case Activity.RESULT_CANCELED:
                    // nothing to do here
                    break;
                default:
                    int errorCode =
                        data.getIntExtra(
                            WalletConstants.EXTRA_ERROR_CODE, -1);
                    handleError(errorCode);
                    break;
            }
            break;
         …
    }
}

Once you have retrieved the full wallet in the onActivityResult() callback, you have enough information to proceed to payment processing for this transaction. The ProxyCard is a valid payment card, except it is distinct from the user’s actual payment instrument in Google Wallet. You can access the details in the ProxyCard from the full wallet as shown below:

String accountNumber  =  mFullWallet.getProxyCard().getPan();
String securityCvv  = mFullWallet.getProxyCard().getCvn();
int expirationYear = mFullWallet.getExpirationYear();
int expirationMonth = mFullWallet.getExpirationMonth();
Address billingAddress = mFullWallet.getBillingAddress();
Address shippingAddress = mFullWallet.getShippingAddress();

With this, you can charge this card in the amount that you requested in the full wallet request, just as you would charge any other credit card received in your app in non Google Wallet flow.

Notify Google Wallet of your Transaction Processing

Once you have obtained the payment instrument information from the ProxyCard and processed the payment, notify Google Wallet of the success or failure of the payment. Based on the response from your server, create the NotifyTransactionStatusRequest object.

NotifyTransactionStatusRequest notifyTxRequest = NotifyTransactionStatusRequest.newBuilder()
  .setGoogleTransactionId(googleTransactionId)
  .setStatus(NotifyTransactionStatusRequest.Status.SUCCESS)
  .build();

Wallet.notifyTransactionStatus(mGoogleApiClient,
        WalletUtil.createNotifyTransactionStatusRequest(fullWallet.getGoogleTransactionId(),
                NotifyTransactionStatusRequest.Status.SUCCESS));

Finally, you may want to clean up by calling the Google API Client’s disconnect method. This is typically done in the onStop() method of your Activity.

@Override
public void onStop() {
  super.onStop();
  mGoogleApiClient.disconnect();
}

Handle errors

Since a typical activity or fragment can call multiple Google API methods, it's advisable to have a common code block in onActivityResult()that fetches error codes.

    int errorCode = -1;
    if (data != null) {
        errorCode = data.getIntExtra(
            WalletConstants.EXTRA_ERROR_CODE, -1);
    }

After you fetch the codes, you can handle errors in the default block of a switch/case statement by calling handleError(errorCode):

void handleError(int errorCode) {
    switch (errorCode) {
        case WalletConstants.ERROR_CODE_SPENDING_LIMIT_EXCEEDED:
            Toast.makeText(getActivity(),
                    getString(R.string.spending_limit_exceeded, errorCode),
                    Toast.LENGTH_LONG).show();
            break;
        case WalletConstants.ERROR_CODE_INVALID_PARAMETERS:
        case WalletConstants.ERROR_CODE_AUTHENTICATION_FAILURE:
        case WalletConstants.ERROR_CODE_BUYER_ACCOUNT_ERROR:
        case WalletConstants.ERROR_CODE_MERCHANT_ACCOUNT_ERROR:
        case WalletConstants.ERROR_CODE_SERVICE_UNAVAILABLE:
        case WalletConstants.ERROR_CODE_UNSUPPORTED_API_VERSION:
        case WalletConstants.ERROR_CODE_UNKNOWN:
        default:
            // unrecoverable error
            mGoogleWalletDisabled = true;
            displayGoogleWalletErrorToast(errorCode);
            break;
    }
}

You can augment this implementation to handle your app's specific needs. For example, if you support multiple payment methods, you can choose to use another payment method for unrecoverable errors.

Enable Wallet optimization in your manifest

You can greatly improve your users' first-time experience by enabling Wallet optimization in your app manifest. This option signals to Google Play Services that your app uses Google Wallet, which can then perform optimizations when the user first launches your app.

To enable Wallet optimization, add the following broadcast receiver to your app manifest:

    <receiver
        android:name="com.google.android.gms.wallet.EnableWalletOptimizationReceiver"
        android:exported="false">
        <intent-filter>
            <action android:name="com.google.android.gms.wallet.ENABLE_WALLET_OPTIMIZATION" />
        </intent-filter>
    </receiver>

Enabling Wallet optimization is optional, but highly recommended for its enhancement of performance and user experience for your app.

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.