Google Pay Top-Up
Embed the Wirex-hosted Google Pay checkout in an Android app for a top-up your backend priced, in token or legacy mode.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| Android SDK | Surfaces, isolation model and flows |
| SDK Setup | Environment name and clientId |
| Top-Up Transfer | Estimate with google_pay and the wallet-method execute body |
Overview
WirexWalletCheckoutView embeds the Wirex-hosted checkout for a top-up your backend has already priced. The
checkout renders the quote and the Google Pay button; the Pay sheet is native.
A Google Pay top-up requires the WalletTopup capability — separate from CardTopup. Check it via
GET /api/v2/user before offering the Pay button.
| Input | What it is | Source |
|---|---|---|
quote | WirexWalletQuote(estimationId, sourceAmount = WirexWalletMoney(amount, currency)). Optional: destinationAmount, fee, rate, expiresAtMs | estimation_id and source_amount / source_currency from POST /api/v1/cards/topup/estimate |
providerConfig | WirexWalletProviderConfig.fromJson(text) — the wallet provider block, passed verbatim | The topup.providers.<name> block of your app configuration |
auth | WirexWalletCheckoutAuth(checkoutToken) — token mode only | access_token from POST /api/v1/cards/topup/token |
WirexWalletMoney takes the amount as a decimal string or a number.
Trap: pass the provider block exactly as received. Do not re-case keys or add defaults — an absent
google_paykey means Google Pay is not offered.WirexWalletProviderConfigResolver.resolve(topup, provider)
picks the block out of your app configuration.
Choose a Mode
| Token mode | Legacy mode | |
|---|---|---|
auth | WirexWalletCheckoutAuth(checkoutToken) | null |
| Who executes the top-up | The checkout, with the restricted token | Your backend |
| Events after the Pay sheet | Executing → Executed or ExecuteFailed | Token |
| What reaches app code | The top-up id and status | walletPaymentToken for your backend to relay |
| Custom Tab launch | Supported | Not supported |
The mode is fixed when the view is created. A failed token mint is not fatal in-app: create the view in
legacy mode instead.
Step 1: Price the Top-Up
Your backend calls POST /api/v1/cards/topup/estimate with payment_method set to google_pay. Request and
response: Estimate Top-Up.
Step 2: Mint a Checkout Token (Token Mode)
Your backend calls POST /api/v1/cards/topup/token with the user token and passes access_token from the
response to the app as checkoutToken. The token is accepted only by the top-up estimate and execute
endpoints. Request, response and code: Restricted Top-Up Token.
- One
checkoutTokenper armed quote. Mint a new one on every re-arm. estimationIdandcheckoutTokencome from two independent calls; neither is derived from the other.- Never hand the user's session bearer to the checkout — pass only the restricted token.
Step 3: Check Availability
private val config = WirexWalletConfig(environment = WirexWalletEnvironment.resolve("test"))
fun checkAvailability(providerConfig: WirexWalletProviderConfig) {
val checker = WirexWalletAvailabilityChecker(activity, config, providerConfig)
checker.onChange = { availability -> payButton.isVisible = availability.googlePay }
checker.start()
this.checker = checker
}onChange reports the native Google Pay answer. Start the checker with the real provider block before showing
a Pay button, and call checker.stop() when done.
- Below the provider block's
min_webview_version/min_play_services_versionfloors the checker answers
unavailable without loading anything. WirexWalletConfig(cache = …)takes an optionalWirexWalletAvailabilityCachekey-value store so the answer
survives relaunch. The SDK writes one key with two booleans.
Note: most emulators have no Google Pay and answer unavailable. Test on a device with Google Pay set up.
Step 4: Add the Checkout
fun mount(container: FrameLayout, quote: WirexWalletQuote, providerConfigJson: String, checkoutToken: String?) {
val view = WirexWalletCheckoutView(
context = activity,
config = config,
quote = quote,
providerConfig = WirexWalletProviderConfig.fromJson(providerConfigJson),
auth = checkoutToken?.let { WirexWalletCheckoutAuth(checkoutToken = it) },
)
view.onEvent = ::onEvent
container.addView(view, MATCH_PARENT, MATCH_PARENT)
checkout = view
}
private fun onEvent(event: WirexWalletCheckoutEvent) {
when (event) {
is WirexWalletCheckoutEvent.NaturalHeight -> resizeContainer(event.height)
WirexWalletCheckoutEvent.EstimationExpired -> repriceAndRearm()
is WirexWalletCheckoutEvent.Token -> sendToBackend(event.token.walletPaymentToken)
is WirexWalletCheckoutEvent.Executed -> showResult(event.result.status)
is WirexWalletCheckoutEvent.ExecuteFailed -> showError(event.error.reason)
is WirexWalletCheckoutEvent.Failed -> showError(event.error.kind)
else -> Unit
}
}The estimation has a short TTL. On EstimationExpired, re-price on your backend and call
checkout.rearm(quote, providerConfig, auth = …) — in token mode with a freshly minted token. Call
checkout.destroy() when the screen goes away.
Events
| Event | Mode | Meaning |
|---|---|---|
Ready | Both | The checkout is rendered |
NaturalHeight | Both | height in CSS px — size the container |
SheetOpen | Both | The Google Pay sheet is open |
Cancelled | Both | The user closed the sheet |
AttemptFailed | Both | reason — the attempt failed; the checkout stays armed |
EstimationExpired | Both | Re-price and rearm |
Token | Legacy | token.walletPaymentToken — your backend executes the top-up with it |
Executing | Token | The checkout is executing the top-up |
Executed | Token | result.status — the top-up was executed |
ExecuteFailed | Token | error.reason — execute was rejected |
Failed | Both | Terminal. error.kind — the checkout failed |
Diagnostic, Debug | Both | Diagnostics. Debug fires only with debug = true |
Legacy Mode: Execute on Your Backend
Pass walletPaymentToken as wallet_payment_token to POST /api/v1/cards/topup/execute, together with the
other wallet-method fields — see Execute Top-Up.
Custom Tab Launch
When your own WebView cannot complete Google Pay, open the checkout in a Chrome Custom Tab. Token mode only.
The SDK builds the URL and parses the return link; it opens no browser itself.
fun launchUrl(config: WirexWalletConfig, checkoutToken: String, quote: WirexWalletQuote, provider: String, returnUrl: String): String =
WirexWalletLaunch.buildUrl(config, WirexWalletLaunchParams(
checkoutToken = checkoutToken, quote = quote, provider = provider, returnUrl = returnUrl,
))
fun readReturn(url: String?, returnUrl: String, quote: WirexWalletQuote): SpecWalletLaunchOutcome? {
if (url == null || !WirexWalletReturnLink.matches(url, returnUrl)) return null
val back = WirexWalletReturnLink.parse(url)
if (back.estimationId != null && back.estimationId != quote.estimationId) return null
return when (back.outcome) {
SpecWalletLaunchOutcome.COMPLETED, SpecWalletLaunchOutcome.PENDING, SpecWalletLaunchOutcome.FAILED -> back.outcome
else -> null
}
}returnUrlmust be an https App Link verified for your app (assetlinks.json). Any installed app can
claim a custom scheme.- Android cannot close the tab for you, and a closed tab says nothing about the payment. Treat every ending
other thanCOMPLETED,PENDINGorFAILED— includingALREADY_EXECUTEDand no outcome — as unknown and
reconcile from the top-up status.
Updated about 3 hours ago

