# JavaScript Native Payments SDK Reference

The Banxa JavaScript Native Payments SDK is the payment execution layer of Banxa Native for web. It's the web counterpart to the [React Native SDK](/products/native-api/docs/sdk/sdk-reference): used alongside the Native API to present native card and wallet payments in the browser, the payment methods that can't be handled through the API alone due to PCI requirements.

Bank transfers are handled directly by the Native API and don't involve the SDK. For the end-to-end integration walkthrough, see the [Integration Guide](/products/native-api/docs/guides/foundations).

> **Which parts are relevant for Banxa Native?**
In a Banxa Native integration, your backend handles pricing, identity, and eligibility via the Native API. The SDK's role is limited to payment execution: the `<banxa-primer-checkout>` web component renders the native payment sheet in your page, gated on the eligibility `paymentReady` flag.
The full API surface is documented below for completeness.


## Installation

```bash
npm install @banxa-official/javascript-native-payments-sdk
```

For native payment checkout, install the Primer peer dependency:

```bash
npm install @primer-io/primer-js
```

`@primer-io/primer-js` is an optional peer dependency, required only for the native checkout web component.

## Entry points

The SDK ships dual module output (ESM + CJS) with separate server and browser entry points, so backend code never pulls in DOM or web component code.

| Import | Environment | Exports |
|  --- | --- | --- |
| `@banxa-official/javascript-native-payments-sdk` | Node / server | `BanxaApiClient`, `BanxaApiError`, API types |
| `@banxa-official/javascript-native-payments-sdk/api` | Node / server | Same as above |
| `@banxa-official/javascript-native-payments-sdk/web` | Browser | `registerBanxaCheckout`, `registerBanxaPrimerCheckout`, `registerBanxaHostedCheckout`, `runBuyCheckoutFlow`, checkout and Primer types |


The web entry does not auto-register custom elements. Call `registerBanxaCheckout()` (or register components individually) before using them.

## Creating an API client

The `BanxaApiClient` is Node-safe and handles all Banxa API requests from your backend.

```typescript
import { BanxaApiClient } from '@banxa-official/javascript-native-payments-sdk';

const client = new BanxaApiClient({
  apiKey: process.env.BANXA_API_KEY,
  partner: process.env.BANXA_PARTNER,
  environment: 'sandbox', // or 'production'
});
```

### Configuration options

| Option | Type | Required | Description |
|  --- | --- | --- | --- |
| `apiKey` | string | Yes | Your Banxa API key from the Partner Dashboard. |
| `partner` | string | Yes | Your partner identifier. |
| `environment` | `'sandbox'` | `'production'` | Yes | Selects the environment base URL. |


Requests use `x-api-key` authentication. Keep the API key server-side, never in browser or client-side code.

## Backend: create an order

Create a buy order from your backend, then return the payment token to your frontend.

```typescript
const order = await client.createOrder({
  externalCustomerId: 'user-123',
  fiat: 'AUD',
  crypto: 'USDT',
  fiatAmount: '100', // or cryptoAmount, at least one is required
  walletAddress: '0x1234567890abcdef...',
  redirectUrl: 'https://yoursite.com/order-complete',
  paymentMethodId: 'debit-credit-card', // optional
  blockchain: 'TRON', // optional
});

// Native payments: pass this token to the checkout web component
const nativeToken = order.nativeToken;
```

`createOrder` maps to `POST /v2/buy`.

### CreateOrderRequest

| Field | Required | Description |
|  --- | --- | --- |
| `externalCustomerId` | Yes | Your customer reference. Use the same value across identity and eligibility flows. |
| `fiat` | Yes | Fiat currency code (for example `AUD`, `USD`). |
| `crypto` | Yes | Crypto currency code (for example `USDT`, `BTC`). |
| `walletAddress` | Yes | Customer wallet address. |
| `redirectUrl` | Yes | URL the customer returns to after checkout completes, fails, or is cancelled. |
| `fiatAmount` | One of | Fiat amount to buy. |
| `cryptoAmount` | One of | Crypto amount to buy. |
| `paymentMethodId` | No | Pre-select a payment method (for example `debit-credit-card`). |
| `blockchain` | No | Blockchain network for the crypto token. |
| `walletAddressTag` | No | Memo or tag for wallets that require it. |
| `externalOrderId` | No | Your order reference. |
| `discountCode` | No | Promotion or discount code. |
| `email` | No | Pre-fill the customer email. |
| `metadata` | No | Opaque string returned on order lookup. |
| `subPartnerId` | No | Channel identifier (exchange, wallet, web, and so on). |


Provide either `fiatAmount` or `cryptoAmount`. If both are set, Banxa uses `cryptoAmount`.

Pricing is indicative. The SDK does not accept a locked `quoteId`, so re-quote close to order creation to minimise rate drift.

### Order response

