Google Pay - React Native SDK
@paysafe/paysafe-google-pay is part of the Paysafe PH Mobile React Native SDK — a suite of packages designed to provide seamless payment integrations for React Native applications.
This package enables Google Pay payments by bridging the existing Paysafe native SDK into the React Native environment. It allows developers to integrate Google Pay checkout flows, handle payment results, and interact with Google Pay from TypeScript, while leveraging the reliability and performance of Paysafe’s native SDK.
It resides within a monorepo alongside other React Native payment modules and uses React Native bridges to communicate with the underlying native Android SDK.
Prerequisites
API key
For information on obtaining your API key, refer to the React Native SDK Overview.
Using the Demo app
To test the Google Pay integration using the Demo app, you need to modify some arguments representing the API key and the account id associated with the Google Pay method.
- open DemoAppExpo/android/app/src/main/java/com/DemoAppExpo/MainApplication.kt file
- pass a valid API key to setupPaysafeSdk method
- pass a valid accountId to initialize method
- start DemoAppExpo with npx expo run:android runner from DemoAppExpo directory
Integrating the Google Pay package
To integrate the Google Pay package into your React Native application, install it via npm:
npm install @paysafe/paysafe-google-pay@current-version
NOTE: Replace current-version with the correct version number from the GitHub version catalog.
Enable the Google Pay API in your manifest file
To use Google Pay on Android, first enable the Google Pay API by adding the following metadata tag to your app's AndroidManifest.xml file:
<meta-data
android:name="com.google.android.gms.wallet.api.enabled"
android:value="true" />
Initialization
NOTE: You must call setupPaysafeSdk before initializing any components.
To use the Google Pay module, PSGooglePayContext must be initialized.
Initialize method signature
/**
Initializes the Google Pay integration from native android code.
@param activity The component activity instance from which the Google Pay integration is initialized.
@param countryCode The country code that customer use for Google Pay integration.
@param currencyCode The currency that customer use for Google Pay integration.
@param accountId The Google Pay account id that customer use for Google Pay integration.
@param requestBillingAddress boolean value that represent if billing address is required for GooglePay payment.
@param onInitSuccess an optional callback invoked when the Google Pay context is successfully initialized.
@param onInitFailure an optional callback invoked when initialization fails, providing the [Exception] that caused the failure.
*/
fun initialize(
activity: Activity? = null,
countryCode: String,
currencyCode: String,
accountId: String,
requestBillingAddress: Boolean,
onInitSuccess: (() -> Unit)? = null,
onInitFailure: ((Exception) -> Unit)? = null
)
/**
Initializes the Google Pay integration from native android code.
@param fragment The fragment instance from which the Google Pay integration is initialized.
@param countryCode The country code that customer use for Google Pay integration.
@param currencyCode The currency that customer use for Google Pay integration.
@param accountId The Google Pay account id that customer use for Google Pay integration.
@param requestBillingAddress boolean value that represent if billing address is required for GooglePay payment.
@param onInitSuccess an optional callback invoked when the Google Pay context is successfully initialized.
@param onInitFailure an optional callback invoked when initialization fails, providing the [Exception] that caused the failure.
*/
fun initialize(
fragment: Fragment? = null,
countryCode: String,
currencyCode: String,
accountId: String,
requestBillingAddress: Boolean,
onInitSuccess: (() -> Unit)? = null,
onInitFailure: ((Exception) -> Unit)? = null
)
/**
Initializes the Google Pay integration from native android code.
@param countryCode The country code that customer use for Google Pay integration.
@param currencyCode The currency that customer use for Google Pay integration.
@param accountId The Google Pay account id that customer use for Google Pay integration.
@param requestBillingAddress boolean value that represent if billing address is required for GooglePay payment.
function initializeGooglePay(
countryCode: string,
currencyCode: string,
accountId: string,
requestBillingAddress: boolean
)
The initializeGooglePay method uses the currentActivity from reactApplicationContext.
| Parameter | Required | Type | Description |
|---|---|---|---|
| activity | false | ComponentActivity | Optional Activity instance to use for initialization. If null, the current React Native activity is retrieved from SingletonGooglePayContext. |
| currencyCode | true | string | Three-letter ISO currency code (e.g. USD, EUR). |
| accountId | true | string | ID of the selected merchant account used to process the payment. Required only if multiple accounts are configured for the same payment method and currency. Mandatory for partners using a shared API key. |
| countryCode | true | string | Two-letter ISO 3166 code for the merchant’s principal place of business, typically where the payment is settled. |
| requestBillingAddress | true | boolean | Set to true if the billing address is required for a Google Pay payment. |
This method bridges Google Pay initialization between React Native and the Android SDK, ensuring the proper lifecycle context is used and the status is reported back to JavaScript.
After the initialization process, an event will be sent for communication with JavaScript.
| case | event |
|---|---|
| onSuccess | GooglePayInitializedSuccessful |
| onFailure | GooglePayInitializationFailed |
Initialize exceptions
| Error code | Display message | Detailed message | Comments |
|---|---|---|---|
9061 | There was an error (9061), please contact our support. | Invalid account id for ${paymentMethod} | AccountId is present, but not configured for the intended payment context. |
9101 | There was an error (9101), please contact our support. | Invalid accountId parameter | The SDK checks if the accountId string contains only numbers. |
9055 | There was an error (9055), please contact our support. | Invalid currency parameter | The SDK checks if the currency parameter has only 3 letters. This exception is thrown when the server responds with code 5001. |
9085 | There was an error (9085), please contact our support. | There are no available payment methods for this API key. |
|
9084 | There was an error (9084), please contact our support. | Failed to load available payment methods | Triggered by an internal server error during the payment methods API call. |
9073 | There was an error (9073), please contact our support. | Account not configured correctly. | Caused by an improperly created merchant account configuration:
|
Usage examples
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.paysafegooglepay.PaysafeGooglePayModule
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import android.content.Context
class GooglePayFragment(
private val countryCode: String,
private val currencyCode: String,
private val accountId: String,
private val requestBillingAddress: Boolean
) : Fragment() {
private var listener: GooglePayInitListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is GooglePayInitListener) {
listener = context
} else {
error ("Host must implement GooglePayInitListener")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
PaysafeGooglePayModule.initialize(
fragment = this,
countryCode = countryCode,
currencyCode = currencyCode,
accountId = accountId,
requestBillingAddress = requestBillingAddress,
onInitSuccess = {
listener?.onGooglePayInitSuccess()
},
onInitFailure = { exception ->
listener?.onGooglePayInitFailure(exception)
}
)
}
override fun onDetach() {
super.onDetach()
listener = null
}
}
import android.app.AlertDialog
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Lifecycle
import com.DemoAppExpo.databinding.ActivityGooglePayBinding
import com.paysafegooglepay.PaysafeGooglePayModule
import com.DemoAppExpo.R
class GooglePayActivity : AppCompatActivity(), GooglePayInitListener {
private lateinit var binding: ActivityGooglePayBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityGooglePayBinding.inflate(layoutInflater)
setContentView(binding.root)
val fragment = GooglePayFragment(
countryCode = "US",
currencyCode = "USD",
accountId = "1001234110",
requestBillingAddress = true
)
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.setMaxLifecycle(fragment, Lifecycle.State.CREATED)
.commitNow()
}
override fun onGooglePayInitSuccess() {
binding.googlePayProgressBar.visibility = View.GONE
binding.googlePayButton.visibility = View.VISIBLE
}
override fun onGooglePayInitFailure(exception: Exception) {
binding.googlePayProgressBar.visibility = View.GONE
binding.googlePayButton.visibility = View.GONE
AlertDialog.Builder(this)
.setTitle("Initialization Failed")
.setMessage(exception.message ?: "Unknown error")
.setPositiveButton("OK", null)
.show()
}
}
Google Pay button
The Paysafe Android SDK provides a convenient method for creating a Google Pay button to add to your checkout screens.
The Google Pay button can be added in your XML layout:
<com.paysafe.android.google_pay.button.PSGooglePayButton
android:id="@+id/selectPayMethodGooglePay"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
In your Activity or Fragment, initialize your PSGooglePayButton:
if (googlePayContext != null) {
binding.selectPayMethodGooglePay.initialize(
PSGooglePayButtonOptions.Builder(googlePayContext.providePaymentMethodConfig()) // Pass payment method config from google pay context
.buttonTheme(PSGooglePayButtonTheme.DARK) // or .LIGHT
.buttonType(PSGooglePayButtonType.BUY) // or .DONATE
.cornerRadius(3)
.build()
)
}
PSGooglePayButtonOptions.Builder parameter description:
| Parameter | Required | Type | Description |
|---|---|---|---|
| paymentMethodConfig | true | PSGooglePayPaymentMethodConfig | Automatically available after the context initialization; contains the configuration details provided in the Paysafe Business Portal. |
The modifiers for PSGooglePayButtonOptions are described below:
| Parameter | Required | Type | Description |
|---|---|---|---|
| type | false | PSGooglePayButtonType | PSGooglePayButtonType is an enum type with the following cases:
|
| theme | false | PSGooglePayButtonTheme | PSGooglePayButtonTheme is an enum type with the following cases:
|
| cornerRadius | false | Int | Corner radius of the GPay button. |
This tokenize method returns a PaymentHandle result, which resolves to a single-use payment handle representing the customer's sensitive card data. This handle can be used with the Payments API to process a payment. Single-use handles are valid for only 15 minutes and are not consumed by verification.
For more details, see Payments with a Handle.
/**
Tokenization using Google Pay from native android code.
@param readableGooglePayTokenizeOptions containing various parameters for tokenization of type ReadableMap
@param coroutineScope The [CoroutineScope] used to launch the tokenization request (defaults to [Dispatchers.IO]).
@param onTokenizeSuccess An optional callback invoked when tokenization is successful. Provides the resulting payment handle token as a [String].
@param onTokenizeFailure An optional callback invoked when tokenization fails. Provides the [Exception] that caused the failure.
@param onTokenizeCancelled An optional callback invoked when tokenization is cancelled by the user. Provides the [Exception] representing the cancellation reason.
*/
fun tokenize(
readableGooglePayTokenizeOptions: ReadableMap,
coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO),
onTokenizeSuccess: ((String) -> Unit)? = null,
onTokenizeFailure: ((Exception) -> Unit)? = null,
onTokenizeCancelled: ((Exception) -> Unit)? = null
)
/**
Tokenization using Google Pay from typescript.
@param readableGooglePayTokenizeOptions containing various parameters for tokenization of type ReadableMap
function tokenizeGooglePay(readableGooglePayTokenizeOptions: unknown)
The tokenize() function provides a convenient method for tokenizing, allowing secure payment processing. Upon invocation, this function returns a callback that resolves to a single-use payment handle. This handle can be utilized by the Payments API for payment transactions. It's important to note that single-use handles are valid for only 15 minutes and are not consumed during verification.
Parameters:
- options: An instance of ReadableMap containing various parameters for tokenization, such as the payment amount, transaction type, account ID, and Google Pay configuration.
PSGooglePayTokenizeCallback is executed when the tokenise method is called (which invokes the native SDK's tokenize method). Its interface is outlined below:
/**
Callback interface for receiving the result of Google Pay tokenization.
*/
interface PSGooglePayTokenizeCallback {
/**
Called when Google Pay tokenization is successful.
@param paymentHandle The payment handle representing the tokenized Google Pay data.
*/
fun onSuccess(paymentHandleToken: String)
/**
Called when an error occurs during Google Pay tokenization.
@param paysafeException The Paysafe exception indicating the error encountered during tokenization.
*/
fun onFailure(paysafeException: PaysafeException)
/**
Called when Google Pay tokenization is cancelled.
@param paysafeException The Paysafe exception indicating the cancellation reason.
*/
fun onCancelled(paysafeException: PaysafeException)
}
PSGooglePayTokenizeOptions
The PSGooglePayTokenizeOptions structure provides a comprehensive set of options for configuring the tokenization process. For a detailed explanation of these options, see Android SDK Card Payments Tokenize Options.
After the tokenization process, an event is sent to communicate with JavaScript:
| Case | Event | paymentResult (paymentHandleToken) | Paysafe exception |
|---|---|---|---|
| onSuccess | GooglePayTokenizationSuccessful | yes | no |
| onFailure | GooglePayTokenizationFailed | no | yes |
| onCancelled | GooglePayTokenizationCanceled | no | yes |
object GooglePayUtils {
fun provideGooglePayTokenizeOptions(): ReadableMap =
Arguments.createMap().apply {
putInt("amount", 1000)
putString("currencyCode", "USD")
putString("transactionType", "PAYMENT")
putString("merchantRefNum", "12345678")
putMap("billingDetails", createBillingDetails())
putMap("profile", createProfile())
putString("accountId", "123456789")
putMap("merchantDescriptor", createMerchantDescriptor())
putMap("shippingDetails", createShippingDetails())
putString("simulator", "INTERNAL")
putMap("threeDS", createThreeDS())
}
private fun createBillingDetails(): WritableMap =
Arguments.createMap().apply {
putString("nickName", "nickName")
putString("street", "street")
putString("city", "city")
putString("state", "AL")
putString("country", "US")
putString("zip", "12345")
}
private fun createProfile(): WritableMap =
Arguments.createMap().apply {
putString("firstName", "firstName")
putString("lastName", "lastName")
putString("locale", "EN_GB")
putString("merchantCustomerId", "merchantCustomerId")
putMap("dateOfBirth", createDateOfBirth())
putString("email", "email@mail.com")
putString("phone", "0123456789")
putString("mobile", "0123456789")
putString("gender", "MALE")
putString("nationality", "nationality")
putArray("identityDocuments", createIdentityDocuments())
}
private fun createDateOfBirth(): WritableMap =
Arguments.createMap().apply {
putInt("day", 1)
putInt("month", 1)
putInt("year", 1990)
}
private fun createIdentityDocuments(): WritableArray {
val idDoc = Arguments.createMap().apply {
putString("documentNumber", "SSN123456")
}
return Arguments.createArray().apply {
pushMap(idDoc)
}
}
private fun createMerchantDescriptor(): WritableMap =
Arguments.createMap().apply {
putString("dynamicDescriptor", "dynamicDescriptor")
putString("phone", "0123456789")
}
private fun createShippingDetails(): WritableMap =
Arguments.createMap().apply {
putString("shipMethod", "NEXT_DAY_OR_OVERNIGHT")
putString("street", "street")
putString("street2", "street2")
putString("city", "Marbury")
putString("state", "AL")
putString("countryCode", "US")
putString("zip", "36051")
}
private fun createThreeDS(): WritableMap =
Arguments.createMap().apply {
putString("merchantUrl", "https://api.qa.paysafe.com/checkout/v2/index.html#/desktop")
putBoolean("process", true)
}
}
val googlePayTokenizeOptions = GooglePayUtils.provideGooglePayTokenizeOptions()
binding.googlePayButton.setOnClickListener {
PaysafeGooglePayModule.tokenize(googlePayTokenizeOptions)
}
The paymentHandleToken can be received from the event as:
private fun subscribeToGooglePayEvents() {
val reactContext = PaysafeGooglePayModule.SingletonGooglePayContext.getReactApplicationContext()
reactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.let { emitter ->
emitter.addListener(PaysafeGooglePayModule.GOOGLE_PAY_TOKENIZATION_SUCCESSFUL) { data ->
runOnUiThread {
binding.googlePayProgressBar.visibility = View.GONE
// Extract token from the event payload
val map = data as? Map<*, *>
val token = map?.get("paymentResult") as? String ?: "No token received"
AlertDialog.Builder(this)
.setTitle("Success")
.setMessage("Google Pay tokenization successful!\nToken:\n$token")
.setPositiveButton("OK", null)
.show()
}
}
}
}
const subscriptionTokenizationSuccess = eventEmitter.addListener(
'GooglePayTokenizationSuccessful',
(eventData) => {
const token = eventData?.paymentHandleToken;
router.push({
pathname: '/paymentSuccessScreen',
params: { token },
});
}
);
Google Pay exceptions
| Error code | Display message | Detailed message | Comments |
|---|---|---|---|
| 9061 | There was an error (9061) please contact our support. | Invalid account id for ${paymentMethod} | The provided AccountId exists but is not configured for the selected payment method. |
| 9042 | There was an error (9042) please contact our support. | User aborted authentication | The customer canceled the authentication process with the payment provider (e.g. PayPal or Google Pay). |
| 9054 | There was an error (9054) please contact our support. | Amount should be a number greater than 0 no longer than 11 characters | The amount must be a positive numeric value, no longer than 11 characters. |
| 9136 | There was an error (9136) please contact our support. | Tokenization is already in progress. | A new tokenization request was made before the previous one completed. |
NOTE: You can perform both initialization and tokenization either in TypeScript or in the native part of your code.