Issue passes with the Android SDK

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:

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

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
  • Add to Google Wallet button condensed

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

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 .