| Field | Description |
|  --- | --- |
| `id` | Banxa order ID. |
| `nativeToken` | Primer client token for `<banxa-primer-checkout>` (native payments). |
| `checkoutUrl` | Hosted checkout URL, used for the fallback flow when native payments are not available. |
| `fiat`, `fiatAmount`, `crypto`, `cryptoAmount`, `blockchain` | Order amounts and assets. |
| `externalCustomerId`, `externalId` | Customer and external references. |


## Backend: check eligibility

Check whether an order can proceed before presenting the native payment sheet. The response's `paymentReady` field determines whether to render native Primer checkout or fall back to hosted checkout.

```typescript
const eligibility = await client.checkOrderEligibility({
  externalCustomerId: 'user-123',
  fiat: 'AUD',
  crypto: 'USDT',
  fiatAmount: '100',
  walletAddress: '0x1234567890abcdef...',
  redirectUrl: 'https://yoursite.com/order-complete',
}, 'buy');

if (eligibility.paymentReady) {
  // safe to render native Primer checkout
}
```

`checkOrderEligibility` maps to `POST /eligibility` and takes the same body as `createOrder` plus the order type.

| Field | Description |
|  --- | --- |
| `eligible` | Whether the order context is eligible. |
| `paymentReady` | When `true`, the customer can use native Primer checkout. |
| `requirements` | Outstanding KYC or compliance requirements when not payment-ready. |
| `message` | Optional human-readable detail. |


> Use `paymentReady` to choose between native Primer checkout and the hosted fallback. Do not rely on other fields for gating.


## Frontend: native payment checkout

The `<banxa-primer-checkout>` web component renders the native payment sheet in your page. No redirect, no Banxa-hosted screen.

```html
<script type="module">
  import { registerBanxaPrimerCheckout } from '@banxa-official/javascript-native-payments-sdk/web';

  registerBanxaPrimerCheckout();
</script>

<banxa-primer-checkout id="checkout" locale="en"></banxa-primer-checkout>

<script type="module">
  const response = await fetch('/api/create-order', { method: 'POST' });
  const { nativeToken } = await response.json();

  const checkout = document.getElementById('checkout');
  checkout.clientToken = nativeToken;

  checkout.addEventListener('banxa:payment-success', (event) => {
    console.log('Payment successful', event.detail);
  });

  checkout.addEventListener('banxa:payment-failure', (event) => {
    console.error('Payment failed', event.detail);
  });
</script>
```

Set the token via the `clientToken` property (or `client-token` attribute) after your backend returns `nativeToken` from `createOrder`.

### Attributes

| Attribute | Description |
|  --- | --- |
| `client-token` | Required. Primer client token (`order.nativeToken` from `createOrder`). |
| `locale` | Locale string (for example `en`). |
| `layout-mode` | `preset` (default), `auto`, or `custom`. |
| `payment-methods` | Comma-separated Primer types for preset mode (for example `PAYMENT_CARD,APPLE_PAY`). |
| `loader-disabled` | Disable the loading placeholder. |
| `custom-styles` | CSS string injected into the component shadow root. |


### Properties

| Property | Description |
|  --- | --- |
| `clientToken` | Get or set the `client-token` attribute. |
| `locale` | Get or set the locale. |
| `layoutMode` | Get or set the layout mode. |
| `paymentMethods` | Get or set the preset payment methods. |
| `checkoutLayout` | Programmatic `CheckoutLayoutConfig`, merged with attributes. |
| `checkoutTemplate` | Raw HTML for custom layout mode. |
| `loaderDisabled` | Get or set the loading placeholder. |
| `customStyles` | Get or set custom CSS. |


### Methods

| Method | Description |
|  --- | --- |
| `refreshSession()` | Delegates to the Primer `refreshSession()` when available. |


### Events

Primer events are re-emitted on the host element with a `banxa:` prefix. Use native `addEventListener`.

| Event | Description |
|  --- | --- |
| `banxa:ready` | Checkout ready. |
| `banxa:payment-start` | Payment started. |
| `banxa:payment-success` | Payment succeeded. |
| `banxa:payment-failure` | Payment failed. |
| `banxa:payment-cancel` | Payment cancelled. |
| `banxa:state-change` | Primer SDK state changed. |
| `banxa:methods-update` | Available payment methods updated. |
| `banxa:card-success`, `banxa:card-error` | Card validation events. |
| `banxa:bin-data-available`, `banxa:bin-data-loading-change` | Card BIN lookup events. |
| `checkout-error` | The SDK failed to load or initialise (not prefixed). |


Event `detail` objects are sanitised for JSON serialisation: DOM nodes and circular references are replaced with string placeholders.

### Layout modes

| Mode | Behaviour |
|  --- | --- |
| `preset` (default) | Builds markup from `paymentMethods` (default: card plus Apple Pay). Card payments use a standalone Primer card form with slotted fields. |
| `auto` | Omits the payments slot so Primer renders all session payment methods. |
| `custom` | Uses `checkoutTemplate`, `checkoutLayout.mainHtml`, or a `<template slot="checkout-layout">` child for full control. |


