React SDK
React 18/19용 공식 바인딩입니다. Provider + hooks 기반으로 SDK 로드부터 popup checkout, embedded widget까지 선언적으로 다룰 수 있습니다.
호환성
| 항목 | 지원 버전 |
|---|---|
| React | 18.x · 19.x (peer dependency) |
| TypeScript | 5.0+ |
| Module formats | ESM · CJS |
설치
pnpm add @nexus-cross/cross-pay-react
사전 준비
모든 예시의 checkoutId / idempotencyKey는 가맹점 백엔드가 POST /v1/payments를 호출해 발급받은 뒤 클라이언트로 전달한 값입니다. React 앱은 MERCHANT_API_KEY를 절대 보관하지 않습니다.
복사해서 시작하기
import { CrossPayProvider, CrossPaySDKError, usePayment } from '@nexus-cross/cross-pay-react';
export default function App() {
return (
<CrossPayProvider theme="light" locale="ko">
<CheckoutButton />
</CrossPayProvider>
);
}
function CheckoutButton() {
const { requestPayment, isLoading } = usePayment();
async function handlePay() {
try {
const result = await requestPayment({
checkoutId: 'cs_발급받은_checkout_id',
idempotencyKey: 'ik_발급받은_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 ? 'SDK 로드 중...' : '결제'}
</button>
);
}
Provider
CrossPayProvider는 SDK loader 옵션과 기본 UI 옵션을 받습니다.
<CrossPayProvider
src="https://js.crosspay.ai/pay-js/latest/cross-pay.js"
integrity="sha384-abc123..."
theme="auto"
locale="ko"
onError={(error) => console.error(error)}
>
<Checkout />
</CrossPayProvider>
| prop | 설명 |
|---|---|
src | 로드할 cross-pay.js URL 오버라이드 |
integrity | 고정 스크립트 URL 배포에서 SRI 검증 적용 |
theme | Provider 기본 theme. hook 호출에서 생략하면 주입됨 |
locale | Provider 기본 locale. hook 호출에서 생략하면 주입됨 |
onError | SDK 로드 실패를 callback으로 처리. 지정하면 Provider가 에러를 re-throw하지 않음 |
onError를 생략하면 로드 실패 시 다음 렌더에서 Error를 throw합니다. 이 모드에서는 React Error Boundary로 감싸세요.
Popup checkout
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}>결제</button>;
}
embeddedWallet은 Provider prop이 아니라 checkout 호출별 옵션입니다. 기본값은 true입니다.
Widget 모드
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)}
/>
Embedded wallet 표시
</label>
<div id="payment-methods" />
<button onClick={handlePay} disabled={status !== CheckoutStatus.READY}>
결제하기
</button>
</>
);
}
usePaymentWidget은 widget instance 재생성 시 마지막 render 파라미터를 replay합니다. terminal 상태 이후 새 결제를 시작할 때는 반드시 새 checkoutId/idempotencyKey로 다시 렌더링하세요.
에러 표시
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 로드 실 패: {error.message}</div>;
return <button type="button">결제</button>;
}
Hooks 레퍼런스
| Hook | 반환 타입 | 설명 |
|---|---|---|
useCrossPay() | CrossPayInstance | null | 저수준 인스턴스 직접 접근 |
useCrossPayLoading() | boolean | 스크립트 로드 중 여부 |
useCrossPayError() | Error | null | SDK 로드 실패 에러 (onError 모드에서 사용) |
usePayment() | { requestPayment, isLoading } | Popup 결제 편의 hook |
usePaymentWidget(options?) | { widgets, status, paymentMethodWidget, renderPaymentMethods } | Widget 생명주기 관리 |
재노출 API
@nexus-cross/cross-pay-react는 React 연동에 필요한 SDK API와 주요 shared contract를 함께 재노출합니다.
- 값:
loadCrossPay,clearCrossPay,CrossPaySDKError,ERROR_CODES - Hooks/Provider:
CrossPayProvider,useCrossPay,useCrossPayError,useCrossPayLoading,usePayment,usePaymentWidget - 주요 타입:
Amount,CheckoutParams,CheckoutPaymentResult,CrossPayAppearance,CrossPayError,CrossPayInstance,CrossPayLocale,CrossPayTheme,ErrorCode,ErrorType,RenderPaymentMethodsParams,WidgetHandle,WidgetsInstance,WidgetsOptions - enums/상수:
CheckoutStatus,Currency,PaymentType,ProductCategory,UpdateReason