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:

GuideWhy
Android SDKSurfaces, isolation model and flows
SDK SetupEnvironment name and clientId
Top-Up TransferEstimate 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.

InputWhat it isSource
quoteWirexWalletQuote(estimationId, sourceAmount = WirexWalletMoney(amount, currency)). Optional: destinationAmount, fee, rate, expiresAtMsestimation_id and source_amount / source_currency from POST /api/v1/cards/topup/estimate
providerConfigWirexWalletProviderConfig.fromJson(text) — the wallet provider block, passed verbatimThe topup.providers.<name> block of your app configuration
authWirexWalletCheckoutAuth(checkoutToken) — token mode onlyaccess_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_pay key means Google Pay is not offered. WirexWalletProviderConfigResolver.resolve(topup, provider)
picks the block out of your app configuration.


Choose a Mode

Token modeLegacy mode
authWirexWalletCheckoutAuth(checkoutToken)null
Who executes the top-upThe checkout, with the restricted tokenYour backend
Events after the Pay sheetExecuting → Executed or ExecuteFailedToken
What reaches app codeThe top-up id and statuswalletPaymentToken for your backend to relay
Custom Tab launchSupportedNot 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 checkoutToken per armed quote. Mint a new one on every re-arm.
  • estimationId and checkoutToken come 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_version floors the checker answers
    unavailable without loading anything.
  • WirexWalletConfig(cache = …) takes an optional WirexWalletAvailabilityCache key-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

EventModeMeaning
ReadyBothThe checkout is rendered
NaturalHeightBothheight in CSS px — size the container
SheetOpenBothThe Google Pay sheet is open
CancelledBothThe user closed the sheet
AttemptFailedBothreason — the attempt failed; the checkout stays armed
EstimationExpiredBothRe-price and rearm
TokenLegacytoken.walletPaymentToken — your backend executes the top-up with it
ExecutingTokenThe checkout is executing the top-up
ExecutedTokenresult.status — the top-up was executed
ExecuteFailedTokenerror.reason — execute was rejected
FailedBothTerminal. error.kind — the checkout failed
Diagnostic, DebugBothDiagnostics. 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
    }
}
  • returnUrl must 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 than COMPLETED, PENDING or FAILED — including ALREADY_EXECUTED and no outcome — as unknown and
    reconcile from the top-up status.

Did this page help you?