Custom card forms must use Primer hosted input components only. Raw HTML inputs cannot host PCI card data. In preset mode a custom `cardFormHtml` is ignored with a console warning; use `custom` layout mode or the checkout-layout slot instead.

```typescript
import { registerBanxaPrimerCheckout, PrimerPaymentMethodTypes } from '@banxa-official/javascript-native-payments-sdk/web';

registerBanxaPrimerCheckout();

const checkout = document.querySelector('banxa-primer-checkout');
checkout.checkoutLayout = {
  mode: 'preset',
  paymentMethods: [
    { type: PrimerPaymentMethodTypes.PAYMENT_CARD },
    { type: PrimerPaymentMethodTypes.APPLE_PAY },
  ],
};
```

## Eligibility-aware buy flow

`runBuyCheckoutFlow` checks eligibility, creates the order, and selects the right UI automatically: native Primer checkout when `paymentReady` is `true`, or the hosted fallback when it isn't.

```typescript
import { BanxaApiClient } from '@banxa-official/javascript-native-payments-sdk';
import { runBuyCheckoutFlow } from '@banxa-official/javascript-native-payments-sdk/web';

const client = new BanxaApiClient({ apiKey, partner, environment: 'sandbox' });
const container = document.getElementById('checkout');

const { mode, order, element } = await runBuyCheckoutFlow({
  client,
  request: {
    externalCustomerId: 'user-123',
    fiat: 'AUD',
    crypto: 'USDT',
    fiatAmount: '100',
    walletAddress: '0xabc',
    redirectUrl: `${window.location.origin}/checkout/return`,
    paymentMethodId: 'debit-credit-card',
  },
  container,
});

if (mode === 'primer') {
  element.addEventListener('banxa:payment-success', () => { /* ... */ });
} else {
  element.addEventListener('banxa:checkout-success', () => { /* hosted fallback completed */ });
}
```

| `paymentReady` | `nativeToken` | Result |
|  --- | --- | --- |
| `true` | present | `<banxa-primer-checkout>` (native payment sheet) |
| `false` or no token | `checkoutUrl` present | `<banxa-hosted-checkout>` (hosted fallback iframe) |


| Option | Description |
|  --- | --- |
| `skipEligibilityCheck` | Skip the eligibility call and assume payment-ready. |
| `primerCheckoutOptions` | Pass-through for `locale`, `layoutMode`, `checkoutLayout`, `checkoutTemplate`, `loaderDisabled`, `customStyles`. |


Use the same `redirectUrl` on your checkout page origin so the fallback iframe can detect return navigation after payment.

## Quotes

Retrieve indicative pricing.

```typescript
const quote = await client.getQuote({
  fiat: 'AUD',
  crypto: 'USDT',
  blockchain: 'TRON',
  paymentMethodId: 'debit-credit-card',
  fiatAmount: '100',
}, 'buy');
```

`getQuote` maps to `GET /quotes/{orderType}`. Either `fiatAmount` or `cryptoAmount` is required, along with `fiat`, `crypto`, `blockchain`, and `paymentMethodId`.

## Payment methods, currencies, and countries

```typescript
// Payment methods, optionally filtered by source, target, and country
const paymentMethods = await client.getPaymentMethods('AUD', 'USDT', 'AU');

// Currencies (default order type is 'buy')
const currencies = await client.getCurrencies('buy');
const fiats = await client.getFiatCurrencies('buy');
const crypto = await client.getCryptoCurrencies('buy');

// Countries
const countries = await client.getCountries();
```

## Retrieve an order

```typescript
const orderDetails = await client.getOrder(order.id);
```

`getOrder` maps to `GET /orders/{orderId}`.

## Error handling

API failures throw `BanxaApiError`.

```typescript
import { BanxaApiClient, BanxaApiError } from '@banxa-official/javascript-native-payments-sdk';

try {
  const order = await client.createOrder({ /* ... */ });
} catch (error) {
  if (error instanceof BanxaApiError) {
    console.error('Status code:', error.statusCode);
    console.error('Response body:', error.responseBody);
    console.error('Errors:', error.errors);
  } else {
    console.error('Unknown error:', error);
  }
}
```

### BanxaApiError properties

| Property | Description |
|  --- | --- |
| `statusCode` | HTTP status code from the API response. |
| `responseBody` | Optional raw response body. |
| `errors` | Optional array of `BanxaApiErrorItem` with per-field detail. |


Show user-facing messages only from validated error fields. Do not expose raw error strings that may include internal detail.

## TypeScript support

The SDK is written in TypeScript and ships with type definitions for both entry points.

```typescript
import { BanxaApiClient } from '@banxa-official/javascript-native-payments-sdk';
import type { CreateOrderRequest, Order, Quote } from '@banxa-official/javascript-native-payments-sdk';
```

## Environment URLs

| Environment | Base URL |
|  --- | --- |
| Sandbox | `https://api.banxa-sandbox.com/{partner}/v2` |
| Production | `https://api.banxa.com/{partner}/v2` |