Skip to content
Last updated

Every Banxa Native integration shares the same core steps: establish an identity, get a price, check eligibility, then execute payment. The payment method guides — bank transfer, Apple Pay, Google Pay, cards — each pick up from where this page leaves off.

Read this page once. Then follow the guide for your payment method.


Prerequisites

Before you begin:

  • HMAC API credentials — API key and secret from the Merchant Dashboard. Required for all server-to-server calls.
  • A backend — all Native API calls use server-to-server HMAC authentication. Client-side API calls are not supported.
  • An identityReference strategy — a stable per-user identifier that you own. See Choosing an identityReference before proceeding.
  • The Banxa React Native SDK — required for card, Apple Pay, and Google Pay payments. Not required for bank transfers.

Install the SDK and its peer dependencies:

npm install @banxa-official/react-native-sdk @primer-io/react-native react-native-webview
cd ios && pod install

Initialise the SDK once at app startup:

import { Banxa } from '@banxa-official/react-native-sdk';

const banxa = new Banxa({
  apiKey: 'YOUR_API_KEY',
  partner: 'your-partner-id',
  environment: 'sandbox', // or 'production'
});

For full SDK configuration options, see the React Native SDK Reference.


Two integration approaches

Before diving into the steps below, decide how much of the flow you want to own. This guide documents the full control approach — steps 1–4 below apply to that path.

Full control

Your backend handles identity, pricing, and eligibility before payment is invoked. You call the SDK only at the payment step. Any screens for collecting KYC requirements are yours — your design, your brand, your UX.

This is the recommended approach for most Banxa Native integrations.

Quick start

Call createOrderAndShowPrimerCheckout without running eligibility first. The SDK creates the order and Banxa routes the user automatically: verified users go directly to the native payment sheet; users who need KYC are routed to a Banxa-hosted WebView where Banxa handles verification.

const result = await banxa.buy.createOrderAndShowPrimerCheckout(orderRequest, {
  paymentMethod: 'applePay', // 'card' | 'applePay' | 'googlePay'
  callbacks: {
    onCheckoutComplete: () => { /* payment succeeded */ },
    onError: (error) => { /* payment failed or unavailable */ },
    onDismiss: () => { /* user dismissed */ },
  },
});

if (isPrimerCheckoutWebViewResult(result)) {
  // Banxa routed to hosted WebView (KYC required or payment method unavailable natively)
  // render <CheckoutWebView {...result.webViewProps} />
}

The trade-off: Banxa runs eligibility internally and you don't see the result. If the user needs KYC, it happens in Banxa's WebView — outside your product.

Use quick start when you want the simplest possible path to a working integration — for example, during early development — or when you're explicitly comfortable with Banxa handling KYC for users who need it. It is available for the React Native SDK only. If you choose this path, skip steps 3 and 4 below.


Step 1 — Establish an identityReference

Create an identity record for the user before checking eligibility. The minimum required field is identityReference — include any personal details you already have, as they reduce the likelihood of requirements being returned at eligibility.

POST /eapi/v0/identities/basic
Content-Type: application/json

{
  "identityReference": "partner-abc123",
  "email": "[email protected]",
  "dateOfBirth": "1990-01-15"
}

For returning users, reuse the existing identityReference. If you don't have it stored, see Choosing an identityReference for retrieval options and all new-user flow variants.

Never recreate an identity

If you attempt to create an identity for an email that already exists in Banxa, you will receive a 422 with code 81: "This identity exists for this reference or email address." Do not retry the creation. Instead, retrieve the existing identity using GET /eapi/v0/identities/{identityReference}[email protected] — Banxa returns the actual identityReference linked to that email, regardless of what value you supply in the path parameter.

If you verify users with Sumsub, share the verification token at this point. Token sharing satisfies NAME, DOB, SELFIE, and DOCUMENT requirements upfront so eligibility is more likely to return paymentReady: true on the first check. See KYC Token Sharing.


Step 2 — Get pricing

