@sayren/store-sdk 0.3.0 → 0.5.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.
package/dist/index.d.mts CHANGED
@@ -1108,6 +1108,226 @@ export declare const approveClaimResultSchema: z.ZodObject<{
1108
1108
  }, z.core.$strip>;
1109
1109
  export type ApproveClaimResult = z.infer<typeof approveClaimResultSchema>;
1110
1110
  //#endregion
1111
+ //#region src/schemas/customer.d.ts
1112
+ /** 구매자 목록 정렬 */
1113
+ export declare const customerSortSchema: z.ZodEnum<{
1114
+ joined_desc: "joined_desc";
1115
+ joined_asc: "joined_asc";
1116
+ total_paid_desc: "total_paid_desc";
1117
+ order_count_desc: "order_count_desc";
1118
+ last_ordered_desc: "last_ordered_desc";
1119
+ }>;
1120
+ export type CustomerSort = z.infer<typeof customerSortSchema>;
1121
+ /** `GET /v1/customers` 쿼리 */
1122
+ export declare const customerListQuerySchema: z.ZodObject<{
1123
+ page: z.ZodOptional<z.ZodString>;
1124
+ size: z.ZodOptional<z.ZodString>;
1125
+ q: z.ZodOptional<z.ZodString>;
1126
+ joinedFrom: z.ZodOptional<z.ZodString>;
1127
+ joinedTo: z.ZodOptional<z.ZodString>;
1128
+ marketing: z.ZodOptional<z.ZodEnum<{
1129
+ agreed: "agreed";
1130
+ declined: "declined";
1131
+ }>>;
1132
+ sort: z.ZodOptional<z.ZodEnum<{
1133
+ joined_desc: "joined_desc";
1134
+ joined_asc: "joined_asc";
1135
+ total_paid_desc: "total_paid_desc";
1136
+ order_count_desc: "order_count_desc";
1137
+ last_ordered_desc: "last_ordered_desc";
1138
+ }>>;
1139
+ }, z.core.$strip>;
1140
+ /** 구매 통계 — 구매 금액은 취소된 주문 상품을 빼고, 완료된 반품은 수량 비율로 뺀 금액이다 */
1141
+ export declare const customerStatsSchema: z.ZodObject<{
1142
+ orderCount: z.ZodNumber;
1143
+ totalPaid: z.ZodNumber;
1144
+ lastOrderedAt: z.ZodNullable<z.ZodString>;
1145
+ }, z.core.$strip>;
1146
+ /** 목록 행 — 휴대폰은 가운데를 가린다(`010-****-1234`) */
1147
+ export declare const customerSummarySchema: z.ZodObject<{
1148
+ customerId: z.ZodString;
1149
+ name: z.ZodString;
1150
+ email: z.ZodNullable<z.ZodString>;
1151
+ maskedPhone: z.ZodNullable<z.ZodString>;
1152
+ joinedAt: z.ZodString;
1153
+ point: z.ZodNumber;
1154
+ marketingAgreed: z.ZodBoolean;
1155
+ orderCount: z.ZodNumber;
1156
+ totalPaid: z.ZodNumber;
1157
+ lastOrderedAt: z.ZodNullable<z.ZodString>;
1158
+ }, z.core.$strip>;
1159
+ export type CustomerSummary = z.infer<typeof customerSummarySchema>;
1160
+ export declare const customerAddressSchema: z.ZodObject<{
1161
+ addressId: z.ZodString;
1162
+ alias: z.ZodString;
1163
+ isDefault: z.ZodBoolean;
1164
+ receiverName: z.ZodString;
1165
+ phone: z.ZodString;
1166
+ zipCode: z.ZodString;
1167
+ address1: z.ZodString;
1168
+ address2: z.ZodNullable<z.ZodString>;
1169
+ }, z.core.$strip>;
1170
+ /** 구매자 상세 — 휴대폰 전체를 보여 준다 */
1171
+ export declare const customerDetailSchema: z.ZodObject<{
1172
+ customerId: z.ZodString;
1173
+ name: z.ZodString;
1174
+ email: z.ZodNullable<z.ZodString>;
1175
+ phone: z.ZodNullable<z.ZodString>;
1176
+ joinedAt: z.ZodString;
1177
+ point: z.ZodNumber;
1178
+ marketingAgreed: z.ZodBoolean;
1179
+ loginMethods: z.ZodArray<z.ZodObject<{
1180
+ method: z.ZodEnum<{
1181
+ kakao: "kakao";
1182
+ naver: "naver";
1183
+ google: "google";
1184
+ password: "password";
1185
+ }>;
1186
+ linkedAt: z.ZodString;
1187
+ }, z.core.$strip>>;
1188
+ addresses: z.ZodArray<z.ZodObject<{
1189
+ addressId: z.ZodString;
1190
+ alias: z.ZodString;
1191
+ isDefault: z.ZodBoolean;
1192
+ receiverName: z.ZodString;
1193
+ phone: z.ZodString;
1194
+ zipCode: z.ZodString;
1195
+ address1: z.ZodString;
1196
+ address2: z.ZodNullable<z.ZodString>;
1197
+ }, z.core.$strip>>;
1198
+ stats: z.ZodObject<{
1199
+ orderCount: z.ZodNumber;
1200
+ totalPaid: z.ZodNumber;
1201
+ lastOrderedAt: z.ZodNullable<z.ZodString>;
1202
+ canceledOrReturnedCount: z.ZodNumber;
1203
+ reviewCount: z.ZodNumber;
1204
+ inquiryCount: z.ZodNumber;
1205
+ }, z.core.$strip>;
1206
+ }, z.core.$strip>;
1207
+ export type CustomerDetail = z.infer<typeof customerDetailSchema>;
1208
+ /** 구매자의 주문 — 전체 기간 */
1209
+ export declare const customerOrderSchema: z.ZodObject<{
1210
+ orderId: z.ZodString;
1211
+ orderedAt: z.ZodString;
1212
+ totalAmount: z.ZodNumber;
1213
+ testPayment: z.ZodBoolean;
1214
+ items: z.ZodArray<z.ZodObject<{
1215
+ orderItemId: z.ZodString;
1216
+ productName: z.ZodString;
1217
+ optionName: z.ZodNullable<z.ZodString>;
1218
+ quantity: z.ZodNumber;
1219
+ status: z.ZodEnum<{
1220
+ PAID: "PAID";
1221
+ PAYMENT_WAITING: "PAYMENT_WAITING";
1222
+ CONFIRMED: "CONFIRMED";
1223
+ DISPATCHED: "DISPATCHED";
1224
+ DELIVERING: "DELIVERING";
1225
+ DELIVERED: "DELIVERED";
1226
+ PURCHASE_DECIDED: "PURCHASE_DECIDED";
1227
+ CANCELED: "CANCELED";
1228
+ }>;
1229
+ claimStatus: z.ZodNullable<z.ZodEnum<{
1230
+ CANCEL_REQUESTED: "CANCEL_REQUESTED";
1231
+ RETURN_REQUESTED: "RETURN_REQUESTED";
1232
+ EXCHANGE_REQUESTED: "EXCHANGE_REQUESTED";
1233
+ }>>;
1234
+ }, z.core.$strip>>;
1235
+ }, z.core.$strip>;
1236
+ export type CustomerOrder = z.infer<typeof customerOrderSchema>;
1237
+ /** `POST /v1/customers/{id}/withdraw` 본문 */
1238
+ export declare const withdrawCustomerRequestSchema: z.ZodObject<{
1239
+ reason: z.ZodEnum<{
1240
+ BUYER_REQUEST: "BUYER_REQUEST";
1241
+ DORMANT: "DORMANT";
1242
+ ABUSE: "ABUSE";
1243
+ OTHER: "OTHER";
1244
+ }>;
1245
+ memo: z.ZodOptional<z.ZodString>;
1246
+ }, z.core.$strip>;
1247
+ export type WithdrawCustomerRequest = z.infer<typeof withdrawCustomerRequestSchema>;
1248
+ /** `WITHDRAWAL_BLOCKED`의 `error.details` — 탈퇴를 막는 항목 수(스토어프런트 탈퇴와 같다) */
1249
+ export declare const customerWithdrawalBlockedDetailsSchema: z.ZodObject<{
1250
+ inProgressOrderItems: z.ZodNumber;
1251
+ openClaims: z.ZodNumber;
1252
+ pendingPayments: z.ZodNumber;
1253
+ }, z.core.$strip>;
1254
+ export type CustomerWithdrawalBlockedDetails = z.infer<typeof customerWithdrawalBlockedDetailsSchema>;
1255
+ //#endregion
1256
+ //#region src/schemas/login.d.ts
1257
+ /** 소셜 로그인 공급자 */
1258
+ export declare const loginProviderSchema: z.ZodEnum<{
1259
+ kakao: "kakao";
1260
+ naver: "naver";
1261
+ google: "google";
1262
+ }>;
1263
+ export type LoginProvider = z.infer<typeof loginProviderSchema>;
1264
+ /** 공급자 연결 상태 — 비밀 값은 앞 4자리만 */
1265
+ export declare const loginProviderStatusSchema: z.ZodObject<{
1266
+ provider: z.ZodEnum<{
1267
+ kakao: "kakao";
1268
+ naver: "naver";
1269
+ google: "google";
1270
+ }>;
1271
+ connected: z.ZodBoolean;
1272
+ enabled: z.ZodBoolean;
1273
+ clientIdPrefix: z.ZodNullable<z.ZodString>;
1274
+ secretPrefix: z.ZodNullable<z.ZodString>;
1275
+ lastTestedAt: z.ZodNullable<z.ZodString>;
1276
+ lastTestOk: z.ZodNullable<z.ZodBoolean>;
1277
+ linkedMemberCount: z.ZodNumber;
1278
+ onlyMethodMemberCount: z.ZodNumber;
1279
+ callbackUrl: z.ZodURL;
1280
+ }, z.core.$strip>;
1281
+ export type LoginProviderStatus = z.infer<typeof loginProviderStatusSchema>;
1282
+ /** `GET /v1/store/login-settings` */
1283
+ export declare const loginSettingsSchema: z.ZodObject<{
1284
+ passwordLoginEnabled: z.ZodBoolean;
1285
+ passwordOnlyMemberCount: z.ZodNumber;
1286
+ storefrontOrigins: z.ZodArray<z.ZodString>;
1287
+ providers: z.ZodArray<z.ZodObject<{
1288
+ provider: z.ZodEnum<{
1289
+ kakao: "kakao";
1290
+ naver: "naver";
1291
+ google: "google";
1292
+ }>;
1293
+ connected: z.ZodBoolean;
1294
+ enabled: z.ZodBoolean;
1295
+ clientIdPrefix: z.ZodNullable<z.ZodString>;
1296
+ secretPrefix: z.ZodNullable<z.ZodString>;
1297
+ lastTestedAt: z.ZodNullable<z.ZodString>;
1298
+ lastTestOk: z.ZodNullable<z.ZodBoolean>;
1299
+ linkedMemberCount: z.ZodNumber;
1300
+ onlyMethodMemberCount: z.ZodNumber;
1301
+ callbackUrl: z.ZodURL;
1302
+ }, z.core.$strip>>;
1303
+ }, z.core.$strip>;
1304
+ export type LoginSettings = z.infer<typeof loginSettingsSchema>;
1305
+ /** `PATCH /v1/store/login-settings` */
1306
+ export declare const updateLoginSettingsRequestSchema: z.ZodObject<{
1307
+ passwordLoginEnabled: z.ZodBoolean;
1308
+ }, z.core.$strip>;
1309
+ export type UpdateLoginSettingsRequest = z.infer<typeof updateLoginSettingsRequestSchema>;
1310
+ /** `PUT /v1/store/auth-providers/{provider}` — 저장 전에 연결 테스트를 한다 */
1311
+ export declare const saveLoginProviderRequestSchema: z.ZodObject<{
1312
+ clientId: z.ZodString;
1313
+ clientSecret: z.ZodString;
1314
+ }, z.core.$strip>;
1315
+ export type SaveLoginProviderRequest = z.infer<typeof saveLoginProviderRequestSchema>;
1316
+ /** `PATCH /v1/store/auth-providers/{provider}` */
1317
+ export declare const updateLoginProviderRequestSchema: z.ZodObject<{
1318
+ enabled: z.ZodBoolean;
1319
+ }, z.core.$strip>;
1320
+ /** 연결 테스트 결과 */
1321
+ export declare const loginProviderTestSchema: z.ZodObject<{
1322
+ ok: z.ZodBoolean;
1323
+ code: z.ZodNullable<z.ZodEnum<{
1324
+ INVALID_CLIENT: "INVALID_CLIENT";
1325
+ PROVIDER_UNAVAILABLE: "PROVIDER_UNAVAILABLE";
1326
+ }>>;
1327
+ testedAt: z.ZodString;
1328
+ }, z.core.$strip>;
1329
+ export type LoginProviderTest = z.infer<typeof loginProviderTestSchema>;
1330
+ //#endregion
1111
1331
  //#region src/schemas/misc.d.ts
