{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-products/hosted-checkout/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"JS Native Payments SDK Integration Guide (Web)","description":"Official Banxa API documentation – on-ramp and off-ramp transfers with identity verification and compliance.","llmstxt":{"title":"Banxa Developer Documentation","description":"Integrate crypto-fiat exchange with Banxa's licensed infrastructure: payments, KYC, compliance, and settlement handled. 150+ countries, 45 global licences.","details":{"content":"Two integration products: **Banxa Native API** for partners who manage their own KYC and want full UX control (headless, HMAC server-to-server, no Banxa-hosted screens); **Banxa Hosted Checkout** for partners who want Banxa to handle KYC and payments (three paths: Referral URL, API, or React Native SDK). Both use the same sandbox and production environments at `https://api.banxa-sandbox.com` and `https://api.banxa.com`.\n\n## Constraints\n\n- **Authentication**: HMAC credentials must be stored server-side only, never expose in frontend, mobile, or client-side code. HMAC is required for all Native API calls and for KYC sharing in Hosted Checkout; `x-api-key` is used for all other Hosted Checkout endpoints.\n- **`externalCustomerId`**: Required on every buy and sell order. Use a stable opaque identifier, never PII.\n- **`identityReference`**: Must remain constant for the same user across all requests. Must not contain PII. If you attempt to create an identity for an email that already exists, you will receive a 422 / code 81, so retrieve the existing record rather than retrying creation. Use `GET /eapi/v0/identities/{identityReference}?email=user@example.com` to look up the real `identityReference` linked to an email.\n- **`quoteId`**: Only supported by `POST /eapi/v0/ramps` (bank transfer). The React Native SDK and Embedded Payment Button do not accept a `quoteId`, so use indicative pricing (`GET /eapi/v0/price`) for SDK and Embedded Payment Button flows.\n- **Quotes**: Indicative prices are not rate-locked, so refresh close to order creation to minimise rate drift. Locked quotes (`GET /eapi/v0/quote`) expire after approximately 3 minutes and are only valid for bank transfer ramp creation.\n- **Eligibility gate**: Never create a ramp or invoke the SDK when `paymentReady` is `false`. Always check eligibility and satisfy all requirements before payment execution.\n- **Webhooks**: Verify all inbound webhook signatures with HMAC-SHA256 before processing. Return HTTP 200 immediately and process asynchronously.\n- **Product selection**: Banxa Native is for partners who manage their own KYC. Banxa Hosted Checkout is for partners who do not do KYC. These are separate products with separate flows: do not mix endpoints across products.\n- **SDK scope**: The React Native SDK has no `banxa.customerIdentity` module. Identity and KYC are handled through the Native API only. `primerCallbacks` and `primerSettings` are Native context only and must not be referenced in Hosted Checkout integrations.\n- **Payment method naming**: Never use \"eAPI\" or \"EAPI\" as a product name. The correct name is \"Banxa Native API\". The URL path prefix `/eapi/` is correct and should not be changed.\n"},"hide":false,"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"js-native-payments-sdk-integration-guide-web","__idx":0},"children":["JS Native Payments SDK Integration Guide (Web)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This guide walks through a complete Banxa Hosted Checkout integration on the web using the JS Native Payments SDK. By the end, you'll have a working buy flow: create an order on your backend, embed the checkout in an iframe on your page, and track the final order status."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Web works differently from React Native. There is no in-app WebView and no single client that runs everywhere. Instead the SDK ships two entry points, and you use both:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Backend (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/api"]},"):"]}," the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["BanxaApiClient"]}," creates orders and fetches quotes. It uses ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x-api-key"]}," authentication, so it must run server-side. Never ship your API key to the browser."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Frontend (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/web"]},"):"]}," the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<banxa-hosted-checkout>"]}," web component embeds the returned ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["checkoutUrl"]}," in an iframe and reports success, failure, and cancel events."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For the full API surface, see the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/hosted-checkout/docs/sdk-integration/javascript-native-payments-sdk-reference"},"children":["JS Native Payments SDK Reference"]},"."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Why 'Native Payments' in the name?"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The package name refers to its Banxa Native payment sheet capability, a separate feature for partners who run their own KYC. Here, you're using it to create orders and embed the hosted checkout iframe, not the native payment sheet. Ignore the \"Native Payments\" framing unless you later move to Banxa Native."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"before-you-start","__idx":1},"children":["Before you start"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"prerequisites","__idx":2},"children":["Prerequisites"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A web app with a backend you control (Node, or any runtime that can run the API client)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your Banxa partner reference and API key (sandbox for development, production after approval)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A configured webhook endpoint (optional but recommended for order status tracking)."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"install-the-sdk","__idx":3},"children":["Install the SDK"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"npm install @banxa-official/javascript-native-payments-sdk\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You do not need the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@primer-io/primer-js"]}," peer dependency for a Hosted Checkout iframe integration. It is only required for the native payment sheet, which is a ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://banxa-enterprise.redocly.app/enterprise-api/v0-beta"},"children":["Banxa Native"]}," feature."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-1-initialise-the-client-on-your-backend","__idx":4},"children":["Step 1: Initialise the client on your backend"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Create a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["BanxaApiClient"]}," on your server. Import from the root (or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/api"]},") entry point so no browser or web component code is pulled in."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import { BanxaApiClient } from '@banxa-official/javascript-native-payments-sdk';\n\nconst banxa = new BanxaApiClient({\n  apiKey: process.env.BANXA_API_KEY,\n  partner: process.env.BANXA_PARTNER,\n  environment: 'sandbox', // or 'production'\n});\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Field"},"children":["Field"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["apiKey"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Your v2 API key. Keep it server-side only."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["partner"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Your partner identifier (for example ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["binance"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["metamask"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["environment"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["'sandbox'"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["'production'"]},"."]}]}]}]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-2-get-a-quote","__idx":5},"children":["Step 2: Get a quote"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Fetch live pricing before showing an amount to the customer. Call this close to when you show the price, because crypto rates move quickly and quotes are indicative."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"const quote = await banxa.getQuote({\n  fiat: 'AUD',\n  crypto: 'USDT',\n  blockchain: 'TRON',\n  paymentMethodId: 'debit-credit-card',\n  fiatAmount: '100',\n}, 'buy');\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Either ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fiatAmount"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["cryptoAmount"]}," is required, along with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fiat"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["crypto"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["blockchain"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentMethodId"]},". Quotes carry no quote ID; there is nothing to pass into order creation."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-3-create-an-order-from-your-backend","__idx":6},"children":["Step 3: Create an order from your backend"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Expose an endpoint that your frontend calls when the customer confirms. Create the order there and return the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["checkoutUrl"]}," to the browser."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"// server.ts (Express example)\nimport express from 'express';\nimport { BanxaApiClient } from '@banxa-official/javascript-native-payments-sdk';\n\nconst app = express();\napp.use(express.json());\n\nconst banxa = new BanxaApiClient({\n  apiKey: process.env.BANXA_API_KEY,\n  partner: process.env.BANXA_PARTNER,\n  environment: 'production',\n});\n\napp.post('/api/create-order', async (req, res) => {\n  const { userId, amount, fiat, crypto, walletAddress } = req.body;\n\n  try {\n    const order = await banxa.createOrder({\n      externalCustomerId: userId,\n      fiat,\n      crypto,\n      fiatAmount: amount,\n      walletAddress,\n      redirectUrl: `${process.env.FRONTEND_URL}/checkout/return`,\n    });\n\n    res.json({ orderId: order.id, checkoutUrl: order.checkoutUrl });\n  } catch (error) {\n    const message = error instanceof Error ? error.message : 'Order creation failed';\n    res.status(500).json({ error: message });\n  }\n});\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"required-fields","__idx":7},"children":["Required fields"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["externalCustomerId"]},": your stable customer identifier. Use one per customer, not per order, so Banxa recognises returning customers."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fiat"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["crypto"]},", and either ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fiatAmount"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["cryptoAmount"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["walletAddress"]},": the customer's receiving wallet address."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["redirectUrl"]},": where the customer is returned after checkout. Use a URL on your checkout page origin so the iframe can detect the return."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"response","__idx":8},"children":["Response"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"checkoutUrl\": \"https://partner.banxa.com/portal?expires=xxx&oid=xxx&signature=xxx\",\n  \"id\": \"191fa5b4b1f45e1cf784422e09317d56\",\n  \"externalOrderId\": \"a4b427ccb872a1744b317456bd0d165f\",\n  \"externalCustomerId\": \"user-123\",\n  \"fiat\": \"CAD\",\n  \"fiatAmount\": \"1000.00\",\n  \"crypto\": \"USDC\",\n  \"cryptoAmount\": null,\n  \"blockchain\": \"TRON\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"The checkout URL expires in 1 minute"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["checkoutUrl"]}," is valid for 1 minute from order creation and must be loaded within that window. Once loaded it does not expire. Create the order at the moment the customer confirms, then mount the component immediately. Do not create orders in advance. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/hosted-checkout/docs/reference/faq"},"children":["FAQ"]},"."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-4-embed-the-checkout-on-your-page","__idx":9},"children":["Step 4: Embed the checkout on your page"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Register the component once, then set the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["checkout-url"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["return-url"]}," and listen for the outcome events."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"vanilla-js","__idx":10},"children":["Vanilla JS"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"html","header":{"controls":{"copy":{}}},"source":"<script type=\"module\">\n  import { registerBanxaHostedCheckout } from '@banxa-official/javascript-native-payments-sdk/web';\n  registerBanxaHostedCheckout();\n</script>\n\n<banxa-hosted-checkout id=\"checkout\"></banxa-hosted-checkout>\n\n<script type=\"module\">\n  async function startCheckout() {\n    const res = await fetch('/api/create-order', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({\n        userId: 'user-123',\n        amount: '100',\n        fiat: 'AUD',\n        crypto: 'USDT',\n        walletAddress: '0x...',\n      }),\n    });\n    if (!res.ok) throw new Error('Failed to create order');\n\n    const { checkoutUrl } = await res.json();\n    const checkout = document.getElementById('checkout');\n    checkout.setAttribute('checkout-url', checkoutUrl);\n    checkout.setAttribute('return-url', `${window.location.origin}/checkout/return`);\n\n    checkout.addEventListener('banxa:checkout-success', () => { window.location.href = '/success'; });\n    checkout.addEventListener('banxa:checkout-failure', () => { /* show retry */ });\n    checkout.addEventListener('banxa:checkout-cancelled', () => { /* customer cancelled */ });\n  }\n\n  startCheckout().catch(console.error);\n</script>\n","lang":"html"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"react","__idx":11},"children":["React"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Register the component once (for example in an entry module), then bind through a ref. Custom-element events are not React synthetic events, so attach them with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["addEventListener"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"tsx","header":{"controls":{"copy":{}}},"source":"import { useEffect, useRef } from 'react';\nimport { registerBanxaHostedCheckout } from '@banxa-official/javascript-native-payments-sdk/web';\n\nregisterBanxaHostedCheckout();\n\nfunction Checkout({ checkoutUrl }: { checkoutUrl: string }) {\n  const ref = useRef<HTMLElement>(null);\n\n  useEffect(() => {\n    const el = ref.current;\n    if (!el) return;\n    const onSuccess = () => { /* ... */ };\n    el.addEventListener('banxa:checkout-success', onSuccess);\n    return () => el.removeEventListener('banxa:checkout-success', onSuccess);\n  }, []);\n\n  return (\n    <banxa-hosted-checkout\n      ref={ref}\n      checkout-url={checkoutUrl}\n      return-url={`${window.location.origin}/checkout/return`}\n    />\n  );\n}\n","lang":"tsx"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"vue","__idx":12},"children":["Vue"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Register the component once, bind attributes, and listen for events with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@banxa:checkout-success"]},". Tell your bundler to treat ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["banxa-hosted-checkout"]}," as a custom element so Vue does not try to resolve it as a component."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"let-the-sdk-pick-the-flow","__idx":13},"children":["Let the SDK pick the flow"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you would rather not wire the component by hand, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["runBuyCheckoutFlow"]}," creates the order and mounts the iframe for you:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import { runBuyCheckoutFlow } from '@banxa-official/javascript-native-payments-sdk/web';\n\nconst { element } = await runBuyCheckoutFlow({\n  client, // a BanxaApiClient; safe here only if this code runs server-adjacent, otherwise create the order via your backend\n  request: { externalCustomerId: 'user-123', fiat: 'AUD', crypto: 'USDT', fiatAmount: '100', walletAddress: '0x...', redirectUrl: `${window.location.origin}/checkout/return` },\n  container: document.getElementById('checkout'),\n});\n\nelement.addEventListener('banxa:checkout-success', () => { /* completed */ });\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Because the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["BanxaApiClient"]}," uses your API key, prefer creating the order through your backend (Step 3) and mounting ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<banxa-hosted-checkout>"]}," with the returned ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["checkoutUrl"]},". Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["runBuyCheckoutFlow"]}," only where the client can run without exposing the key."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"permissions","__idx":14},"children":["Permissions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For KYC camera capture, the embedding page must grant ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["camera"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["microphone"]}," permissions. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/hosted-checkout/docs/checkout-experience/iframe/webview-mobile"},"children":["Embedded Checkout, Mobile"]},"."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-5-look-up-order-status","__idx":15},"children":["Step 5: Look up order status"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The success event is a UI signal, not the authoritative order state. Confirm the final status from your backend using the order ID you stored in Step 3."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"const orderDetails = await banxa.getOrder(orderId);\n\nif (orderDetails.status === 'complete') {\n  // Credit the customer, update your database, and so on.\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Only the terminal statuses are final. Do not credit the customer until the order reaches ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["complete"]},". For the full list, see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/hosted-checkout/docs/transaction-lifecycle/order-statuses"},"children":["Order Statuses"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For a full list of all orders across all customers (reconciliation, reporting), use the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/hosted-checkout/docs/api-integration/api-integration-overview"},"children":["Banxa API"]}," directly. The SDK is not the reconciliation surface."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-6-handle-webhooks-recommended","__idx":16},"children":["Step 6: Handle webhooks (recommended)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["banxa:checkout-success"]}," event fires in the browser when the customer finishes, but for reliable order tracking configure webhooks on your backend."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Webhooks fire for every order status change. Configure your webhook URL in the merchant dashboard."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The typical pattern:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Browser success event: optimistic UI update (\"your order is processing\")."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Webhook to your backend: authoritative order state."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Backend updates your records and notifies the client."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/hosted-checkout/docs/transaction-lifecycle/webhooks"},"children":["Webhooks"]}," for payload structure and signature verification. Webhook signatures are verified with your HMAC secret, not the v2 ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x-api-key"]},"."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"error-handling","__idx":17},"children":["Error handling"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Wrap backend calls in try/catch. The client throws ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["BanxaApiError"]}," for API and network failures."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import { BanxaApiClient, BanxaApiError } from '@banxa-official/javascript-native-payments-sdk';\n\ntry {\n  const order = await banxa.createOrder({ /* ... */ });\n} catch (error) {\n  if (error instanceof BanxaApiError) {\n    console.error('Status code:', error.statusCode);\n    console.error('Response body:', error.responseBody);\n    console.error('Errors:', error.errors);\n  } else {\n    console.error('Unknown error:', error);\n  }\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Show user-facing messages only from validated error fields. Do not expose raw error strings that may include internal detail. For error codes and their meanings, see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/hosted-checkout/docs/reference/error-codes"},"children":["Error Codes"]},"."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"testing","__idx":18},"children":["Testing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the sandbox environment for all development and testing:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"const banxa = new BanxaApiClient({\n  apiKey: process.env.BANXA_SANDBOX_API_KEY,\n  partner: process.env.BANXA_PARTNER,\n  environment: 'sandbox',\n});\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For test credentials (mobile numbers, card numbers, PIN codes, KYC data), see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/hosted-checkout/docs/testing/sandbox-test-data"},"children":["Sandbox Test Data"]},"."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"next-steps","__idx":19},"children":["Next steps"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/hosted-checkout/docs/sdk-integration/javascript-native-payments-sdk-reference"},"children":["JS Native Payments SDK Reference"]},": full method and component reference."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/hosted-checkout/docs/transaction-lifecycle/webhooks"},"children":["Webhooks"]},": configure webhook notifications."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/hosted-checkout/docs/transaction-lifecycle/order-statuses"},"children":["Order Statuses"]},": full status reference."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/hosted-checkout/docs/testing/sandbox-test-data"},"children":["Sandbox Test Data"]},": credentials and test values."]}]}]},"headings":[{"value":"JS Native Payments SDK Integration Guide (Web)","id":"js-native-payments-sdk-integration-guide-web","depth":1},{"value":"Before you start","id":"before-you-start","depth":2},{"value":"Prerequisites","id":"prerequisites","depth":3},{"value":"Install the SDK","id":"install-the-sdk","depth":3},{"value":"Step 1: Initialise the client on your backend","id":"step-1-initialise-the-client-on-your-backend","depth":2},{"value":"Step 2: Get a quote","id":"step-2-get-a-quote","depth":2},{"value":"Step 3: Create an order from your backend","id":"step-3-create-an-order-from-your-backend","depth":2},{"value":"Required fields","id":"required-fields","depth":3},{"value":"Response","id":"response","depth":3},{"value":"Step 4: Embed the checkout on your page","id":"step-4-embed-the-checkout-on-your-page","depth":2},{"value":"Vanilla JS","id":"vanilla-js","depth":3},{"value":"React","id":"react","depth":3},{"value":"Vue","id":"vue","depth":3},{"value":"Let the SDK pick the flow","id":"let-the-sdk-pick-the-flow","depth":3},{"value":"Permissions","id":"permissions","depth":3},{"value":"Step 5: Look up order status","id":"step-5-look-up-order-status","depth":2},{"value":"Step 6: Handle webhooks (recommended)","id":"step-6-handle-webhooks-recommended","depth":2},{"value":"Error handling","id":"error-handling","depth":2},{"value":"Testing","id":"testing","depth":2},{"value":"Next steps","id":"next-steps","depth":2}],"frontmatter":{"title":"JS Native Payments SDK Integration Guide (Web) | Banxa Docs","description":"End-to-end web integration for Banxa Hosted Checkout with the JS Native Payments SDK: backend order creation, embedding the hosted checkout iframe, and handling completion.","seo":{"title":"JS Native Payments SDK Integration Guide (Web)"}},"lastModified":"2026-07-16T01:30:13.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/products/hosted-checkout/docs/sdk-integration/javascript-native-payments-sdk-guide","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}