Getting started with the Android SDK

Before getting started with the Android SDK, make sure you've completed the prerequisites.

The Android SDK lets you add Passes in Google Wallet. When you add the Google Wallet button in your app, your users will enjoy a simple and delightful experience of adding their passes to Google Wallet.

Follow the steps to add the Google Wallet button to your Android application:

1. Create a Passes Object

Note: A Passes Class is required to create a Passes Object. If you haven't already created one, follow the instructions on how to Create a Passes Class. Define a corresponding OfferObject, including the following required attributes:

  • classId: The id of the Passes Class created in prerequisites.
  • id: A unique id for the object.

  • state: This field is used to determine how an object is displayed. For example, an inactive object is moved to the "Expired passes" section.

Refer to the Layout template for more information on how these attributes are represented in the offer.

Here's the definition for a sample offer:

JSON

      
{
  "id": "ISSUER_ID.OBJECT_ID",
  "classId": "ISSUER_ID.CLASS_ID",
  "state": "ACTIVE"
}
      
    

2. Create an unsigned JWT with the object

When the OfferObject is created, wrap it with an unsigned JWT with the payload.OfferObjects attribute, as shown in the following snippet:

JSON

{
  "iss": "OWNER_EMAIL_ADDRESS",
  "aud": "google",
  "typ": "savetowallet",
  "iat": "UNIX_TIME",
  "origins": [],
  "payload": {
      "offerObjects": [ NEW_OBJECT ]
  }
}

3. Include the Google Wallet button in your UI

Google Wallet provides a familiar button that you can use to trigger the Add to Google Wallet flow in your application. Vector assets for the button are available in the Button guidelines.

You can import vector assets in Android Studio under File > New > Vector Asset. Select "Local file" in the wizard, add a name (eg.: add_to_google_wallet_button.xml) and locate the file in your local drive to import it.

Now, you can use the imported drawable to add the button to your user interface:

<ImageButton
    android:id="@+id/addToGoogleWalletButton"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:minWidth="200dp"
    android:clickable="true"
    android:src="@drawable/add_to_google_wallet_button" />

The button has a layout_height of 48 dp and must be at least 200 dp wide.

4. Check if the Google Wallet API is available on the target device

Before saving the new object, ensure that the Google Wallet API is available on the target device by calling the getPayApiAvailabilityStatus method in the PayClient class. Start by adding a member variable to the activity where you will show the button and instantiate it when the activity is created:

Kotlin

import com.google.android.gms.pay.PayClient

private lateinit var walletClient: PayClient

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  walletClient = Pay.getClient(this)

  // Additional logic in your onCreate method
}

Java

import com.google.android.gms.pay.PayClient;

private final PayClient walletClient;

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  walletClient = Pay.getClient(application);

  // Additional logic in your onCreate method
}

Now, use the client to check whether the API is available:

Kotlin

import com.google.android.gms.pay.PayApiAvailabilityStatus

private fun fetchCanUseGoogleWalletApi() {
  walletClient
    .getPayApiAvailabilityStatus(PayClient.RequestType.SAVE_PASSES)
    .addOnSuccessListener { status ->
      if (status == PayApiAvailabilityStatus.AVAILABLE) {
        // The API is available, show the button in your UI
      } else {
        // The user or device is not eligible for using the Pay API
      }
    }
    .addOnFailureListener {
      // Hide the button and optionally show an error message
    }
}

Java

import com.google.android.gms.pay.PayApiAvailabilityStatus;

private void fetchCanAddPassesToGoogleWallet() {
  walletClient
    .getPayApiAvailabilityStatus(PayClient.RequestType.SAVE_PASSES)
    .addOnSuccessListener(status -> {
      if (status == PayApiAvailabilityStatus.AVAILABLE) {
        // The API is available, show the button in your UI
      } else {
        // The user or device is not eligible for using the Pay API
      };
    })
    .addOnFailureListener(exception -> {
      // Google Play Services is too old, or API availability not verified
      // Hide the button and optionally show an error message
    });
}

Finally, call the method defined above in your application when you need to determine the availability of the API.

5. Add the object to Google Wallet

The OfferObject can be added by passing the unsigned JWT from step 2 to the savePasses method. You can start the add operation as a result of clicking the Google Wallet button:

Kotlin

import android.os.Bundle
import android.view.View
import com.google.android.gms.samples.wallet.databinding.ActivityCheckoutBinding

private val addToGoogleWalletRequestCode = 1000

private lateinit var layout: ActivityCheckoutBinding
private lateinit var addToGoogleWalletButton: View

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  // Use view binding to access the UI elements
  layout = ActivityCheckoutBinding.inflate(layoutInflater)
  setContentView(layout.root)

  addToGoogleWalletButton = layout.addToGoogleWalletButton
  addToGoogleWalletButton.setOnClickListener {
    walletClient.savePasses(newObjectJson, this, addToGoogleWalletRequestCode)
  }

  // Additional logic in your onCreate method
}

Java

import android.os.Bundle;
import android.view.View;
import com.google.android.gms.samples.wallet.databinding.ActivityCheckoutBinding;

private static final int ADD_TO_GOOGLE_WALLET_REQUEST_CODE = 999;

private ActivityCheckoutBinding layout:
private View addToGoogleWalletButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // Use view binding to access the UI elements
  layout = ActivityCheckoutBinding.inflate(getLayoutInflater());
  setContentView(layout.getRoot());

  addToGoogleWalletButton = layout.addToGoogleWalletButton;
  addToGoogleWalletButton.setOnClickListener(v -> {
    walletClient.savePasses(newObjectJson, this, ADD_TO_GOOGLE_WALLET_REQUEST_CODE);
  });

  // Additional logic in your onCreate method
}

The savePasses method triggers the save flow and invokes the onActivityResult method after the save flow has completed. The implementation of onActivityResult should be similar to the following:

Kotlin

import android.content.Intent

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
  super.onActivityResult(requestCode, resultCode, data)

  if (requestCode == addToGoogleWalletRequestCode) {
    when (resultCode) {
      RESULT_OK -> {
        // Pass saved successfully
      }

      RESULT_CANCELED -> {
        // Save operation canceled
      }

      PayClient.SavePassesResult.SAVE_ERROR -> data?.let { intentData ->
        val errorMessage = intentData.getStringExtra(PayClient.EXTRA_API_ERROR_MESSAGE)
        // Handle error
      }

      else -> {
          // Handle unexpected (non-API) exception
      }
    }
  }
}

Java

import android.content.Intent;

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

  if (requestCode == ADD_TO_GOOGLE_WALLET_REQUEST_CODE) {
    switch (resultCode) {
      case RESULT_OK: {
        // Pass saved successfully
        break;
      }

      case RESULT_CANCELED: {
        // Save operation canceled
        break;
      }

      case PayClient.SavePassesResult.SAVE_ERROR: {
        if (data != null) {
          String apiErrorMessage = data.getStringExtra(PayClient.EXTRA_API_ERROR_MESSAGE);
          // Handle error
        }
        break;
      }

      default: {
        // Handle unexpected (non-API) exception
      }
    }
  }
}

When the pass is successfully added, the resultCode contains the value of Activity.RESULT_OK.

[TEST ONLY] passes

When you are still in demo mode, all the passes that you create will have an additional text "[TEST ONLY]" in the title of your pass. This is to differentiate demo passes from live passes. Once you get production approval from our team these demo mode passes will no longer have the additional text when the user reopens the wallet app on a connected device.

Next steps