There are two pricing endpoints:

EndpointRate locked?Expiry to manage?
GET /eapi/v0/priceNoNo
GET /eapi/v0/quoteYes — quoteId with 3-minute TTLYes

This guide uses indicative pricing. It can be called at any point in the flow and has no expiry to handle. The trade-off is that the rate is not locked — if you display a price and then create the ramp without refreshing, the rate may have moved and the user receives a different amount than shown. Refresh the price close to ramp creation to minimise the gap.

The locked quote (GET /eapi/v0/quote) eliminates this risk but is only supported for bank transfer ramp creation — the React Native SDK and Embedded Payment Button do not accept a quoteId.

GET /eapi/v0/price?fiat=AUD&crypto=USDT&blockchain=TRON&method=payid-bank-transfer&transactionType=ONRAMP&fiatAmount=500

Pass fiatAmount or cryptoAmount — not both. If you're building a bank transfer integration and want to lock the rate shown on the confirmation screen, see Pricing & Quote ID.


Step 3 — Check eligibility

Before every transaction, confirm the user is cleared to proceed. Eligibility evaluates the user's identity state against the transaction context — amount, payment method, and jurisdiction.

POST /eapi/v0/identities/transactions/eligibility
Content-Type: application/json

{
  "identityReference": "partner-abc123",
  "method": "payid-bank-transfer",
  "transactionType": "ONRAMP",
  "fiat": "AUD",
  "crypto": "USDT",
  "blockchain": "TRON",
  "fiatAmount": "500"
}

Response: cleared to proceed

{
  "paymentReady": true,
  "requirements": []
}

Response: additional information required

{
  "paymentReady": false,
  "requirements": ["OCCUPATION", "SOURCE_FUNDS"]
}

Do not create a ramp or invoke the SDK when paymentReady is false. See Interpreting Eligibility for the full requirements dictionary.


Step 4 — Handle requirements (if any)

When eligibility returns requirements, collect the missing information in your UX and submit it before re-checking.

For structured fields (NAME, DOB, ADDRESS, OCCUPATION, SOURCE_FUNDS, PURPOSE_OF_TX, TIN, DOCUMENT, POA):

PATCH /eapi/v0/identities
Content-Type: application/json

{
  "identityReference": "partner-abc123",
  "email": "[email protected]",
  "occupationIndustry": "Consulting, IT, or business services",
  "occupation": "IT developer",
  "sourceOfFunds": "Salary"
}

email is required on every PATCH request. Partial updates are supported — include only the fields you're updating.

For SELFIE — complete KYC provider token sharing first. Token sharing may satisfy multiple requirements in a single call. Then submit any remaining structured fields via PATCH. See KYC Token Sharing.

After submitting, re-run eligibility. Repeat until paymentReady: true.

true

false

Yes

No

POST /eligibility

paymentReady?

Proceed to payment

Collect requirements in your UX

SELFIE required?

POST /identities/share/token
KYC token sharing

PATCH /identities
Structured fields

true

false

Yes

No

POST /eligibility

paymentReady?

Proceed to payment

Collect requirements in your UX

SELFIE required?

POST /identities/share/token
KYC token sharing

PATCH /identities
Structured fields


Step 5 — Execute payment

Once paymentReady: true is confirmed, proceed to your payment method guide for the execution step:


Tracking transaction status

Transactions are asynchronous. Configure a webhook endpoint to receive status updates:

{
  "order_id": "b7f1ffbb2f1bd7a5e2ba152b4049d234",
  "status": "COMPLETED",
  "status_date": "2026-04-17 03:45:00",
  "external_reason": "...",
  "fiat_currency": "AUD",
  "fiat_amount": "500.00",
  "crypto_coin": "USDT",
  "crypto_amount": "324.51",
  "transaction_hash": "0x..."
}

Acknowledge every webhook with a 200 response immediately, then process asynchronously. You can also poll directly using GET /eapi/v0/ramps/{id}. See Webhooks for the full payload schema and status values.