Apple Pay and Google Pay Top-Up
Embed the Wirex-hosted Apple Pay and Google Pay checkout for a top-up your backend priced, in token or legacy mode.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| React Native SDK | Surfaces, isolation model and flows |
| SDK Setup | environment, clientId, Google Pay intents |
| Top-Up Transfer | Estimate with apple_pay / 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 wallet button; the Pay sheet is native. Your app needs no Apple Pay
entitlement and no merchant identifier.
A wallet 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 | { estimationId, sourceAmount: { amount, currency } } | estimation_id and source_amount / source_currency from POST /api/v1/cards/topup/estimate |
providerConfig | The wallet provider block, passed verbatim as an object | The topup.providers.<name> block of your app configuration |
provider | The provider name the block belongs to | Your app configuration |
auth | { checkoutToken } — token mode only | access_token from POST /api/v1/cards/topup/token |
Trap: pass
providerConfigexactly as received. Do not re-case keys or add defaults — an absent
apple_payorgoogle_paykey means that wallet is not offered.resolveWalletProviderBlock(topup, provider)
picks the block out of your app configuration.
Choose a Mode
| Token mode | Legacy mode | |
|---|---|---|
auth | { checkoutToken } | Omitted |
| 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 JavaScript | The top-up id and status | walletPaymentToken for your backend to relay |
| Custom Tab launch | Supported | Not supported |
Token mode keeps the wallet payment token out of JavaScript. A failed token mint is not fatal in-app: arm
the checkout in legacy mode instead.
Step 1: Price the Top-Up
Your backend calls POST /api/v1/cards/topup/estimate with payment_method set to apple_pay (iOS) or
google_pay (Android). Request and response: Estimate Top-Up.
The estimation has a short TTL. When it expires the checkout emits estimationExpired; re-price and pass a
new quote.
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
import * as React from 'react';
import {
WirexWalletAvailabilityProbe, useWalletAvailability,
type WirexWalletConfig, type WirexWalletProviderConfig,
} from '@wirexapp/mobile-sdk';
const config: WirexWalletConfig = { environment: 'test' };
export function PayButtonGate({ providerConfig }: { providerConfig: WirexWalletProviderConfig }) {
const availability = useWalletAvailability(config, { providerConfig });
return (
<>
<WirexWalletAvailabilityProbe config={config} providerConfig={providerConfig} />
{availability?.applePay || availability?.googlePay ? <PayEntryPoint /> : null}
</>
);
}useWalletAvailability returns { applePay, googlePay } from the native canMakePayments check, or null
until the probe resolves. Mount the probe with the real provider block before showing a Pay button.
Note: the iOS Simulator reports Apple Pay unavailable and cannot present the Pay sheet. Test on a
device with a provisioned card.
Step 4: Render the Checkout
import * as React from 'react';
import {
WirexWalletCheckoutView,
type WirexWalletCheckoutEvent, type WirexWalletProviderConfig, type WirexWalletQuote,
} from '@wirexapp/mobile-sdk';
export function Checkout({ quote, providerConfig, provider, checkoutToken }: {
quote: WirexWalletQuote; providerConfig: WirexWalletProviderConfig; provider: string; checkoutToken?: string;
}) {
const [height, setHeight] = React.useState<number | undefined>();
const onEvent = (event: WirexWalletCheckoutEvent) => {
switch (event.type) {
case 'naturalHeight': return setHeight(event.height);
case 'estimationExpired': return repriceAndRearm();
case 'token': return sendToBackend(event.token.walletPaymentToken);
case 'executed': return showResult(event.result.id, event.result.status);
case 'executeFailed': return showError(event.error.reason);
case 'failed': return showError(event.error.kind);
default: return;
}
};
return (
<WirexWalletCheckoutView
config={{ environment: 'test' }}
quote={quote}
providerConfig={providerConfig}
provider={provider}
auth={checkoutToken ? { checkoutToken } : undefined}
onEvent={onEvent}
style={{ width: '100%', height }}
/>
);
}There is no imperative API. To re-arm, pass a new quote — and in token mode a fresh auth — and the view
re-arms.
Events
event.type | Mode | Meaning |
|---|---|---|
ready | Both | The checkout is rendered |
naturalHeight | Both | event.height — apply it to the view's style |
sheetOpen | Both | The Pay sheet is open |
cancelled | Both | The user closed the Pay sheet |
attemptFailed | Both | event.reason — the attempt failed; the checkout stays armed |
estimationExpired | Both | Re-price on your backend and pass a new quote |
token | Legacy | event.token.walletPaymentToken — your backend executes the top-up with it |
executing | Token | The checkout is executing the top-up |
executed | Token | event.result.id, event.result.status — the top-up was executed |
executeFailed | Token | event.error.reason — execute was rejected |
failed | Both | event.error.kind — the checkout failed |
diagnostic, debug | Both | Diagnostics; ignore in production |
Legacy Mode: Execute on Your Backend
Pass event.token.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 (Android)
When your own WebView cannot complete Google Pay, open the checkout in a Chrome Custom Tab. Token mode only.
import {
buildWalletLaunchUrl, isWalletReturnLink, parseWalletReturnLink,
type WirexWalletConfig, type WirexWalletQuote,
} from '@wirexapp/mobile-sdk';
export const launchUrl = (config: WirexWalletConfig, checkoutToken: string, quote: WirexWalletQuote, provider: string, returnUrl: string) =>
buildWalletLaunchUrl(config, { checkoutToken, quote, provider, returnUrl });
export function readReturn(url: string | undefined, returnUrl: string, quote: WirexWalletQuote) {
if (!url || !isWalletReturnLink(url, returnUrl)) return 'unknown';
const back = parseWalletReturnLink(url);
if (back.estimationId && back.estimationId !== quote.estimationId) return 'unknown';
switch (back.outcome) {
case 'completed': case 'pending': case 'failed': return back.outcome;
default: return 'unknown';
}
}returnUrlmust be an https App Link verified for your app (assetlinks.json). Any installed app can
claim a custom scheme.- Open the URL with an auth-session API, for example
expo-web-browser'sopenAuthSessionAsync(url, returnUrl). - 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

