@sayren/storefront-sdk 0.7.0 → 0.9.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.
@@ -1,4 +1,4 @@
1
- import { gt as ApiError, t as createStorefrontClient } from "../client-B1uXGefY.mjs";
1
+ import { it as ApiError, t as createStorefrontClient } from "../client-D9iQIWnV.mjs";
2
2
  //#region src/auth/index.ts
3
3
  /** 소셜 로그인 실패 — 구매자가 취소했거나(`access_denied`) state가 맞지 않는다 */
4
4
  var IdpCallbackError = class extends Error {
@@ -0,0 +1,189 @@
1
+ import { z } from "zod";
2
+ //#region src/schemas/checkout.ts
3
+ const createCheckoutRequestSchema = z.object({
4
+ cartItemIds: z.array(z.string()).min(1).optional(),
5
+ directItem: z.object({
6
+ productId: z.string(),
7
+ optionId: z.string().optional(),
8
+ quantity: z.number().int().min(1)
9
+ }).optional()
10
+ }).refine((value) => value.cartItemIds ? !value.directItem : !!value.directItem, { message: "cartItemIds와 directItem 중 하나만 전달해야 합니다" });
11
+ const checkoutSessionSchema = z.object({
12
+ checkoutId: z.string(),
13
+ items: z.array(z.object({
14
+ productId: z.string(),
15
+ productName: z.string(),
16
+ /** variantId. 옵션 없는 상품은 default variant id */
17
+ optionId: z.string().nullable(),
18
+ optionName: z.string().nullable(),
19
+ quantity: z.number().int(),
20
+ unitPrice: z.number().int(),
21
+ totalPrice: z.number().int()
22
+ })),
23
+ amounts: z.object({
24
+ productAmount: z.number().int(),
25
+ deliveryFee: z.number().int(),
26
+ totalAmount: z.number().int()
27
+ }),
28
+ expiresAt: z.string(),
29
+ paymentOptions: z.lazy(() => paymentOptionListSchema).describe("이 주문서에서 고를 수 있는 결제 옵션, PG 우선순위순. 셀러가 켠 PG·결제수단·간편결제만 담긴다. 비어 있으면 지금 결제할 수 없다"),
30
+ testPayment: z.boolean().describe("지금 결제하면 테스트 결제(샌드박스)인가. true면 실제로 돈이 나가지 않아요 — 주문서에 테스트 결제 안내를 표시해요. 결제 요청 시점에 스토어 결제 설정으로 다시 정해지니 최종 값은 결제 요청 응답의 `testPayment`예요")
31
+ });
32
+ const paymentMethodSchema = z.enum([
33
+ "CARD",
34
+ "BANK_TRANSFER",
35
+ "VIRTUAL_ACCOUNT",
36
+ "MOBILE",
37
+ "EASY_PAY"
38
+ ]);
39
+ /** 결제를 처리하는 PG. 셀러가 직접 계약하고 콘솔 설정 › 결제에서 연결한다 */
40
+ const pgProviderSchema = z.enum(["tosspayments", "portone"]);
41
+ /** 간편결제사. 토스페이먼츠·포트원 모두 같은 코드를 쓴다 */
42
+ const easyPayProviderSchema = z.enum([
43
+ "NAVERPAY",
44
+ "KAKAOPAY",
45
+ "TOSSPAY",
46
+ "PAYCO"
47
+ ]);
48
+ /**
49
+ * 응답에 실리는 간편결제사 — 서버가 간편결제사를 추가해도 파싱이 깨지지 않게 문자열로 받는다
50
+ * (`pgProvider`·결제 상태와 같은 방식). 요청과 결제 옵션은 값을 골라 보내는 자리라 enum 그대로다.
51
+ */
52
+ const easyPayProviderResponseSchema = z.string();
53
+ const pg = pgProviderSchema.describe("결제를 처리할 PG");
54
+ /**
55
+ * 결제 옵션 — 어느 PG로 어떤 결제수단을 쓸지. `method`로 좁히면 필요한 필드가 타입으로 정해진다
56
+ * (`EASY_PAY`만 `provider`가 있다). 주문서의 `paymentOptions` 항목을 그대로 넘기거나 직접 써도 된다.
57
+ * 셀러가 켜지 않은 조합이면 서버가 409 `PAYMENT_OPTION_UNAVAILABLE`로 거절한다.
58
+ */
59
+ const paymentOptionSchema = z.discriminatedUnion("method", [
60
+ z.object({
61
+ pg,
62
+ method: z.literal("CARD")
63
+ }),
64
+ z.object({
65
+ pg,
66
+ method: z.literal("EASY_PAY"),
67
+ provider: easyPayProviderSchema.describe("간편결제사 — 이 간편결제 결제창을 바로 연다")
68
+ }),
69
+ z.object({
70
+ pg,
71
+ method: z.literal("BANK_TRANSFER")
72
+ }),
73
+ z.object({
74
+ pg,
75
+ method: z.literal("VIRTUAL_ACCOUNT")
76
+ }),
77
+ z.object({
78
+ pg,
79
+ method: z.literal("MOBILE")
80
+ })
81
+ ]);
82
+ /** 주문서가 내려주는 결제 옵션 — 결제 옵션 + 표시 이름 */
83
+ const availablePaymentOptionSchema = paymentOptionSchema.and(z.object({
84
+ label: z.string().describe("결제수단 표시 이름 (예: 신용·체크카드, 네이버페이)"),
85
+ pgName: z.string().describe("PG 표시 이름 (예: 토스페이먼츠)")
86
+ }));
87
+ /**
88
+ * 결제 옵션 목록 — 서버가 나중에 추가한 결제수단·PG를 이 SDK가 모르면 **그 항목만 빼고** 읽는다(목록 파싱은 깨지지 않는다).
89
+ *
90
+ * 모르는 항목을 버리는 일은 `preprocess`가 하고 항목 타입은 그대로 남는다 — OpenAPI 문서에 `items` 없는
91
+ * 배열로 나가지 않게 하기 위해서다(`z.array(z.unknown()).transform(...)`은 출력 타입을 표현하지 못한다).
92
+ */
93
+ const paymentOptionListSchema = z.preprocess((value) => Array.isArray(value) ? value.filter((item) => availablePaymentOptionSchema.safeParse(item).success) : value, z.array(availablePaymentOptionSchema));
94
+ /** 결제 옵션을 화면 키로 — 라디오 value·React key. 예: `tosspayments:CARD`, `portone:EASY_PAY:NAVERPAY` */
95
+ function paymentOptionKey(option) {
96
+ return option.method === "EASY_PAY" ? `${option.pg}:EASY_PAY:${option.provider}` : `${option.pg}:${option.method}`;
97
+ }
98
+ const shippingAddressInputSchema = z.object({
99
+ addressId: z.string().optional(),
100
+ receiverName: z.string().min(1, "수령인명을 입력해주세요").max(50),
101
+ phone: z.string().regex(/^01[0-9]{8,9}$/, "연락처 형식이 아닙니다 (예: 01012345678)"),
102
+ zipCode: z.string().regex(/^[0-9]{5}$/, "우편번호는 5자리 숫자입니다"),
103
+ address1: z.string().min(1, "기본 주소를 입력해주세요"),
104
+ address2: z.string().optional(),
105
+ deliveryMemo: z.string().max(100).optional(),
106
+ entranceCode: z.string().max(20).optional()
107
+ });
108
+ const guestInfoSchema = z.object({
109
+ name: z.string().min(1),
110
+ phone: z.string().regex(/^01[0-9]{8,9}$/),
111
+ email: z.email(),
112
+ orderPassword: z.string().min(6, "주문 조회 비밀번호는 6자 이상이어야 합니다")
113
+ });
114
+ /**
115
+ * 결제 시작 — 배송지를 확정하고 고른 결제 옵션으로 결제 시도를 연다. 응답의 `payUrl`(결제 서비스)을 팝업(`mode=popup`)이나
116
+ * 전체 페이지로 열면 결제 서비스가 PG 결제창을 띄우고 승인까지 한다. 리다이렉트 결제가 끝나면 구매자는 `returnUrl`로 돌아온다.
117
+ * `returnUrl`의 도메인은 스토어 결제 도메인(콘솔 설정 › 결제)에 등록돼 있어야 한다(테스트 결제는 localhost 허용).
118
+ */
119
+ const startPaymentRequestSchema = z.object({
120
+ option: paymentOptionSchema,
121
+ returnUrl: z.url().max(2048).describe("결제를 마치면 돌아올 스토어프론트 주소. 스토어 결제 도메인에 등록된 도메인이어야 한다"),
122
+ shippingAddress: shippingAddressInputSchema,
123
+ guest: guestInfoSchema.optional()
124
+ });
125
+ /** 같은 결제에서 다른 결제 옵션으로 다시 시도. `returnUrl`을 생략하면 결제 시작 때의 복귀 주소를 쓴다 */
126
+ const retryPaymentRequestSchema = z.object({
127
+ option: paymentOptionSchema,
128
+ returnUrl: startPaymentRequestSchema.shape.returnUrl.optional()
129
+ });
130
+ const paymentStartSchema = z.object({
131
+ paymentId: z.string().describe("결제 id. 상태 조회·재시도의 키다"),
132
+ attemptId: z.string().describe("결제 시도 id. PG 주문번호(토스 orderId, 포트원 paymentId)다"),
133
+ option: paymentOptionSchema.describe("이 시도의 결제 옵션"),
134
+ payUrl: z.string().describe("결제 서비스 주소. 팝업으로 열려면 쿼리 `mode=popup`을 붙이고, 그대로 이동하면 리다이렉트 결제다"),
135
+ expiresAt: z.string().describe("결제 요청 만료 시각"),
136
+ testPayment: z.boolean().describe("테스트 결제(샌드박스) 여부. true면 실제로 돈이 나가지 않는다 — 결제 화면에 테스트 결제 안내를 표시한다")
137
+ });
138
+ /** 결제창에서 결제하지 못한 이유 */
139
+ const paymentFailureCategorySchema = z.enum([
140
+ "PREPARE_FAILURE",
141
+ "DECLINED",
142
+ "USER_CANCELED"
143
+ ]);
144
+ /**
145
+ * 결제 상태 조회의 현재 상태 값 — 응답 스키마는 서버가 상태를 추가해도 파싱이 깨지지 않게 문자열로 받는다
146
+ * (`pgProvider`와 같은 방식). 화면 분기는 이 목록으로 하고 모르는 값은 확인 중으로 다룬다.
147
+ */
148
+ const PAYMENT_STATUS_VALUES = [
149
+ "pending",
150
+ "processing",
151
+ "completed",
152
+ "failed",
153
+ "expired"
154
+ ];
155
+ /** 결제 결과 구분 — 결제 서비스가 스토어프론트에 돌려주는 값과 같다 */
156
+ const PAYMENT_RESULT_VALUES = [
157
+ "COMPLETED",
158
+ "PROCESSING",
159
+ "CANCELED",
160
+ "FAILED",
161
+ "EXPIRED",
162
+ "PENDING"
163
+ ];
164
+ const paymentStatusSchema = z.object({
165
+ paymentId: z.string(),
166
+ status: z.string().describe("결제 상태. 현재 값은 `pending` 결제 대기 · `processing` 승인 확인 중 · `completed` 결제 완료(주문 생성) · `failed` 실패 · `expired` 만료예요. 상태가 추가될 수 있어 문자열로 받아요 — 모르는 값은 `processing`처럼 다루고 계속 조회하세요"),
167
+ result: z.string().describe("화면 분기용 결과. `COMPLETED` 결제 완료 · `PROCESSING` 결과 확인 중 · `CANCELED` 구매자가 결제창을 닫음 · `FAILED` 결제 실패(다른 결제 옵션으로 재시도 가능) · `EXPIRED` 결제 요청 만료 · `PENDING` 결제 전이에요. 값이 추가될 수 있어 문자열로 받아요 — 모르는 값은 `PROCESSING`처럼 다루세요"),
168
+ orderId: z.string().nullable().describe("결제가 완료됐으면 주문 번호, 그 외에는 null"),
169
+ lastFailure: z.object({
170
+ attemptId: z.string(),
171
+ category: z.string().describe("`PREPARE_FAILURE`·`DECLINED`·`USER_CANCELED`(값이 추가될 수 있다)"),
172
+ code: z.string().nullable().describe("PG가 준 실패 코드. 내부 사유는 null이에요. 화면 분기는 `category`로 하세요"),
173
+ message: z.string().nullable().describe("구매자에게 보여 줄 안내 문구")
174
+ }).nullable().describe("현재 결제 시도가 실패·취소로 끝났으면 그 내용, 아니면 null"),
175
+ testPayment: z.boolean().describe("테스트 결제(샌드박스)인가. true면 실제로 돈이 나가지 않았어요"),
176
+ expiresAt: z.string().describe("결제 요청 만료 시각")
177
+ });
178
+ /**
179
+ * 결제 서비스가 팝업 opener에 보내는 메시지 — `type`으로 거르고 결과의 원천은 결제 상태 조회다.
180
+ * 안내 문구는 싣지 않는다. 화면 문구는 상태 조회의 `lastFailure`를 쓴다
181
+ */
182
+ const paymentMessageSchema = z.object({
183
+ type: z.literal("sayren:payment"),
184
+ paymentId: z.string(),
185
+ result: z.string(),
186
+ orderId: z.string().optional()
187
+ });
188
+ //#endregion
189
+ export { pgProviderSchema as _, createCheckoutRequestSchema as a, startPaymentRequestSchema as b, guestInfoSchema as c, paymentMethodSchema as d, paymentOptionKey as f, paymentStatusSchema as g, paymentStartSchema as h, checkoutSessionSchema as i, paymentFailureCategorySchema as l, paymentOptionSchema as m, PAYMENT_STATUS_VALUES as n, easyPayProviderResponseSchema as o, paymentOptionListSchema as p, availablePaymentOptionSchema as r, easyPayProviderSchema as s, PAYMENT_RESULT_VALUES as t, paymentMessageSchema as u, retryPaymentRequestSchema as v, shippingAddressInputSchema as y };
@@ -1,4 +1,5 @@
1
1
  import { a as ANALYTICS_VISITOR_HEADER, r as ANALYTICS_SESSION_HEADER } from "./analytics-CgD70OqH.mjs";
2
+ import { a as createCheckoutRequestSchema, b as startPaymentRequestSchema, d as paymentMethodSchema, g as paymentStatusSchema, h as paymentStartSchema, i as checkoutSessionSchema, o as easyPayProviderResponseSchema, v as retryPaymentRequestSchema } from "./checkout-JFk3b8RG.mjs";
2
3
  import { z } from "zod";
3
4
  //#region src/http.ts
4
5
  /** 표준 엔벨로프의 $.error 상세 */
@@ -218,7 +219,7 @@ const cartItemSchema = z.object({
218
219
  cartItemId: z.string(),
219
220
  productId: z.string(),
220
221
  productName: z.string(),
221
- thumbnailUrl: z.url(),
222
+ thumbnailUrl: z.url().nullable().describe("담은 상품의 대표 이미지. 없으면 null"),
222
223
  optionId: z.string().nullable(),
223
224
  optionName: z.string().nullable(),
224
225
  quantity: z.number().int(),
@@ -291,7 +292,7 @@ const categoryNodeSchema = z.lazy(() => z.object({
291
292
  const productCardSchema = z.object({
292
293
  productId: z.string(),
293
294
  name: z.string(),
294
- thumbnailUrl: z.url(),
295
+ thumbnailUrl: z.url().nullable().describe("대표 이미지 주소. 등록된 이미지가 없으면 null"),
295
296
  salePrice: z.number().int().describe("판매가. 원(KRW) 단위 정수"),
296
297
  discountedPrice: z.number().int().nullable().describe("즉시 할인 적용가. 원 단위. 할인이 없으면 null"),
297
298
  discountRate: z.number().int().nullable().describe("할인율(%). 할인이 없으면 null"),
@@ -364,113 +365,6 @@ const productFacetsSchema = z.object({
364
365
  /** `GET /products` 응답 — 상품 카드 페이지와 검색 패싯 */
365
366
  const productPageSchema = pageSchema(productCardSchema).extend({ facets: productFacetsSchema.optional() });
366
367
  //#endregion
367
- //#region src/schemas/checkout.ts
368
- const createCheckoutRequestSchema = z.object({
369
- cartItemIds: z.array(z.string()).min(1).optional(),
370
- directItem: z.object({
371
- productId: z.string(),
372
- optionId: z.string().optional(),
373
- quantity: z.number().int().min(1)
374
- }).optional()
375
- }).refine((value) => value.cartItemIds ? !value.directItem : !!value.directItem, { message: "cartItemIds와 directItem 중 하나만 전달해야 합니다" });
376
- const checkoutSessionSchema = z.object({
377
- checkoutId: z.string(),
378
- items: z.array(z.object({
379
- productId: z.string(),
380
- productName: z.string(),
381
- /** variantId. 옵션 없는 상품은 default variant id */
382
- optionId: z.string().nullable(),
383
- optionName: z.string().nullable(),
384
- quantity: z.number().int(),
385
- unitPrice: z.number().int(),
386
- totalPrice: z.number().int()
387
- })),
388
- amounts: z.object({
389
- productAmount: z.number().int(),
390
- deliveryFee: z.number().int(),
391
- totalAmount: z.number().int()
392
- }),
393
- expiresAt: z.string(),
394
- testPayment: z.boolean().describe("지금 결제하면 테스트 결제(샌드박스)인가. true면 실제로 돈이 나가지 않아요 — 주문서에 테스트 결제 안내를 표시해요. 결제 요청 시점에 스토어 결제 설정으로 다시 정해지니 최종 값은 결제 요청 응답의 `testPayment`예요")
395
- });
396
- const paymentMethodSchema = z.enum([
397
- "CARD",
398
- "BANK_TRANSFER",
399
- "VIRTUAL_ACCOUNT",
400
- "MOBILE",
401
- "EASY_PAY"
402
- ]);
403
- const shippingAddressInputSchema = z.object({
404
- addressId: z.string().optional(),
405
- receiverName: z.string().min(1, "수령인명을 입력해주세요").max(50),
406
- phone: z.string().regex(/^01[0-9]{8,9}$/, "연락처 형식이 아닙니다 (예: 01012345678)"),
407
- zipCode: z.string().regex(/^[0-9]{5}$/, "우편번호는 5자리 숫자입니다"),
408
- address1: z.string().min(1, "기본 주소를 입력해주세요"),
409
- address2: z.string().optional(),
410
- deliveryMemo: z.string().max(100).optional(),
411
- entranceCode: z.string().max(20).optional()
412
- });
413
- const guestInfoSchema = z.object({
414
- name: z.string().min(1),
415
- phone: z.string().regex(/^01[0-9]{8,9}$/),
416
- email: z.email(),
417
- orderPassword: z.string().min(6, "주문 조회 비밀번호는 6자 이상이어야 합니다")
418
- });
419
- const requestPaymentRequestSchema = z.object({
420
- shippingAddress: shippingAddressInputSchema,
421
- paymentMethod: paymentMethodSchema,
422
- guest: guestInfoSchema.optional()
423
- });
424
- /**
425
- * 결제창 파라미터. 스토어프론트가 쓰는 계약 값은 `popupUrl` 하나다(결제 팝업을 연다).
426
- *
427
- * 나머지는 결제 팝업 내부 값이라 선택 필드로만 타입을 준다(`attemptId`·`provider`·`mock`, 그리고
428
- * 토스 `clientKey`·`orderId`…, 포트원 `storeId`·`channelKey`… 같은 PG별 결제창 값). 공개 값뿐이며
429
- * 시크릿은 어떤 키에도 없다. 서버가 키를 추가·변경해도 파싱이 깨지지 않도록 모르는 키는 그대로 통과시킨다.
430
- */
431
- const pgParamsSchema = z.looseObject({
432
- popupUrl: z.string().describe("결제 팝업 진입 URL. `window.open`으로 띄운다"),
433
- attemptId: z.string().optional().describe("첫 결제 시도 id(`att_…`). PG에 보내는 주문번호(토스 `orderId`, 포트원 `paymentId`)다. 팝업 내부 값"),
434
- provider: z.string().optional().describe("첫 결제 시도의 PG (`pgProvider`와 같다). 팝업 내부 값"),
435
- mock: z.boolean().optional().describe("모의 결제 여부. true면 결제 팝업이 PG SDK 대신 모의 결제 UI를 띄운다. 팝업 내부 값"),
436
- environment: z.string().optional().describe("첫 결제 시도의 결제 환경 (`TEST` 샌드박스 · `LIVE` 실결제). 팝업 내부 값")
437
- });
438
- const paymentParamsSchema = z.object({
439
- paymentId: z.string(),
440
- pgProvider: z.string().describe("첫 결제 시도의 PG. 현재 값은 `tosspayments`·`portone`이다. PG가 추가될 수 있어 문자열로 받는다"),
441
- pgParams: pgParamsSchema,
442
- expiresAt: z.string(),
443
- testPayment: z.boolean().describe("테스트 결제(샌드박스) 여부. true면 실제로 돈이 나가지 않는다 — 결제 화면에 테스트 결제 안내를 표시한다")
444
- });
445
- const confirmPaymentRequestSchema = z.object({
446
- pgToken: z.string().min(1).describe("PG 승인 키 (토스 `paymentKey`, 포트원 `txId`)"),
447
- attemptId: z.string().min(1).optional().describe("승인할 결제 시도 id (결제창이 돌려준 주문번호). 생략하면 현재 시도")
448
- });
449
- const confirmPaymentResultSchema = z.object({
450
- orderId: z.string(),
451
- status: z.literal("PAID"),
452
- totalAmount: z.number().int(),
453
- paidAt: z.string()
454
- });
455
- /**
456
- * 결제 상태 조회의 현재 상태 값 — 응답 스키마는 서버가 상태를 추가해도 파싱이 깨지지 않게 문자열로 받는다
457
- * (`pgProvider`와 같은 방식). 화면 분기는 이 목록으로 하고 모르는 값은 확인 중으로 다룬다.
458
- */
459
- const PAYMENT_STATUS_VALUES = [
460
- "pending",
461
- "processing",
462
- "completed",
463
- "failed",
464
- "expired"
465
- ];
466
- const paymentStatusSchema = z.object({
467
- paymentId: z.string(),
468
- status: z.string().describe("결제 상태. 현재 값은 `pending` 결제 대기 · `processing` 승인 확인 중 · `completed` 결제 완료(주문 생성) · `failed` 실패 · `expired` 만료예요. 상태가 추가될 수 있어 문자열로 받아요 — 모르는 값은 `processing`처럼 다루고 계속 조회하세요"),
469
- orderId: z.string().nullable().describe("결제가 완료됐으면 주문 번호, 그 외에는 null"),
470
- testPayment: z.boolean().describe("테스트 결제(샌드박스)인가. true면 실제로 돈이 나가지 않았어요"),
471
- expiresAt: z.string().describe("결제 요청 만료 시각")
472
- });
473
- //#endregion
474
368
  //#region src/schemas/claim.ts
475
369
  const claimTypeSchema = z.enum([
476
370
  "CANCEL",
@@ -609,7 +503,7 @@ const myOrderItemSchema = z.object({
609
503
  orderItemId: z.string(),
610
504
  productId: z.string(),
611
505
  productName: z.string(),
612
- thumbnailUrl: z.url(),
506
+ thumbnailUrl: z.url().nullable().describe("주문 시점의 대표 이미지. 없었으면 null"),
613
507
  optionName: z.string().nullable(),
614
508
  quantity: z.number().int(),
615
509
  totalPrice: z.number().int(),
@@ -638,6 +532,7 @@ const myOrderSchema = z.object({
638
532
  shippingAddress: orderShippingAddressSchema,
639
533
  payment: z.object({
640
534
  method: paymentMethodSchema.describe("실제 결제수단(PG가 보고한 수단, 모르면 주문서에서 고른 수단). 결제창에서 바뀔 수 있다(카드 결제창의 간편결제 탭 등)"),
535
+ easyPayProvider: easyPayProviderResponseSchema.nullable().optional().default(null).describe("간편결제사. `method`가 `EASY_PAY`일 때만 값이 있고, PG가 알려 주지 않았으면 주문서에서 고른 간편결제사다. 그 밖의 결제수단과 간편결제 이전 주문은 null. 서버는 항상 싣는다. 현재 값은 `NAVERPAY`·`KAKAOPAY`·`TOSSPAY`·`PAYCO`이고 값이 추가될 수 있어 문자열로 받는다"),
641
536
  totalAmount: z.number().int(),
642
537
  paidAt: z.string().nullable(),
643
538
  receiptUrl: z.url().nullable()
@@ -690,7 +585,7 @@ const writableReviewSchema = z.object({
690
585
  productId: z.string(),
691
586
  productName: z.string(),
692
587
  optionName: z.string().nullable(),
693
- thumbnailUrl: z.url(),
588
+ thumbnailUrl: z.url().nullable().describe("주문 시점의 대표 이미지. 없었으면 null"),
694
589
  writableUntil: z.string()
695
590
  });
696
591
  const createReviewRequestSchema = z.object({
@@ -827,43 +722,34 @@ get: () => http.request("GET", "/store", storefrontStoreSchema) },
827
722
  checkout: {
828
723
  create: (body) => http.request("POST", "/checkout", checkoutSessionSchema, { body: createCheckoutRequestSchema.parse(body) }),
829
724
  /**
830
- * 결제 시작 — 결제 세션을 만들고 결제 팝업 URL(`pgParams.popupUrl`)을 돌려준다.
831
- * `pgProvider`는 첫 결제 시도의 PG다(현재 `tosspayments`·`portone`, 새 PG가 추가돼도 파싱은 깨지지 않는다).
725
+ * `POST /checkout/{checkoutId}/payment` — 결제 시작. 배송지와 결제 옵션(`paymentOptions` 항목 또는 직접 쓴 옵션)으로
726
+ * 결제 세션·첫 시도를 만들고 결제 서비스 주소(`payUrl`)를 돌려준다. 그 주소를 열어 결제를 진행하는 것은
727
+ * `@sayren/storefront-sdk/payments`의 `createPayments().start()`다 — 이 메서드는 값만 받는다.
832
728
  *
833
- * PG 관련 에러(`ApiError.code`) — 둘 다 "결제 수단 준비 중" 안내를 권장한다.
834
- * - 409 `PAYMENT_NOT_CONFIGURED`: 스토어에 사용 중인 PG가 없다. 결제 세션을 만들지 않는다
835
- * - 503 `PAYMENT_PROVIDER_UNAVAILABLE`: 사용 중인 PG가 모두 준비 장애다. 잠시 후 재시도할 수 있다
729
+ * 에러(`ApiError.code`):
730
+ * - 409 `PAYMENT_OPTION_UNAVAILABLE`: 이 스토어에서 쓸 수 없는 옵션. `details.paymentOptions`에 지금 목록
731
+ * - 409 `PAYMENT_NOT_CONFIGURED`: 사용 중인 PG가 없다
732
+ * - 400 `RETURN_URL_NOT_ALLOWED`: `returnUrl`이 https가 아니거나(테스트 결제의 localhost는 예외),
733
+ * 스토어가 결제 도메인을 등록했는데 그 안이 아니다. 등록하지 않았으면 https 주소는 모두 받는다
734
+ * - 503 `PAYMENT_PROVIDER_UNAVAILABLE`: 고른 PG의 결제창을 준비하지 못했다(다른 옵션으로 다시 시도)
836
735
  */
837
- requestPayment: (checkoutId, body) => http.request("POST", path`/checkout/${checkoutId}/payment`, paymentParamsSchema, { body: requestPaymentRequestSchema.parse(body) }),
736
+ startPayment: (checkoutId, body) => http.request("POST", path`/checkout/${checkoutId}/payment`, paymentStartSchema, { body: startPaymentRequestSchema.parse(body) })
737
+ },
738
+ payments: {
838
739
  /**
839
- * 결제 승인 확정 — 같은 `paymentId + pgToken` 재호출은 저장된 결과를 돌려준다(멱등).
840
- * `options.attemptId`는 결제창이 돌려준 주문번호(시도 id)이고, 생략하면 현재 시도를 승인한다.
740
+ * `GET /storefront/v1/payments/{paymentId}` — 결제 상태 조회. 복귀 화면에서 결과가 확인 중(`processing`)이면
741
+ * `expiresAt`까지 폴링한다. `completed`면 `orderId`로 완료 화면, `failed`·`expired`면 실패 안내다.
742
+ * TTL이 지난 `pending`은 `expired`로 보고된다. `testPayment`는 완료면 주문 플래그, 아니면 세션 환경이다.
841
743
  *
842
- * 요청 본문은 보내기 전에 `confirmPaymentRequestSchema`로 검증한다. 빈 `pgToken`처럼 형식이 틀리면
843
- * 요청을 보내지 않고 **동기로 ZodError를 던진다**(Promise reject 아님 — 다른 메서드의 요청 검증과 같다).
844
- *
845
- * 카드 거절(402 `PG_DECLINED` 등)·402 `PAYMENT_NOT_APPROVED`·502 `PG_REQUEST_FAILED`·
846
- * 503 `PG_CREDENTIALS_UNAVAILABLE` 뒤에도 세션은 `pending`으로 남아 다시 시도할 수 있다.
847
- * 409 `LATE_APPROVAL_CANCELED`(미승인으로 판정된 뒤 늦게 온 승인을 서버가 PG 취소함)도 세션이 `pending`으로 남는다.
848
- * 409 `PAYMENT_RESULT_UNKNOWN`은 결과 확인 중이라 재시도하면 안 된다. 그 밖의 409:
849
- * `ATTEMPT_NOT_CURRENT`·`INVALID_ATTEMPT_STATE`·`PAYMENT_SESSION_OUTDATED`·`PG_AMOUNT_MISMATCH`.
850
- * 503 `PG_MERCHANT_MISMATCH`: 승인 시도의 가맹점과 현재 저장된 자격 증명의 가맹점이 다르다.
744
+ * 에러: 404 `PAYMENT_NOT_FOUND`(없음·다른 스토어) · 410 `STORE_CLOSED` · 503 `STORE_SUSPENDED`
745
+ */
746
+ getStatus: (paymentId) => http.request("GET", path`/payments/${paymentId}`, paymentStatusSchema),
747
+ /**
748
+ * `POST /payments/{paymentId}/attempts` — 같은 결제에서 다른 결제 옵션으로 다시 시도한다. 새 시도의 결제창 값을
749
+ * 돌려준다. 승인 중이거나 결과 확인 중이면 409 `PAYMENT_IN_PROGRESS`다.
851
750
  */
852
- confirmPayment: (paymentId, pgToken, options) => http.request("POST", path`/payments/${paymentId}/confirm`, confirmPaymentResultSchema, { body: confirmPaymentRequestSchema.parse({
853
- pgToken,
854
- attemptId: options?.attemptId
855
- }) })
751
+ retry: (paymentId, body) => http.request("POST", path`/payments/${paymentId}/attempts`, paymentStartSchema, { body: retryPaymentRequestSchema.parse(body) })
856
752
  },
857
- payments: {
858
- /**
859
- * `GET /storefront/v1/payments/{paymentId}` — 결제 상태 조회. 결제 팝업이 결과(postMessage)를 알리지 못하고 닫혔을 때
860
- * 부모 창이 `expiresAt`까지 폴링해 결과를 확인한다. `completed`면 `orderId`로 완료 화면, `processing`이면 계속 폴링,
861
- * `pending`이면 기존 안내, `failed`·`expired`면 실패 안내다. TTL이 지난 `pending`은 `expired`로 보고된다.
862
- * `testPayment`는 완료면 주문 플래그, 아니면 세션 환경이다.
863
- *
864
- * 에러: 404 `PAYMENT_NOT_FOUND`(없음·다른 스토어) · 410 `STORE_CLOSED` · 503 `STORE_SUSPENDED`
865
- */
866
- getStatus: (paymentId) => http.request("GET", path`/payments/${paymentId}`, paymentStatusSchema) },
867
753
  myOrders: {
868
754
  list: (params) => http.request("GET", "/me/orders", pageSchema(myOrderSchema), { query: params }),
869
755
  get: (orderId) => http.request("GET", path`/me/orders/${orderId}`, myOrderSchema),
@@ -902,4 +788,4 @@ getStatus: (paymentId) => http.request("GET", path`/payments/${paymentId}`, paym
902
788
  };
903
789
  }
904
790
  //#endregion
905
- export { pageSchema as $, createClaimResultSchema as A, paymentStatusSchema as B, publicInquirySchema as C, claimReasonSchema as D, customerInquirySchema as E, confirmPaymentResultSchema as F, productCardSchema as G, requestPaymentRequestSchema as H, createCheckoutRequestSchema as I, productPageSchema as J, productDetailSchema as K, guestInfoSchema as L, PAYMENT_STATUS_VALUES as M, checkoutSessionSchema as N, claimTypeSchema as O, confirmPaymentRequestSchema as P, pageInfoSchema as Q, paymentMethodSchema as R, createInquiryResultSchema as S, customerInquiryCategorySchema as T, shippingAddressInputSchema as U, pgParamsSchema as V, categoryNodeSchema as W, claimStatusSchema as X, productSortSchema as Y, orderItemStatusSchema as Z, memberAddressSchema as _, buildQuery as _t, publicReviewSchema as a, idpAuthorizeRequestSchema as at, wishlistAddResultSchema as b, updateReviewRequestSchema as c, linkIdentityRequestSchema as ct, deliveryTrackingSchema as d, signupRequestSchema as dt, addCartItemRequestSchema as et, myOrderItemSchema as f, socialProviderSchema as ft, memberAddressRequestSchema as g, ApiError as gt, purchaseDecisionResultSchema as h, withdrawalBlockedDetailsSchema as ht, createReviewResultSchema as i, anonymousSessionSchema as it, myClaimSchema as j, createClaimRequestSchema as k, updateReviewResultSchema as l, loginRequestSchema as lt, orderShippingAddressSchema as m, withdrawRequestSchema as mt, storefrontStoreSchema as n, cartSchema as nt, reviewPageSchema as o, idpAuthorizeResponseSchema as ot, myOrderSchema as p, tokenPairSchema as pt, productFacetsSchema as q, createReviewRequestSchema as r, updateCartItemRequestSchema as rt, reviewSummarySchema as s, idpTokenRequestSchema as st, createStorefrontClient as t, cartItemSchema as tt, writableReviewSchema as u, memberIdentitiesSchema as ut, memberSchema as v, toApiError as vt, createCustomerInquiryRequestSchema as w, createInquiryRequestSchema as x, updateProfileRequestSchema as y, unwrapData as yt, paymentParamsSchema as z };
791
+ export { signupRequestSchema as $, createClaimResultSchema as A, pageInfoSchema as B, publicInquirySchema as C, claimReasonSchema as D, customerInquirySchema as E, productFacetsSchema as F, updateCartItemRequestSchema as G, addCartItemRequestSchema as H, productPageSchema as I, idpAuthorizeResponseSchema as J, anonymousSessionSchema as K, productSortSchema as L, categoryNodeSchema as M, productCardSchema as N, claimTypeSchema as O, productDetailSchema as P, memberIdentitiesSchema as Q, claimStatusSchema as R, createInquiryResultSchema as S, customerInquiryCategorySchema as T, cartItemSchema as U, pageSchema as V, cartSchema as W, linkIdentityRequestSchema as X, idpTokenRequestSchema as Y, loginRequestSchema as Z, memberAddressSchema as _, publicReviewSchema as a, buildQuery as at, wishlistAddResultSchema as b, updateReviewRequestSchema as c, deliveryTrackingSchema as d, socialProviderSchema as et, myOrderItemSchema as f, memberAddressRequestSchema as g, purchaseDecisionResultSchema as h, createReviewResultSchema as i, ApiError as it, myClaimSchema as j, createClaimRequestSchema as k, updateReviewResultSchema as l, orderShippingAddressSchema as m, storefrontStoreSchema as n, withdrawRequestSchema as nt, reviewPageSchema as o, toApiError as ot, myOrderSchema as p, idpAuthorizeRequestSchema as q, createReviewRequestSchema as r, withdrawalBlockedDetailsSchema as rt, reviewSummarySchema as s, unwrapData as st, createStorefrontClient as t, tokenPairSchema as tt, writableReviewSchema as u, memberSchema as v, createCustomerInquiryRequestSchema as w, createInquiryRequestSchema as x, updateProfileRequestSchema as y, orderItemStatusSchema as z };