@sayren/store-sdk 0.29.3 → 0.29.5

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.
package/dist/index.mjs CHANGED
@@ -1970,7 +1970,7 @@ const COLLECTION_SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
1970
1970
  /** 유형 — `MANUAL`(직접 선택)·`AUTOMATIC`(조건 자동) */
1971
1971
  const COLLECTION_TYPES = ["MANUAL", "AUTOMATIC"];
1972
1972
  /**
1973
- * 상품 정렬 — `MANUAL`은 직접 정한 순서(직접 선택 컬렉션만)다. `BEST_SELLING`(판매량순)은 판매량 집계가 열릴 때 받는다.
1973
+ * 상품 정렬 — `MANUAL`은 직접 정한 순서(직접 선택 컬렉션만)다. `BEST_SELLING`(판매량순)은 판매 수량이 많은 순이다(M3).
1974
1974
  * 저장 값 전체 목록이고, 쓰기에서 받는 값은 `collectionProductSortRequestSchema`다.
1975
1975
  */
1976
1976
  const COLLECTION_PRODUCT_SORTS = [
@@ -1986,12 +1986,13 @@ const COLLECTION_PRODUCT_SORTS = [
1986
1986
  const collectionProductSortRequestSchema = z.enum([
1987
1987
  "MANUAL",
1988
1988
  "LATEST",
1989
+ "BEST_SELLING",
1989
1990
  "PRICE_ASC",
1990
1991
  "PRICE_DESC",
1991
1992
  "REVIEW_COUNT",
1992
1993
  "RATING_DESC",
1993
1994
  "RECOMMEND"
1994
- ]).describe("스토어프론트 상품 기본 정렬. `MANUAL`(직접 정한 순서)·`LATEST`(최신순)·`PRICE_ASC`·`PRICE_DESC`·`REVIEW_COUNT`·`RATING_DESC`·`RECOMMEND`(추천순)");
1995
+ ]).describe("스토어프론트 상품 기본 정렬. `MANUAL`(직접 정한 순서)·`LATEST`(최신순)·`BEST_SELLING`(판매량순)·`PRICE_ASC`·`PRICE_DESC`·`REVIEW_COUNT`·`RATING_DESC`·`RECOMMEND`(추천순). 판매량순의 기간은 판매량 상위 조건의 기간이고, 조건이 없으면 최근 30일이다");
1995
1996
  /** 노출 상태 — 노출 여부와 기간에서 나온 값이다. 숨김이 기간보다 앞선다 */
