사용법
초기화
설치 방식과 무관하게 동일한 OozooPayClient 클래스를 사용합니다.
npm (ES Module)
import { OozooPayClient } from '@team-oozoo/oozoo-pay';
const client = new OozooPayClient('pk_xxxxxxxxxxxxxxxx');
CDN (Standalone global)
<script src="https://cdn.oozoopay.com/latest/standalone.global.js"></script>
<script>
// OozooPay.load(...) 또는 new OozooPay.OozooPayClient(...) 둘 다 가능
const client = await OozooPay.load('pk_xxxxxxxxxxxxxxxx');
</script>
| 인자 | 타입 | 필수 | 설명 |
|---|---|---|---|
clientKey | string | ✓ | Client Key (pk_xxx) |
결제 요청
pay()를 호출하면 결제 오버레이가 열립니다. 사용자가 "결제" 버튼을 누르면 onCreateInvoice 콜백이 호출되며, 가맹점 서버는 HMAC API로 인보이스를 생성하여 invoiceId를 반환해야 합니다.
try {
await client.pay({
price: 100,
unit: 'usd',
successUrl: 'https://your-shop.com/payment/success',
failUrl: 'https://your-shop.com/payment/fail',
onCreateInvoice: async ({ price, unit, chainId, tokenAddress, sender }) => {
const res = await fetch('/api/create-invoice', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ price, unit, chainId, tokenAddress, sender }),
});
const { invoiceId } = await res.json();
return invoiceId;
},
});
} catch (err) {
// err.code === 'UNSUPPORTED_TOKEN' 등
console.error(err);
}
pay() 옵션
| 옵션 | 타입 | 필수 | 설명 |
|---|---|---|---|
price | number | ✓ | 결제 금액 |
unit | 'usd' | 통화 단위 (기본: 'usd') | |
successUrl | string | ✓ | 결제 성공 시 리다이렉트 URL |
failUrl | string | 취소/실패 시 리다이렉트 URL (생략 시 모달 만 닫힘) | |
onCreateInvoice | function | ✓ | 인보이스 생성 콜백 (아래 참고) |
chainId | string | Prefill — 결제창에 미리 선택할 체인 (tokenAddress와 함께 지정) | |
tokenAddress | string | Prefill — 결제창에 미리 선택할 토큰 (chainId와 함께 지정) |
onCreateInvoice 콜백
사용자가 토큰과 네트워크를 선택하고 "결제" 버튼을 누르면 호출됩니다. 가맹점 서버에서 인보이스를 생성하고 invoiceId(UUID)를 반환해야 합니다.
| 파라미터 | 타입 | 설명 |
|---|---|---|
price | number | 결제 금액 |
unit | 'usd' | 통화 단위 |
chainId | string | 선택된 블록체인 네트워크 chain ID |
tokenAddress | string | 선택된 토큰의 컨트랙트 주소 |
sender | string | 결제자의 지갑 주소 |
서버 구현은 서버 연동 참고.
토큰·체인 미리 선택 (Prefill)
chainId와 tokenAddress를 함께 전달하면 해당 토큰이 미리 선택된 상태로 결제창이 열립니다. 사용자는 결제 확정 전에 다른 토큰으로 변경할 수 있습니다.
await client.pay({
price: 100,
unit: 'usd',
successUrl: 'https://your-shop.com/payment/success',
failUrl: 'https://your-shop.com/payment/fail',
// 미리 선택: Ethereum Sepolia USDC
chainId: '11155111',
tokenAddress: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
onCreateInvoice: async ({ chainId, tokenAddress, sender }) => {
// ...
},
});
동작
| 케이스 | 결과 |
|---|---|
chainId, tokenAddress 모두 미지정 | 기본 동작 — 사용자가 결제창에서 직 접 토큰 선택 |
| 둘 중 하나만 지정 | Prefill 무시 — 사용자가 직접 선택 |
| 둘 다 지정 + 가맹점이 활성화한 토큰과 매칭 | 해당 토큰이 미리 선택됨 (사용자는 변경 가능) |
| 둘 다 지정했으나 매칭되는 토큰 없음 | pay() Promise가 reject (code: 'UNSUPPORTED_TOKEN') — 아래 에러 처리 참고 |
매칭 규칙:
chainId는 문자열로 정확히 비교합니다 ('11155111'==='11155111')tokenAddress는 대소문자 무시하고 비교합니다- 네이티브 코인(ETH, BNB, KAIA 등)은 EVM 센티넬 주소
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE또는 SVM 센티넬So11111111111111111111111111111111111111111사용
GET /api/tokens에서 조회가맹점 서버에서 GET /api/tokens (HMAC 인증) 를 호출해 이 가맹점이 활성화한 chain/token 조합을 받아오세요. 그 목록을 클라이언트에 내려 드롭다운 등을 구성하면 UNSUPPORTED_TOKEN 에러를 사전에 방지할 수 있습니다.
에러 처리
pay()와 transfer()는 Promise<void>를 반환합니다:
- 성공 시 resolve (
successUrl로 리다이렉트 후) - 사용자 취소 시 resolve (배경 클릭 / ESC / 닫기)
- 체크아웃에서 에러 신호를 받으면
ApiError로 reject
try {
await client.pay({ ... });
} catch (err) {
// err.name === 'ApiError'
// err.code === 'UNSUPPORTED_TOKEN' | 'PAYMENT_FAILED' | ...
// err.message — 사람이 읽을 수 있는 메시지
}
에러 코드
code | 발생 원인 |
|---|---|
UNSUPPORTED_TOKEN | Prefill로 넘긴 chainId + tokenAddress가 이 가맹점이 활성화한 토큰과 매칭되지 않음. |
PAYMENT_FAILED | 블록체인 트랜잭션 실패 (서명은 됐으나 revert 또는 RPC 에러). |
TRANSFER_FAILED | 위와 동일, transfer() 흐름에서 발생. |
failUrl이 설정되어 있으면 브라우저가 ?code=...&message=... 쿼리와 함께 리다이렉트도 수행합니다. Promise는 그래도 reject되므로 try/catch에서 처리하는 것이 가장 안전합니다.
결제 흐름
- SDK 초기화 — Client Key로 클라이언트를 만듭니다.
pay()호출 — 결제 오버레이가 열립니다 (필요 시 prefill).- 사용자가 토큰·네트워크 확인 (prefill했다면 미리 선택된 상태)
onCreateInvoice호출 — 가맹점 서버에서 HMAC으로 인보이스 생성.- 블록체인 트랜잭션 — 사용자가 지갑에서 서명하고 브로드캐스트.
- 온체인 컨펌 —
successUrl로 리다이렉트. - 웹훅 수신 — 가맹점 서버가
invoice.confirmed웹훅을 받아 주문 확정.
successUrl 리다이렉트만으로 주문을 확정하지 마세요 — 사용자가 탭을 닫거나 다른 페이지로 이동할 수 있습니다. 반드시 서버에서 invoice.confirmed 웹훅을 받은 후 처리해야 합니다.
출금 요청
transfer()는 같은 오버레이를 송금 방향으로 엽니다. 출금 요청을 생성하며, 즉시 온체인으로 자금이 전송되지 않습니다. 요청은 REQUESTED 상태로 가맹점 관리자 대시보드에 등록되고, 관리자가 2FA로 승인해야 실제 온체인 트랜잭션이 실행됩니다.
사용자 출금, 오프플로우 환불, 리워드 지급 등 사람의 승인 단계가 필요한 경우에 사용합니다.
await client.transfer({
price: 100,
unit: 'usd',
successUrl: 'https://your-shop.com/withdrawal/success',
failUrl: 'https://your-shop.com/withdrawal/fail',
// Prefill은 출금에서도 동일하게 지원됩니다
chainId: '11155111',
tokenAddress: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
onCreateInvoice: async ({ price, unit, chainId, tokenAddress, receiver }) => {
const res = await fetch('/api/create-withdrawal', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ price, unit, chainId, tokenAddress, receiver }),
});
const { withdrawalId } = await res.json();
return withdrawalId;
},
});
transfer() 옵션
pay()와 동일한 구조 — chainId/tokenAddress prefill도 동일하게 동작합니다. 콜백 파라미터의 주소 필드만 다릅니다 (sender → receiver).
onCreateInvoice 콜백 (transfer)
| 파라미터 | 타입 | 설명 |
|---|---|---|
price | number | 송금 금액 |
unit | 'usd' | 통화 단위 |
chainId | string | 선택된 블록체인 네트워크 chain ID |
tokenAddress | string | 선택된 토큰의 컨트랙트 주소 |
receiver | string | 수령인의 지갑 주소 (사용자가 UI에서 입력) |
서버 측 엔드포인트가 다릅니다 — POST /api/invoices/withdrawals 를 호출하고 invoiceId 대신 withdrawalId 를 반환해야 합니다. 자세한 구현은 서버 연동 → 출금 생성 (Transfer) 엔드포인트 참고.