1112
1332
  export declare const inquirySchema: z.ZodObject<{
1113
1333
  inquiryId: z.ZodString;
@@ -3333,6 +3553,8 @@ export declare const updateStoreRequestSchema: z.ZodObject<{
3333
3553
  customerCenterPhone: z.ZodOptional<z.ZodString>;
3334
3554
  businessHours: z.ZodOptional<z.ZodString>;
3335
3555
  analyticsAllowedOrigins: z.ZodOptional<z.ZodArray<z.ZodString>>;
3556
+ storefrontOrigins: z.ZodOptional<z.ZodArray<z.ZodString>>;
3557
+ guestCheckoutEnabled: z.ZodOptional<z.ZodBoolean>;
3336
3558
  }, z.core.$strip>;
3337
3559
  export type UpdateStoreRequest = z.infer<typeof updateStoreRequestSchema>;
3338
3560
  export declare const storeSchema: z.ZodObject<{
@@ -3349,6 +3571,8 @@ export declare const storeSchema: z.ZodObject<{
3349
3571
  businessNumber: z.ZodNullable<z.ZodString>;
3350
3572
  customerCenterPhone: z.ZodNullable<z.ZodString>;
3351
3573
  analyticsAllowedOrigins: z.ZodArray<z.ZodString>;
3574
+ storefrontOrigins: z.ZodArray<z.ZodString>;
3575
+ guestCheckoutEnabled: z.ZodBoolean;
3352
3576
  memberCount: z.ZodNumber;
3353
3577
  createdAt: z.ZodString;
3354
3578
  }, z.core.$strip>;
@@ -4464,6 +4688,176 @@ export declare function createStoreClient(options: StoreClientOptions): {
4464
4688
  answer: (inquiryId: string, content: string) => Promise<void>;
4465
4689
  updateAnswer: (inquiryId: string, content: string) => Promise<void>;
4466
4690
  };
4691
+ /**
4692
+ * 구매자 로그인 설정 — 이메일·비밀번호 허용, 소셜 공급자 연결. 비밀 값을 다루므로 콘솔에 로그인한 OWNER만 호출할 수 있다
4693
+ */
4694
+ loginSettings: {
4695
+ get: () => Promise<{
4696
+ passwordLoginEnabled: boolean;
4697
+ passwordOnlyMemberCount: number;
4698
+ storefrontOrigins: string[];
4699
+ providers: {
4700
+ provider: "kakao" | "naver" | "google";
4701
+ connected: boolean;
4702
+ enabled: boolean;
4703
+ clientIdPrefix: string | null;
4704
+ secretPrefix: string | null;
4705
+ lastTestedAt: string | null;
4706
+ lastTestOk: boolean | null;
4707
+ linkedMemberCount: number;
4708
+ onlyMethodMemberCount: number;
4709
+ callbackUrl: string;
4710
+ }[];
4711
+ }>;
4712
+ update: (body: UpdateLoginSettingsRequest) => Promise<{
4713
+ passwordLoginEnabled: boolean;
4714
+ passwordOnlyMemberCount: number;
4715
+ storefrontOrigins: string[];
4716
+ providers: {
4717
+ provider: "kakao" | "naver" | "google";
4718
+ connected: boolean;
4719
+ enabled: boolean;
4720
+ clientIdPrefix: string | null;
4721
+ secretPrefix: string | null;
4722
+ lastTestedAt: string | null;
4723
+ lastTestOk: boolean | null;
4724
+ linkedMemberCount: number;
4725
+ onlyMethodMemberCount: number;
4726
+ callbackUrl: string;
4727
+ }[];
4728
+ }>;
4729
+ /** 키 저장 — 저장 전에 연결 테스트를 하고, 실패하면 422 `LOGIN_PROVIDER_VERIFICATION_FAILED` */
4730
+ saveProvider: (provider: LoginProvider, body: SaveLoginProviderRequest) => Promise<{
4731
+ passwordLoginEnabled: boolean;
4732
+ passwordOnlyMemberCount: number;
4733
+ storefrontOrigins: string[];
4734
+ providers: {
4735
+ provider: "kakao" | "naver" | "google";
4736
+ connected: boolean;
4737
+ enabled: boolean;
4738
+ clientIdPrefix: string | null;
4739
+ secretPrefix: string | null;
4740
+ lastTestedAt: string | null;
4741
+ lastTestOk: boolean | null;
4742
+ linkedMemberCount: number;
4743
+ onlyMethodMemberCount: number;
4744
+ callbackUrl: string;
4745
+ }[];
4746
+ }>;
4747
+ setProviderEnabled: (provider: LoginProvider, enabled: boolean) => Promise<{
4748
+ passwordLoginEnabled: boolean;
4749
+ passwordOnlyMemberCount: number;
4750
+ storefrontOrigins: string[];
4751
+ providers: {
4752
+ provider: "kakao" | "naver" | "google";
4753
+ connected: boolean;
4754
+ enabled: boolean;
4755
+ clientIdPrefix: string | null;
4756
+ secretPrefix: string | null;
4757
+ lastTestedAt: string | null;
4758
+ lastTestOk: boolean | null;
4759
+ linkedMemberCount: number;
4760
+ onlyMethodMemberCount: number;
4761
+ callbackUrl: string;
4762
+ }[];
4763
+ }>;
4764
+ testProvider: (provider: LoginProvider) => Promise<{
4765
+ ok: boolean;
4766
+ code: "INVALID_CLIENT" | "PROVIDER_UNAVAILABLE" | null;
4767
+ testedAt: string;
4768
+ }>;
4769
+ /** 연결 해제 — 키를 지운다. 연결된 구매자 계정은 남는다 */
4770
+ removeProvider: (provider: LoginProvider) => Promise<{
4771
+ passwordLoginEnabled: boolean;
4772
+ passwordOnlyMemberCount: number;
4773
+ storefrontOrigins: string[];
4774
+ providers: {
4775
+ provider: "kakao" | "naver" | "google";
4776
+ connected: boolean;
4777
+ enabled: boolean;
4778
+ clientIdPrefix: string | null;
4779
+ secretPrefix: string | null;
4780
+ lastTestedAt: string | null;
4781
+ lastTestOk: boolean | null;
4782
+ linkedMemberCount: number;
4783
+ onlyMethodMemberCount: number;
4784
+ callbackUrl: string;
4785
+ }[];
4786
+ }>;
4787
+ };
4788
+ /** 구매자 관리 — 조회 `customer:r`, 탈퇴 처리 `customer:rw`(콘솔 OWNER·ADMIN) */
4789
+ customers: {
4790
+ list: (params?: PageParams & Omit<z.input<typeof customerListQuerySchema>, "page" | "size">) => Promise<{
4791
+ page: number;
4792
+ size: number;
4793
+ totalElements: number;
4794
+ totalPages: number;
4795
+ contents: {
4796
+ customerId: string;
4797
+ name: string;
4798
+ email: string | null;
4799
+ maskedPhone: string | null;
4800
+ joinedAt: string;
4801
+ point: number;
4802
+ marketingAgreed: boolean;
4803
+ orderCount: number;
4804
+ totalPaid: number;
4805
+ lastOrderedAt: string | null;
4806
+ }[];
4807
+ }>;
4808
+ get: (customerId: string) => Promise<{
4809
+ customerId: string;
4810
+ name: string;
4811
+ email: string | null;
4812
+ phone: string | null;
4813
+ joinedAt: string;
4814
+ point: number;
4815
+ marketingAgreed: boolean;
4816
+ loginMethods: {
4817
+ method: "kakao" | "naver" | "google" | "password";
4818
+ linkedAt: string;
4819
+ }[];
4820
+ addresses: {
4821
+ addressId: string;
4822
+ alias: string;
4823
+ isDefault: boolean;
4824
+ receiverName: string;
4825
+ phone: string;
4826
+ zipCode: string;
4827
+ address1: string;
4828
+ address2: string | null;
4829
+ }[];
4830
+ stats: {
4831
+ orderCount: number;
4832
+ totalPaid: number;
4833
+ lastOrderedAt: string | null;
4834
+ canceledOrReturnedCount: number;
4835
+ reviewCount: number;
4836
+ inquiryCount: number;
4837
+ };
4838
+ }>;
4839
+ orders: (customerId: string, params?: PageParams) => Promise<{
4840
+ page: number;
4841
+ size: number;
4842
+ totalElements: number;
4843
+ totalPages: number;
4844
+ contents: {
4845
+ orderId: string;
4846
+ orderedAt: string;
4847
+ totalAmount: number;
4848
+ testPayment: boolean;
4849
+ items: {
4850
+ orderItemId: string;
4851
+ productName: string;
4852
+ optionName: string | null;
4853
+ quantity: number;
4854
+ status: "PAID" | "PAYMENT_WAITING" | "CONFIRMED" | "DISPATCHED" | "DELIVERING" | "DELIVERED" | "PURCHASE_DECIDED" | "CANCELED";
4855
+ claimStatus: "CANCEL_REQUESTED" | "RETURN_REQUESTED" | "EXCHANGE_REQUESTED" | null;
4856
+ }[];
4857
+ }[];
4858
+ }>;
4859
+ withdraw: (customerId: string, body: WithdrawCustomerRequest) => Promise<void>;
4860
+ };
4467
4861
  customerInquiries: {
4468
4862
  list: (params?: PageParams & {
4469
4863
  answered?: boolean;
@@ -5723,6 +6117,8 @@ export declare function createStoreClient(options: StoreClientOptions): {
5723
6117
  businessNumber: string | null;
5724
6118
  customerCenterPhone: string | null;
5725
6119
  analyticsAllowedOrigins: string[];
6120
+ storefrontOrigins: string[];
6121
+ guestCheckoutEnabled: boolean;
5726
6122
  memberCount: number;
5727
6123
  createdAt: string;
5728
6124
  }>;
@@ -5740,6 +6136,8 @@ export declare function createStoreClient(options: StoreClientOptions): {
5740
6136
  businessNumber: string | null;
5741
6137
  customerCenterPhone: string | null;
5742
6138
  analyticsAllowedOrigins: string[];
6139
+ storefrontOrigins: string[];
6140
+ guestCheckoutEnabled: boolean;
5743
6141
  memberCount: number;
5744
6142
  createdAt: string;
5745
6143
  }>;
@@ -5849,6 +6247,7 @@ export declare const STORE_SCOPE_RESOURCES: {
5849
6247
  readonly webhook: readonly ["r", "rw"];
5850
6248
  readonly store: readonly ["r", "rw"];
5851
6249
  readonly member: readonly ["r", "rw"];
6250
+ readonly customer: readonly ["r", "rw"];
5852
6251
  };
5853
6252
  export type StoreScopeResource = keyof typeof STORE_SCOPE_RESOURCES;
5854
6253
  export declare const storeScopeResourceSchema: z.ZodEnum<{
@@ -5863,6 +6262,7 @@ export declare const storeScopeResourceSchema: z.ZodEnum<{
5863
6262
  webhook: "webhook";
5864
6263
  store: "store";
5865
6264
  member: "member";
6265
+ customer: "customer";
5866
6266
  }>;
5867
6267
  /** 카탈로그상 존재하는 스코프 문자열인가 (예: `settlement:rw`는 false) */
5868
6268
  export declare function isStoreScope(scope: string): boolean;
@@ -5872,9 +6272,9 @@ export declare function isStoreScope(scope: string): boolean;
5872
6272
  * 발급 시 지정한 스코프만 갖는다(발급자 보유 스코프 이내).
5873
6273
  */
5874
6274
  export declare const STORE_ROLE_SCOPES: {
5875
- readonly OWNER: readonly ["product:rw", "category:rw", "order:rw", "claim:rw", "inquiry:rw", "review:rw", "settlement:r", "analytics:r", "webhook:rw", "store:rw", "member:rw"];
5876
- readonly ADMIN: readonly ["product:rw", "category:rw", "order:rw", "claim:rw", "inquiry:rw", "review:rw", "analytics:r", "webhook:rw", "store:rw", "member:rw"];
5877
- readonly STAFF: readonly ["product:rw", "category:rw", "order:rw", "claim:rw", "inquiry:rw", "review:rw"];
6275
+ readonly OWNER: readonly ["product:rw", "category:rw", "order:rw", "claim:rw", "inquiry:rw", "review:rw", "settlement:r", "analytics:r", "webhook:rw", "store:rw", "member:rw", "customer:rw"];
6276
+ readonly ADMIN: readonly ["product:rw", "category:rw", "order:rw", "claim:rw", "inquiry:rw", "review:rw", "analytics:r", "webhook:rw", "store:rw", "member:rw", "customer:rw"];
6277
+ readonly STAFF: readonly ["product:rw", "category:rw", "order:rw", "claim:rw", "inquiry:rw", "review:rw", "customer:r"];
5878
6278
  };
5879
6279
  //#endregion
5880
6280
  //#region src/schemas/webhook-event.d.ts
package/dist/index.mjs CHANGED
@@ -698,6 +698,160 @@ const approveClaimResultSchema = z.object({
698
698
  refundedAt: z.string()
699
699
  });
700
700
  //#endregion
701
+ //#region src/schemas/customer.ts
702
+ /** 구매자 목록 정렬 */
703
+ const customerSortSchema = z.enum([
704
+ "joined_desc",
705
+ "joined_asc",
706
+ "total_paid_desc",
707
+ "order_count_desc",
708
+ "last_ordered_desc"
709
+ ]).describe("정렬. `joined_desc`(기본, 최근 가입순)·`joined_asc`·`total_paid_desc`(구매 금액순)·`order_count_desc`(주문 수순)·`last_ordered_desc`(최근 주문순)");
710
+ /** `GET /v1/customers` 쿼리 */
711
+ const customerListQuerySchema = z.object({
712
+ q: z.string().optional().describe("검색어 — 이름·이메일(포함). 숫자 4자리면 휴대폰 뒤 4자리로도 찾는다"),
713
+ joinedFrom: z.string().optional().describe("가입일 시작(YYYY-MM-DD, KST, 포함)"),
714
+ joinedTo: z.string().optional().describe("가입일 끝(YYYY-MM-DD, KST, 포함)"),
715
+ marketing: z.enum(["agreed", "declined"]).optional().describe("마케팅 수신 동의 필터. 생략하면 전체"),
716
+ sort: customerSortSchema.optional(),
717
+ ...pageQueryShape()
718
+ });
719
+ /** 구매 통계 — 구매 금액은 취소된 주문 상품을 빼고, 완료된 반품은 수량 비율로 뺀 금액이다 */
720
+ const customerStatsSchema = z.object({
721
+ orderCount: z.number().int().describe("주문 수(취소 포함)"),
722
+ totalPaid: z.number().int().describe("구매 금액(원) — 취소·완료된 반품 제외"),
723
+ lastOrderedAt: z.string().nullable().describe("최근 주문 시각")
724
+ });
725
+ /** 목록 행 — 휴대폰은 가운데를 가린다(`010-****-1234`) */
726
+ const customerSummarySchema = z.object({
727
+ customerId: z.string(),
728
+ name: z.string(),
729
+ /** 소셜 가입에서 이메일을 받지 못했으면 null */
730
+ email: z.string().nullable(),
731
+ maskedPhone: z.string().nullable(),
732
+ joinedAt: z.string(),
733
+ point: z.number().int(),
734
+ marketingAgreed: z.boolean()
735
+ }).extend(customerStatsSchema.shape);
736
+ const customerAddressSchema = z.object({
737
+ addressId: z.string(),
738
+ alias: z.string(),
739
+ isDefault: z.boolean(),
740
+ receiverName: z.string(),
741
+ phone: z.string(),
742
+ zipCode: z.string(),
743
+ address1: z.string(),
744
+ address2: z.string().nullable()
745
+ });
746
+ /** 구매자 상세 — 휴대폰 전체를 보여 준다 */
747
+ const customerDetailSchema = z.object({
748
+ customerId: z.string(),
749
+ name: z.string(),
750
+ email: z.string().nullable(),
751
+ phone: z.string().nullable(),
752
+ joinedAt: z.string(),
753
+ point: z.number().int(),
754
+ marketingAgreed: z.boolean(),
755
+ loginMethods: z.array(z.object({
756
+ method: z.enum([
757
+ "password",
758
+ "kakao",
759
+ "naver",
760
+ "google"
761
+ ]),
762
+ /** 소셜 연결 시각. 비밀번호는 가입 시각 */
763
+ linkedAt: z.string()
764
+ })).describe("로그인 방법 — 이메일·비밀번호(`password`)와 연결된 소셜 계정"),
765
+ addresses: z.array(customerAddressSchema),
766
+ stats: customerStatsSchema.extend({
767
+ canceledOrReturnedCount: z.number().int().describe("취소된 주문 상품 + 완료된 반품 수"),
768
+ reviewCount: z.number().int(),
769
+ inquiryCount: z.number().int().describe("상품 문의 + 1:1 문의")
770
+ })
771
+ });
772
+ /** 구매자의 주문 — 전체 기간 */
773
+ const customerOrderSchema = z.object({
774
+ orderId: z.string(),
775
+ orderedAt: z.string(),
776
+ totalAmount: z.number().int().describe("결제 금액"),
777
+ testPayment: z.boolean(),
778
+ items: z.array(z.object({
779
+ orderItemId: z.string(),
780
+ productName: z.string(),
781
+ optionName: z.string().nullable(),
782
+ quantity: z.number().int(),
783
+ status: orderItemStatusSchema,
784
+ claimStatus: z.enum([
785
+ "CANCEL_REQUESTED",
786
+ "RETURN_REQUESTED",
787
+ "EXCHANGE_REQUESTED"
788
+ ]).nullable()
789
+ }))
790
+ });
791
+ /** `POST /v1/customers/{id}/withdraw` 본문 */
792
+ const withdrawCustomerRequestSchema = z.object({
793
+ reason: z.enum([
794
+ "BUYER_REQUEST",
795
+ "DORMANT",
796
+ "ABUSE",
797
+ "OTHER"
798
+ ]).describe("처리 사유 — 구매자 요청·장기 미이용·부정 이용·기타. 감사 로그에 남는다"),
799
+ memo: z.string().max(200).optional().describe("사유 보충(선택)")
800
+ });
801
+ /** `WITHDRAWAL_BLOCKED`의 `error.details` — 탈퇴를 막는 항목 수(스토어프런트 탈퇴와 같다) */
802
+ const customerWithdrawalBlockedDetailsSchema = z.object({
803
+ inProgressOrderItems: z.number().int(),
804
+ openClaims: z.number().int(),
805
+ pendingPayments: z.number().int()
806
+ });
807
+ //#endregion
808
+ //#region src/schemas/login.ts
809
+ /** 소셜 로그인 공급자 */
810
+ const loginProviderSchema = z.enum([
811
+ "kakao",
812
+ "naver",
813
+ "google"
814
+ ]);
815
+ /** 공급자 연결 상태 — 비밀 값은 앞 4자리만 */
816
+ const loginProviderStatusSchema = z.object({
817
+ provider: loginProviderSchema,
818
+ connected: z.boolean(),
819
+ /** 스토어프런트에서 사용 중인지. 연결 안 됐으면 false */
820
+ enabled: z.boolean(),
821
+ clientIdPrefix: z.string().nullable().describe("클라이언트 ID(카카오 REST API 키) 앞 4자리"),
822
+ secretPrefix: z.string().nullable(),
823
+ lastTestedAt: z.string().nullable(),
824
+ lastTestOk: z.boolean().nullable(),
825
+ linkedMemberCount: z.number().int().describe("이 공급자로 연결된 구매자 수"),
826
+ /** 이 공급자로만 로그인하는 구매자 수 — 연결을 해제하면 로그인할 방법이 없어진다 */
827
+ onlyMethodMemberCount: z.number().int(),
828
+ callbackUrl: z.url().describe("공급자 개발자 콘솔에 등록할 리디렉트 URI")
829
+ });
830
+ /** `GET /v1/store/login-settings` */
831
+ const loginSettingsSchema = z.object({
832
+ passwordLoginEnabled: z.boolean(),
833
+ /** 비밀번호로만 로그인하는 구매자 수 — 이메일·비밀번호를 끄면 이들은 로그인할 수 없다 */
834
+ passwordOnlyMemberCount: z.number().int(),
835
+ storefrontOrigins: z.array(z.string()),
836
+ providers: z.array(loginProviderStatusSchema)
837
+ });
838
+ /** `PATCH /v1/store/login-settings` */
839
+ const updateLoginSettingsRequestSchema = z.object({ passwordLoginEnabled: z.boolean() });
840
+ /** `PUT /v1/store/auth-providers/{provider}` — 저장 전에 연결 테스트를 한다 */
841
+ const saveLoginProviderRequestSchema = z.object({
842
+ clientId: z.string().trim().min(1).max(200),
843
+ clientSecret: z.string().trim().min(1).max(500)
844
+ });
845
+ /** `PATCH /v1/store/auth-providers/{provider}` */
846
+ const updateLoginProviderRequestSchema = z.object({ enabled: z.boolean() });
847
+ /** 연결 테스트 결과 */
848
+ const loginProviderTestSchema = z.object({
849
+ ok: z.boolean(),
850
+ /** 실패 이유 — `INVALID_CLIENT`(키가 틀림)·`PROVIDER_UNAVAILABLE`(공급자 응답 없음) */
851
+ code: z.enum(["INVALID_CLIENT", "PROVIDER_UNAVAILABLE"]).nullable(),
852
+ testedAt: z.string()
853
+ });
854
+ //#endregion
701
855
  //#region src/schemas/misc.ts
702
856
  const inquirySchema = z.object({
703
857
  inquiryId: z.string(),
@@ -1729,7 +1883,9 @@ const updateStoreRequestSchema = z.object({
1729
1883
  logoUrl: z.url().optional(),
1730
1884
  customerCenterPhone: z.string().optional(),
1731
1885
  businessHours: z.string().max(100).optional(),
1732
- analyticsAllowedOrigins: z.array(analyticsOriginSchema).max(20).optional().describe("방문 분석 수집을 받을 스토어프런트 오리진(최대 20개). 비우면 모든 오리진에서 받는다. 넣으면 목록에 없는 오리진의 수집 요청은 403 `ORIGIN_NOT_ALLOWED`이고, 페이지 URL이 목록 밖인 이벤트는 버린다")
1886
+ analyticsAllowedOrigins: z.array(analyticsOriginSchema).max(20).optional().describe("@deprecated `storefrontOrigins`를 쓴다(같은 목록). 방문 분석 수집을 받을 스토어프런트 오리진(최대 20개). 비우면 모든 오리진에서 받는다. 넣으면 목록에 없는 오리진의 수집 요청은 403 `ORIGIN_NOT_ALLOWED`이고, 페이지 URL이 목록 밖인 이벤트는 버린다"),
1887
+ storefrontOrigins: z.array(analyticsOriginSchema).max(20).optional().describe("스토어프런트 도메인(최대 20개) — 소셜 로그인 뒤 돌아갈 주소와 방문 분석 수집을 함께 제한한다. `analyticsAllowedOrigins`와 같은 목록이다(둘 다 주면 이 값이 이긴다)"),
1888
+ guestCheckoutEnabled: z.boolean().optional().describe("비회원 주문 허용. false(회원만)면 로그인하지 않은 구매자의 주문서 생성·결제 요청이 403 `GUEST_CHECKOUT_DISABLED`다. 장바구니 담기와 이미 받은 비회원 주문의 조회·취소는 막지 않는다")
1733
1889
  }).refine((value) => Object.values(value).some((v) => v !== void 0), { message: "최소 1개 필드가 필요합니다" });
1734
1890
  const storeSchema = z.object({
1735
1891
  storeId: z.string(),
@@ -1745,6 +1901,8 @@ const storeSchema = z.object({
1745
1901
  businessNumber: z.string().nullable(),
1746
1902
  customerCenterPhone: z.string().nullable(),
1747
1903
  analyticsAllowedOrigins: z.array(z.string()).describe("방문 분석 수집을 받는 스토어프런트 오리진. 비어 있으면 모든 오리진에서 받는다"),
1904
+ storefrontOrigins: z.array(z.string()).describe("스토어프런트 도메인 — 소셜 로그인 리디렉트와 방문 분석 수집이 함께 쓴다(`analyticsAllowedOrigins`와 같은 값)"),
1905
+ guestCheckoutEnabled: z.boolean().describe("비회원 주문 허용. false면 회원만 주문할 수 있다"),
1748
1906
  memberCount: z.number().int(),
1749
1907
  createdAt: z.string()
1750
1908
  });
@@ -2052,6 +2210,26 @@ function createStoreClient(options) {
2052
2210
  answer: (inquiryId, content) => storeHttp.requestVoid("POST", path`/inquiries/${inquiryId}/answer`, { body: { content } }),
2053
2211
  updateAnswer: (inquiryId, content) => storeHttp.requestVoid("PUT", path`/inquiries/${inquiryId}/answer`, { body: { content } })
2054
2212
  },
2213
+ /**
2214
+ * 구매자 로그인 설정 — 이메일·비밀번호 허용, 소셜 공급자 연결. 비밀 값을 다루므로 콘솔에 로그인한 OWNER만 호출할 수 있다
2215
+ */
2216
+ loginSettings: {
2217
+ get: () => storeHttp.request("GET", "/store/login-settings", loginSettingsSchema),
2218
+ update: (body) => storeHttp.request("PATCH", "/store/login-settings", loginSettingsSchema, { body: updateLoginSettingsRequestSchema.parse(body) }),
2219
+ /** 키 저장 — 저장 전에 연결 테스트를 하고, 실패하면 422 `LOGIN_PROVIDER_VERIFICATION_FAILED` */
2220
+ saveProvider: (provider, body) => storeHttp.request("PUT", path`/store/auth-providers/${provider}`, loginSettingsSchema, { body: saveLoginProviderRequestSchema.parse(body) }),
2221
+ setProviderEnabled: (provider, enabled) => storeHttp.request("PATCH", path`/store/auth-providers/${provider}`, loginSettingsSchema, { body: { enabled } }),
2222
+ testProvider: (provider) => storeHttp.request("POST", path`/store/auth-providers/${provider}/test`, loginProviderTestSchema),
2223
+ /** 연결 해제 — 키를 지운다. 연결된 구매자 계정은 남는다 */
2224
+ removeProvider: (provider) => storeHttp.request("DELETE", path`/store/auth-providers/${provider}`, loginSettingsSchema)
2225
+ },
2226
+ /** 구매자 관리 — 조회 `customer:r`, 탈퇴 처리 `customer:rw`(콘솔 OWNER·ADMIN) */
2227
+ customers: {
2228
+ list: (params) => storeHttp.request("GET", "/customers", pageSchema(customerSummarySchema), { query: params }),
2229
+ get: (customerId) => storeHttp.request("GET", path`/customers/${customerId}`, customerDetailSchema),
2230
+ orders: (customerId, params) => storeHttp.request("GET", path`/customers/${customerId}/orders`, pageSchema(customerOrderSchema), { query: params }),
2231
+ withdraw: (customerId, body) => storeHttp.requestVoid("POST", path`/customers/${customerId}/withdraw`, { body: withdrawCustomerRequestSchema.parse(body) })
2232
+ },
2055
2233
  customerInquiries: {
2056
2234
  list: (params) => storeHttp.request("GET", "/customer-inquiries", pageSchema(sellerCustomerInquirySchema), { query: params }),
2057
2235
  get: (inquiryId) => storeHttp.request("GET", path`/customer-inquiries/${inquiryId}`, sellerCustomerInquirySchema),
@@ -2344,7 +2522,8 @@ const STORE_SCOPE_RESOURCES = {
2344
2522
  analytics: ["r"],
2345
2523
  webhook: ["r", "rw"],
2346
2524
  store: ["r", "rw"],
2347
- member: ["r", "rw"]
2525
+ member: ["r", "rw"],
2526
+ customer: ["r", "rw"]
2348
2527
  };
2349
2528
  const storeScopeResourceSchema = z.enum(Object.keys(STORE_SCOPE_RESOURCES)).describe("스코프 리소스");
2350
2529
  /** 카탈로그상 존재하는 스코프 문자열인가 (예: `settlement:rw`는 false) */
@@ -2370,7 +2549,8 @@ const STORE_ROLE_SCOPES = {
2370
2549
  "analytics:r",
2371
2550
  "webhook:rw",
2372
2551
  "store:rw",
2373
- "member:rw"
2552
+ "member:rw",
2553
+ "customer:rw"
2374
2554
  ],
2375
2555
  ADMIN: [
2376
2556
  "product:rw",
@@ -2382,7 +2562,8 @@ const STORE_ROLE_SCOPES = {
2382
2562
  "analytics:r",
2383
2563
  "webhook:rw",
2384
2564
  "store:rw",
2385
- "member:rw"
2565
+ "member:rw",
2566
+ "customer:rw"
2386
2567
  ],
2387
2568
  STAFF: [
2388
2569
  "product:rw",
@@ -2390,7 +2571,8 @@ const STORE_ROLE_SCOPES = {
2390
2571
  "order:rw",
2391
2572
  "claim:rw",
2392
2573
  "inquiry:rw",
2393
- "review:rw"
2574
+ "review:rw",
2575
+ "customer:r"
2394
2576
  ]
2395
2577
  };
2396
2578
  //#endregion
@@ -2621,4 +2803,4 @@ function createStoreSession(options) {
2621
2803
  };
2622
2804
  }
2623
2805
  //#endregion
2624
- export { ApiError, MAX_ANALYTICS_ALLOWED_ORIGINS, MAX_OPTION_GROUPS, MAX_VALUES_PER_GROUP, MAX_VARIANTS, PAYMENT_ROUTING_EXCLUSION_CODES, PORTONE_CHANNEL_NAME_MAX_LENGTH, PORTONE_MAX_CHANNELS, PORTONE_MIN_CHANNELS, RESERVED_STORE_CODES, 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, VARIANT_STOCK_EXCLUSIVE_MESSAGE, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_HEADERS, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, addressChangeRequestListSchema, addressTypeSchema, analyticsChannelSchema, analyticsFunnelStepSchema, analyticsOriginSchema, analyticsOverviewSchema, analyticsProductRowSchema, analyticsProductSortSchema, analyticsProductsQuerySchema, analyticsProductsSchema, analyticsRangeQuerySchema, analyticsSourceGroupBySchema, analyticsSourceRowSchema, analyticsSourcesQuerySchema, analyticsSourcesSchema, analyticsTotalsSchema, apiClientListQuerySchema, apiClientSchema, apiClientSecretRotatedSchema, apiClientStatusSchema, apiClientTypeSchema, apiLogErrorSchema, apiLogListQuerySchema, apiLogPageSchema, apiLogPrincipalSchema, apiLogPrincipalTypeSchema, apiLogSchema, apiLogStatusFilterSchema, apiLogSummarySchema, apiTokenCreateRequestSchema, apiTokenCreatedSchema, apiTokenSchema, approveClaimResultSchema, buildAuthorizeUrl, buildQuery, bulkResultSchema, cancelBySellerRequestSchema, cancelBySellerResultSchema, carrierSchema, categoryAttributeSchema, categoryCreateRequestSchema, categoryDeleteQuerySchema, categoryDeleteStrategySchema, categoryListQuerySchema, categoryPatchRequestSchema, categoryProductSortSchema, categorySchema, categorySearchResultSchema, categoryTreeNodeSchema, categoryWithPathSchema, changeTypeSchema, claimDetailSchema, claimHoldResultSchema, claimListQuerySchema, claimReasonSchema, claimSchema, claimStatusSchema, claimTypeSchema, claimWebhookEventDataSchema, claimWebhookEventSchema, clientCredentialsTokenResponseSchema, createPkcePair, createStoreClient, createStoreSession, customerInquiryCategorySchema, customerInquiryListQuerySchema, deliveryInfoSchema, deliveryMethodSchema, deliveryStatusSchema, deliveryTypeSchema, discountRequestSchema, discountSchema, dispatchRequestSchema, effectivePaymentRoutingModeSchema, eventTypeSchema, exchangeAuthorizationCode, exchangeStoreTokenViaOAuth, holdOrderItemRequestSchema, inquiryListQuerySchema, inquirySchema, inquiryWebhookEventDataSchema, inquiryWebhookEventSchema, invitationListQuerySchema, invitationSchema, isReservedStoreCode, isStoreScope, isWebOrigin, liveGateConditionCodeSchema, liveGateConditionSchema, liveGateErrorDetailsSchema, liveGateSchema, maskedSecretSchema, myAccountSchema, myStoreSchema, oauthClientRegistrationRequestSchema, oauthClientRegistrationResponseSchema, oauthTokenResponseSchema, optionGroupRequestSchema, optionGroupSchema, optionModelRequestSchema, optionModelSchema, optionValueRequestSchema, optionValueSchema, orderItemListQuerySchema, orderItemMemoResultSchema, orderItemPageSchema, orderItemSchema, orderItemStatusSchema, orderListQuerySchema, orderSchema, orderSummaryPageSchema, orderSummarySchema, orderWebhookEventDataSchema, orderWebhookEventSchema, pageInfoSchema, pageQueryShape, pageSchema, paymentAttemptRecordSchema, paymentAttemptStatusSchema, paymentCredentialSetSchema, paymentListQuerySchema, paymentProviderSchema, paymentProviderVerificationSchema, paymentRecordDetailSchema, paymentRecordPageSchema, paymentRecordSchema, paymentRecordStatusSchema, paymentRefundRecordSchema, paymentRoutingModeSchema, paymentSettingsSchema, pgEnvironmentSchema, pgPaymentMethodSchema, pgProviderSchema, pgVerificationResultSchema, portOneChannelSchema, portOneCredentialsInputSchema, productCreateRequestSchema, productImageUploadResultSchema, productListQuerySchema, productOutOfStockWebhookEventDataSchema, productOutOfStockWebhookEventSchema, productPatchRequestSchema, productSchema, productSortSchema, productStatusSchema, productSummarySchema, productVariantRequestSchema, productVariantSchema, purchaseDecisionExtendResultSchema, purchaseDecisionRequestResultSchema, refreshUserToken, rejectClaimRequestSchema, reviewListQuerySchema, reviewReportResultSchema, reviewSchema, reviewSortSchema, reviewWebhookEventDataSchema, reviewWebhookEventSchema, salesReportGroupBySchema, salesReportQuerySchema, salesReportRowSchema, salesReportSchema, salesTotalsSchema, scopeAccessSchema, sellerAddressListQuerySchema, sellerAddressRequestSchema, sellerAddressSchema, sellerCustomerInquirySchema, settlementDetailQuerySchema, settlementDetailSchema, settlementListQuerySchema, settlementStatusSchema, settlementSummarySchema, settlementWithDetailsSchema, shippingAddressSchema, storeCodeSchema, storeMemberRoleUpdatedSchema, storeMemberSchema, storeRoleSchema, storeSchema, storeScopeResourceSchema, storeTokenResponseSchema, tossPaymentsCredentialsInputSchema, trackingUpdateResultSchema, updatePaymentProviderRequestSchema, updatePaymentSettingsRequestSchema, updateStoreRequestSchema, uploadRequestSchema, uploadTicketSchema, upsertPortOneProviderRequestSchema, upsertTossPaymentsProviderRequestSchema, userTokenResponseSchema, variantStockRequestSchema, variantStockSchema, verifyPaymentProviderRequestSchema, webhookCreatedSchema, webhookDeliveryErrorCodeSchema, webhookDeliveryListQuerySchema, webhookDeliverySchema, webhookDeliveryStatusSchema, webhookEventSchema, webhookRedeliverFailedResultSchema, webhookRequestSchema, webhookSchema, webhookSecretRotatedSchema, webhookStatusSchema, webhookTestRequestSchema, webhookTestResultSchema };
2806
+ export { ApiError, MAX_ANALYTICS_ALLOWED_ORIGINS, MAX_OPTION_GROUPS, MAX_VALUES_PER_GROUP, MAX_VARIANTS, PAYMENT_ROUTING_EXCLUSION_CODES, PORTONE_CHANNEL_NAME_MAX_LENGTH, PORTONE_MAX_CHANNELS, PORTONE_MIN_CHANNELS, RESERVED_STORE_CODES, 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, VARIANT_STOCK_EXCLUSIVE_MESSAGE, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_HEADERS, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, addressChangeRequestListSchema, addressTypeSchema, analyticsChannelSchema, analyticsFunnelStepSchema, analyticsOriginSchema, analyticsOverviewSchema, analyticsProductRowSchema, analyticsProductSortSchema, analyticsProductsQuerySchema, analyticsProductsSchema, analyticsRangeQuerySchema, analyticsSourceGroupBySchema, analyticsSourceRowSchema, analyticsSourcesQuerySchema, analyticsSourcesSchema, analyticsTotalsSchema, apiClientListQuerySchema, apiClientSchema, apiClientSecretRotatedSchema, apiClientStatusSchema, apiClientTypeSchema, apiLogErrorSchema, apiLogListQuerySchema, apiLogPageSchema, apiLogPrincipalSchema, apiLogPrincipalTypeSchema, apiLogSchema, apiLogStatusFilterSchema, apiLogSummarySchema, apiTokenCreateRequestSchema, apiTokenCreatedSchema, apiTokenSchema, approveClaimResultSchema, buildAuthorizeUrl, buildQuery, bulkResultSchema, cancelBySellerRequestSchema, cancelBySellerResultSchema, carrierSchema, categoryAttributeSchema, categoryCreateRequestSchema, categoryDeleteQuerySchema, categoryDeleteStrategySchema, categoryListQuerySchema, categoryPatchRequestSchema, categoryProductSortSchema, categorySchema, categorySearchResultSchema, categoryTreeNodeSchema, categoryWithPathSchema, changeTypeSchema, claimDetailSchema, claimHoldResultSchema, claimListQuerySchema, claimReasonSchema, claimSchema, claimStatusSchema, claimTypeSchema, claimWebhookEventDataSchema, claimWebhookEventSchema, clientCredentialsTokenResponseSchema, createPkcePair, createStoreClient, createStoreSession, customerAddressSchema, customerDetailSchema, customerInquiryCategorySchema, customerInquiryListQuerySchema, customerListQuerySchema, customerOrderSchema, customerSortSchema, customerStatsSchema, customerSummarySchema, customerWithdrawalBlockedDetailsSchema, deliveryInfoSchema, deliveryMethodSchema, deliveryStatusSchema, deliveryTypeSchema, discountRequestSchema, discountSchema, dispatchRequestSchema, effectivePaymentRoutingModeSchema, eventTypeSchema, exchangeAuthorizationCode, exchangeStoreTokenViaOAuth, holdOrderItemRequestSchema, inquiryListQuerySchema, inquirySchema, inquiryWebhookEventDataSchema, inquiryWebhookEventSchema, invitationListQuerySchema, invitationSchema, isReservedStoreCode, isStoreScope, isWebOrigin, liveGateConditionCodeSchema, liveGateConditionSchema, liveGateErrorDetailsSchema, liveGateSchema, loginProviderSchema, loginProviderStatusSchema, loginProviderTestSchema, loginSettingsSchema, maskedSecretSchema, myAccountSchema, myStoreSchema, oauthClientRegistrationRequestSchema, oauthClientRegistrationResponseSchema, oauthTokenResponseSchema, optionGroupRequestSchema, optionGroupSchema, optionModelRequestSchema, optionModelSchema, optionValueRequestSchema, optionValueSchema, orderItemListQuerySchema, orderItemMemoResultSchema, orderItemPageSchema, orderItemSchema, orderItemStatusSchema, orderListQuerySchema, orderSchema, orderSummaryPageSchema, orderSummarySchema, orderWebhookEventDataSchema, orderWebhookEventSchema, pageInfoSchema, pageQueryShape, pageSchema, paymentAttemptRecordSchema, paymentAttemptStatusSchema, paymentCredentialSetSchema, paymentListQuerySchema, paymentProviderSchema, paymentProviderVerificationSchema, paymentRecordDetailSchema, paymentRecordPageSchema, paymentRecordSchema, paymentRecordStatusSchema, paymentRefundRecordSchema, paymentRoutingModeSchema, paymentSettingsSchema, pgEnvironmentSchema, pgPaymentMethodSchema, pgProviderSchema, pgVerificationResultSchema, portOneChannelSchema, portOneCredentialsInputSchema, productCreateRequestSchema, productImageUploadResultSchema, productListQuerySchema, productOutOfStockWebhookEventDataSchema, productOutOfStockWebhookEventSchema, productPatchRequestSchema, productSchema, productSortSchema, productStatusSchema, productSummarySchema, productVariantRequestSchema, productVariantSchema, purchaseDecisionExtendResultSchema, purchaseDecisionRequestResultSchema, refreshUserToken, rejectClaimRequestSchema, reviewListQuerySchema, reviewReportResultSchema, reviewSchema, reviewSortSchema, reviewWebhookEventDataSchema, reviewWebhookEventSchema, salesReportGroupBySchema, salesReportQuerySchema, salesReportRowSchema, salesReportSchema, salesTotalsSchema, saveLoginProviderRequestSchema, scopeAccessSchema, sellerAddressListQuerySchema, sellerAddressRequestSchema, sellerAddressSchema, sellerCustomerInquirySchema, settlementDetailQuerySchema, settlementDetailSchema, settlementListQuerySchema, settlementStatusSchema, settlementSummarySchema, settlementWithDetailsSchema, shippingAddressSchema, storeCodeSchema, storeMemberRoleUpdatedSchema, storeMemberSchema, storeRoleSchema, storeSchema, storeScopeResourceSchema, storeTokenResponseSchema, tossPaymentsCredentialsInputSchema, trackingUpdateResultSchema, updateLoginProviderRequestSchema, updateLoginSettingsRequestSchema, updatePaymentProviderRequestSchema, updatePaymentSettingsRequestSchema, updateStoreRequestSchema, uploadRequestSchema, uploadTicketSchema, upsertPortOneProviderRequestSchema, upsertTossPaymentsProviderRequestSchema, userTokenResponseSchema, variantStockRequestSchema, variantStockSchema, verifyPaymentProviderRequestSchema, webhookCreatedSchema, webhookDeliveryErrorCodeSchema, webhookDeliveryListQuerySchema, webhookDeliverySchema, webhookDeliveryStatusSchema, webhookEventSchema, webhookRedeliverFailedResultSchema, webhookRequestSchema, webhookSchema, webhookSecretRotatedSchema, webhookStatusSchema, webhookTestRequestSchema, webhookTestResultSchema, withdrawCustomerRequestSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sayren/store-sdk",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "main": "./dist/index.mjs",
5
5
  "types": "./dist/index.d.mts",
6
6
  "exports": {
package/src/client.ts CHANGED
@@ -52,6 +52,23 @@ import {
52
52
  rejectClaimRequestSchema,
53
53
  } from "./schemas/claim";
54
54
  import { bulkResultSchema, type PageParams, pageSchema } from "./schemas/common";
55
+ import {
56
+ customerDetailSchema,
57
+ type customerListQuerySchema,
58
+ customerOrderSchema,
59
+ customerSummarySchema,
60
+ type WithdrawCustomerRequest,
61
+ withdrawCustomerRequestSchema,
62
+ } from "./schemas/customer";
63
+ import {
64
+ type LoginProvider,
65
+ loginProviderTestSchema,
66
+ loginSettingsSchema,
67
+ type SaveLoginProviderRequest,
68
+ saveLoginProviderRequestSchema,
69
+ type UpdateLoginSettingsRequest,
70
+ updateLoginSettingsRequestSchema,
71
+ } from "./schemas/login";
55
72
  import {
56
73
  type CustomerInquiryCategory,
57
74
  inquirySchema,
@@ -559,6 +576,60 @@ export function createStoreClient(options: StoreClientOptions) {
559
576
  storeHttp.requestVoid("PUT", path`/inquiries/${inquiryId}/answer`, { body: { content } }),
560
577
  },
561
578
 
579
+ /**
580
+ * 구매자 로그인 설정 — 이메일·비밀번호 허용, 소셜 공급자 연결. 비밀 값을 다루므로 콘솔에 로그인한 OWNER만 호출할 수 있다
581
+ */
582
+ loginSettings: {
583
+ get: () => storeHttp.request("GET", "/store/login-settings", loginSettingsSchema),
584
+ update: (body: UpdateLoginSettingsRequest) =>
585
+ storeHttp.request("PATCH", "/store/login-settings", loginSettingsSchema, {
586
+ body: updateLoginSettingsRequestSchema.parse(body),
587
+ }),
588
+ /** 키 저장 — 저장 전에 연결 테스트를 하고, 실패하면 422 `LOGIN_PROVIDER_VERIFICATION_FAILED` */
589
+ saveProvider: (provider: LoginProvider, body: SaveLoginProviderRequest) =>
590
+ storeHttp.request("PUT", path`/store/auth-providers/${provider}`, loginSettingsSchema, {
591
+ body: saveLoginProviderRequestSchema.parse(body),
592
+ }),
593
+ setProviderEnabled: (provider: LoginProvider, enabled: boolean) =>
594
+ storeHttp.request("PATCH", path`/store/auth-providers/${provider}`, loginSettingsSchema, {
595
+ body: { enabled },
596
+ }),
597
+ testProvider: (provider: LoginProvider) =>
598
+ storeHttp.request(
599
+ "POST",
600
+ path`/store/auth-providers/${provider}/test`,
601
+ loginProviderTestSchema,
602
+ ),
603
+ /** 연결 해제 — 키를 지운다. 연결된 구매자 계정은 남는다 */
604
+ removeProvider: (provider: LoginProvider) =>
605
+ storeHttp.request("DELETE", path`/store/auth-providers/${provider}`, loginSettingsSchema),
606
+ },
607
+
608
+ /** 구매자 관리 — 조회 `customer:r`, 탈퇴 처리 `customer:rw`(콘솔 OWNER·ADMIN) */
609
+ customers: {
610
+ list: (
611
+ params?: PageParams & Omit<z.input<typeof customerListQuerySchema>, "page" | "size">,
612
+ ) =>
613
+ storeHttp.request("GET", "/customers", pageSchema(customerSummarySchema), {
614
+ query: params as Query,
615
+ }),
616
+ get: (customerId: string) =>
617
+ storeHttp.request("GET", path`/customers/${customerId}`, customerDetailSchema),
618
+ orders: (customerId: string, params?: PageParams) =>
619
+ storeHttp.request(
620
+ "GET",
621
+ path`/customers/${customerId}/orders`,
622
+ pageSchema(customerOrderSchema),
623
+ {
624
+ query: params as Query,
625
+ },
626
+ ),
627
+ withdraw: (customerId: string, body: WithdrawCustomerRequest) =>
628
+ storeHttp.requestVoid("POST", path`/customers/${customerId}/withdraw`, {
629
+ body: withdrawCustomerRequestSchema.parse(body),
630
+ }),
631
+ },
632
+
562
633
  customerInquiries: {
563
634
  list: (
564
635
  params?: PageParams & {
package/src/index.ts CHANGED
@@ -18,6 +18,8 @@ export * from "./schemas/auth";
18
18
  export * from "./schemas/category";
19
19
  export * from "./schemas/claim";
20
20
  export * from "./schemas/common";
21
+ export * from "./schemas/customer";
22
+ export * from "./schemas/login";
21
23
  export * from "./schemas/misc";
22
24
  export * from "./schemas/order";
23
25
  export * from "./schemas/payment";
@@ -0,0 +1,126 @@
1
+ import { z } from "zod";
2
+ import { orderItemStatusSchema, pageQueryShape } from "./common";
3
+
4
+ /** 구매자 목록 정렬 */
5
+ export const customerSortSchema = z
6
+ .enum(["joined_desc", "joined_asc", "total_paid_desc", "order_count_desc", "last_ordered_desc"])
7
+ .describe(
8
+ "정렬. `joined_desc`(기본, 최근 가입순)·`joined_asc`·`total_paid_desc`(구매 금액순)·`order_count_desc`(주문 수순)·`last_ordered_desc`(최근 주문순)",
9
+ );
10
+ export type CustomerSort = z.infer<typeof customerSortSchema>;
11
+
12
+ /** `GET /v1/customers` 쿼리 */
13
+ export const customerListQuerySchema = z.object({
14
+ q: z
15
+ .string()
16
+ .optional()
17
+ .describe("검색어 — 이름·이메일(포함). 숫자 4자리면 휴대폰 뒤 4자리로도 찾는다"),
18
+ joinedFrom: z.string().optional().describe("가입일 시작(YYYY-MM-DD, KST, 포함)"),
19
+ joinedTo: z.string().optional().describe("가입일 끝(YYYY-MM-DD, KST, 포함)"),
20
+ marketing: z
21
+ .enum(["agreed", "declined"])
22
+ .optional()
23
+ .describe("마케팅 수신 동의 필터. 생략하면 전체"),
24
+ sort: customerSortSchema.optional(),
25
+ ...pageQueryShape(),
26
+ });
27
+
28
+ /** 구매 통계 — 구매 금액은 취소된 주문 상품을 빼고, 완료된 반품은 수량 비율로 뺀 금액이다 */
29
+ export const customerStatsSchema = z.object({
30
+ orderCount: z.number().int().describe("주문 수(취소 포함)"),
31
+ totalPaid: z.number().int().describe("구매 금액(원) — 취소·완료된 반품 제외"),
32
+ lastOrderedAt: z.string().nullable().describe("최근 주문 시각"),
33
+ });
34
+
35
+ /** 목록 행 — 휴대폰은 가운데를 가린다(`010-****-1234`) */
36
+ export const customerSummarySchema = z
37
+ .object({
38
+ customerId: z.string(),
39
+ name: z.string(),
40
+ /** 소셜 가입에서 이메일을 받지 못했으면 null */
41
+ email: z.string().nullable(),
42
+ maskedPhone: z.string().nullable(),
43
+ joinedAt: z.string(),
44
+ point: z.number().int(),
45
+ marketingAgreed: z.boolean(),
46
+ })
47
+ .extend(customerStatsSchema.shape);
48
+ export type CustomerSummary = z.infer<typeof customerSummarySchema>;
49
+
50
+ export const customerAddressSchema = z.object({
51
+ addressId: z.string(),
52
+ alias: z.string(),
53
+ isDefault: z.boolean(),
54
+ receiverName: z.string(),
55
+ phone: z.string(),
56
+ zipCode: z.string(),
57
+ address1: z.string(),
58
+ address2: z.string().nullable(),
59
+ });
60
+
61
+ /** 구매자 상세 — 휴대폰 전체를 보여 준다 */
62
+ export const customerDetailSchema = z.object({
63
+ customerId: z.string(),
64
+ name: z.string(),
65
+ email: z.string().nullable(),
66
+ phone: z.string().nullable(),
67
+ joinedAt: z.string(),
68
+ point: z.number().int(),
69
+ marketingAgreed: z.boolean(),
70
+ loginMethods: z
71
+ .array(
72
+ z.object({
73
+ method: z.enum(["password", "kakao", "naver", "google"]),
74
+ /** 소셜 연결 시각. 비밀번호는 가입 시각 */
75
+ linkedAt: z.string(),
76
+ }),
77
+ )
78
+ .describe("로그인 방법 — 이메일·비밀번호(`password`)와 연결된 소셜 계정"),
79
+ addresses: z.array(customerAddressSchema),
80
+ stats: customerStatsSchema.extend({
81
+ canceledOrReturnedCount: z.number().int().describe("취소된 주문 상품 + 완료된 반품 수"),
82
+ reviewCount: z.number().int(),
83
+ inquiryCount: z.number().int().describe("상품 문의 + 1:1 문의"),
84
+ }),
85
+ });
86
+ export type CustomerDetail = z.infer<typeof customerDetailSchema>;
87
+
88
+ /** 구매자의 주문 — 전체 기간 */
89
+ export const customerOrderSchema = z.object({
90
+ orderId: z.string(),
91
+ orderedAt: z.string(),
92
+ totalAmount: z.number().int().describe("결제 금액"),
93
+ testPayment: z.boolean(),
94
+ items: z.array(
95
+ z.object({
96
+ orderItemId: z.string(),
97
+ productName: z.string(),
98
+ optionName: z.string().nullable(),
99
+ quantity: z.number().int(),
100
+ status: orderItemStatusSchema,
101
+ claimStatus: z
102
+ .enum(["CANCEL_REQUESTED", "RETURN_REQUESTED", "EXCHANGE_REQUESTED"])
103
+ .nullable(),
104
+ }),
105
+ ),
106
+ });
107
+ export type CustomerOrder = z.infer<typeof customerOrderSchema>;
108
+
109
+ /** `POST /v1/customers/{id}/withdraw` 본문 */
110
+ export const withdrawCustomerRequestSchema = z.object({
111
+ reason: z
112
+ .enum(["BUYER_REQUEST", "DORMANT", "ABUSE", "OTHER"])
113
+ .describe("처리 사유 — 구매자 요청·장기 미이용·부정 이용·기타. 감사 로그에 남는다"),
114
+ memo: z.string().max(200).optional().describe("사유 보충(선택)"),
115
+ });
116
+ export type WithdrawCustomerRequest = z.infer<typeof withdrawCustomerRequestSchema>;
117
+
118
+ /** `WITHDRAWAL_BLOCKED`의 `error.details` — 탈퇴를 막는 항목 수(스토어프런트 탈퇴와 같다) */
119
+ export const customerWithdrawalBlockedDetailsSchema = z.object({
120
+ inProgressOrderItems: z.number().int(),
121
+ openClaims: z.number().int(),
122
+ pendingPayments: z.number().int(),
123
+ });
124
+ export type CustomerWithdrawalBlockedDetails = z.infer<
125
+ typeof customerWithdrawalBlockedDetailsSchema
126
+ >;
@@ -0,0 +1,59 @@
1
+ import { z } from "zod";
2
+
3
+ /** 소셜 로그인 공급자 */
4
+ export const loginProviderSchema = z.enum(["kakao", "naver", "google"]);
5
+ export type LoginProvider = z.infer<typeof loginProviderSchema>;
6
+
7
+ /** 공급자 연결 상태 — 비밀 값은 앞 4자리만 */
8
+ export const loginProviderStatusSchema = z.object({
9
+ provider: loginProviderSchema,
10
+ connected: z.boolean(),
11
+ /** 스토어프런트에서 사용 중인지. 연결 안 됐으면 false */
12
+ enabled: z.boolean(),
13
+ clientIdPrefix: z.string().nullable().describe("클라이언트 ID(카카오 REST API 키) 앞 4자리"),
14
+ secretPrefix: z.string().nullable(),
15
+ lastTestedAt: z.string().nullable(),
16
+ lastTestOk: z.boolean().nullable(),
17
+ linkedMemberCount: z.number().int().describe("이 공급자로 연결된 구매자 수"),
18
+ /** 이 공급자로만 로그인하는 구매자 수 — 연결을 해제하면 로그인할 방법이 없어진다 */
19
+ onlyMethodMemberCount: z.number().int(),
20
+ callbackUrl: z.url().describe("공급자 개발자 콘솔에 등록할 리디렉트 URI"),
21
+ });
22
+ export type LoginProviderStatus = z.infer<typeof loginProviderStatusSchema>;
23
+
24
+ /** `GET /v1/store/login-settings` */
25
+ export const loginSettingsSchema = z.object({
26
+ passwordLoginEnabled: z.boolean(),
27
+ /** 비밀번호로만 로그인하는 구매자 수 — 이메일·비밀번호를 끄면 이들은 로그인할 수 없다 */
28
+ passwordOnlyMemberCount: z.number().int(),
29
+ storefrontOrigins: z.array(z.string()),
30
+ providers: z.array(loginProviderStatusSchema),
31
+ });
32
+ export type LoginSettings = z.infer<typeof loginSettingsSchema>;
33
+
34
+ /** `PATCH /v1/store/login-settings` */
35
+ export const updateLoginSettingsRequestSchema = z.object({
36
+ passwordLoginEnabled: z.boolean(),
37
+ });
38
+ export type UpdateLoginSettingsRequest = z.infer<typeof updateLoginSettingsRequestSchema>;
39
+
40
+ /** `PUT /v1/store/auth-providers/{provider}` — 저장 전에 연결 테스트를 한다 */
41
+ export const saveLoginProviderRequestSchema = z.object({
42
+ clientId: z.string().trim().min(1).max(200),
43
+ clientSecret: z.string().trim().min(1).max(500),
44
+ });
45
+ export type SaveLoginProviderRequest = z.infer<typeof saveLoginProviderRequestSchema>;
46
+
47
+ /** `PATCH /v1/store/auth-providers/{provider}` */
48
+ export const updateLoginProviderRequestSchema = z.object({
49
+ enabled: z.boolean(),
50
+ });
51
+
52
+ /** 연결 테스트 결과 */
53
+ export const loginProviderTestSchema = z.object({
54
+ ok: z.boolean(),
55
+ /** 실패 이유 — `INVALID_CLIENT`(키가 틀림)·`PROVIDER_UNAVAILABLE`(공급자 응답 없음) */
56
+ code: z.enum(["INVALID_CLIENT", "PROVIDER_UNAVAILABLE"]).nullable(),
57
+ testedAt: z.string(),
58
+ });
59
+ export type LoginProviderTest = z.infer<typeof loginProviderTestSchema>;
@@ -27,6 +27,7 @@ export const STORE_SCOPE_RESOURCES = {
27
27
  webhook: ["r", "rw"],
28
28
  store: ["r", "rw"],
29
29
  member: ["r", "rw"],
30
+ customer: ["r", "rw"],
30
31
  } as const satisfies Record<string, readonly ScopeAccess[]>;
31
32
 
32
33
  export type StoreScopeResource = keyof typeof STORE_SCOPE_RESOURCES;
@@ -61,6 +62,7 @@ export const STORE_ROLE_SCOPES = {
61
62
  "webhook:rw",
62
63
  "store:rw",
63
64
  "member:rw",
65
+ "customer:rw",
64
66
  ],
65
67
  ADMIN: [
66
68
  "product:rw",
@@ -73,6 +75,15 @@ export const STORE_ROLE_SCOPES = {
73
75
  "webhook:rw",
74
76
  "store:rw",
75
77
  "member:rw",
78
+ "customer:rw",
79
+ ],
80
+ STAFF: [
81
+ "product:rw",
82
+ "category:rw",
83
+ "order:rw",
84
+ "claim:rw",
85
+ "inquiry:rw",
86
+ "review:rw",
87
+ "customer:r",
76
88
  ],
77
- STAFF: ["product:rw", "category:rw", "order:rw", "claim:rw", "inquiry:rw", "review:rw"],
78
89
  } as const satisfies Record<StoreRole, readonly string[]>;
@@ -114,9 +114,24 @@ export const updateStoreRequestSchema = z
114
114
  .max(MAX_ANALYTICS_ALLOWED_ORIGINS)
115
115
  .optional()
116
116
  .describe(
117
- "방문 분석 수집을 받을 스토어프런트 오리진(최대 20개). 비우면 모든 오리진에서 받는다. " +
117
+ "@deprecated `storefrontOrigins`를 쓴다(같은 목록). 방문 분석 수집을 받을 스토어프런트 오리진(최대 20개). 비우면 모든 오리진에서 받는다. " +
118
118
  "넣으면 목록에 없는 오리진의 수집 요청은 403 `ORIGIN_NOT_ALLOWED`이고, 페이지 URL이 목록 밖인 이벤트는 버린다",
119
119
  ),
120
+ storefrontOrigins: z
121
+ .array(analyticsOriginSchema)
122
+ .max(MAX_ANALYTICS_ALLOWED_ORIGINS)
123
+ .optional()
124
+ .describe(
125
+ "스토어프런트 도메인(최대 20개) — 소셜 로그인 뒤 돌아갈 주소와 방문 분석 수집을 함께 제한한다. " +
126
+ "`analyticsAllowedOrigins`와 같은 목록이다(둘 다 주면 이 값이 이긴다)",
127
+ ),
128
+ guestCheckoutEnabled: z
129
+ .boolean()
130
+ .optional()
131
+ .describe(
132
+ "비회원 주문 허용. false(회원만)면 로그인하지 않은 구매자의 주문서 생성·결제 요청이 403 `GUEST_CHECKOUT_DISABLED`다. " +
133
+ "장바구니 담기와 이미 받은 비회원 주문의 조회·취소는 막지 않는다",
134
+ ),
120
135
  })
121
136
  .refine((value) => Object.values(value).some((v) => v !== undefined), {
122
137
  message: "최소 1개 필드가 필요합니다",
@@ -135,6 +150,12 @@ export const storeSchema = z.object({
135
150
  analyticsAllowedOrigins: z
136
151
  .array(z.string())
137
152
  .describe("방문 분석 수집을 받는 스토어프런트 오리진. 비어 있으면 모든 오리진에서 받는다"),
153
+ storefrontOrigins: z
154
+ .array(z.string())
155
+ .describe(
156
+ "스토어프런트 도메인 — 소셜 로그인 리디렉트와 방문 분석 수집이 함께 쓴다(`analyticsAllowedOrigins`와 같은 값)",
157
+ ),
158
+ guestCheckoutEnabled: z.boolean().describe("비회원 주문 허용. false면 회원만 주문할 수 있다"),
138
159
  memberCount: z.number().int(),
139
160
  createdAt: z.string(),
140
161
  });