# Google Pay

Google Pay lets users pay with a card already saved to their device — no card entry, no redirect. The payment sheet is native and dismisses back into your app once the transaction is confirmed.

Identity creation, KYC, eligibility, and OTP must be completed before reaching the payment step. See the [Integration Guide](/products/native-api/docs/guides/foundations) if you haven't done this yet. This guide picks up from `paymentReady: true`.

## Choose your approach

| Approach | Best for |
|  --- | --- |
| [React Native SDK](#react-native-sdk) | React Native mobile apps (Android only) |
| [Embedded Payment Button](#embedded-payment-button) | Web apps (Vanilla JS, React, Vue) |
| JS SDK | Web apps — coming soon |


## React Native SDK

Google Pay via the React Native SDK is Android only.

### Android app approval

When your Android app presents Google Pay directly, Google requires app-level approval before you can process live payments. This is separate from any approval Banxa holds.

Banxa's website approval does not cover your app
Banxa has Google Pay approval for its own website. That approval applies only when a user is redirected to an external browser or Custom Chrome Tab — not when Google Pay is presented inside your app. If you are integrating via the React Native SDK, your app requires its own approval.

To apply:

1. Integrate Google Pay in your app following [Google's integration requirements](https://developers.google.com/pay/api/android/guides/tutorial)
2. Complete the [Google Pay API production access request](https://developers.google.com/pay/api/android/guides/test-and-deploy/request-prod-access)
3. Google will review your integration and confirm approval


You can develop and test against Google Pay's test environment while your approval is in progress. See [Testing](#testing) below.

### Execute payment

Once your app has Google Pay approval (or is in test mode), trigger the payment sheet after your backend confirms `paymentReady: true`:


```typescript
import {
  isPrimerCheckoutWebViewResult,
} from '@banxa-official/react-native-sdk';

const result = await banxa.buy.createOrderAndShowPrimerCheckout(orderRequest, {
  paymentMethod: 'googlePay',
  callbacks: {
    onCheckoutComplete: () => {
      // Google Pay confirmed — transaction is in progress
    },
    onError: (error) => {
      // Google Pay unavailable or failed — see error handling below
    },
    onDismiss: () => {
      // User dismissed the payment sheet without completing
    },
  },
});

if (isPrimerCheckoutWebViewResult(result)) {
  // Google Pay unavailable for this session — SDK fell back to hosted WebView
  render(<CheckoutWebView {...result.webViewProps} />);
}
```

The `orderRequest` must include `externalCustomerId` — pass the same `identityReference` you used for eligibility.

### Error handling

`onError` is called when Google Pay is unavailable for the current session. Common causes:

- User has not set up Google Pay on their device
- No cards added to Google Pay
- None of the user's cards match supported networks
- Device doesn't support Google Pay


### Testing

Google Pay provides a test environment that returns simulated payment results without processing real transactions. Use this during development before your production approval is confirmed.


```typescript
const banxa = new Banxa({
  environment: 'sandbox',
  // ...
});
```

Google Pay can be tested on a physical Android device or on an Android emulator with Google Play Services installed.

### Troubleshooting

The error codes below are Primer errors surfaced through the SDK.

**Google Pay button does not appear**

Google Pay is not available for the current user or session. Common reasons:

- User has not set up Google Pay on their device
- No cards added to Google Pay
- None of the user's cards match supported networks
- Device does not have Google Play Services


The SDK filters Google Pay from available payment methods in these cases. Only show a Google Pay option when the SDK confirms it is available.

**Google Pay sheet appears in test but fails in production**

Your app has not yet received Google Pay production approval. Ensure your production access request has been approved by Google before releasing to users.

**`[unable-to-present-payment-method]`**

Google Pay is not available for this session. See causes above.

**`[payment-cancelled]`**

The user dismissed the Google Pay sheet without completing payment. No action required — handle via `onDismiss` or `onError` in your callbacks.

### Going live

Before releasing to production:

- Confirm your Google Pay production approval is active
- Switch to production credentials in the SDK
- Confirm with your Banxa integration contact that your account is provisioned for production


## Embedded Payment Button

The Embedded Payment Button renders Google Pay inside a web component — no SDK install required. It is a web integration for Vanilla JS, React, and Vue apps. No additional Google Pay setup is required — Banxa's approval covers the web integration.

**Before you start:** Contact your Banxa integration contact to get provisioned. The button-only setting must be enabled on your environment before this works.

Environment-level setting
The button-only configuration applies to your entire environment. If it is enabled, the standard full checkout widget will no longer render for that environment. If you need both — for example, the button on one surface and full checkout on another — you will need a separate environment provisioned by Banxa.

### Install the web component


```bash
npm install @banxa-official/embedded-checkout-web-component
```

Import the package once to register the custom element:


```js
import '@banxa-official/embedded-checkout-web-component';
```

### Create an order

Once your backend confirms `paymentReady: true` from eligibility, create an order. This returns a `checkoutUrl` which you pass to the web component.


```http
POST /{partner}/v2/buy
Content-Type: application/json
x-api-key: YOUR_API_KEY

{
  "externalCustomerId": "user-123",
  "fiat": "AUD",
  "crypto": "BTC",
  "walletAddress": "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq",
  "fiatAmount": "500.00",
  "redirectUrl": "https://app.yourcompany.com/complete"
}
```

### Embed the component

Pass the `checkoutUrl` as the `source` attribute:


```html
<banxa-checkout source="https://checkout.banxa.com/..."></banxa-checkout>
```

The component stays hidden until the checkout page signals it is ready — at that point it resizes to fit the payment button and becomes visible.

**Lifecycle callbacks**

Assign functions to these properties on the element instance:

| Property | When called |
|  --- | --- |
| `onLoading` | Checkout is loading |
| `onReady` | Component is ready and visible |
| `onPaymentPending` | Payment initiated, awaiting confirmation |
| `onComplete` | Payment completed successfully |
| `onError` | An error occurred |


Each callback receives a `detail` object, not a DOM event.

### Framework examples


```html Vanilla JS
<script type="module">
  import '@banxa-official/embedded-checkout-web-component';
</script>

<banxa-checkout id="checkout" source="https://checkout.banxa.com/..."></banxa-checkout>

<script>
  const el = document.getElementById('checkout');
  el.onLoading = (detail) => console.log('loading', detail);
  el.onReady = (detail) => console.log('ready', detail);
  el.onPaymentPending = (detail) => console.log('payment pending', detail);
  el.onComplete = (detail) => console.log('complete', detail);
  el.onError = (detail) => console.log('error', detail);
</script>
```


```jsx React
import { useEffect, useRef } from 'react';
import '@banxa-official/embedded-checkout-web-component';

export function Checkout() {
  const checkoutRef = useRef(null);

  useEffect(() => {
    const el = checkoutRef.current;
    if (!el) return;

    el.onLoading = (detail) => console.log('loading', detail);
    el.onReady = (detail) => console.log('ready', detail);
    el.onPaymentPending = (detail) => console.log('payment pending', detail);
    el.onComplete = (detail) => console.log('complete', detail);
    el.onError = (detail) => console.log('error', detail);
  }, []);

  return <banxa-checkout ref={checkoutRef} source="https://checkout.banxa.com/..." />;
}
```


```vue Vue
<template>
  <banxa-checkout
    source="https://checkout.banxa.com/..."
    @loading="onLoading"
    @ready="onReady"
    @paymentPending="onPaymentPending"
    @complete="onComplete"
    @error="onError"
  />
</template>

<script setup>
import '@banxa-official/embedded-checkout-web-component';

const onLoading = (event) => console.log('loading', event.detail);
const onReady = (event) => console.log('ready', event.detail);
const onPaymentPending = (event) => console.log('payment pending', event.detail);
const onComplete = (event) => console.log('complete', event.detail);
const onError = (event) => console.log('error', event.detail);
</script>
```

### Going live

Before releasing to production:

- Confirm with your Banxa integration contact that your environment is provisioned for the Embedded Payment Button
- Switch to production credentials for order creation