Skip to main content

React SDK

The official bindings for React 18/19. The Provider + hooks pattern lets you declaratively handle everything from SDK loading to popup checkout and embedded widgets.

Compatibility

ItemSupported versions
React18.x · 19.x (peer dependency)
TypeScript5.0+
Module formatsESM · CJS

Installation

pnpm add @nexus-cross/cross-pay-react

Prerequisites

In every example, checkoutId / idempotencyKey are values your merchant backend obtains by calling POST /v1/payments and passes to the client. The React app never holds MERCHANT_API_KEY.

Copy and start

import { CrossPayProvider, CrossPaySDKError, usePayment } from '@nexus-cross/cross-pay-react';

export default function App() {
return (
<CrossPayProvider theme="light" locale="en">
<CheckoutButton />
</CrossPayProvider>
);
}

function CheckoutButton() {
const { requestPayment, isLoading } = usePayment();

async function handlePay() {
try {
const result = await requestPayment({
checkoutId: 'cs_issued_checkout_id',
idempotencyKey: 'ik_issued_idempotency_key',
embeddedWallet: true,
});

console.log(result.paymentId, result.status);
} catch (error) {
if (error instanceof CrossPaySDKError && error.code === 'USER_REJECTED') {
return;
}
throw error;
}
}

return (
<button type="button" onClick={handlePay} disabled={isLoading}>
{isLoading ? 'Loading SDK...' : 'Pay'}
</button>
);
}

Provider

CrossPayProvider accepts SDK loader options and default UI options.

<CrossPayProvider
src="https://js.crosspay.ai/pay-js/latest/cross-pay.js"
integrity="sha384-abc123..."
theme="auto"
locale="en"
onError={(error) => console.error(error)}
>
<Checkout />
</CrossPayProvider>
propDescription
srcOverride the cross-pay.js URL to load
integrityEnforce SRI verification on pinned-URL deployments
themeDefault Provider theme. Injected when the hook call omits it
localeDefault Provider locale. Injected when the hook call omits it
onErrorHandle SDK load failure via callback. When set, the Provider does not re-throw the error

If onError is omitted, a load failure throws an Error on the next render. In that mode, wrap with a React Error Boundary.

function Checkout() {
const { requestPayment, isLoading } = usePayment();

async function handlePay() {
const result = await requestPayment({
checkoutId: 'cs_abc123',
idempotencyKey: 'ik_xyz',
successUrl: 'https://shop.example.com/pay/success',
failUrl: 'https://shop.example.com/pay/fail',
embeddedWallet: false,
});

console.log(result.paymentId, result.status);
}

return <button onClick={handlePay} disabled={isLoading}>Pay</button>;
}

embeddedWallet is a per-checkout option, not a Provider prop. It defaults to true.

Widget mode

import { usePaymentWidget, CheckoutStatus } from '@nexus-cross/cross-pay-react';
import { useEffect, useState } from 'react';

function WidgetCheckout({ checkoutId }: { checkoutId: string }) {
const [embeddedWallet, setEmbeddedWallet] = useState(true);
const { widgets, status, paymentMethodWidget, renderPaymentMethods } = usePaymentWidget();

useEffect(() => {
if (!checkoutId) return;
renderPaymentMethods('#payment-methods', {
checkoutId,
idempotencyKey: 'ik_xyz',
embeddedWallet,
});
}, [checkoutId, embeddedWallet, renderPaymentMethods]);

useEffect(() => {
paymentMethodWidget?.update({ embeddedWallet });
}, [embeddedWallet, paymentMethodWidget]);

async function handlePay() {
const result = await widgets?.submit({ idempotencyKey: 'ik_xyz' });
console.log(result);
}

return (
<>
<label>
<input
type="checkbox"
checked={embeddedWallet}
onChange={(event) => setEmbeddedWallet(event.currentTarget.checked)}
/>
Show embedded wallet
</label>
<div id="payment-methods" />
<button onClick={handlePay} disabled={status !== CheckoutStatus.READY}>
Pay
</button>
</>
);
}

usePaymentWidget replays the last render parameters when the widget instance is recreated. To start a new payment after a terminal state, always re-render with a new checkoutId/idempotencyKey.

Surfacing errors

import { CrossPayProvider, useCrossPayError } from '@nexus-cross/cross-pay-react';

function App() {
return (
<CrossPayProvider onError={(error) => console.error(error)}>
<Checkout />
</CrossPayProvider>
);
}

function Checkout() {
const error = useCrossPayError();
if (error) return <div>SDK load failed: {error.message}</div>;
return <button type="button">Pay</button>;
}

Hooks reference

HookReturn typeDescription
useCrossPay()CrossPayInstance | nullDirect access to the low-level instance
useCrossPayLoading()booleanWhether the script is currently loading
useCrossPayError()Error | nullSDK load failure error (used with onError mode)
usePayment(){ requestPayment, isLoading }Convenience hook for popup checkout
usePaymentWidget(options?){ widgets, status, paymentMethodWidget, renderPaymentMethods }Widget lifecycle management

Re-exported API

@nexus-cross/cross-pay-react re-exports the SDK API needed for React integration alongside key shared contracts.

  • Values: loadCrossPay, clearCrossPay, CrossPaySDKError, ERROR_CODES
  • Hooks/Provider: CrossPayProvider, useCrossPay, useCrossPayError, useCrossPayLoading, usePayment, usePaymentWidget
  • Key types: Amount, CheckoutParams, CheckoutPaymentResult, CrossPayAppearance, CrossPayError, CrossPayInstance, CrossPayLocale, CrossPayTheme, ErrorCode, ErrorType, RenderPaymentMethodsParams, WidgetHandle, WidgetsInstance, WidgetsOptions
  • Enums/constants: CheckoutStatus, Currency, PaymentType, ProductCategory, UpdateReason