1996
1997
  const collectionStateSchema = z.enum([
1997
1998
  "ACTIVE",
@@ -2015,6 +2016,16 @@ const MAX_COLLECTION_RULE_CATEGORIES = 20;
2015
2016
  const MAX_COLLECTION_CREATED_WITHIN_DAYS = 365;
2016
2017
  /** 미리보기(`POST /v1/collections/preview`)가 싣는 상품 최대 개수 */
2017
2018
  const COLLECTION_PREVIEW_SIZE = 20;
2019
+ /** 판매량 상위 조건의 기간 선택지(일, 오늘 포함 한국 날짜) */
2020
+ const COLLECTION_BEST_SELLING_DAYS = [
2021
+ 7,
2022
+ 30,
2023
+ 90
2024
+ ];
2025
+ /** 판매량 상위 조건이 담는 상품 최대 개수 */
2026
+ const MAX_COLLECTION_BEST_SELLING_TOP = 100;
2027
+ /** 판매량순(`BEST_SELLING`) 정렬의 기간 — 판매량 상위 조건이 없을 때 쓴다 */
2028
+ const COLLECTION_SALES_SORT_DEFAULT_DAYS = 30;
2018
2029
  const createdWithinDaysRuleSchema = z.object({
2019
2030
  field: z.literal("createdWithinDays"),
2020
2031
  value: z.number().int().min(1).max(365).describe("등록한 지 N일 안의 상품(1~365). 조회 시각에서 N일을 뺀 시각 이후에 등록한 상품이다")
@@ -2032,15 +2043,26 @@ const discountedRuleSchema = z.object({
2032
2043
  field: z.literal("discounted"),
2033
2044
  value: z.literal(true).describe("즉시할인이 지금 적용되는 상품")
2034
2045
  });
2046
+ const bestSellingRuleSchema = z.object({
2047
+ field: z.literal("bestSelling"),
2048
+ days: z.union([
2049
+ z.literal(7),
2050
+ z.literal(30),
2051
+ z.literal(90)
2052
+ ]).describe("판매량을 세는 기간(일). 7·30·90 중 하나이고 오늘을 포함한 한국 날짜 기준이다"),
2053
+ top: z.number().int().min(1).max(100).describe("담을 상품 수(1~100). 다른 조건을 만족하는 상품 중 판매 수량 상위 N개다")
2054
+ });
2035
2055
  /**
2036
2056
  * 조건 하나 — `field`로 가른다. 가격은 즉시할인을 반영한 판매가(`discountedPrice ?? salePrice`)다.
2037
- * 판매량 상위(`bestSelling`)는 판매량 집계가 열릴 때 더한다.
2057
+ * 판매량 상위(`bestSelling`)는 술어가 아니라 순위 자르기다. 다른 조건을 모두 만족한 상품 중 판매 수량 상위 N개만 남긴다(`rankBestSelling`).
2058
+ * 판매 수량은 실결제 주문의 결제 수량에서 취소·반품 수량을 뺀 값이고, 날짜는 주문 시각의 한국 날짜다. 집계는 최대 10분 늦다.
2038
2059
  */
2039
2060
  const collectionRuleSchema = z.discriminatedUnion("field", [
2040
2061
  createdWithinDaysRuleSchema,
2041
2062
  categoryRuleSchema,
2042
2063
  priceRuleSchema,
2043
- discountedRuleSchema
2064
+ discountedRuleSchema,
2065
+ bestSellingRuleSchema
2044
2066
  ]).superRefine((rule, ctx) => {
2045
2067
  if (rule.field !== "price") return;
2046
2068
  if (rule.min === void 0 && rule.max === void 0) ctx.addIssue({
@@ -2074,6 +2096,7 @@ const collectionRuleViewSchema = z.union([
2074
2096
  categoryRuleSchema,
2075
2097
  priceRuleSchema,
2076
2098
  discountedRuleSchema,
2099
+ bestSellingRuleSchema,
2077
2100
  z.looseObject({ field: z.string() })
2078
2101
  ]);
2079
2102
  const DAY_MS = 864e5;
@@ -2098,6 +2121,7 @@ function descendantCategoryIdsOf(roots, categories) {
2098
2121
  /**
2099
2122
  * 조건 판정 함수를 만든다 — 모든 조건을 만족해야 true다(AND). 카테고리 트리는 여기서 한 번만 푼다.
2100
2123
  * 알 수 없는 조건(`field`)은 만족하지 않는 것으로 본다. 모르는 조건을 건너뛰면 상품이 예고 없이 늘어난다.
2124
+ * 판매량 상위(`bestSelling`)는 여기서 모두 통과시킨다. 순위 자르기는 판매량을 아는 호출부가 `rankBestSelling`으로 한다.
2101
2125
  * 판매 상태는 보지 않는다. 판매 중만 거르는 것은 호출부다.
2102
2126
  */
2103
2127
  function collectionRulesPredicate(rules, context) {
@@ -2119,6 +2143,7 @@ function collectionRulesPredicate(rules, context) {
2119
2143
  return (parsed.min === void 0 || price >= parsed.min) && (parsed.max === void 0 || price <= parsed.max);
2120
2144
  };
2121
2145
  case "discounted": return (product) => product.discountedPrice !== null && product.discountedPrice < product.salePrice;
2146
+ case "bestSelling": return () => true;
2122
2147
  default: return () => false;
2123
2148
  }
2124
2149
  });
@@ -2128,6 +2153,31 @@ function collectionRulesPredicate(rules, context) {
2128
2153
  function matchesCollectionRules(product, rules, context) {
2129
2154
  return collectionRulesPredicate(rules, context)(product);
2130
2155
  }
2156
+ /** 판매량 상위 조건 — 없으면 null. 같은 조건은 한 번만 쓰므로 첫 번째다 */
2157
+ function bestSellingRuleOf(rules) {
2158
+ for (const rule of rules) {
2159
+ const parsed = bestSellingRuleSchema.safeParse(rule);
2160
+ if (parsed.success) return {
2161
+ days: parsed.data.days,
2162
+ top: parsed.data.top
2163
+ };
2164
+ }
2165
+ return null;
2166
+ }
2167
+ /**
2168
+ * 판매량 비교 — 판매 수량이 많은 순, 같으면 최근 등록 순, 그것도 같으면 상품 id 순이다. 결과가 입력 순서에 기대지 않는다.
2169
+ * `sales`에 없는 상품은 0개다.
2170
+ */
2171
+ function compareBySales(sales) {
2172
+ return (a, b) => (sales.get(b.productId) ?? 0) - (sales.get(a.productId) ?? 0) || Date.parse(b.createdAt) - Date.parse(a.createdAt) || (a.productId < b.productId ? -1 : a.productId > b.productId ? 1 : 0);
2173
+ }
2174
+ /**
2175
+ * 판매량 상위 자르기 — 다른 조건을 통과한 상품 중 판매 수량이 1개 이상인 상품을 `compareBySales` 순으로 `top`개 남긴다.
2176
+ * 판매가 없는 상품은 넣지 않는다.
2177
+ */
2178
+ function rankBestSelling(products, sales, top) {
2179
+ return products.filter((product) => (sales.get(product.productId) ?? 0) > 0).sort(compareBySales(sales)).slice(0, Math.max(0, top));
2180
+ }
2131
2181
  /** 조건이 참조하는 카테고리 id — 카테고리 삭제의 참조 검사에 쓴다 */
2132
2182
  function collectionRuleCategoryIds(rules) {
2133
2183
  const ids = [];
@@ -2153,6 +2203,7 @@ const collectionSchema = z.object({
2153
2203
  metaTitle: z.string().nullable(),
2154
2204
  metaDescription: z.string().nullable(),
2155
2205
  state: collectionStateSchema.describe("지금 노출 상태"),
2206
+ salesComputedAt: z.string().nullable().default(null).describe("판매량 기준 시각. 판매량 상위 조건이나 판매량순 정렬을 쓰는 컬렉션은 이 시각까지의 주문을 센다(최대 10분 늦다). 판매량을 쓰지 않거나 아직 집계 전이면 null"),
2156
2207
  productCount: z.number().int().describe("상품 수. 직접 선택은 담긴 상품 수(판매 상태 무관), 조건 자동은 지금 조건을 만족하는 판매 중 상품 수"),
2157
2208
  revision: z.number().int().describe("수정 번호. 쓰기마다 1씩 오른다. 수정 본문에 주면 그 사이 다른 수정이 있을 때 409다"),
2158
2209
  createdAt: z.string(),
@@ -2182,7 +2233,7 @@ const periodRefine = (body, ctx) => {
2182
2233
  /**
2183
2234
  * `POST /v1/collections` — 컬렉션을 만든다. `slug`를 생략하면 `c-{id 뒤 8자}`로 만든다.
2184
2235
  * 직접 선택(`MANUAL`)은 `productIds`가 처음 담을 상품과 순서다. 조건 자동(`AUTOMATIC`)은 `rules`가 있어야 하고 `productIds`를 받지 않는다.
2185
- * `productSort`를 생략하면 직접 선택은 `MANUAL`, 조건 자동은 `LATEST`다. 조건 자동은 `MANUAL`을 쓸 수 없다.
2236
+ * `productSort`를 생략하면 직접 선택은 `MANUAL`, 조건 자동은 `LATEST`(판매량 상위 조건이 있으면 `BEST_SELLING`)다. 조건 자동은 `MANUAL`을 쓸 수 없다.
2186
2237
  */
2187
2238
  const collectionCreateRequestSchema = z.object({
2188
2239
  title: z.string().trim().min(1).max(60).describe("제목"),
@@ -2191,7 +2242,7 @@ const collectionCreateRequestSchema = z.object({
2191
2242
  rules: collectionRulesSchema.optional().describe("조건 자동 컬렉션의 조건(AND). 조건 자동이면 필수, 직접 선택이면 줄 수 없다"),
2192
2243
  description: commonFields.description.default(""),
2193
2244
  imageUrl: commonFields.imageUrl.optional(),
2194
- productSort: commonFields.productSort.optional().describe("스토어프론트 상품 기본 정렬. 생략하면 직접 선택은 `MANUAL`, 조건 자동은 `LATEST`다. 조건 자동은 `MANUAL`을 쓸 수 없다"),
2245
+ productSort: commonFields.productSort.optional().describe("스토어프론트 상품 기본 정렬. 생략하면 직접 선택은 `MANUAL`, 조건 자동은 `LATEST`(판매량 상위 조건이 있으면 `BEST_SELLING`)다. 조건 자동은 `MANUAL`을 쓸 수 없다"),
2195
2246
  visible: commonFields.visible.default(true),
2196
2247
  startsAt: commonFields.startsAt,
2197
2248
  endsAt: commonFields.endsAt,
@@ -2265,12 +2316,13 @@ const collectionProductListQuerySchema = z.object({ ...pageQueryShape({
2265
2316
  /** `POST /v1/collections/preview` — 저장하지 않고 조건으로 나오는 상품을 본다 */
2266
2317
  const collectionPreviewRequestSchema = z.object({
2267
2318
  rules: collectionRulesSchema,
2268
- productSort: collectionProductSortRequestSchema.optional().describe("미리보기 순서. 생략하거나 `MANUAL`이면 `LATEST`다")
2319
+ productSort: collectionProductSortRequestSchema.optional().describe("미리보기 순서. 생략하면 `LATEST`(판매량 상위 조건이 있으면 `BEST_SELLING`)이고, `MANUAL`이면 `LATEST`다")
2269
2320
  });
2270
2321
  /** 미리보기 응답 — 조건을 만족하는 판매 중 상품 수와 앞 20개 */
2271
2322
  const collectionPreviewSchema = z.object({
2272
2323
  count: z.number().int().describe("조건을 만족하는 판매 중 상품 수(조회 시각 기준)"),
2273
- products: z.array(collectionProductSchema).describe("앞 20개(정렬 순)")
2324
+ products: z.array(collectionProductSchema).describe("앞 20개(정렬 순)"),
2325
+ salesComputedAt: z.string().nullable().default(null).describe("판매량 기준 시각. 판매량 상위 조건이나 판매량순 정렬을 쓰면 이 시각까지의 주문을 센다. 판매량을 쓰지 않거나 집계 전이면 null")
2274
2326
  });
2275
2327
  //#endregion
2276
2328
  //#region src/schemas/promotion.ts
@@ -2678,8 +2730,10 @@ function quotePricing(input) {
2678
2730
  };
2679
2731
  }
2680
2732
  /**
2681
- * 사용 적립금 금액(순수) — 요청·잔액·최대 비율·「결제 금액 100원 남기기」 중 작은 값을 단위로 내린다.
2682
- * 최소 보유액·최소 사용액에 못 미치면 0이다. 결제 금액이 0원이 되는 사용(적립금 전액 결제)은 아직 받지 않는다(P4).
2733
+ * 사용 적립금 금액(순수) — 요청·잔액·최대 비율 중 작은 값을 쓴다(#93 P3·P4).
2734
+ * - 결제 금액 전체를 덮을 수 있으면 전액을 쓴다(0원 결제, 이때만 사용 단위를 보지 않는다)
2735
+ * - 그 밖에는 사용 단위로 내리고, 남는 결제 금액이 1~99원이면 적립금을 줄여 100원을 남긴다(`MIN_PAYMENT`)
2736
+ * 최소 보유액·최소 사용액에 못 미치면 0이다.
2683
2737
  */
2684
2738
  function pointUseOf(points, amounts) {
2685
2739
  const none = {
@@ -2693,12 +2747,29 @@ function pointUseOf(points, amounts) {
2693
2747
  const unit = Math.max(1, Math.floor(rules.unit));
2694
2748
  const base = amounts.productPaid + (rules.includeDeliveryFee ? amounts.deliveryPaid : 0);
2695
2749
  const byRatio = Math.floor(base * rules.maxRatioBp / 1e4);
2696
- const byPayment = Math.max(0, amounts.total - 100);
2697
2750
  const balance = Math.max(0, points.balance);
2751
+ const total = Math.max(0, amounts.total);
2698
2752
  const floorUnit = (value) => Math.floor(Math.max(0, value) / unit) * unit;
2753
+ /** 상한 `cap`으로 쓸 수 있는 금액 — 전액이면 0원 결제, 아니면 단위 내림 + 100원 남기기 */
2754
+ const usableWithin = (cap) => {
2755
+ if (total > 0 && cap >= total) return {
2756
+ amount: total,
2757
+ adjusted: false
2758
+ };
2759
+ const amount = floorUnit(Math.min(cap, total));
2760
+ const remaining = total - amount;
2761
+ if (remaining > 0 && remaining < 100) return {
2762
+ amount: floorUnit(total - 100),
2763
+ adjusted: true
2764
+ };
2765
+ return {
2766
+ amount,
2767
+ adjusted: false
2768
+ };
2769
+ };
2699
2770
  const eligible = balance >= rules.minBalance;
2700
- const ceiling = Math.min(balance, byRatio, byPayment);
2701
- const maxUsable = eligible && floorUnit(ceiling) >= rules.minAmount ? floorUnit(ceiling) : 0;
2771
+ const ceiling = usableWithin(Math.min(balance, byRatio)).amount;
2772
+ const maxUsable = eligible && ceiling > 0 && ceiling >= rules.minAmount ? ceiling : 0;
2702
2773
  const requested = Math.max(0, Math.floor(points.requested));
2703
2774
  if (requested === 0) return {
2704
2775
  ...none,
@@ -2710,23 +2781,23 @@ function pointUseOf(points, amounts) {
2710
2781
  adjustReason: null,
2711
2782
  rejectReason: "MIN_BALANCE"
2712
2783
  };
2713
- const amount = floorUnit(Math.min(requested, balance, byRatio, byPayment));
2714
- if (amount <= 0) return {
2784
+ const used = usableWithin(Math.min(requested, balance, byRatio));
2785
+ if (used.amount <= 0) return {
2715
2786
  amount: 0,
2716
2787
  maxUsable,
2717
2788
  adjustReason: null,
2718
2789
  rejectReason: "NOT_PAYABLE"
2719
2790
  };
2720
- if (amount < rules.minAmount) return {
2791
+ if (used.amount < rules.minAmount) return {
2721
2792
  amount: 0,
2722
2793
  maxUsable,
2723
2794
  adjustReason: null,
2724
2795
  rejectReason: "MIN_AMOUNT"
2725
2796
  };
2726
2797
  return {
2727
- amount,
2798
+ amount: used.amount,
2728
2799
  maxUsable,
2729
- adjustReason: floorUnit(Math.min(requested, balance, byRatio)) > amount ? "MIN_PAYMENT" : null,
2800
+ adjustReason: used.adjusted ? "MIN_PAYMENT" : null,
2730
2801
  rejectReason: null
2731
2802
  };
2732
2803
  }
@@ -7378,4 +7449,4 @@ function createStoreSession(options) {
7378
7449
  };
7379
7450
  }
7380
7451
  //#endregion
7381
- export { APPROVAL_ALREADY_DECIDED_CODE, APPROVAL_REQUIRED_CODE, ApiError, ApprovalPendingError, BUSINESS_INFO_MAX_LENGTH, BUSINESS_NUMBER_PATTERN, CASH_RECEIPT_STATUS_VALUES, COLLECTION_PREVIEW_SIZE, COLLECTION_PRODUCT_SORTS, COLLECTION_SLUG_PATTERN, COLLECTION_TYPES, COUPON_CODE_PATTERN, COUPON_CONDITIONS_DEFAULTS, COUPON_TARGET_ALL, COUPON_TARGET_LIST_MAX, CUSTOM_INPUT_FORBIDDEN_WORDS, FULFILLMENT_RULES, MAX_ADDON_GROUPS, MAX_ANALYTICS_ALLOWED_ORIGINS, MAX_AUTO_TRANSITION_DAYS, MAX_COLLECTIONS, MAX_COLLECTION_CREATED_WITHIN_DAYS, MAX_COLLECTION_DESCRIPTION, MAX_COLLECTION_PRODUCTS, MAX_COLLECTION_PRODUCTS_PER_ADD, MAX_COLLECTION_RULES, MAX_COLLECTION_RULE_CATEGORIES, MAX_COLLECTION_TITLE, MAX_CUSTOM_INPUTS, MAX_CUSTOM_INPUT_LENGTH, MAX_OPTION_GROUPS, MAX_PRODUCT_IMAGES, MAX_SAVED_OPTION_SETS, MAX_SAVED_OPTION_SET_NAME, MAX_VALUES_PER_GROUP, MAX_VARIANTS, MCP_TELEMETRY_MAX_RESULTS, MIN_PAYMENT_AMOUNT, NO_COUPON_SETTLEMENT, NO_SHIPPING_BUNDLE_KEY, PAYMENT_ROUTING_EXCLUSION_CODES, POINT_ADJUSTMENT_MAX, POINT_EARN_RATE_BP_MAX, POINT_ENTRY_TYPES, POINT_EXPIRY_DAYS_MAX, POINT_EXPIRY_DAYS_MIN, POINT_LOT_SOURCES, POINT_POLICY_DEFAULTS, POINT_REVIEW_REWARD_MAX, POINT_USE_UNITS, PORTONE_CHANNEL_NAME_MAX_LENGTH, PORTONE_MAX_CHANNELS, PORTONE_MIN_CHANNELS, REFUND_POLICY_DEFAULTS, REFUND_POLICY_DELIVERY_FEE_KEYS, REMOTE_AREA_ZIP_RANGES, RESERVED_STOREFRONT_SLUGS, RESERVED_STORE_CODES, SHIPPING_POLICY_DEFAULTS, STOREFRONT_BLOCKED_PATH_SEGMENTS, STOREFRONT_BUILD_STAGES, STOREFRONT_BUILD_TERMINAL_STATUSES, STOREFRONT_CHECK_TERMINAL_STATUSES, STOREFRONT_CUSTOM_DOMAIN_LIMIT_DEFAULT, STOREFRONT_DOMAIN_DNS_GRACE_DAYS, STOREFRONT_DOMAIN_DNS_WAIT_DAYS, STOREFRONT_DOMAIN_TXT_LABEL, STOREFRONT_EDIT_ACTIVITY_TIMEOUT_MINUTES, STOREFRONT_EDIT_CAPACITY, STOREFRONT_EDIT_IDLE_MINUTES, STOREFRONT_EDIT_PREVIEW_SUFFIX, STOREFRONT_EDIT_START_STAGES, STOREFRONT_FILE_BATCH_MAX, STOREFRONT_FILE_MAX_BYTES, STOREFRONT_PATH_SEGMENT_PATTERN, STOREFRONT_PLATFORM_DOMAINS, STOREFRONT_PUBLIC_SUFFIXES, STOREFRONT_SEARCH_REGEX_MAX_LENGTH, STOREFRONT_SHOWCASE_SLUG_PREFIX, STOREFRONT_SITE_CREATE_LIMIT, STOREFRONT_SLUG_CHANGE_LIMIT, STOREFRONT_SLUG_CHANGE_WINDOW_DAYS, STOREFRONT_SLUG_HOLD_DAYS, STOREFRONT_SLUG_PATTERN, STOREFRONT_SOURCE_BLOB_BATCH_MAX, STOREFRONT_SOURCE_PART_BYTES, STOREFRONT_WORKSPACE_MAX_BYTES, STOREFRONT_WORKSPACE_MAX_FILES, STORE_CODE_PATTERN, STORE_ROLE_SCOPES, STORE_SCOPE_RESOURCES, STORE_SESSION_KEYS, TOSS_WIDGET_CLIENT_KEY_PATTERN, TOSS_WIDGET_KEY_MESSAGE, TOSS_WIDGET_SECRET_KEY_PATTERN, UPLOAD_PURPOSE_LIMITS, VARIANT_COUNT_WARNING, VARIANT_STOCK_EXCLUSIVE_MESSAGE, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_FAILURE_REASONS, WEBHOOK_HEADERS, WEBHOOK_PAYLOAD_EXCERPT_MAX_BYTES, WEBHOOK_RESOURCE_TYPES, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, addStorefrontDomainRequestSchema, addonGroupRequestSchema, addonGroupSchema, addonValueRequestSchema, addonValueSchema, addressChangeRequestListSchema, addressTypeSchema, agentPolicyDecisionSchema, allocateByWeight, analyticsChannelSchema, analyticsCheckoutOutcomeSchema, analyticsCheckoutSchema, analyticsDeviceRowSchema, analyticsDevicesSchema, analyticsFunnelStepSchema, analyticsOriginSchema, analyticsOverviewSchema, analyticsPagesQuerySchema, analyticsPagesSchema, analyticsProductRowSchema, analyticsProductSortSchema, analyticsProductsQuerySchema, analyticsProductsSchema, analyticsRangeQuerySchema, analyticsRealtimeSchema, analyticsSearchQuerySchema, analyticsSearchSchema, analyticsSourceGroupBySchema, analyticsSourceRowSchema, analyticsSourcesQuerySchema, analyticsSourcesSchema, analyticsTotalsSchema, apiLogErrorSchema, apiLogListQuerySchema, apiLogPageSchema, apiLogPrincipalSchema, apiLogPrincipalTypeSchema, apiLogSchema, apiLogStatusFilterSchema, apiLogSummarySchema, approvalDecisionRequestSchema, approvalListQuerySchema, approvalPageSchema, approvalRequestSchema, approvalStatusSchema, approveClaimResultSchema, auditActorSchema, auditActorTypeSchema, auditLogListQuerySchema, auditLogOutcomeSchema, auditLogPageSchema, auditLogSchema, auditLogSourceSchema, buildAuthorizeUrl, buildQuery, bulkResultSchema, businessInfoPatchShape, businessInfoResponseShape, businessNumberSchema, cancelBySellerRequestSchema, cancelBySellerResultSchema, carrierSchema, cashReceiptSchema, categoryAttributeSchema, categoryCreateRequestSchema, categoryDeleteQuerySchema, categoryDeleteStrategySchema, categoryListQuerySchema, categoryPatchRequestSchema, categoryProductSortSchema, categorySchema, categorySearchResultSchema, categoryTreeNodeSchema, categoryWithPathSchema, changeStorefrontSlugRequestSchema, changeStorefrontTemplateRequestSchema, changeTypeSchema, claimDetailSchema, claimHoldResultSchema, claimListQuerySchema, claimReasonSchema, claimReturnAddressSchema, claimSchema, claimStatusSchema, claimTypeSchema, claimWebhookEventDataSchema, claimWebhookEventSchema, collectionCreateRequestSchema, collectionListQuerySchema, collectionPageSchema, collectionPatchRequestSchema, collectionPreviewRequestSchema, collectionPreviewSchema, collectionProductListQuerySchema, collectionProductSchema, collectionProductSortRequestSchema, collectionProductsAddRequestSchema, collectionProductsReplaceRequestSchema, collectionRuleCategoryIds, collectionRuleSchema, collectionRuleViewSchema, collectionRulesPredicate, collectionRulesSchema, collectionSchema, collectionStateCountsSchema, collectionStateOf, collectionStateSchema, consentActionSchema, consentKindSchema, consentSourceSchema, couponBenefitSchema, couponConditionsInputSchema, couponConditionsSchema, couponDiscountOf, couponIssueListQuerySchema, couponIssueMethodSchema, couponIssueSchema, couponIssueStatusSchema, couponKindSchema, couponListQuerySchema, couponRedemptionListQuerySchema, couponRedemptionSchema, couponRejectReasonSchema, couponRuleSchema, couponSchema, couponStatsSchema, couponStatusSchema, couponTargetInputSchema, couponTargetSchema, createCouponRequestSchema, createPkcePair, createStoreClient, createStoreSession, createStorefrontEditPreviewLinkRequestSchema, createStorefrontPreviewLinkRequestSchema, createStorefrontSiteRequestSchema, createStorefrontVersionRequestSchema, customInputLabelForbiddenWord, customInputRequestSchema, customInputSchema, customInputValueSchema, customerAddressSchema, customerConsentListQuerySchema, customerConsentSchema, customerConsentStateSchema, customerConsentsSchema, customerCouponSchema, customerDelegationRequestSchema, customerDelegationSchema, customerDetailSchema, customerInquiryCategorySchema, customerInquiryListQuerySchema, customerListQuerySchema, customerOrderSchema, customerPointsSchema, customerSortSchema, customerStatsSchema, customerSummarySchema, customerWithdrawalBlockedDetailsSchema, deleteStorefrontFileRequestSchema, deliveryBundleKeyOf, deliveryFeeWaiverSchema, deliveryInfoSchema, deliveryMethodSchema, deliveryStatusSchema, deliveryTypeSchema, descendantCategoryIdsOf, disableStorefrontHostingRequestSchema, discountRequestSchema, discountSchema, dispatchRequestSchema, easyPayProviderResponseSchema, easyPayProviderSchema, eventTypeSchema, exchangeAuthorizationCode, fulfillBulkRequestSchema, fulfillRequestSchema, fulfillmentRuleOf, fulfillmentTypeOf, fulfillmentTypeResponseSchema, fulfillmentTypeSchema, holdOrderItemRequestSchema, inquiryListQuerySchema, inquirySchema, inquiryWebhookEventDataSchema, inquiryWebhookEventSchema, invitationDeliveryStatusSchema, invitationListQuerySchema, invitationSchema, inviteMemberRequestSchema, isCouponTargetLine, isEditableStorefrontPath, isReservedStoreCode, isReservedStorefrontSlug, isShowcaseStorefrontSlug, isStoreScope, isWebOrigin, issueCouponRequestSchema, issueCouponResultSchema, liveGateConditionCodeSchema, liveGateConditionSchema, liveGateErrorDetailsSchema, liveGateSchema, loginProviderSchema, loginProviderStatusSchema, loginProviderTestSchema, loginSettingsSchema, maskedSecretSchema, matchesCollectionRules, mcpRuleResultSchema, mcpToolEventSchema, moveStorefrontFileRequestSchema, myAccountSchema, myStoreSchema, normalizeBusinessNumber, normalizeCouponCode, normalizeStorefrontHostname, normalizeStorefrontPath, oauthTokenResponseSchema, openFulfillmentUnion, optionGroupKindSchema, optionGroupRequestSchema, optionGroupSchema, optionModelRequestSchema, optionModelSchema, optionSelectionSchema, optionSortSchema, optionValueRequestSchema, optionValueSchema, orderCanceledWebhookEventDataSchema, orderCanceledWebhookEventSchema, orderDiscountSchema, orderFulfilledWebhookEventDataSchema, orderFulfilledWebhookEventSchema, orderItemFulfillmentSchema, orderItemFulfillmentSnapshotResponseSchema, orderItemFulfillmentSnapshotSchema, orderItemListQuerySchema, orderItemMemoResultSchema, orderItemPageSchema, orderItemSchema, orderItemStatusSchema, orderListQuerySchema, orderSchema, orderSummaryPageSchema, orderSummarySchema, orderWebhookEventDataSchema, orderWebhookEventSchema, pageInfoSchema, pageQueryShape, pageSchema, paymentAttemptRecordSchema, paymentAttemptStatusSchema, paymentCredentialSetSchema, paymentFailureWarningSchema, paymentListQuerySchema, paymentProviderSchema, paymentProviderVerificationSchema, paymentRecordDetailSchema, paymentRecordPageSchema, paymentRecordSchema, paymentRecordStatusSchema, paymentRefundRecordSchema, paymentSettingsSchema, pgEnvironmentSchema, pgPaymentMethodSchema, pgProviderSchema, pgVerificationResultSchema, planCodeSchema, planEntitlementsSchema, planErrorDetailsSchema, pointAdjustmentRequestSchema, pointAdjustmentResultSchema, pointEarnOf, pointEarnRateBpSchema, pointEarningEnabled, pointEntrySchema, pointExpiresAtOf, pointHistoryQuerySchema, pointLotSchema, pointPolicyNoticeSchema, pointPolicySchema, pointRestoreExpiresAtOf, pointUseOf, pointUsePolicySchema, policyDocumentUrlSchema, policyVersionSchema, portOneChannelSchema, portOneCredentialsInputSchema, productCreateRequestSchema, productFulfillmentCreateSchema, productFulfillmentResponseSchema, productFulfillmentSchema, productFulfillmentUpdateSchema, productImageRejection, productImageUrlSchema, productListQuerySchema, productOutOfStockWebhookEventDataSchema, productOutOfStockWebhookEventSchema, productPatchRequestSchema, productRefundPolicyOverride, productRefundPolicyPatchSchema, productReplaceRequestSchema, productSchema, productSortSchema, productStatusSchema, productSummarySchema, productVariantRequestSchema, productVariantSchema, proratedPaidRefundBase, proratedPointRefund, proratedRefundBase, publicPlanSchema, publicPlansSchema, publishStorefrontVersionRequestSchema, purchaseDecisionExtendResultSchema, purchaseDecisionRequestResultSchema, purchaseEarnPolicySchema, purchaseEarnRateOf, quoteDelivery, quotePricing, refreshUserToken, refundPolicyPatchSchema, refundPolicySchema, rejectClaimRequestSchema, remoteAreaLabelOf, remoteAreaOf, remoteAreaSchema, remoteSurchargeOf, replaceStorefrontSourceRequestSchema, requiresCollection, requiresShipping, resolvePointPolicy, resolveRefundPolicy, resolveShippingPolicy, responsePlanCodeSchema, responsePointEntryTypeSchema, reviewListQuerySchema, reviewReportResultSchema, reviewRewardPointOf, reviewRewardPolicySchema, reviewSchema, reviewSortSchema, reviewWebhookEventDataSchema, reviewWebhookEventSchema, rollbackStorefrontRequestSchema, runStorefrontCheckRequestSchema, salesReportGroupBySchema, salesReportQuerySchema, salesReportRowSchema, salesReportSchema, salesTotalsSchema, saveLoginProviderRequestSchema, savedCustomInputRequestSchema, savedCustomInputSchema, savedOptionGroupRequestSchema, savedOptionGroupSchema, savedOptionSetCompositionSchema, savedOptionSetFromProductRequestSchema, savedOptionSetFromProductResultSchema, savedOptionSetListQuerySchema, savedOptionSetRequestSchema, savedOptionSetSchema, savedOptionSetSortSchema, savedOptionValueRequestSchema, savedOptionValueSchema, scopeAccessSchema, searchStorefrontFilesRequestSchema, sellerAddressListQuerySchema, sellerAddressRequestSchema, sellerAddressSchema, sellerCustomerInquirySchema, setStorefrontVisibilityRequestSchema, settleCouponShortfall, settlementDetailQuerySchema, settlementDetailSchema, settlementListQuerySchema, settlementStatusSchema, settlementSummarySchema, settlementWithDetailsSchema, shippingAddressSchema, shippingPolicyPatchSchema, shippingPolicySchema, splitRefund, storeCodeSchema, storeMemberRoleUpdatedSchema, storeMemberSchema, storePointPolicySchema, storeRoleSchema, storeSchema, storeScopeResourceSchema, storeSubscriptionSchema, storefrontBuildDependenciesSchema, storefrontBuildListQuerySchema, storefrontBuildSchema, storefrontBuildStatusSchema, storefrontCheckSchema, storefrontCheckStatusSchema, storefrontCustomDomainStatusSchema, storefrontCustomHostnameProblem, storefrontDependencyFindingSchema, storefrontDeploymentLogSchema, storefrontDeploymentSchema, storefrontDeploymentStepSchema, storefrontDnsRecordSchema, storefrontDomainSchema, storefrontDomainTxtNameOf, storefrontEditPreviewLabelOf, storefrontEditPreviewSchema, storefrontEditSessionSchema, storefrontEditSessionStatusSchema, storefrontEditingSchema, storefrontFileConflictDetailsSchema, storefrontFileContentQuerySchema, storefrontFileContentSchema, storefrontFileDeleteResultSchema, storefrontFileEncodingSchema, storefrontFileEntrySchema, storefrontFileHashSchema, storefrontFileListQuerySchema, storefrontFileListSchema, storefrontFileMoveResultSchema, storefrontFilePathSchema, storefrontFileSearchResultSchema, storefrontFileWriteResultSchema, storefrontHostingSchema, storefrontPreviewLinkSchema, storefrontProvisioningSchema, storefrontRuleResultSchema, storefrontRuleSeveritySchema, storefrontSiteDomainSchema, storefrontSiteDomainsSchema, storefrontSiteSchema, storefrontSiteStatusSchema, storefrontSlugAvailabilityQuerySchema, storefrontSlugAvailabilitySchema, storefrontSlugSchema, storefrontSourceArchivePartSchema, storefrontSourceArchiveQuerySchema, storefrontSourceBlobsResultSchema, storefrontSourceModeSchema, storefrontSourceRefSchema, storefrontSourceReplacedSchema, storefrontSourceSchema, storefrontSuspensionSchema, storefrontTemplateSchema, storefrontVerifyReportSchema, storefrontVersionCreatedSchema, storefrontVersionSchema, storefrontVersionStatusSchema, storefrontVisibilitySchema, subscriptionLimitsSchema, subscriptionStatusSchema, subscriptionUsageSchema, tossPaymentsCredentialsInputSchema, trackingStageResponseSchema, trackingUpdateResultSchema, updateCouponRequestSchema, updateLoginProviderRequestSchema, updateLoginSettingsRequestSchema, updateMemberRoleRequestSchema, updatePaymentProviderRequestSchema, updatePaymentSettingsRequestSchema, updatePointPolicyRequestSchema, updateStoreRequestSchema, uploadExtensionOf, uploadPurposeSchema, uploadRejection, uploadRequestSchema, uploadStorefrontSourceBlobsRequestSchema, uploadTicketSchema, upsertPortOneProviderRequestSchema, upsertTossPaymentsProviderRequestSchema, variantStockRequestSchema, variantStockSchema, verifyPaymentProviderRequestSchema, webhookCreatedSchema, webhookDeliveryDetailSchema, webhookDeliveryErrorCodeSchema, webhookDeliveryListQuerySchema, webhookDeliveryResourceSchema, webhookDeliverySchema, webhookDeliveryStatusSchema, webhookEventSchema, webhookFailureReasonSchema, webhookRedeliverFailedResultSchema, webhookRequestHeaderSchema, webhookRequestSchema, webhookResourceTypeSchema, webhookSchema, webhookSecretRotatedSchema, webhookStatusSchema, webhookTestRequestSchema, webhookTestResultSchema, withdrawCustomerRequestSchema, writeDryRunResultSchema, writePreviewSchema, writeStorefrontFilesRequestSchema };
7452
+ export { APPROVAL_ALREADY_DECIDED_CODE, APPROVAL_REQUIRED_CODE, ApiError, ApprovalPendingError, BUSINESS_INFO_MAX_LENGTH, BUSINESS_NUMBER_PATTERN, CASH_RECEIPT_STATUS_VALUES, COLLECTION_BEST_SELLING_DAYS, COLLECTION_PREVIEW_SIZE, COLLECTION_PRODUCT_SORTS, COLLECTION_SALES_SORT_DEFAULT_DAYS, COLLECTION_SLUG_PATTERN, COLLECTION_TYPES, COUPON_CODE_PATTERN, COUPON_CONDITIONS_DEFAULTS, COUPON_TARGET_ALL, COUPON_TARGET_LIST_MAX, CUSTOM_INPUT_FORBIDDEN_WORDS, FULFILLMENT_RULES, MAX_ADDON_GROUPS, MAX_ANALYTICS_ALLOWED_ORIGINS, MAX_AUTO_TRANSITION_DAYS, MAX_COLLECTIONS, MAX_COLLECTION_BEST_SELLING_TOP, MAX_COLLECTION_CREATED_WITHIN_DAYS, MAX_COLLECTION_DESCRIPTION, MAX_COLLECTION_PRODUCTS, MAX_COLLECTION_PRODUCTS_PER_ADD, MAX_COLLECTION_RULES, MAX_COLLECTION_RULE_CATEGORIES, MAX_COLLECTION_TITLE, MAX_CUSTOM_INPUTS, MAX_CUSTOM_INPUT_LENGTH, MAX_OPTION_GROUPS, MAX_PRODUCT_IMAGES, MAX_SAVED_OPTION_SETS, MAX_SAVED_OPTION_SET_NAME, MAX_VALUES_PER_GROUP, MAX_VARIANTS, MCP_TELEMETRY_MAX_RESULTS, MIN_PAYMENT_AMOUNT, NO_COUPON_SETTLEMENT, NO_SHIPPING_BUNDLE_KEY, PAYMENT_ROUTING_EXCLUSION_CODES, POINT_ADJUSTMENT_MAX, POINT_EARN_RATE_BP_MAX, POINT_ENTRY_TYPES, POINT_EXPIRY_DAYS_MAX, POINT_EXPIRY_DAYS_MIN, POINT_LOT_SOURCES, POINT_POLICY_DEFAULTS, POINT_REVIEW_REWARD_MAX, POINT_USE_UNITS, PORTONE_CHANNEL_NAME_MAX_LENGTH, PORTONE_MAX_CHANNELS, PORTONE_MIN_CHANNELS, REFUND_POLICY_DEFAULTS, REFUND_POLICY_DELIVERY_FEE_KEYS, REMOTE_AREA_ZIP_RANGES, RESERVED_STOREFRONT_SLUGS, RESERVED_STORE_CODES, SHIPPING_POLICY_DEFAULTS, STOREFRONT_BLOCKED_PATH_SEGMENTS, STOREFRONT_BUILD_STAGES, STOREFRONT_BUILD_TERMINAL_STATUSES, STOREFRONT_CHECK_TERMINAL_STATUSES, STOREFRONT_CUSTOM_DOMAIN_LIMIT_DEFAULT, STOREFRONT_DOMAIN_DNS_GRACE_DAYS, STOREFRONT_DOMAIN_DNS_WAIT_DAYS, STOREFRONT_DOMAIN_TXT_LABEL, STOREFRONT_EDIT_ACTIVITY_TIMEOUT_MINUTES, STOREFRONT_EDIT_CAPACITY, STOREFRONT_EDIT_IDLE_MINUTES, STOREFRONT_EDIT_PREVIEW_SUFFIX, STOREFRONT_EDIT_START_STAGES, STOREFRONT_FILE_BATCH_MAX, STOREFRONT_FILE_MAX_BYTES, STOREFRONT_PATH_SEGMENT_PATTERN, STOREFRONT_PLATFORM_DOMAINS, STOREFRONT_PUBLIC_SUFFIXES, STOREFRONT_SEARCH_REGEX_MAX_LENGTH, STOREFRONT_SHOWCASE_SLUG_PREFIX, STOREFRONT_SITE_CREATE_LIMIT, STOREFRONT_SLUG_CHANGE_LIMIT, STOREFRONT_SLUG_CHANGE_WINDOW_DAYS, STOREFRONT_SLUG_HOLD_DAYS, STOREFRONT_SLUG_PATTERN, STOREFRONT_SOURCE_BLOB_BATCH_MAX, STOREFRONT_SOURCE_PART_BYTES, STOREFRONT_WORKSPACE_MAX_BYTES, STOREFRONT_WORKSPACE_MAX_FILES, STORE_CODE_PATTERN, STORE_ROLE_SCOPES, STORE_SCOPE_RESOURCES, STORE_SESSION_KEYS, TOSS_WIDGET_CLIENT_KEY_PATTERN, TOSS_WIDGET_KEY_MESSAGE, TOSS_WIDGET_SECRET_KEY_PATTERN, UPLOAD_PURPOSE_LIMITS, VARIANT_COUNT_WARNING, VARIANT_STOCK_EXCLUSIVE_MESSAGE, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_FAILURE_REASONS, WEBHOOK_HEADERS, WEBHOOK_PAYLOAD_EXCERPT_MAX_BYTES, WEBHOOK_RESOURCE_TYPES, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, addStorefrontDomainRequestSchema, addonGroupRequestSchema, addonGroupSchema, addonValueRequestSchema, addonValueSchema, addressChangeRequestListSchema, addressTypeSchema, agentPolicyDecisionSchema, allocateByWeight, analyticsChannelSchema, analyticsCheckoutOutcomeSchema, analyticsCheckoutSchema, analyticsDeviceRowSchema, analyticsDevicesSchema, analyticsFunnelStepSchema, analyticsOriginSchema, analyticsOverviewSchema, analyticsPagesQuerySchema, analyticsPagesSchema, analyticsProductRowSchema, analyticsProductSortSchema, analyticsProductsQuerySchema, analyticsProductsSchema, analyticsRangeQuerySchema, analyticsRealtimeSchema, analyticsSearchQuerySchema, analyticsSearchSchema, analyticsSourceGroupBySchema, analyticsSourceRowSchema, analyticsSourcesQuerySchema, analyticsSourcesSchema, analyticsTotalsSchema, apiLogErrorSchema, apiLogListQuerySchema, apiLogPageSchema, apiLogPrincipalSchema, apiLogPrincipalTypeSchema, apiLogSchema, apiLogStatusFilterSchema, apiLogSummarySchema, approvalDecisionRequestSchema, approvalListQuerySchema, approvalPageSchema, approvalRequestSchema, approvalStatusSchema, approveClaimResultSchema, auditActorSchema, auditActorTypeSchema, auditLogListQuerySchema, auditLogOutcomeSchema, auditLogPageSchema, auditLogSchema, auditLogSourceSchema, bestSellingRuleOf, buildAuthorizeUrl, buildQuery, bulkResultSchema, businessInfoPatchShape, businessInfoResponseShape, businessNumberSchema, cancelBySellerRequestSchema, cancelBySellerResultSchema, carrierSchema, cashReceiptSchema, categoryAttributeSchema, categoryCreateRequestSchema, categoryDeleteQuerySchema, categoryDeleteStrategySchema, categoryListQuerySchema, categoryPatchRequestSchema, categoryProductSortSchema, categorySchema, categorySearchResultSchema, categoryTreeNodeSchema, categoryWithPathSchema, changeStorefrontSlugRequestSchema, changeStorefrontTemplateRequestSchema, changeTypeSchema, claimDetailSchema, claimHoldResultSchema, claimListQuerySchema, claimReasonSchema, claimReturnAddressSchema, claimSchema, claimStatusSchema, claimTypeSchema, claimWebhookEventDataSchema, claimWebhookEventSchema, collectionCreateRequestSchema, collectionListQuerySchema, collectionPageSchema, collectionPatchRequestSchema, collectionPreviewRequestSchema, collectionPreviewSchema, collectionProductListQuerySchema, collectionProductSchema, collectionProductSortRequestSchema, collectionProductsAddRequestSchema, collectionProductsReplaceRequestSchema, collectionRuleCategoryIds, collectionRuleSchema, collectionRuleViewSchema, collectionRulesPredicate, collectionRulesSchema, collectionSchema, collectionStateCountsSchema, collectionStateOf, collectionStateSchema, compareBySales, consentActionSchema, consentKindSchema, consentSourceSchema, couponBenefitSchema, couponConditionsInputSchema, couponConditionsSchema, couponDiscountOf, couponIssueListQuerySchema, couponIssueMethodSchema, couponIssueSchema, couponIssueStatusSchema, couponKindSchema, couponListQuerySchema, couponRedemptionListQuerySchema, couponRedemptionSchema, couponRejectReasonSchema, couponRuleSchema, couponSchema, couponStatsSchema, couponStatusSchema, couponTargetInputSchema, couponTargetSchema, createCouponRequestSchema, createPkcePair, createStoreClient, createStoreSession, createStorefrontEditPreviewLinkRequestSchema, createStorefrontPreviewLinkRequestSchema, createStorefrontSiteRequestSchema, createStorefrontVersionRequestSchema, customInputLabelForbiddenWord, customInputRequestSchema, customInputSchema, customInputValueSchema, customerAddressSchema, customerConsentListQuerySchema, customerConsentSchema, customerConsentStateSchema, customerConsentsSchema, customerCouponSchema, customerDelegationRequestSchema, customerDelegationSchema, customerDetailSchema, customerInquiryCategorySchema, customerInquiryListQuerySchema, customerListQuerySchema, customerOrderSchema, customerPointsSchema, customerSortSchema, customerStatsSchema, customerSummarySchema, customerWithdrawalBlockedDetailsSchema, deleteStorefrontFileRequestSchema, deliveryBundleKeyOf, deliveryFeeWaiverSchema, deliveryInfoSchema, deliveryMethodSchema, deliveryStatusSchema, deliveryTypeSchema, descendantCategoryIdsOf, disableStorefrontHostingRequestSchema, discountRequestSchema, discountSchema, dispatchRequestSchema, easyPayProviderResponseSchema, easyPayProviderSchema, eventTypeSchema, exchangeAuthorizationCode, fulfillBulkRequestSchema, fulfillRequestSchema, fulfillmentRuleOf, fulfillmentTypeOf, fulfillmentTypeResponseSchema, fulfillmentTypeSchema, holdOrderItemRequestSchema, inquiryListQuerySchema, inquirySchema, inquiryWebhookEventDataSchema, inquiryWebhookEventSchema, invitationDeliveryStatusSchema, invitationListQuerySchema, invitationSchema, inviteMemberRequestSchema, isCouponTargetLine, isEditableStorefrontPath, isReservedStoreCode, isReservedStorefrontSlug, isShowcaseStorefrontSlug, isStoreScope, isWebOrigin, issueCouponRequestSchema, issueCouponResultSchema, liveGateConditionCodeSchema, liveGateConditionSchema, liveGateErrorDetailsSchema, liveGateSchema, loginProviderSchema, loginProviderStatusSchema, loginProviderTestSchema, loginSettingsSchema, maskedSecretSchema, matchesCollectionRules, mcpRuleResultSchema, mcpToolEventSchema, moveStorefrontFileRequestSchema, myAccountSchema, myStoreSchema, normalizeBusinessNumber, normalizeCouponCode, normalizeStorefrontHostname, normalizeStorefrontPath, oauthTokenResponseSchema, openFulfillmentUnion, optionGroupKindSchema, optionGroupRequestSchema, optionGroupSchema, optionModelRequestSchema, optionModelSchema, optionSelectionSchema, optionSortSchema, optionValueRequestSchema, optionValueSchema, orderCanceledWebhookEventDataSchema, orderCanceledWebhookEventSchema, orderDiscountSchema, orderFulfilledWebhookEventDataSchema, orderFulfilledWebhookEventSchema, orderItemFulfillmentSchema, orderItemFulfillmentSnapshotResponseSchema, orderItemFulfillmentSnapshotSchema, orderItemListQuerySchema, orderItemMemoResultSchema, orderItemPageSchema, orderItemSchema, orderItemStatusSchema, orderListQuerySchema, orderSchema, orderSummaryPageSchema, orderSummarySchema, orderWebhookEventDataSchema, orderWebhookEventSchema, pageInfoSchema, pageQueryShape, pageSchema, paymentAttemptRecordSchema, paymentAttemptStatusSchema, paymentCredentialSetSchema, paymentFailureWarningSchema, paymentListQuerySchema, paymentProviderSchema, paymentProviderVerificationSchema, paymentRecordDetailSchema, paymentRecordPageSchema, paymentRecordSchema, paymentRecordStatusSchema, paymentRefundRecordSchema, paymentSettingsSchema, pgEnvironmentSchema, pgPaymentMethodSchema, pgProviderSchema, pgVerificationResultSchema, planCodeSchema, planEntitlementsSchema, planErrorDetailsSchema, pointAdjustmentRequestSchema, pointAdjustmentResultSchema, pointEarnOf, pointEarnRateBpSchema, pointEarningEnabled, pointEntrySchema, pointExpiresAtOf, pointHistoryQuerySchema, pointLotSchema, pointPolicyNoticeSchema, pointPolicySchema, pointRestoreExpiresAtOf, pointUseOf, pointUsePolicySchema, policyDocumentUrlSchema, policyVersionSchema, portOneChannelSchema, portOneCredentialsInputSchema, productCreateRequestSchema, productFulfillmentCreateSchema, productFulfillmentResponseSchema, productFulfillmentSchema, productFulfillmentUpdateSchema, productImageRejection, productImageUrlSchema, productListQuerySchema, productOutOfStockWebhookEventDataSchema, productOutOfStockWebhookEventSchema, productPatchRequestSchema, productRefundPolicyOverride, productRefundPolicyPatchSchema, productReplaceRequestSchema, productSchema, productSortSchema, productStatusSchema, productSummarySchema, productVariantRequestSchema, productVariantSchema, proratedPaidRefundBase, proratedPointRefund, proratedRefundBase, publicPlanSchema, publicPlansSchema, publishStorefrontVersionRequestSchema, purchaseDecisionExtendResultSchema, purchaseDecisionRequestResultSchema, purchaseEarnPolicySchema, purchaseEarnRateOf, quoteDelivery, quotePricing, rankBestSelling, refreshUserToken, refundPolicyPatchSchema, refundPolicySchema, rejectClaimRequestSchema, remoteAreaLabelOf, remoteAreaOf, remoteAreaSchema, remoteSurchargeOf, replaceStorefrontSourceRequestSchema, requiresCollection, requiresShipping, resolvePointPolicy, resolveRefundPolicy, resolveShippingPolicy, responsePlanCodeSchema, responsePointEntryTypeSchema, reviewListQuerySchema, reviewReportResultSchema, reviewRewardPointOf, reviewRewardPolicySchema, reviewSchema, reviewSortSchema, reviewWebhookEventDataSchema, reviewWebhookEventSchema, rollbackStorefrontRequestSchema, runStorefrontCheckRequestSchema, salesReportGroupBySchema, salesReportQuerySchema, salesReportRowSchema, salesReportSchema, salesTotalsSchema, saveLoginProviderRequestSchema, savedCustomInputRequestSchema, savedCustomInputSchema, savedOptionGroupRequestSchema, savedOptionGroupSchema, savedOptionSetCompositionSchema, savedOptionSetFromProductRequestSchema, savedOptionSetFromProductResultSchema, savedOptionSetListQuerySchema, savedOptionSetRequestSchema, savedOptionSetSchema, savedOptionSetSortSchema, savedOptionValueRequestSchema, savedOptionValueSchema, scopeAccessSchema, searchStorefrontFilesRequestSchema, sellerAddressListQuerySchema, sellerAddressRequestSchema, sellerAddressSchema, sellerCustomerInquirySchema, setStorefrontVisibilityRequestSchema, settleCouponShortfall, settlementDetailQuerySchema, settlementDetailSchema, settlementListQuerySchema, settlementStatusSchema, settlementSummarySchema, settlementWithDetailsSchema, shippingAddressSchema, shippingPolicyPatchSchema, shippingPolicySchema, splitRefund, storeCodeSchema, storeMemberRoleUpdatedSchema, storeMemberSchema, storePointPolicySchema, storeRoleSchema, storeSchema, storeScopeResourceSchema, storeSubscriptionSchema, storefrontBuildDependenciesSchema, storefrontBuildListQuerySchema, storefrontBuildSchema, storefrontBuildStatusSchema, storefrontCheckSchema, storefrontCheckStatusSchema, storefrontCustomDomainStatusSchema, storefrontCustomHostnameProblem, storefrontDependencyFindingSchema, storefrontDeploymentLogSchema, storefrontDeploymentSchema, storefrontDeploymentStepSchema, storefrontDnsRecordSchema, storefrontDomainSchema, storefrontDomainTxtNameOf, storefrontEditPreviewLabelOf, storefrontEditPreviewSchema, storefrontEditSessionSchema, storefrontEditSessionStatusSchema, storefrontEditingSchema, storefrontFileConflictDetailsSchema, storefrontFileContentQuerySchema, storefrontFileContentSchema, storefrontFileDeleteResultSchema, storefrontFileEncodingSchema, storefrontFileEntrySchema, storefrontFileHashSchema, storefrontFileListQuerySchema, storefrontFileListSchema, storefrontFileMoveResultSchema, storefrontFilePathSchema, storefrontFileSearchResultSchema, storefrontFileWriteResultSchema, storefrontHostingSchema, storefrontPreviewLinkSchema, storefrontProvisioningSchema, storefrontRuleResultSchema, storefrontRuleSeveritySchema, storefrontSiteDomainSchema, storefrontSiteDomainsSchema, storefrontSiteSchema, storefrontSiteStatusSchema, storefrontSlugAvailabilityQuerySchema, storefrontSlugAvailabilitySchema, storefrontSlugSchema, storefrontSourceArchivePartSchema, storefrontSourceArchiveQuerySchema, storefrontSourceBlobsResultSchema, storefrontSourceModeSchema, storefrontSourceRefSchema, storefrontSourceReplacedSchema, storefrontSourceSchema, storefrontSuspensionSchema, storefrontTemplateSchema, storefrontVerifyReportSchema, storefrontVersionCreatedSchema, storefrontVersionSchema, storefrontVersionStatusSchema, storefrontVisibilitySchema, subscriptionLimitsSchema, subscriptionStatusSchema, subscriptionUsageSchema, tossPaymentsCredentialsInputSchema, trackingStageResponseSchema, trackingUpdateResultSchema, updateCouponRequestSchema, updateLoginProviderRequestSchema, updateLoginSettingsRequestSchema, updateMemberRoleRequestSchema, updatePaymentProviderRequestSchema, updatePaymentSettingsRequestSchema, updatePointPolicyRequestSchema, updateStoreRequestSchema, uploadExtensionOf, uploadPurposeSchema, uploadRejection, uploadRequestSchema, uploadStorefrontSourceBlobsRequestSchema, uploadTicketSchema, upsertPortOneProviderRequestSchema, upsertTossPaymentsProviderRequestSchema, variantStockRequestSchema, variantStockSchema, verifyPaymentProviderRequestSchema, webhookCreatedSchema, webhookDeliveryDetailSchema, webhookDeliveryErrorCodeSchema, webhookDeliveryListQuerySchema, webhookDeliveryResourceSchema, webhookDeliverySchema, webhookDeliveryStatusSchema, webhookEventSchema, webhookFailureReasonSchema, webhookRedeliverFailedResultSchema, webhookRequestHeaderSchema, webhookRequestSchema, webhookResourceTypeSchema, webhookSchema, webhookSecretRotatedSchema, webhookStatusSchema, webhookTestRequestSchema, webhookTestResultSchema, withdrawCustomerRequestSchema, writeDryRunResultSchema, writePreviewSchema, writeStorefrontFilesRequestSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sayren/store-sdk",
3
- "version": "0.29.3",
3
+ "version": "0.29.5",
4
4
  "main": "./dist/index.mjs",
5
5
  "types": "./dist/index.d.mts",
6
6
  "exports": {
@@ -28,7 +28,7 @@ export const COLLECTION_TYPES = ["MANUAL", "AUTOMATIC"] as const;
28
28
  export type CollectionType = (typeof COLLECTION_TYPES)[number];
29
29
 
30
30
  /**
31
- * 상품 정렬 — `MANUAL`은 직접 정한 순서(직접 선택 컬렉션만)다. `BEST_SELLING`(판매량순)은 판매량 집계가 열릴 때 받는다.
31
+ * 상품 정렬 — `MANUAL`은 직접 정한 순서(직접 선택 컬렉션만)다. `BEST_SELLING`(판매량순)은 판매 수량이 많은 순이다(M3).
32
32
  * 저장 값 전체 목록이고, 쓰기에서 받는 값은 `collectionProductSortRequestSchema`다.
33
33
  */
34
34
  export const COLLECTION_PRODUCT_SORTS = [
@@ -44,9 +44,19 @@ export const COLLECTION_PRODUCT_SORTS = [
44
44
  export type CollectionProductSort = (typeof COLLECTION_PRODUCT_SORTS)[number];
45
45
 
46
46
  export const collectionProductSortRequestSchema = z
47
- .enum(["MANUAL", "LATEST", "PRICE_ASC", "PRICE_DESC", "REVIEW_COUNT", "RATING_DESC", "RECOMMEND"])
47
+ .enum([
48
+ "MANUAL",
49
+ "LATEST",
50
+ "BEST_SELLING",
51
+ "PRICE_ASC",
52
+ "PRICE_DESC",
53
+ "REVIEW_COUNT",
54
+ "RATING_DESC",
55
+ "RECOMMEND",
56
+ ])
48
57
  .describe(
49
- "스토어프론트 상품 기본 정렬. `MANUAL`(직접 정한 순서)·`LATEST`(최신순)·`PRICE_ASC`·`PRICE_DESC`·`REVIEW_COUNT`·`RATING_DESC`·`RECOMMEND`(추천순)",
58
+ "스토어프론트 상품 기본 정렬. `MANUAL`(직접 정한 순서)·`LATEST`(최신순)·`BEST_SELLING`(판매량순)·`PRICE_ASC`·`PRICE_DESC`·`REVIEW_COUNT`·`RATING_DESC`·`RECOMMEND`(추천순). " +
59
+ "판매량순의 기간은 판매량 상위 조건의 기간이고, 조건이 없으면 최근 30일이다",
50
60
  );
51
61
 
52
62
  /** 노출 상태 — 노출 여부와 기간에서 나온 값이다. 숨김이 기간보다 앞선다 */
@@ -73,6 +83,13 @@ export const MAX_COLLECTION_RULE_CATEGORIES = 20;
73
83
  export const MAX_COLLECTION_CREATED_WITHIN_DAYS = 365;
74
84
  /** 미리보기(`POST /v1/collections/preview`)가 싣는 상품 최대 개수 */
75
85
  export const COLLECTION_PREVIEW_SIZE = 20;
86
+ /** 판매량 상위 조건의 기간 선택지(일, 오늘 포함 한국 날짜) */
87
+ export const COLLECTION_BEST_SELLING_DAYS = [7, 30, 90] as const;
88
+ export type CollectionBestSellingDays = (typeof COLLECTION_BEST_SELLING_DAYS)[number];
89
+ /** 판매량 상위 조건이 담는 상품 최대 개수 */
90
+ export const MAX_COLLECTION_BEST_SELLING_TOP = 100;
91
+ /** 판매량순(`BEST_SELLING`) 정렬의 기간 — 판매량 상위 조건이 없을 때 쓴다 */
92
+ export const COLLECTION_SALES_SORT_DEFAULT_DAYS: CollectionBestSellingDays = 30;
76
93
 
77
94
  const createdWithinDaysRuleSchema = z.object({
78
95
  field: z.literal("createdWithinDays"),
@@ -106,9 +123,23 @@ const discountedRuleSchema = z.object({
106
123
  value: z.literal(true).describe("즉시할인이 지금 적용되는 상품"),
107
124
  });
108
125
 
126
+ const bestSellingRuleSchema = z.object({
127
+ field: z.literal("bestSelling"),
128
+ days: z
129
+ .union([z.literal(7), z.literal(30), z.literal(90)])
130
+ .describe("판매량을 세는 기간(일). 7·30·90 중 하나이고 오늘을 포함한 한국 날짜 기준이다"),
131
+ top: z
132
+ .number()
133
+ .int()
134
+ .min(1)
135
+ .max(MAX_COLLECTION_BEST_SELLING_TOP)
136
+ .describe("담을 상품 수(1~100). 다른 조건을 만족하는 상품 중 판매 수량 상위 N개다"),
137
+ });
138
+
109
139
  /**
110
140
  * 조건 하나 — `field`로 가른다. 가격은 즉시할인을 반영한 판매가(`discountedPrice ?? salePrice`)다.
111
- * 판매량 상위(`bestSelling`)는 판매량 집계가 열릴 때 더한다.
141
+ * 판매량 상위(`bestSelling`)는 술어가 아니라 순위 자르기다. 다른 조건을 모두 만족한 상품 중 판매 수량 상위 N개만 남긴다(`rankBestSelling`).
142
+ * 판매 수량은 실결제 주문의 결제 수량에서 취소·반품 수량을 뺀 값이고, 날짜는 주문 시각의 한국 날짜다. 집계는 최대 10분 늦다.
112
143
  */
113
144
  export const collectionRuleSchema = z
114
145
  .discriminatedUnion("field", [
@@ -116,6 +147,7 @@ export const collectionRuleSchema = z
116
147
  categoryRuleSchema,
117
148
  priceRuleSchema,
118
149
  discountedRuleSchema,
150
+ bestSellingRuleSchema,
119
151
  ])
120
152
  .superRefine((rule, ctx) => {
121
153
  if (rule.field !== "price") return;
@@ -163,6 +195,7 @@ export const collectionRuleViewSchema = z.union([
163
195
  categoryRuleSchema,
164
196
  priceRuleSchema,
165
197
  discountedRuleSchema,
198
+ bestSellingRuleSchema,
166
199
  z.looseObject({ field: z.string() }),
167
200
  ]);
168
201
 
@@ -213,6 +246,7 @@ export function descendantCategoryIdsOf(
213
246
  /**
214
247
  * 조건 판정 함수를 만든다 — 모든 조건을 만족해야 true다(AND). 카테고리 트리는 여기서 한 번만 푼다.
215
248
  * 알 수 없는 조건(`field`)은 만족하지 않는 것으로 본다. 모르는 조건을 건너뛰면 상품이 예고 없이 늘어난다.
249
+ * 판매량 상위(`bestSelling`)는 여기서 모두 통과시킨다. 순위 자르기는 판매량을 아는 호출부가 `rankBestSelling`으로 한다.
216
250
  * 판매 상태는 보지 않는다. 판매 중만 거르는 것은 호출부다.
217
251
  */
218
252
  export function collectionRulesPredicate(
@@ -243,6 +277,8 @@ export function collectionRulesPredicate(
243
277
  case "discounted":
244
278
  return (product) =>
245
279
  product.discountedPrice !== null && product.discountedPrice < product.salePrice;
280
+ case "bestSelling":
281
+ return () => true;
246
282
  default:
247
283
  return () => false;
248
284
  }
@@ -259,6 +295,45 @@ export function matchesCollectionRules(
259
295
  return collectionRulesPredicate(rules, context)(product);
260
296
  }
261
297
 
298
+ /** 판매량 상위 조건 — 없으면 null. 같은 조건은 한 번만 쓰므로 첫 번째다 */
299
+ export function bestSellingRuleOf(
300
+ rules: readonly CollectionRuleView[],
301
+ ): { days: CollectionBestSellingDays; top: number } | null {
302
+ for (const rule of rules) {
303
+ const parsed = bestSellingRuleSchema.safeParse(rule);
304
+ if (parsed.success) return { days: parsed.data.days, top: parsed.data.top };
305
+ }
306
+ return null;
307
+ }
308
+
309
+ /**
310
+ * 판매량 비교 — 판매 수량이 많은 순, 같으면 최근 등록 순, 그것도 같으면 상품 id 순이다. 결과가 입력 순서에 기대지 않는다.
311
+ * `sales`에 없는 상품은 0개다.
312
+ */
313
+ export function compareBySales<T extends { productId: string; createdAt: string }>(
314
+ sales: ReadonlyMap<string, number>,
315
+ ): (a: T, b: T) => number {
316
+ return (a, b) =>
317
+ (sales.get(b.productId) ?? 0) - (sales.get(a.productId) ?? 0) ||
318
+ Date.parse(b.createdAt) - Date.parse(a.createdAt) ||
319
+ (a.productId < b.productId ? -1 : a.productId > b.productId ? 1 : 0);
320
+ }
321
+
322
+ /**
323
+ * 판매량 상위 자르기 — 다른 조건을 통과한 상품 중 판매 수량이 1개 이상인 상품을 `compareBySales` 순으로 `top`개 남긴다.
324
+ * 판매가 없는 상품은 넣지 않는다.
325
+ */
326
+ export function rankBestSelling<T extends { productId: string; createdAt: string }>(
327
+ products: readonly T[],
328
+ sales: ReadonlyMap<string, number>,
329
+ top: number,
330
+ ): T[] {
331
+ return products
332
+ .filter((product) => (sales.get(product.productId) ?? 0) > 0)
333
+ .sort(compareBySales(sales))
334
+ .slice(0, Math.max(0, top));
335
+ }
336
+
262
337
  /** 조건이 참조하는 카테고리 id — 카테고리 삭제의 참조 검사에 쓴다 */
263
338
  export function collectionRuleCategoryIds(rules: readonly CollectionRuleView[]): string[] {
264
339
  const ids: string[] = [];
@@ -288,6 +363,14 @@ export const collectionSchema = z.object({
288
363
  metaTitle: z.string().nullable(),
289
364
  metaDescription: z.string().nullable(),
290
365
  state: collectionStateSchema.describe("지금 노출 상태"),
366
+ salesComputedAt: z
367
+ .string()
368
+ .nullable()
369
+ .default(null)
370
+ .describe(
371
+ "판매량 기준 시각. 판매량 상위 조건이나 판매량순 정렬을 쓰는 컬렉션은 이 시각까지의 주문을 센다(최대 10분 늦다). " +
372
+ "판매량을 쓰지 않거나 아직 집계 전이면 null",
373
+ ),
291
374
  productCount: z
292
375
  .number()
293
376
  .int()
@@ -347,7 +430,7 @@ const periodRefine = (
347
430
  /**
348
431
  * `POST /v1/collections` — 컬렉션을 만든다. `slug`를 생략하면 `c-{id 뒤 8자}`로 만든다.
349
432
  * 직접 선택(`MANUAL`)은 `productIds`가 처음 담을 상품과 순서다. 조건 자동(`AUTOMATIC`)은 `rules`가 있어야 하고 `productIds`를 받지 않는다.
350
- * `productSort`를 생략하면 직접 선택은 `MANUAL`, 조건 자동은 `LATEST`다. 조건 자동은 `MANUAL`을 쓸 수 없다.
433
+ * `productSort`를 생략하면 직접 선택은 `MANUAL`, 조건 자동은 `LATEST`(판매량 상위 조건이 있으면 `BEST_SELLING`)다. 조건 자동은 `MANUAL`을 쓸 수 없다.
351
434
  */
352
435
  export const collectionCreateRequestSchema = z
353
436
  .object({
@@ -365,7 +448,7 @@ export const collectionCreateRequestSchema = z
365
448
  productSort: commonFields.productSort
366
449
  .optional()
367
450
  .describe(
368
- "스토어프론트 상품 기본 정렬. 생략하면 직접 선택은 `MANUAL`, 조건 자동은 `LATEST`다. 조건 자동은 `MANUAL`을 쓸 수 없다",
451
+ "스토어프론트 상품 기본 정렬. 생략하면 직접 선택은 `MANUAL`, 조건 자동은 `LATEST`(판매량 상위 조건이 있으면 `BEST_SELLING`)다. 조건 자동은 `MANUAL`을 쓸 수 없다",
369
452
  ),
370
453
  visible: commonFields.visible.default(true),
371
454
  startsAt: commonFields.startsAt,
@@ -483,7 +566,9 @@ export const collectionPreviewRequestSchema = z.object({
483
566
  rules: collectionRulesSchema,
484
567
  productSort: collectionProductSortRequestSchema
485
568
  .optional()
486
- .describe("미리보기 순서. 생략하거나 `MANUAL`이면 `LATEST`다"),
569
+ .describe(
570
+ "미리보기 순서. 생략하면 `LATEST`(판매량 상위 조건이 있으면 `BEST_SELLING`)이고, `MANUAL`이면 `LATEST`다",
571
+ ),
487
572
  });
488
573
 
489
574
  export type CollectionPreviewRequest = z.infer<typeof collectionPreviewRequestSchema>;
@@ -492,6 +577,13 @@ export type CollectionPreviewRequest = z.infer<typeof collectionPreviewRequestSc
492
577
  export const collectionPreviewSchema = z.object({
493
578
  count: z.number().int().describe("조건을 만족하는 판매 중 상품 수(조회 시각 기준)"),
494
579
  products: z.array(collectionProductSchema).describe("앞 20개(정렬 순)"),
580
+ salesComputedAt: z
581
+ .string()
582
+ .nullable()
583
+ .default(null)
584
+ .describe(
585
+ "판매량 기준 시각. 판매량 상위 조건이나 판매량순 정렬을 쓰면 이 시각까지의 주문을 센다. 판매량을 쓰지 않거나 집계 전이면 null",
586
+ ),
495
587
  });
496
588
 
497
589
  export type CollectionPreview = z.infer<typeof collectionPreviewSchema>;
@@ -316,7 +316,7 @@ export interface PricingResult {
316
316
  pointAmount: number;
317
317
  /** 지금 쓸 수 있는 최대 적립금 — 주문서 안내값(잔액을 모두 요청했을 때의 금액). 적립금 입력이 없으면 0 */
318
318
  pointMaxUsable: number;
319
- /** 요청보다 적게 썼다면 그 이유 — `MIN_PAYMENT`: 결제 금액 100원을 남기려고 줄였다 */
319
+ /** 요청보다 적게 썼다면 그 이유 — `MIN_PAYMENT`: 남는 결제 금액이 1~99원이라 100원을 남기려고 줄였다(전액이면 0원 결제) */
320
320
  pointAdjustReason: "MIN_PAYMENT" | null;
321
321
  /** 요청했지만 쓰지 못했다면 그 이유 */
322
322
  pointRejectReason: PointRejectReason | null;
@@ -662,8 +662,10 @@ export function quotePricing(input: PricingInput): PricingResult {
662
662
  }
663
663
 
664
664
  /**
665
- * 사용 적립금 금액(순수) — 요청·잔액·최대 비율·「결제 금액 100원 남기기」 중 작은 값을 단위로 내린다.
666
- * 최소 보유액·최소 사용액에 못 미치면 0이다. 결제 금액이 0원이 되는 사용(적립금 전액 결제)은 아직 받지 않는다(P4).
665
+ * 사용 적립금 금액(순수) — 요청·잔액·최대 비율 중 작은 값을 쓴다(#93 P3·P4).
666
+ * - 결제 금액 전체를 덮을 수 있으면 전액을 쓴다(0원 결제, 이때만 사용 단위를 보지 않는다)
667
+ * - 그 밖에는 사용 단위로 내리고, 남는 결제 금액이 1~99원이면 적립금을 줄여 100원을 남긴다(`MIN_PAYMENT`)
668
+ * 최소 보유액·최소 사용액에 못 미치면 0이다.
667
669
  */
668
670
  export function pointUseOf(
669
671
  points: PricingPoints | null,
@@ -680,27 +682,38 @@ export function pointUseOf(
680
682
  const unit = Math.max(1, Math.floor(rules.unit));
681
683
  const base = amounts.productPaid + (rules.includeDeliveryFee ? amounts.deliveryPaid : 0);
682
684
  const byRatio = Math.floor((base * rules.maxRatioBp) / 10_000);
683
- // 결제 금액은 0원 또는 100원 이상이어야 하고, 지금은 0원 결제를 받지 않으므로 100원을 남긴다
684
- const byPayment = Math.max(0, amounts.total - MIN_PAYMENT_AMOUNT);
685
685
  const balance = Math.max(0, points.balance);
686
+ const total = Math.max(0, amounts.total);
686
687
  const floorUnit = (value: number) => Math.floor(Math.max(0, value) / unit) * unit;
688
+ /** 상한 `cap`으로 쓸 수 있는 금액 — 전액이면 0원 결제, 아니면 단위 내림 + 100원 남기기 */
689
+ const usableWithin = (cap: number): { amount: number; adjusted: boolean } => {
690
+ if (total > 0 && cap >= total) return { amount: total, adjusted: false };
691
+ const amount = floorUnit(Math.min(cap, total));
692
+ const remaining = total - amount;
693
+ if (remaining > 0 && remaining < MIN_PAYMENT_AMOUNT) {
694
+ return { amount: floorUnit(total - MIN_PAYMENT_AMOUNT), adjusted: true };
695
+ }
696
+ return { amount, adjusted: false };
697
+ };
687
698
  const eligible = balance >= rules.minBalance;
688
- const ceiling = Math.min(balance, byRatio, byPayment);
689
- const maxUsable = eligible && floorUnit(ceiling) >= rules.minAmount ? floorUnit(ceiling) : 0;
699
+ const ceiling = usableWithin(Math.min(balance, byRatio)).amount;
700
+ const maxUsable = eligible && ceiling > 0 && ceiling >= rules.minAmount ? ceiling : 0;
690
701
  const requested = Math.max(0, Math.floor(points.requested));
691
702
  if (requested === 0) return { ...none, maxUsable };
692
703
  if (!eligible) return { amount: 0, maxUsable, adjustReason: null, rejectReason: "MIN_BALANCE" };
693
- const amount = floorUnit(Math.min(requested, balance, byRatio, byPayment));
694
- if (amount <= 0) {
704
+ const used = usableWithin(Math.min(requested, balance, byRatio));
705
+ if (used.amount <= 0) {
695
706
  return { amount: 0, maxUsable, adjustReason: null, rejectReason: "NOT_PAYABLE" };
696
707
  }
697
- if (amount < rules.minAmount) {
708
+ if (used.amount < rules.minAmount) {
698
709
  return { amount: 0, maxUsable, adjustReason: null, rejectReason: "MIN_AMOUNT" };
699
710
  }
700
- // 요청이 결제 금액 100원 남기기에 걸려 줄었다
701
- const adjustReason =
702
- floorUnit(Math.min(requested, balance, byRatio)) > amount ? "MIN_PAYMENT" : null;
703
- return { amount, maxUsable, adjustReason, rejectReason: null };
711
+ return {
712
+ amount: used.amount,
713
+ maxUsable,
714
+ adjustReason: used.adjusted ? "MIN_PAYMENT" : null,
715
+ rejectReason: null,
716
+ };
704
717
  }
705
718
 
706
719
  // ─── 환불 정산: 조건 미달 차감 ─────────────────────────────