Once you've created a pass and encoded it in a JWT, you are ready to issue it in your Android app. To do this, you will need to check that the Google Wallet API is available on the user's device, present them with an 'Add to Google Wallet' button, then save the pass to their Google Wallet once they tap the button.

## Prerequisites

Before you try to issue a pass, be sure you have completed the following:

- Completed all of the steps in the [Onboarding guide](https://developers.google.com/wallet/tickets/getting-started/onboarding-guide).
- Create at least one [Passes Class](https://developers.google.com/wallet/tickets/use-cases/create#creating_a_passes_class).
- Create at least one [Passes Object](https://developers.google.com/wallet/tickets/use-cases/create#creating_a_passes_object).
- [Encoded](https://developers.google.com/wallet/tickets/use-cases/jwt) your Passes Class and Passes Object in a JWT.

> [!TIP]
> **Demo Mode / \[TEST ONLY\] passes**
>
> When you are still in [Demo Mode](https://developers.google.com/wallet/tickets/resources/terminology#demo-mode), all the passes that you create will have an additional text "\[TEST ONLY\]" in the title. This is to differentiate demo passes from live passes. Once you are granted [publishing access](https://developers.google.com/wallet/tickets/test-and-go-live/request-publishing-access), these demo mode passes will no longer have the additional text when the user reopens the wallet app on a connected device.

## 1. Install the Google Wallet Android SDK

To use the Google Wallet Android SDK, add `com.google.android.gms:play-services-pay` to the `dependencies` section of your app-level `build.gradle` file:

```
  implementation "com.google.android.gms:play-services-pay:16.5.0"
```

## 2. Check for Google Wallet API availability

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
    }

If you are using other design patterns consider placing domain specific business logic appropriately. For example, if you are using the MVVM pattern,
place UI related business logic in your Activity or Fragment (eg.: UI elements,
activity result), and operational logic in your view model (eg.: client
instantiation, network call triggers).

Next, use the `PayClient` 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 you just defined in your application when you need to determine the availability of the API.

### Handle when the API is unavailable

Some reasons why the API may be unavailable include the Android or Google
Play services versions being out of date, or that Google Wallet is
unavailable in the user's country.

If the API is not available
consider hiding the button and falling back to a different integration (e.g.
[using a JWT link](https://developers.google.com/wallet/tickets/events/web.md.txt)). Note that the user may become eligible to use the API in the future.

## 3. Add the 'Add to Google Wallet' button

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](https://developers.google.com/wallet/tickets/events/resources/brand-guidelines.md.txt).

> [!TIP]
> **Tip:** Make sure to only include graphics for the configurations supported.

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.
- ![Add to Google Wallet button](https://developers.google.com/static/wallet/images/branding/add-to-wallet-button-primary.png)
- ![Add to Google Wallet button condensed](https://developers.google.com/static/wallet/images/branding/add-to-wallet-button-condensed.png)

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

```xml
    <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. Add a pass to a user's Google Wallet

The `EventObject` can be added by passing an unsigned JWT to the `savePasses` method.
You can start the add operation as a result of clicking the Google Wallet
button.

> [!NOTE]
> **Note:** If you are integrating for both Android and web, you can reuse the signed JWT that is required for the web integration, calling `savePassesJwt` instead of ` savePasses`.

### 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
    }

### Result handling

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`.