@bitpal/checkout 0.3.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,708 @@
1
+ /**
2
+ * @bitpal/checkout SDK — 타입 정의
3
+ */
4
+ interface BitPalConfig {
5
+ /**
6
+ * API 키(서버 전용, secret). prefix 가 mode 를 결정합니다:
7
+ * - `bp_test_...` → TEST mode (testnet 또는 local anvil)
8
+ * - `bp_live_...` → LIVE mode (mainnet)
9
+ *
10
+ * BitPal 콘솔 → Developers → API Keys 에서 발급.
11
+ *
12
+ * **선택**: createSession/listSessions/products 등 서버 메서드는 필수. 그러나 buyer-facing 공개 메서드
13
+ * (getPublicSession/getStatus/issueDepositAddress)는 인증 불요(session_token 기반)이므로 브라우저에서
14
+ * apiKey 없이 쓸 수 있다 — 절대 secret 키를 브라우저에 노출하지 말 것.
15
+ */
16
+ apiKey?: string;
17
+ /**
18
+ * BitPal API 기본 URL. 환경별로 다릅니다:
19
+ * - production: `https://api.bitpal.io` (기본값) — 실제 testnet/mainnet 컨트랙트
20
+ * - local dev: `http://localhost:3100` — anvil 로컬 체인 + 로컬 BitPal API
21
+ *
22
+ * mode (TEST/LIVE) 와 baseUrl 은 직교 (orthogonal): 어떤 baseUrl 이든 키 prefix 가 mode 를 결정.
23
+ */
24
+ baseUrl?: string;
25
+ /** 요청 타임아웃 (ms, 기본값: 30000) */
26
+ timeout?: number;
27
+ }
28
+ /** 응답/정규화된 line item — amount 는 항상 atomic μUSD 정수. */
29
+ interface LineItem {
30
+ name: string;
31
+ /** Atomic μUSD 정수 문자열($1="1000000", $29="29000000"). */
32
+ amount: string;
33
+ currency: string;
34
+ quantity?: number;
35
+ }
36
+ /**
37
+ * 세션 생성 입력용 line item. 금액은 둘 중 **하나**:
38
+ * - `amount`(권장): 사람 표기 USD 문자열(예 "29", "0.5", "29.00"). decimal 신경 쓸 필요 없음.
39
+ * ※ 금액은 **USD 가격**이다(체인/토큰 decimal 무관). 토큰(USDC 6dp / BNB USDT 18dp)별 스케일은 발급 시 서버가 처리.
40
+ * - `amount_atomic`: 이미 raw μUSD 정수 문자열($1="1000000")인 경우(고급/기존 코드).
41
+ * 둘 다 지정하거나 둘 다 생략하면 SDK 가 에러를 throw 한다.
42
+ */
43
+ interface LineItemInput {
44
+ name: string;
45
+ currency: string;
46
+ quantity?: number;
47
+ /** 사람 표기 USD 금액(예 "29", "0.5") — 권장. amount_atomic 과 상호배타. */
48
+ amount?: string;
49
+ /** raw μUSD 정수($1="1000000") — 고급/기존. amount 와 상호배타. */
50
+ amount_atomic?: string;
51
+ }
52
+ interface PriceLineItem {
53
+ /** price_... — bitpal.checkout.prices.create()로 만든 가격 */
54
+ price_id: string;
55
+ quantity?: number;
56
+ }
57
+ type CheckoutLineItemInput = LineItemInput | PriceLineItem;
58
+ interface CreateSessionParams {
59
+ line_items: CheckoutLineItemInput[];
60
+ /** 고객 지갑 주소 (선택) */
61
+ payer_wallet?: string;
62
+ /** 결제 체인 (예: eip155:8453). 단일 고정 — allowed_pay_chains와 상호 배타. */
63
+ pay_chain?: string;
64
+ /**
65
+ * 허용 결제 체인 목록 (CAIP-2). 구매자가 이 중에서 선택하며, 하나뿐이면 자동 선택된다.
66
+ * pay_chain(단일 고정)과 **상호 배타** — 동시 지정 시 SDK가 에러를 throw한다.
67
+ * 예: ['eip155:84532', 'eip155:11155420', 'eip155:80002', 'eip155:421614']
68
+ */
69
+ allowed_pay_chains?: string[];
70
+ /** 결제 자산 (예: USDC) */
71
+ pay_asset?: string;
72
+ /** 허용 오차 (%, 기본값: 2.00) */
73
+ tolerance_percent?: string;
74
+ /** 만료 시간 (초, 기본값: 1800) */
75
+ expires_in_seconds?: number;
76
+ /** 메타데이터 (모든 값은 string 이어야 함) */
77
+ metadata?: Record<string, string>;
78
+ /** 결제 완료 후 buyer 가 redirect 될 URL (선택) */
79
+ success_url?: string;
80
+ /** 결제 취소 시 buyer 가 redirect 될 URL (선택) */
81
+ cancel_url?: string;
82
+ }
83
+ interface CheckoutSession {
84
+ id: string;
85
+ status: string;
86
+ amount_total: string;
87
+ currency: string;
88
+ line_items: LineItem[];
89
+ pay_chain: string | null;
90
+ pay_asset: string | null;
91
+ payer_wallet: string | null;
92
+ seller_wallet: string;
93
+ tolerance_percent: string;
94
+ expires_at: string | null;
95
+ paid_at: string | null;
96
+ settled_at: string | null;
97
+ tx_hash: string | null;
98
+ created_at: string;
99
+ /** 'hosted' — 결제 경로. 안2 는 hosted deposit-address 단일 경로. */
100
+ payment_source?: string;
101
+ /** 호스티드 결제 페이지 URL (token 쿼리 포함). backend 응답 그대로 사용 — buyer를 이 URL로 redirect. */
102
+ checkout_url?: string;
103
+ /** 1회용 session token. `checkout_url`에 이미 query로 박혀 있어 별도 사용 불필요. */
104
+ session_token?: string;
105
+ /** `checkout_url`의 별칭 (backwards compat). buyer redirect 시 이 필드 또는 `checkout_url` 사용. */
106
+ url: string;
107
+ }
108
+ /**
109
+ * 안2 결제 옵션(chain×token). `/public` 이 서버 권위로 내려주며, buyer 가 하나를 골라
110
+ * `issueDepositAddress({ chain, token })` 로 입금주소를 발급받는다. 단일-자산 세션은 1개.
111
+ */
112
+ interface PaymentOption {
113
+ /** CAIP-2 (예: eip155:8453, tron:0x2b6653dc, solana:5eykt4…). 체인 식별의 canonical. */
114
+ caip2: string;
115
+ /** EVM numeric chainId. 비-EVM(Tron/Solana)은 null — 식별은 caip2 사용. */
116
+ chain_id: number | null;
117
+ token_symbol: string;
118
+ /** 토큰 컨트랙트/mint 주소(EVM 0x / Tron base58 / Solana base58 mint). */
119
+ token_address: string;
120
+ /** 토큰 decimals(USDC/USDT=6, BSC-USD=18). atomic↔decimal 변환에 필수 — 6dp 가정 금지. */
121
+ decimals: number;
122
+ }
123
+ /**
124
+ * `getPublicSession()` (`GET /v1/checkout/sessions/:id/public`) 응답 — 인증 불요(공개).
125
+ * buyer-facing 결제 페이지가 표시/폴링에 쓰는 서버 권위 뷰.
126
+ */
127
+ interface PublicSession {
128
+ id: string;
129
+ /** created | awaiting_deposit | deposit_detected | paid_confirmed | swept | unresolved_* | wrong_token | expired ... */
130
+ status: string;
131
+ mode: string | null;
132
+ currency: string;
133
+ /** Atomic 정수 문자열(토큰 decimals 기준). */
134
+ amount_total: string;
135
+ amount_received?: string | null;
136
+ pay_chain: string | null;
137
+ pay_asset: string | null;
138
+ /** 발급된 주문별 forwarder 입금주소(미발급이면 null). */
139
+ deposit_address?: string | null;
140
+ /** 구매자 선택 가능한 결제 옵션(서버 권위). 단일-자산이면 1개. */
141
+ payment_options: PaymentOption[];
142
+ chain_id: number | null;
143
+ token_address: string | null;
144
+ /** 선택된 pay 토큰 decimals(발급 후). amount_total/amount_received atomic 해석에 사용 — 6dp 가정 금지. */
145
+ token_decimals?: number | null;
146
+ tx_hash: string | null;
147
+ release_tx_hash?: string | null;
148
+ expires_at: string | null;
149
+ success_url: string | null;
150
+ cancel_url: string | null;
151
+ fee_amount: string | null;
152
+ net_amount: string | null;
153
+ seller_wallet: string;
154
+ paid_at: string | null;
155
+ settled_at: string | null;
156
+ created_at: string;
157
+ line_items: LineItem[];
158
+ }
159
+ /**
160
+ * `issueDepositAddress()` 요청 파라미터.
161
+ * chain/token 은 멀티옵션 세션에서만 필요(단일-자산 세션은 생략). refundAddress 는 필수 —
162
+ * 금액부족/잘못된토큰/만료 시 이 주소로 환불되며, 입금주소에 커밋되어 발급 후 변경 불가.
163
+ */
164
+ interface IssueDepositAddressParams {
165
+ /** 결제 페이지 접근 증명(HMAC). createSession 응답의 session_token. */
166
+ session_token: string;
167
+ /**
168
+ * 환불 주소. 필수. 선택한 체인의 VM 형식이어야 한다:
169
+ * EVM=0x…, Tron=base58(T…), Solana=base58. 결제와 같은 네트워크의 본인 주소.
170
+ * 금액부족/잘못된토큰/만료 시 이 주소로 환불되며, 입금주소에 커밋되어 발급 후 변경 불가.
171
+ */
172
+ refundAddress: string;
173
+ /** 선택 자산의 chain(CAIP-2 또는 short-name). 멀티옵션 세션 필수. */
174
+ chain?: string;
175
+ /** 선택 자산의 token symbol(예: USDC). 멀티옵션 세션 필수. */
176
+ token?: string;
177
+ }
178
+ /**
179
+ * `issueDepositAddress()` 응답 — 이 주소로 정확 금액을 송금하면 watcher 가 감지, keeper 가 정산한다.
180
+ */
181
+ interface DepositAddress {
182
+ sessionId: string;
183
+ chain: string;
184
+ /** 주문별 결정적 입금주소(EVM/Tron=CREATE2 forwarder, Solana=order PDA 의 deposit ATA). */
185
+ depositAddress: string;
186
+ expectedToken: string;
187
+ /** Atomic 정수 문자열. */
188
+ expectedAmount: string;
189
+ feeAmount: string;
190
+ refundAddress: string;
191
+ /** Unix seconds — on-chain deadline. */
192
+ deadline: string;
193
+ expiresAt: string | null;
194
+ }
195
+ /**
196
+ * `getStatus()` (`GET /v1/checkout/sessions/:id/status`) 응답 — 경량 폴링용(인증 불요).
197
+ */
198
+ interface SessionStatus {
199
+ status: string;
200
+ paid_at: string | null;
201
+ }
202
+ /**
203
+ * 수수료 미리보기 응답.
204
+ * amount, fee_amount, net_amount는 모두 atomic μUSDC 문자열 (e.g. "1500000" = $1.50).
205
+ * fee_rate는 비율 문자열 (e.g. "0.015" = 1.5%).
206
+ */
207
+ interface FeePreview {
208
+ amount: string;
209
+ fee_rate: string;
210
+ fee_amount: string;
211
+ net_amount: string;
212
+ }
213
+ /**
214
+ * 환불은 온체인 자동 경로 — SDK 에 수동 환불 API 는 없다.
215
+ * 금액부족/잘못된토큰/만료 시 issueDepositAddress 에 지정한 refundAddress 로 자동 환불되고,
216
+ * 세션 status(expired/unresolved) + webhook 으로 통지된다.
217
+ */
218
+ /**
219
+ * 멱등성 키 — 동일 키로 재시도 시 backend가 같은 결과 반환.
220
+ */
221
+ interface IdempotencyOptions {
222
+ idempotencyKey?: string;
223
+ }
224
+ interface ApiResponse<T> {
225
+ data: T;
226
+ error?: string;
227
+ }
228
+ interface ListResponse<T> {
229
+ data: T[];
230
+ total?: number;
231
+ }
232
+ interface CheckoutProduct {
233
+ /** prod_... */
234
+ id: string;
235
+ name: string;
236
+ description: string | null;
237
+ image_url: string | null;
238
+ /** 'active' | 'inactive' */
239
+ status: string;
240
+ metadata: Record<string, unknown>;
241
+ mode: string;
242
+ created_at: string;
243
+ updated_at: string;
244
+ }
245
+ interface CheckoutPrice {
246
+ /** price_... */
247
+ id: string;
248
+ /** prod_... */
249
+ product_id: string;
250
+ /** 원자 단위 정수 문자열 ($1 = "1000000") — 불변 */
251
+ amount_atomic: string;
252
+ currency: string;
253
+ token_symbol: string;
254
+ /** v1: 'one_time' */
255
+ billing_type: string;
256
+ nickname: string | null;
257
+ lookup_key: string | null;
258
+ mode: string;
259
+ created_at: string;
260
+ }
261
+ interface CreateProductParams {
262
+ name: string;
263
+ description?: string;
264
+ image_url?: string;
265
+ metadata?: Record<string, unknown>;
266
+ }
267
+ interface UpdateProductParams {
268
+ name?: string;
269
+ description?: string | null;
270
+ image_url?: string | null;
271
+ metadata?: Record<string, unknown>;
272
+ status?: 'active' | 'inactive';
273
+ }
274
+ /**
275
+ * 가격 생성 입력. 금액은 둘 중 **하나** (line item 과 동일 모델):
276
+ * - `amount`(권장): 사람 표기 USD 문자열(예 "29", "0.5"). decimal 신경 쓸 필요 없음.
277
+ * - `amount_atomic`: raw μUSD 정수($1="1000000") — 고급/기존.
278
+ * 둘 다 지정하거나 둘 다 생략하면 서버가 400 을 반환한다.
279
+ */
280
+ interface CreatePriceParams {
281
+ /** 사람 표기 USD 금액(예 "29", "0.5") — 권장. amount_atomic 과 상호배타. */
282
+ amount?: string;
283
+ /** raw μUSD 정수($1="1000000") — 고급/기존. amount 와 상호배타. */
284
+ amount_atomic?: string;
285
+ currency?: string;
286
+ token_symbol?: string;
287
+ billing_type?: 'one_time';
288
+ nickname?: string;
289
+ lookup_key?: string;
290
+ }
291
+ /**
292
+ * Price는 lifecycle 없는 immutable record — 변경 가능한 것은 nickname(표시 메타)뿐.
293
+ * 금액류 필드(amount_atomic 등)는 immutable, "그만 팔기"는 Price 가 아니라 그 Price 를 쓰는
294
+ * Payment Link 를 inactive 로 한다.
295
+ */
296
+ interface UpdatePriceParams {
297
+ nickname?: string | null;
298
+ }
299
+ interface ListProductsParams {
300
+ status?: 'active' | 'inactive';
301
+ limit?: number;
302
+ offset?: number;
303
+ }
304
+ interface ListPricesParams {
305
+ limit?: number;
306
+ offset?: number;
307
+ }
308
+ interface CheckoutPaymentLink {
309
+ /** plink_... */
310
+ id: string;
311
+ /** 고엔트로피 공개 토큰 — 호스티드 URL `{checkoutBase}/plink/{share_token}` 에 사용 */
312
+ share_token: string;
313
+ /** price_... — 이 링크가 파는 가격(생성 후 고정) */
314
+ price_id: string;
315
+ title: string | null;
316
+ description: string | null;
317
+ collect_email: boolean;
318
+ email_required: boolean;
319
+ /** CAIP-2 목록 또는 null(legacy) */
320
+ allowed_pay_chains: string[] | null;
321
+ /** chain×token cell 목록 또는 null */
322
+ allowed_assets: Array<{
323
+ caip2: string;
324
+ token: string;
325
+ }> | null;
326
+ success_url: string | null;
327
+ cancel_url: string | null;
328
+ /** 'active' | 'inactive' */
329
+ status: string;
330
+ max_uses: number | null;
331
+ paid_use_count: number;
332
+ expires_at: string | null;
333
+ mode: string;
334
+ created_at: string;
335
+ updated_at: string;
336
+ }
337
+ interface CreatePaymentLinkParams {
338
+ /** price_... — bitpal.checkout.prices.create() 로 만든 가격. 필수. */
339
+ price_id: string;
340
+ title?: string;
341
+ description?: string;
342
+ /** buyer 이메일 수집(기본 true) */
343
+ collect_email?: boolean;
344
+ /** buyer 이메일 필수(기본 true). true 면 collect_email 도 true 여야 함. */
345
+ email_required?: boolean;
346
+ /** 허용 결제 체인(CAIP-2 또는 short-name). allowed_assets 가 있으면 그쪽 우선. */
347
+ allowed_pay_chains?: string[];
348
+ /** chain×token cell 허용 목록 — allowed_pay_chains 보다 우선. */
349
+ allowed_assets?: Array<{
350
+ chain: string;
351
+ token: string;
352
+ }>;
353
+ success_url?: string;
354
+ cancel_url?: string;
355
+ max_uses?: number;
356
+ /** ISO-8601 datetime */
357
+ expires_at?: string;
358
+ metadata?: Record<string, unknown>;
359
+ }
360
+ interface UpdatePaymentLinkParams {
361
+ title?: string | null;
362
+ description?: string | null;
363
+ collect_email?: boolean;
364
+ email_required?: boolean;
365
+ allowed_pay_chains?: string[] | null;
366
+ allowed_assets?: Array<{
367
+ chain: string;
368
+ token: string;
369
+ }> | null;
370
+ success_url?: string | null;
371
+ cancel_url?: string | null;
372
+ status?: 'active' | 'inactive';
373
+ max_uses?: number | null;
374
+ expires_at?: string | null;
375
+ metadata?: Record<string, unknown>;
376
+ }
377
+ interface ListPaymentLinksParams {
378
+ status?: 'active' | 'inactive';
379
+ limit?: number;
380
+ offset?: number;
381
+ }
382
+
383
+ /**
384
+ * catalog.ts — Product / immutable Price 관리 리소스 (PROD-LINK-3a-SDK)
385
+ *
386
+ * server-side SDK 전용 (secret API key). 브라우저에서 사용 금지.
387
+ *
388
+ * 사용 예:
389
+ * ```ts
390
+ * const bitpal = new BitPal({ apiKey: 'bp_test_...' });
391
+ * const { data: product } = await bitpal.checkout.products.create({ name: 'Pro Plan' });
392
+ * const { data: price } = await bitpal.checkout.prices.create(product.id, {
393
+ * amount_atomic: toAtomicUSDC('29.00'),
394
+ * });
395
+ * ```
396
+ *
397
+ * Price immutable 원칙: 가격 변경은 update가 아니라 "새 price 생성"이다.
398
+ * `prices.update`는 nickname(표시 메타)만 받는다 — 금액류 필드 변경 helper는 의도적으로 없다.
399
+ * "그만 팔기"는 Price 를 끄는 게 아니라 그 Price 를 쓰는 Payment Link 를 inactive 로 한다.
400
+ */
401
+
402
+ /** client.ts의 private request를 리소스에 주입하기 위한 시그니처 */
403
+ type CatalogRequester = <T>(method: string, path: string, body?: unknown) => Promise<T>;
404
+ declare class ProductsResource {
405
+ private readonly request;
406
+ constructor(request: CatalogRequester);
407
+ /** POST /v1/checkout/products */
408
+ create(params: CreateProductParams): Promise<ApiResponse<CheckoutProduct>>;
409
+ /** GET /v1/checkout/products */
410
+ list(params?: ListProductsParams): Promise<ListResponse<CheckoutProduct>>;
411
+ /** GET /v1/checkout/products/:id */
412
+ retrieve(productId: string): Promise<ApiResponse<CheckoutProduct>>;
413
+ /** PATCH /v1/checkout/products/:id */
414
+ update(productId: string, params: UpdateProductParams): Promise<ApiResponse<CheckoutProduct>>;
415
+ }
416
+ declare class PricesResource {
417
+ private readonly request;
418
+ constructor(request: CatalogRequester);
419
+ /** POST /v1/checkout/products/:id/prices */
420
+ create(productId: string, params: CreatePriceParams): Promise<ApiResponse<CheckoutPrice>>;
421
+ /** GET /v1/checkout/products/:id/prices */
422
+ list(productId: string, params?: ListPricesParams): Promise<ListResponse<CheckoutPrice>>;
423
+ /** GET /v1/checkout/prices/:id */
424
+ retrieve(priceId: string): Promise<ApiResponse<CheckoutPrice>>;
425
+ /**
426
+ * PATCH /v1/checkout/prices/:id — nickname(표시 메타)만.
427
+ * 금액류 필드(amount_atomic 등)는 immutable — 서버가 PRICE_IMMUTABLE_FIELD로 거부한다.
428
+ * 가격 변경은 `prices.create()`로 새 price를 만들 것. "그만 팔기"는 해당 Price 를 쓰는
429
+ * Payment Link 를 inactive 로 한다(Price 자체에는 active/inactive 개념이 없다).
430
+ */
431
+ update(priceId: string, params: UpdatePriceParams): Promise<ApiResponse<CheckoutPrice>>;
432
+ }
433
+ /**
434
+ * Payment Link 리소스 — 대시보드 없이 코드로 결제 링크 생성/관리 (PROD-LINK-4).
435
+ *
436
+ * 흐름: product 생성 → price 생성 → payment link 생성. link↔price binding 은 생성 후 고정.
437
+ *
438
+ * ```ts
439
+ * const { data: product } = await bitpal.checkout.products.create({ name: 'Pro Plan' });
440
+ * const { data: price } = await bitpal.checkout.prices.create(product.id, { amount: '29' });
441
+ * const { data: link } = await bitpal.checkout.paymentLinks.create({
442
+ * price_id: price.id,
443
+ * allowed_pay_chains: ['eip155:8453', 'solana:5eykt4Us…'],
444
+ * });
445
+ * // 호스티드 결제 페이지 URL: `{checkoutBase}/plink/${link.share_token}`
446
+ * ```
447
+ */
448
+ declare class PaymentLinksResource {
449
+ private readonly request;
450
+ constructor(request: CatalogRequester);
451
+ /** POST /v1/checkout/payment-links */
452
+ create(params: CreatePaymentLinkParams): Promise<ApiResponse<CheckoutPaymentLink>>;
453
+ /** GET /v1/checkout/payment-links */
454
+ list(params?: ListPaymentLinksParams): Promise<ListResponse<CheckoutPaymentLink>>;
455
+ /** GET /v1/checkout/payment-links/:id */
456
+ retrieve(linkId: string): Promise<ApiResponse<CheckoutPaymentLink>>;
457
+ /** PATCH /v1/checkout/payment-links/:id */
458
+ update(linkId: string, params: UpdatePaymentLinkParams): Promise<ApiResponse<CheckoutPaymentLink>>;
459
+ }
460
+
461
+ /**
462
+ * @bitpal/checkout SDK — 클라이언트
463
+ *
464
+ * Phase 1 범위: 세션 생성/조회, 결제 링크 생성/조회, 수수료 미리보기.
465
+ * 크레딧 잔액/충전은 BitPal non-custodial 모델에서 제거됨.
466
+ */
467
+
468
+ declare class BitPalCheckoutClient {
469
+ /** 서버 메서드용 secret 키. 공개(buyer-facing) 메서드는 없이도 동작. */
470
+ private readonly apiKey;
471
+ private readonly baseUrl;
472
+ private readonly timeout;
473
+ /** PROD-LINK-3a-SDK: Product 카탈로그 관리 (server-side 전용) */
474
+ readonly products: ProductsResource;
475
+ /** PROD-LINK-3a-SDK: immutable Price 관리 — 가격 변경은 비활성화 + 새 price 생성 */
476
+ readonly prices: PricesResource;
477
+ /** PROD-LINK-4-SDK: Payment Link 관리 — 대시보드 없이 코드로 결제 링크 생성 (server-side 전용) */
478
+ readonly paymentLinks: PaymentLinksResource;
479
+ constructor(config: BitPalConfig);
480
+ private request;
481
+ /**
482
+ * 체크아웃 세션 생성.
483
+ *
484
+ * `line_items[].amount` 는 사람 표기 USD 문자열("29", "0.5") — decimal 계산 불필요.
485
+ * 이미 raw μUSD 정수라면 `amount_atomic`("29000000") 사용(고급). 둘은 상호 배타.
486
+ *
487
+ * @example
488
+ * import { BitPal } from '@bitpal/checkout';
489
+ * const bp = new BitPal('bp_test_...');
490
+ * const { data } = await bp.checkout.createSession({
491
+ * // amount: 사람 표기 USD("29", "0.5"). 금액은 USD 가격이라 체인/토큰 decimal 무관.
492
+ * line_items: [{ name: 'Pro', amount: '29', currency: 'USDC' }],
493
+ * });
494
+ * console.log(data.url); // → buyer를 redirect할 결제 페이지
495
+ *
496
+ * // 멀티체인 허용 — 구매자가 Base/Optimism 중 선택(하나뿐이면 자동 선택):
497
+ * // 어떤 체인·토큰이든 amount 그대로 — 서버가 발급 시 토큰 decimals 로 재스케일.
498
+ * await bp.checkout.createSession({
499
+ * line_items: [{ name: 'Pro', amount: '29', currency: 'USDC' }],
500
+ * allowed_pay_chains: ['eip155:84532', 'eip155:11155420'],
501
+ * });
502
+ * // 단일 체인 고정은 pay_chain 사용(둘은 상호 배타).
503
+ */
504
+ createSession(params: CreateSessionParams): Promise<ApiResponse<CheckoutSession>>;
505
+ /** 세션 조회 */
506
+ getSession(sessionId: string): Promise<ApiResponse<CheckoutSession>>;
507
+ /** 세션 목록 조회 */
508
+ listSessions(params?: {
509
+ status?: string;
510
+ limit?: number;
511
+ offset?: number;
512
+ }): Promise<ListResponse<CheckoutSession>>;
513
+ /**
514
+ * 공개 세션 조회 (`GET /v1/checkout/sessions/:id/public`) — 인증 불요.
515
+ *
516
+ * buyer-facing 결제 페이지가 표시/자산선택에 쓰는 서버 권위 뷰. `payment_options` 로
517
+ * 구매자 선택 가능한 chain×token 을 얻고, 발급 후에는 `deposit_address` 가 채워진다.
518
+ */
519
+ getPublicSession(sessionId: string): Promise<ApiResponse<PublicSession>>;
520
+ /**
521
+ * 주문별 비수탁 입금주소 발급 (`POST /v1/checkout/sessions/:id/deposit-address`).
522
+ *
523
+ * 안2 결제 진입점. 구매자가 자산(멀티옵션이면 chain/token)과 환불 주소를 확정하면 서버가
524
+ * 그 주문 전용 CREATE2 forwarder 주소를 돌려준다. 구매자는 아무 지갑/거래소에서 **정확한 금액**을
525
+ * 그 주소로 일반 송금하면 된다(서명·연결 불필요). watcher 가 감지 → keeper 가 정산.
526
+ *
527
+ * refundAddress 는 입금주소에 커밋되어 발급 후 변경 불가. session_token 은 createSession 응답값.
528
+ *
529
+ * @example
530
+ * ```ts
531
+ * const { data } = await bitpal.checkout.issueDepositAddress(sessionId, {
532
+ * session_token: session.session_token!,
533
+ * refundAddress: '0xBuyerWallet...',
534
+ * chain: 'eip155:8453', // 멀티옵션 세션만 필요
535
+ * token: 'USDC',
536
+ * });
537
+ * console.log(data.depositAddress, data.expectedAmount); // 이 주소로 정확 금액 송금
538
+ * ```
539
+ */
540
+ issueDepositAddress(sessionId: string, params: IssueDepositAddressParams): Promise<ApiResponse<DepositAddress>>;
541
+ /**
542
+ * 경량 상태 폴링 (`GET /v1/checkout/sessions/:id/status`) — 인증 불요.
543
+ *
544
+ * status: awaiting_deposit → deposit_detected → paid_confirmed/swept. 확정/정산은 webhook 으로도
545
+ * 통지되므로, 서버 사이드 확정은 webhook 을 신뢰(폴링은 buyer UX 용).
546
+ */
547
+ getStatus(sessionId: string): Promise<ApiResponse<SessionStatus>>;
548
+ /** 수수료 미리보기 — `/v1/checkout/fee-preview` (Phase 1 credit system 폐기 후 canonical path) */
549
+ previewFee(amount: string): Promise<ApiResponse<FeePreview>>;
550
+ }
551
+ declare class BitPalError extends Error {
552
+ readonly statusCode: number;
553
+ constructor(message: string, statusCode: number);
554
+ }
555
+
556
+ /**
557
+ * @bitpal/checkout — Webhook 서명 검증 + 이벤트 타입
558
+ *
559
+ * BitPal 이 보내는 canonical webhook contract(envelope/서명/헤더)와 1:1 정합한다.
560
+ */
561
+ /**
562
+ * BitPal 이 발송하는 webhook 이벤트 타입 전체 목록.
563
+ */
564
+ declare const CHECKOUT_WEBHOOK_EVENTS: readonly ["checkout.session.created", "checkout.session.deposit_detected", "checkout.session.paid", "checkout.session.settled", "checkout.session.expired", "checkout.session.unresolved", "checkout.refund.recorded"];
565
+ type CheckoutWebhookEvent = (typeof CHECKOUT_WEBHOOK_EVENTS)[number];
566
+ /**
567
+ * Canonical envelope (HTTP body).
568
+ * Backend `CanonicalWebhookEnvelope`와 동일 shape.
569
+ */
570
+ interface WebhookPayload {
571
+ /** event_id (replay 시 원본 보존) */
572
+ id: string;
573
+ /** canonical event name */
574
+ event: CheckoutWebhookEvent;
575
+ /** envelope 생성 시각 (ISO) */
576
+ created_at: string;
577
+ /** 이벤트별 payload */
578
+ data: Record<string, unknown>;
579
+ }
580
+ /** Backend가 보내는 canonical 헤더 이름 */
581
+ declare const WEBHOOK_HEADERS: {
582
+ readonly signature: "X-Webhook-Signature-256";
583
+ readonly timestamp: "X-Webhook-Timestamp";
584
+ readonly event: "X-Webhook-Event";
585
+ readonly eventId: "X-Webhook-Id";
586
+ readonly deliveryId: "X-Webhook-Delivery-Id";
587
+ };
588
+ /**
589
+ * Webhook signature 검증.
590
+ *
591
+ * Contract:
592
+ * - Backend는 `X-Webhook-Signature-256: sha256=<hex>` 형식으로 보냄
593
+ * - 서명 대상은 raw HTTP body (envelope JSON string, byte 단위 동일)
594
+ * - HMAC-SHA256(body, secret) hex와 timing-safe compare
595
+ *
596
+ * 본 함수는 두 형식 모두 허용하지 않고 canonical(`sha256=<hex>`)만 처리한다.
597
+ * raw hex만 받는 형식은 폐기됨 (backend 단일 contract).
598
+ *
599
+ * @param payload - HTTP request raw body (string)
600
+ * @param signature - `X-Webhook-Signature-256` 헤더 값 (`sha256=<hex>`)
601
+ * @param secret - webhook 등록 시 1회 노출된 raw secret (`whsec_...`)
602
+ * @returns true if signature matches
603
+ *
604
+ * @example
605
+ * ```ts
606
+ * import express from 'express';
607
+ * import { verifyWebhookSignature, parseWebhookEvent, WEBHOOK_HEADERS } from '@bitpal/checkout';
608
+ *
609
+ * app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
610
+ * const raw = req.body.toString('utf8'); // raw body — parse/re-serialize 하면 서명 깨짐
611
+ * const ok = await verifyWebhookSignature(
612
+ * raw,
613
+ * req.header(WEBHOOK_HEADERS.signature) ?? '',
614
+ * process.env.BITPAL_WEBHOOK_SECRET!,
615
+ * { timestamp: req.header(WEBHOOK_HEADERS.timestamp) ?? '' }, // replay 방어 — 필수
616
+ * );
617
+ * if (!ok) return res.status(400).send('invalid signature');
618
+ * const event = parseWebhookEvent(raw);
619
+ * // dedup 은 서명된 body 의 event.id 로 (헤더 X-Webhook-Id 는 미서명)
620
+ * if (await alreadyProcessed(event.id)) return res.status(200).send('dup');
621
+ * if (event.event === 'checkout.session.paid') { ... }
622
+ * res.status(200).send('ok');
623
+ * });
624
+ * ```
625
+ */
626
+ interface VerifyWebhookOptions {
627
+ /** `X-Webhook-Timestamp` 헤더 값(ISO UTC). 서명 대상에 포함되며 freshness 검사에 쓰인다. 필수. */
628
+ timestamp: string;
629
+ /** freshness 허용 오차(초). 기본 300(5분). |now - timestamp| 초과 시 replay 로 간주해 거부. */
630
+ toleranceSec?: number;
631
+ }
632
+ declare function verifyWebhookSignature(payload: string, signature: string, secret: string, opts?: VerifyWebhookOptions): Promise<boolean>;
633
+ /**
634
+ * Webhook envelope 파싱.
635
+ * 형식 검증 후 `WebhookPayload` 타입으로 캐스팅.
636
+ *
637
+ * @throws Error 형식이 envelope이 아닐 때
638
+ */
639
+ declare function parseWebhookEvent(body: string): WebhookPayload;
640
+
641
+ /**
642
+ * amount.ts — atomic ↔ decimal 변환 헬퍼 (임의 decimals + USDC 하위호환)
643
+ *
644
+ * BitPal API는 모든 amount를 atomic 정수 문자열로 받는다. decimals 는 토큰마다 다르다:
645
+ * USDC/USDT(Base/Arb/Tron/Solana) = 6, BSC-USD(BNB USDT) = 18. → payment_options[].decimals /
646
+ * PublicSession.token_decimals 로 서버가 내려준다. 6dp 를 하드코딩하면 non-6dp 토큰에서 금액이 틀린다.
647
+ *
648
+ * 정밀도 손실 방지를 위해 BigInt 연산만 사용한다 (Number 변환 금지 — CLAUDE.md 정책).
649
+ * $1(6dp) → "1000000" $1(18dp) → "1000000000000000000"
650
+ */
651
+ /**
652
+ * decimal 문자열 → atomic 문자열 (임의 decimals).
653
+ *
654
+ * @param decimal 소수 문자열(음수 불가, 소수 자리 ≤ decimals)
655
+ * @param decimals 토큰 decimals (예: 6, 18). payment_options[].decimals 사용.
656
+ * @throws {Error} 음수, decimals 초과 소수 자리, 형식 오류
657
+ * @example toAtomic('29.00', 6) // '29000000' toAtomic('1.5', 18) // '1500000000000000000'
658
+ */
659
+ declare function toAtomic(decimal: string, decimals: number): string;
660
+ /**
661
+ * atomic 문자열 → 사람이 읽기 좋은 decimal 문자열 (임의 decimals).
662
+ * 최소 2자리 소수를 유지하되(예: '29.00'), decimals 만큼만 노출.
663
+ *
664
+ * @throws {Error} 정수 문자열이 아니면
665
+ * @example fromAtomic('29000000', 6) // '29.00' fromAtomic('1500000000000000000', 18) // '1.50'
666
+ */
667
+ declare function fromAtomic(atomic: string, decimals: number): string;
668
+ /**
669
+ * decimal → atomic μUSDC (6dp). 하위호환 별칭 — 새 코드는 `toAtomic(decimal, decimals)` 권장
670
+ * (BNB USDT=18dp 등 non-6dp 토큰은 이 헬퍼로 처리 불가).
671
+ * @example toAtomicUSDC('29.00') // '29000000'
672
+ */
673
+ declare function toAtomicUSDC(decimal: string): string;
674
+ /**
675
+ * atomic μUSDC(6dp) → decimal. 하위호환 별칭 — 새 코드는 `fromAtomic(atomic, decimals)` 권장.
676
+ * @example fromAtomicUSDC('29000000') // '29.00'
677
+ */
678
+ declare function fromAtomicUSDC(atomic: string): string;
679
+
680
+ /**
681
+ * @bitpal/checkout — BitPal Checkout SDK
682
+ *
683
+ * 체크아웃 세션 생성, 결제 링크 관리, 수수료 미리보기를 위한 공식 SDK
684
+ *
685
+ * @example
686
+ * ```ts
687
+ * import { BitPal } from '@bitpal/checkout';
688
+ *
689
+ * const bitpal = new BitPal({ apiKey: 'bp_test_...' });
690
+ *
691
+ * const { data: session } = await bitpal.checkout.createSession({
692
+ * // amount: 사람 표기 USD("29","0.5"). decimal 계산 불필요 — 체인/토큰 decimal 무관.
693
+ * line_items: [{ name: 'Pro Plan', amount: '29', currency: 'USDC' }],
694
+ * });
695
+ *
696
+ * console.log(session.url); // 호스티드 결제 페이지 URL (checkout_url 별칭)
697
+ * ```
698
+ */
699
+
700
+ /**
701
+ * BitPal SDK 메인 엔트리
702
+ */
703
+ declare class BitPal {
704
+ readonly checkout: BitPalCheckoutClient;
705
+ constructor(config: BitPalConfig | string);
706
+ }
707
+
708
+ export { type ApiResponse, BitPal, BitPalCheckoutClient, type BitPalConfig, BitPalError, CHECKOUT_WEBHOOK_EVENTS, type CheckoutLineItemInput, type CheckoutPaymentLink, type CheckoutPrice, type CheckoutProduct, type CheckoutSession, type CheckoutWebhookEvent, type CreatePaymentLinkParams, type CreatePriceParams, type CreateProductParams, type CreateSessionParams, type DepositAddress, type FeePreview, type IdempotencyOptions, type IssueDepositAddressParams, type LineItem, type ListPaymentLinksParams, type ListPricesParams, type ListProductsParams, type ListResponse, PaymentLinksResource, type PaymentOption, type PriceLineItem, PricesResource, ProductsResource, type PublicSession, type SessionStatus, type UpdatePaymentLinkParams, type UpdatePriceParams, type UpdateProductParams, WEBHOOK_HEADERS, type WebhookPayload, fromAtomic, fromAtomicUSDC, parseWebhookEvent, toAtomic, toAtomicUSDC, verifyWebhookSignature };