@sayren/store-sdk 0.1.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/src/client.ts ADDED
@@ -0,0 +1,877 @@
1
+ import { z } from "zod";
2
+ import { base64Utf8 } from "./base64";
3
+ import { createHttp, path, type Query, resolveToken, type TokenSource, unwrapData } from "./http";
4
+ import {
5
+ type ApiClientListParams,
6
+ apiClientSchema,
7
+ apiClientSecretRotatedSchema,
8
+ type OAuthClientRegistrationRequest,
9
+ oauthClientRegistrationRequestSchema,
10
+ oauthClientRegistrationResponseSchema,
11
+ } from "./schemas/api-client";
12
+ import { type ApiLogListOptions, apiLogPageSchema, apiLogSummarySchema } from "./schemas/api-log";
13
+ import {
14
+ type ApiTokenCreateRequest,
15
+ apiTokenCreatedSchema,
16
+ apiTokenCreateRequestSchema,
17
+ apiTokenSchema,
18
+ clientCredentialsTokenResponseSchema,
19
+ myAccountSchema,
20
+ myStoreSchema,
21
+ } from "./schemas/auth";
22
+ import {
23
+ type CategoryCreateRequest,
24
+ type CategoryDeleteStrategy,
25
+ type CategoryPatchRequest,
26
+ categoryAttributeSchema,
27
+ categoryCreateRequestSchema,
28
+ categoryPatchRequestSchema,
29
+ categorySchema,
30
+ categorySearchResultSchema,
31
+ categoryTreeNodeSchema,
32
+ categoryWithPathSchema,
33
+ } from "./schemas/category";
34
+ import {
35
+ approveClaimResultSchema,
36
+ type ClaimListParams,
37
+ claimSchema,
38
+ claimStatusSchema,
39
+ type RejectClaimRequest,
40
+ rejectClaimRequestSchema,
41
+ } from "./schemas/claim";
42
+ import { bulkResultSchema, type PageParams, pageSchema } from "./schemas/common";
43
+ import {
44
+ type CustomerInquiryCategory,
45
+ inquirySchema,
46
+ type ReviewListParams,
47
+ reviewSchema,
48
+ type SettlementListParams,
49
+ sellerCustomerInquirySchema,
50
+ settlementDetailSchema,
51
+ settlementSummarySchema,
52
+ type WebhookDeliveryListParams,
53
+ type WebhookRequest,
54
+ type WebhookTestRequest,
55
+ webhookCreatedSchema,
56
+ webhookDeliverySchema,
57
+ webhookRedeliverFailedResultSchema,
58
+ webhookRequestSchema,
59
+ webhookSchema,
60
+ webhookSecretRotatedSchema,
61
+ webhookTestRequestSchema,
62
+ webhookTestResultSchema,
63
+ } from "./schemas/misc";
64
+ import {
65
+ type CancelBySellerRequest,
66
+ cancelBySellerRequestSchema,
67
+ carrierSchema,
68
+ type DispatchRequest,
69
+ deliveryInfoSchema,
70
+ dispatchRequestSchema,
71
+ type HoldOrderItemRequest,
72
+ holdOrderItemRequestSchema,
73
+ type OrderItemListParams,
74
+ type OrderListParams,
75
+ orderItemSchema,
76
+ orderSchema,
77
+ orderSummarySchema,
78
+ } from "./schemas/order";
79
+ import {
80
+ type PaymentListParams,
81
+ type PgEnvironment,
82
+ type PgProvider,
83
+ paymentProviderSchema,
84
+ paymentProviderVerificationSchema,
85
+ paymentRecordDetailSchema,
86
+ paymentRecordPageSchema,
87
+ paymentSettingsSchema,
88
+ pgEnvironmentSchema,
89
+ pgProviderSchema,
90
+ type SalesReportParams,
91
+ salesReportQuerySchema,
92
+ salesReportSchema,
93
+ type UpdatePaymentProviderRequest,
94
+ type UpdatePaymentSettingsRequest,
95
+ type UpsertPortOneProviderRequest,
96
+ type UpsertTossPaymentsProviderRequest,
97
+ updatePaymentProviderRequestSchema,
98
+ updatePaymentSettingsRequestSchema,
99
+ upsertPortOneProviderRequestSchema,
100
+ upsertTossPaymentsProviderRequestSchema,
101
+ type VerifyPaymentProviderRequest,
102
+ verifyPaymentProviderRequestSchema,
103
+ } from "./schemas/payment";
104
+ import {
105
+ type DiscountRequest,
106
+ discountRequestSchema,
107
+ discountSchema,
108
+ type OptionModelRequest,
109
+ optionModelRequestSchema,
110
+ optionModelSchema,
111
+ type ProductCreateRequest,
112
+ type ProductListParams,
113
+ type ProductPatchRequest,
114
+ type ProductStatus,
115
+ productCreateRequestSchema,
116
+ productPatchRequestSchema,
117
+ productSchema,
118
+ productStatusSchema,
119
+ productSummarySchema,
120
+ type VariantStockRequest,
121
+ variantStockRequestSchema,
122
+ variantStockSchema,
123
+ } from "./schemas/product";
124
+ import {
125
+ type AddressType,
126
+ invitationSchema,
127
+ type SellerAddressRequest,
128
+ sellerAddressRequestSchema,
129
+ sellerAddressSchema,
130
+ storeMemberSchema,
131
+ storeSchema,
132
+ } from "./schemas/store";
133
+ import { type UploadRequest, uploadRequestSchema, uploadTicketSchema } from "./schemas/upload";
134
+ import type { AuthorizeOptions, StoreSession } from "./session";
135
+
136
+ export interface StoreClientOptions {
137
+ /** ์˜ˆ: https://api.example.com/v1 */
138
+ baseUrl: string;
139
+ /** ์œ ์ € ํ† ํฐ (1๋‹จ๊ณ„). ์ธ์ฆ/์Šคํ† ์–ด ๋ชฉ๋ก API์— ์‚ฌ์šฉ */
140
+ userToken?: TokenSource;
141
+ /** ์Šคํ† ์–ด ํ† ํฐ (3๋‹จ๊ณ„). ํ…Œ๋„ŒํŠธ ๋ฆฌ์†Œ์Šค API์— ์‚ฌ์šฉ */
142
+ storeToken?: TokenSource;
143
+ /** ํ…Œ๋„ŒํŠธ storeId โ€” storeHttp(ํ…Œ๋„ŒํŠธ ๋ฆฌ์†Œ์Šค) ์š”์ฒญ์— X-Store-Id๋กœ ์‹ค๋ฆฐ๋‹ค(userToken ์ง์ ‘ ์ธ๊ฐ€). */
144
+ storeId?: TokenSource;
145
+ /**
146
+ * ํ† ํฐ ์ฃผ์ž… ๋ชจ๋“œ โ€” PAT/storeToken๊ธ‰(ํ…Œ๋„ŒํŠธ ๋ฐ”์ธ๋”ฉ, role ์—†์Œ)์„ storeHttp Bearer๋กœ ์‹ฃ๋Š”๋‹ค.
147
+ * userToken์ด ์•„๋‹ˆ๋‹ค(๋น„์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ M2M/CLI ๊ฒฝ๋กœ์šฉ). storeToken๊ณผ ํ•จ๊ป˜ ์ฃผ๋ฉด storeToken์ด ์šฐ์„ ํ•œ๋‹ค.
148
+ */
149
+ auth?: { apiToken?: string };
150
+ /**
151
+ * SDK ์„ธ์…˜ ๋งค๋‹ˆ์ € โ€” ์ „๋‹ฌํ•˜๋ฉด userToken(Bearer)ยท401 refreshยท๋ณต๊ตฌ๋ถˆ๊ฐ€ ์‹œ clear๋ฅผ
152
+ * ์ด ์„ธ์…˜์—์„œ ์ž๋™ ๋ฐฐ์„ ํ•œ๋‹ค. ๊ฐœ๋ณ„ ์˜ต์…˜(userToken/refresh/onUnauthorized)์„ ๋ช…์‹œํ•˜๋ฉด ๊ทธ์ชฝ์ด ์šฐ์„ ํ•œ๋‹ค.
153
+ */
154
+ session?: StoreSession;
155
+ /** 401 ์‹œ ์„ธ์…˜ ๊ฐฑ์‹  ์‹œ๋„ โ€” true๋ฉด ์š”์ฒญ์„ 1ํšŒ ์žฌ์‹œ๋„ํ•œ๋‹ค(refresh ํ† ํฐ ํšŒ์ „) */
156
+ refresh?: () => Promise<boolean>;
157
+ /** ๋ณต๊ตฌ ๋ถˆ๊ฐ€๋Šฅํ•œ 401(๊ฐฑ์‹  ์‹คํŒจ/์žฌ์‹œ๋„ ํ›„์—๋„ 401) ์‹œ ํ˜ธ์ถœ โ€” ์žฌ๋กœ๊ทธ์ธ ์œ ๋„์šฉ */
158
+ onUnauthorized?: () => void;
159
+ fetch?: typeof globalThis.fetch;
160
+ }
161
+
162
+ function formBody(fields: Record<string, string>): string {
163
+ return new URLSearchParams(fields).toString();
164
+ }
165
+
166
+ export function createStoreClient(options: StoreClientOptions) {
167
+ const fetchFn = options.fetch ?? globalThis.fetch;
168
+ const { session } = options;
169
+
170
+ // session์ด ์žˆ์œผ๋ฉด userToken/refresh/onUnauthorized์˜ ๊ธฐ๋ณธ๊ฐ’์„ ์„ธ์…˜์—์„œ ๋ฐฐ์„ ํ•œ๋‹ค.
171
+ // ๊ฐœ๋ณ„ ์˜ต์…˜์„ ๋ช…์‹œํ•˜๋ฉด ๊ทธ์ชฝ์ด ์šฐ์„ (ํ•˜์œ„ํ˜ธํ™˜) โ€” session ๋ฏธ์ „๋‹ฌ ์‹œ ๊ธฐ์กด ๋™์ž‘ ๊ทธ๋Œ€๋กœ.
172
+ const userTokenSource: TokenSource =
173
+ options.userToken ?? (session ? () => session.getToken() : undefined);
174
+ const refresh = options.refresh ?? (session ? () => session.refresh() : undefined);
175
+ const onUnauthorized = options.onUnauthorized ?? (session ? () => session.clear() : undefined);
176
+ // storeToken(๋ช…์‹œ ์˜ต์…˜)์ด auth.apiToken(PAT/storeToken๊ธ‰ ์ฃผ์ž…)๋ณด๋‹ค ์šฐ์„ ํ•œ๋‹ค.
177
+ const storeTokenSource: TokenSource = options.storeToken ?? options.auth?.apiToken;
178
+
179
+ const userHttp = createHttp({
180
+ baseUrl: options.baseUrl,
181
+ fetch: fetchFn,
182
+ headers: () => {
183
+ const token = resolveToken(userTokenSource);
184
+ return token ? { authorization: `Bearer ${token}` } : ({} as Record<string, string>);
185
+ },
186
+ refresh,
187
+ onUnauthorized,
188
+ });
189
+
190
+ const storeHttp = createHttp({
191
+ baseUrl: options.baseUrl,
192
+ fetch: fetchFn,
193
+ headers: () => {
194
+ const token = resolveToken(storeTokenSource);
195
+ const storeId = resolveToken(options.storeId);
196
+ return {
197
+ ...(token ? { authorization: `Bearer ${token}` } : {}),
198
+ ...(storeId ? { "x-store-id": storeId } : {}),
199
+ };
200
+ },
201
+ refresh,
202
+ onUnauthorized,
203
+ });
204
+
205
+ /** `PATCH /v1/store/payment-providers/{provider}` ๋‹จ์ผ ๊ตฌํ˜„ โ€” `updateProvider`ยท`setSandboxMode`๊ฐ€ ๊ณต์œ ํ•œ๋‹ค */
206
+ function updatePaymentProvider(provider: PgProvider, body: UpdatePaymentProviderRequest) {
207
+ return storeHttp.request(
208
+ "PATCH",
209
+ path`/store/payment-providers/${pgProviderSchema.parse(provider)}`,
210
+ paymentProviderSchema,
211
+ { body: updatePaymentProviderRequestSchema.parse(body) },
212
+ );
213
+ }
214
+
215
+ function idempotency(key?: string): Record<string, string> | undefined {
216
+ return key ? { "idempotency-key": key } : undefined;
217
+ }
218
+
219
+ function requireSession(): StoreSession {
220
+ if (!session) throw new Error("oauth ์„ธ์…˜ ์—†์Œ โ€” createStoreSession ํ•„์š”");
221
+ return session;
222
+ }
223
+
224
+ return {
225
+ /** session์˜ ํ˜„์žฌ userToken (session ๋ฏธ๋ฐฐ์„  ์‹œ null โ€” ๋˜์ง€์ง€ ์•Š์Œ) */
226
+ get accessToken(): string | null {
227
+ return session ? session.accessToken : null;
228
+ },
229
+ /** session์˜ ํ˜„์žฌ refresh ํ† ํฐ (session ๋ฏธ๋ฐฐ์„  ์‹œ null โ€” ๋˜์ง€์ง€ ์•Š์Œ) */
230
+ get refreshToken(): string | null {
231
+ return session ? session.refreshToken : null;
232
+ },
233
+ /** session.authorize ํŒจ์Šค์Šค๋ฃจ โ€” session ๋ฏธ๋ฐฐ์„  ์‹œ ๋ช…ํ™•ํ•œ ์—๋Ÿฌ๋ฅผ ๋˜์ง„๋‹ค */
234
+ authorize: (authorizeOptions: AuthorizeOptions) => requireSession().authorize(authorizeOptions),
235
+ /** session.userinfo ํŒจ์Šค์Šค๋ฃจ โ€” session ๋ฏธ๋ฐฐ์„  ์‹œ ๋ช…ํ™•ํ•œ ์—๋Ÿฌ๋ฅผ ๋˜์ง„๋‹ค */
236
+ userinfo: () => requireSession().userinfo(),
237
+
238
+ auth: {
239
+ /**
240
+ * ์„œ๋ฒ„ ๊ฐ„ ์—ฐ๋™(M2M) โ€” client_credentials grant (client_secret_basic).
241
+ * ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์Šคํ† ์–ด์— ๋ฐ”์ธ๋”ฉ๋˜์–ด ์žˆ์–ด **storeToken์ด ๊ณง๋ฐ”๋กœ ๋ฐœ๊ธ‰**๋œ๋‹ค.
242
+ * โš  client_secret์€ ์„œ๋ฒ„ ํ™˜๊ฒฝ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ๊ฒƒ (๋ธŒ๋ผ์šฐ์ € ๋ฐ˜์ž… ๊ธˆ์ง€)
243
+ */
244
+ async issueStoreTokenByClientCredentials(
245
+ clientId: string,
246
+ clientSecret: string,
247
+ scope?: string,
248
+ ) {
249
+ // RFC 7617 client_secret_basic โ€” UTF-8 ๋ฐ”์ดํŠธ๋ฅผ base64๋กœ ์ธ์ฝ”๋”ฉํ•œ๋‹ค.
250
+ const credentials = base64Utf8(`${clientId}:${clientSecret}`);
251
+ const response = await fetchFn(`${options.baseUrl}/oauth2/token`, {
252
+ method: "POST",
253
+ headers: {
254
+ "content-type": "application/x-www-form-urlencoded",
255
+ authorization: `Basic ${credentials}`,
256
+ },
257
+ body: formBody({ grant_type: "client_credentials", ...(scope ? { scope } : {}) }),
258
+ });
259
+ if (!response.ok) {
260
+ const body = (await response.json().catch(() => null)) as {
261
+ error_description?: string;
262
+ } | null;
263
+ throw new Error(body?.error_description ?? `ํ† ํฐ ๋ฐœ๊ธ‰ ์‹คํŒจ (HTTP ${response.status})`);
264
+ }
265
+ return clientCredentialsTokenResponseSchema.parse(unwrapData(await response.json()));
266
+ },
267
+ /** 2๋‹จ๊ณ„ โ€” ์†Œ์† ์Šคํ† ์–ด ๋ชฉ๋ก */
268
+ listMyStores: () => userHttp.request("GET", "/me/stores", z.array(myStoreSchema)),
269
+ /** RFC 7009 โ€” token์„ ํ๊ธฐํ•œ๋‹ค (public client๋Š” client_id๋งŒ ๋™๋ฐ˜) */
270
+ async revoke(token: string, clientId: string) {
271
+ await fetchFn(`${options.baseUrl}/oauth2/revoke`, {
272
+ method: "POST",
273
+ headers: { "content-type": "application/x-www-form-urlencoded" },
274
+ body: formBody({ token, client_id: clientId }),
275
+ });
276
+ },
277
+ me: () => userHttp.request("GET", "/me", myAccountSchema),
278
+ },
279
+
280
+ /** API ํ† ํฐ(PAT) ๊ด€๋ฆฌ โ€” OWNER/ADMIN ์ „์šฉ. ๋ฐœ๊ธ‰๋œ ํ† ํฐ์€ storeToken์ฒ˜๋Ÿผ Bearer๋กœ ์‚ฌ์šฉ */
281
+ apiTokens: {
282
+ create: (body: ApiTokenCreateRequest) =>
283
+ storeHttp.request("POST", "/store/api-tokens", apiTokenCreatedSchema, {
284
+ body: apiTokenCreateRequestSchema.parse(body),
285
+ }),
286
+ list: () => storeHttp.request("GET", "/store/api-tokens", z.array(apiTokenSchema)),
287
+ revoke: (tokenId: string) =>
288
+ storeHttp.requestVoid("DELETE", path`/store/api-tokens/${tokenId}`),
289
+ },
290
+
291
+ /**
292
+ * ์•ฑ(OAuth ํด๋ผ์ด์–ธํŠธ) ๊ด€๋ฆฌ โ€” OWNER/ADMIN storeToken ์ „์šฉ(PATยทM2M ๊ฑฐ๋ถ€). auth-spec ยง12, ๋ถ€๋ก B-13.
293
+ * ์‹œํฌ๋ฆฟ ํ‰๋ฌธ์€ registerยทrotateSecret ์‘๋‹ต์—์„œ๋งŒ ํ•œ ๋ฒˆ ๋…ธ์ถœ๋œ๋‹ค(apiTokens.create์™€ ๊ฐ™์€ ์„ฑ๊ฒฉ).
294
+ */
295
+ apiClients: {
296
+ /**
297
+ * DCR ๋“ฑ๋ก (RFC 7591) โ€” `POST /v1/oauth2/register`. OAuth ํ”„๋กœํ† ์ฝœ ํ‘œ๋ฉด์ด๋ผ ์‘๋‹ต์ด ํ‰๋ฉด JSON์ด๋‹ค.
298
+ * ๋“ฑ๋ก ์˜ค๋ฅ˜(`invalid_client_metadata`ยท`invalid_redirect_uri`)๋Š” ApiError(code=error,
299
+ * message=error_description)๋กœ, Bearer ์ธ์ฆ ์˜ค๋ฅ˜๋Š” ์—”๋ฒจ๋กœํ”„ ๊ทธ๋Œ€๋กœ ApiError๋กœ ๋˜์ง„๋‹ค.
300
+ */
301
+ register: (body: OAuthClientRegistrationRequest) =>
302
+ storeHttp.request("POST", "/oauth2/register", oauthClientRegistrationResponseSchema, {
303
+ body: oauthClientRegistrationRequestSchema.parse(body),
304
+ }),
305
+ list: (params?: ApiClientListParams) =>
306
+ storeHttp.request("GET", "/store/api-clients", z.array(apiClientSchema), {
307
+ query: params as Query,
308
+ }),
309
+ get: (clientId: string) =>
310
+ storeHttp.request("GET", path`/store/api-clients/${clientId}`, apiClientSchema),
311
+ /** confidential ์ „์šฉ. ๊ธฐ์กด ์‹œํฌ๋ฆฟ์€ ์ฆ‰์‹œ ๋ฌดํšจ(์œ ์˜ˆ ์—†์Œ) */
312
+ rotateSecret: (clientId: string) =>
313
+ storeHttp.request(
314
+ "POST",
315
+ path`/store/api-clients/${clientId}/rotate-secret`,
316
+ apiClientSecretRotatedSchema,
317
+ ),
318
+ /** REVOKED๋กœ ์ „ํ™˜(๋˜๋Œ๋ฆด ์ˆ˜ ์—†์Œ). ์ด๋ฏธ ๋น„ํ™œ์„ฑ์ด๋ฉด ํ˜„์žฌ ์ƒํƒœ๋ฅผ ๊ทธ๋Œ€๋กœ ๋Œ๋ ค์ค€๋‹ค */
319
+ deactivate: (clientId: string) =>
320
+ storeHttp.request("POST", path`/store/api-clients/${clientId}/deactivate`, apiClientSchema),
321
+ },
322
+
323
+ /**
324
+ * ๋ฏธ๋””์–ด ์—…๋กœ๋“œ โ€” ์ž๋ฆฌ๋ฅผ ๋ฐ›์•„(`POST /uploads`) ํŒŒ์ผ ๋ฐ”์ดํŠธ๋Š” ํ˜ธ์ถœ์ž๊ฐ€
325
+ * `uploadUrl`๋กœ ์ง์ ‘ PUTํ•œ๋‹ค. ์—…๋กœ๋“œ๊ฐ€ ๋๋‚˜๋ฉด `url`์ด ๊ณต๊ฐœ ์ฃผ์†Œ๊ฐ€ ๋œ๋‹ค.
326
+ */
327
+ uploads: {
328
+ create: (body: UploadRequest) =>
329
+ storeHttp.request("POST", "/uploads", uploadTicketSchema, {
330
+ body: uploadRequestSchema.parse(body),
331
+ }),
332
+ },
333
+
334
+ products: {
335
+ list: (params?: ProductListParams) =>
336
+ storeHttp.request("GET", "/products", pageSchema(productSummarySchema), {
337
+ query: params as Query,
338
+ }),
339
+ get: (productId: string) =>
340
+ storeHttp.request("GET", path`/products/${productId}`, productSchema),
341
+ create: (body: ProductCreateRequest, idempotencyKey?: string) =>
342
+ storeHttp.request("POST", "/products", productSchema, {
343
+ body: productCreateRequestSchema.parse(body),
344
+ headers: idempotency(idempotencyKey),
345
+ }),
346
+ update: (productId: string, body: ProductCreateRequest) =>
347
+ storeHttp.request("PUT", path`/products/${productId}`, productSchema, {
348
+ body: productCreateRequestSchema.parse(body),
349
+ }),
350
+ patch: (productId: string, body: ProductPatchRequest) =>
351
+ storeHttp.requestVoid("PATCH", path`/products/${productId}`, {
352
+ body: productPatchRequestSchema.parse(body),
353
+ }),
354
+ remove: (productId: string) => storeHttp.requestVoid("DELETE", path`/products/${productId}`),
355
+ bulkUpdateStatus: (productIds: string[], status: ProductStatus, idempotencyKey?: string) =>
356
+ storeHttp.request("POST", "/products/bulk/status", bulkResultSchema, {
357
+ body: { productIds, status: productStatusSchema.parse(status) },
358
+ headers: idempotency(idempotencyKey),
359
+ }),
360
+ /** ์˜ต์…˜ ์ถ•(๊ทธ๋ฃน) ร— ๊ฐ’ โ†’ variant ๋ชจ๋ธ ์กฐํšŒ */
361
+ getOptionModel: (productId: string) =>
362
+ storeHttp.request("GET", path`/products/${productId}/options`, optionModelSchema),
363
+ /**
364
+ * ์˜ต์…˜ ๋ชจ๋ธ ์ „์ฒด ๊ต์ฒด โ€” variants๋Š” valueNames๋กœ ์ถ• ๊ฐ’์„ ์ง€๋ชฉํ•œ๋‹ค.
365
+ * ์‘๋‹ต์œผ๋กœ ์„œ๋ฒ„๊ฐ€ ๋ฐฐ์ •ํ•œ groupIdยทvalueIdยท์‹ ๊ทœ variantId๊ฐ€ ์‹ค๋ฆฐ ๊ต์ฒด ๊ฒฐ๊ณผ๋ฅผ ๋Œ๋ ค์ค€๋‹ค.
366
+ */
367
+ replaceOptionModel: (productId: string, body: OptionModelRequest) =>
368
+ storeHttp.request("PUT", path`/products/${productId}/options`, optionModelSchema, {
369
+ body: optionModelRequestSchema.parse(body),
370
+ }),
371
+ /** ์žฌ๊ณ  ๋ณ€๊ฒฝ โ€” stockQuantity(์ ˆ๋Œ€๊ฐ’) ๋˜๋Š” adjustment(์ฆ๊ฐ) ํ•˜๋‚˜๋งŒ ์ „๋‹ฌํ•œ๋‹ค */
372
+ updateVariantStock: (productId: string, variantId: string, body: VariantStockRequest) =>
373
+ storeHttp.request(
374
+ "PATCH",
375
+ path`/products/${productId}/variants/${variantId}/stock`,
376
+ variantStockSchema,
377
+ { body: variantStockRequestSchema.parse(body) },
378
+ ),
379
+ getDiscount: (productId: string) =>
380
+ storeHttp.request("GET", path`/products/${productId}/discount`, discountSchema),
381
+ setDiscount: (productId: string, body: DiscountRequest) =>
382
+ storeHttp.request("PUT", path`/products/${productId}/discount`, discountSchema, {
383
+ body: discountRequestSchema.parse(body),
384
+ }),
385
+ removeDiscount: (productId: string) =>
386
+ storeHttp.requestVoid("DELETE", path`/products/${productId}/discount`),
387
+ },
388
+
389
+ categories: {
390
+ list: (params?: { parentId?: string; flat?: boolean }) =>
391
+ storeHttp.request("GET", "/categories", z.array(categorySchema), {
392
+ query: params as Query,
393
+ }),
394
+ tree: () =>
395
+ storeHttp.request("GET", "/categories", z.array(categoryTreeNodeSchema), {
396
+ query: { tree: true } as Query,
397
+ }),
398
+ search: (q: string) =>
399
+ storeHttp.request("GET", "/categories", z.array(categorySearchResultSchema), {
400
+ query: { q } as Query,
401
+ }),
402
+ get: (categoryId: string) =>
403
+ storeHttp.request("GET", path`/categories/${categoryId}`, categoryWithPathSchema),
404
+ listAttributes: (categoryId: string) =>
405
+ storeHttp.request(
406
+ "GET",
407
+ path`/categories/${categoryId}/attributes`,
408
+ z.array(categoryAttributeSchema),
409
+ ),
410
+ create: (body: CategoryCreateRequest, idempotencyKey?: string) =>
411
+ storeHttp.request("POST", "/categories", categorySchema, {
412
+ body: categoryCreateRequestSchema.parse(body),
413
+ headers: idempotency(idempotencyKey),
414
+ }),
415
+ update: (categoryId: string, body: CategoryPatchRequest) =>
416
+ storeHttp.request("PATCH", path`/categories/${categoryId}`, categorySchema, {
417
+ body: categoryPatchRequestSchema.parse(body),
418
+ }),
419
+ remove: (categoryId: string, opts?: { strategy?: CategoryDeleteStrategy }) =>
420
+ storeHttp.requestVoid("DELETE", path`/categories/${categoryId}`, {
421
+ query: (opts?.strategy ? { strategy: opts.strategy } : {}) as Query,
422
+ }),
423
+ },
424
+
425
+ orders: {
426
+ /**
427
+ * `GET /v1/orders`. `testPayment: "true"`๋Š” ํ…Œ์ŠคํŠธ ๊ฒฐ์ œ(์ƒŒ๋“œ๋ฐ•์Šค) ์ฃผ๋ฌธ๋งŒ, `"false"`๋Š” ์‹ค๊ฒฐ์ œ ์ฃผ๋ฌธ๋งŒ, ์ƒ๋žตํ•˜๋ฉด ์ „๋ถ€๋‹ค.
428
+ * ๊ทธ ๋ฐ–์˜ ๊ฐ’์€ 400 `INVALID_PARAMETER`. ํ–‰๋งˆ๋‹ค `testPayment`๊ฐ€ ๋ถ™๋Š”๋‹ค.
429
+ */
430
+ list: (params: OrderListParams) =>
431
+ storeHttp.request("GET", "/orders", pageSchema(orderSummarySchema), {
432
+ query: params as unknown as Query,
433
+ }),
434
+ get: (orderId: string) => storeHttp.request("GET", path`/orders/${orderId}`, orderSchema),
435
+ },
436
+
437
+ orderItems: {
438
+ /** `GET /v1/order-items`. `testPayment` ํ•„ํ„ฐ๋Š” `orders.list`์™€ ๊ฐ™๋‹ค */
439
+ listChanged: (params: OrderItemListParams) =>
440
+ storeHttp.request("GET", "/order-items", pageSchema(orderItemSchema), {
441
+ query: params as unknown as Query,
442
+ }),
443
+ confirm: (orderItemId: string, idempotencyKey?: string) =>
444
+ storeHttp.requestVoid("POST", path`/order-items/${orderItemId}/confirm`, {
445
+ headers: idempotency(idempotencyKey),
446
+ }),
447
+ confirmBulk: (orderItemIds: string[], idempotencyKey?: string) =>
448
+ storeHttp.request("POST", "/order-items/confirm", bulkResultSchema, {
449
+ body: { orderItemIds },
450
+ headers: idempotency(idempotencyKey),
451
+ }),
452
+ hold: (orderItemId: string, body: HoldOrderItemRequest) =>
453
+ storeHttp.requestVoid("POST", path`/order-items/${orderItemId}/hold`, {
454
+ body: holdOrderItemRequestSchema.parse(body),
455
+ }),
456
+ releaseHold: (orderItemId: string) =>
457
+ storeHttp.requestVoid("DELETE", path`/order-items/${orderItemId}/hold`),
458
+ cancelBySeller: (orderItemId: string, body: CancelBySellerRequest, idempotencyKey?: string) =>
459
+ storeHttp.request(
460
+ "POST",
461
+ path`/order-items/${orderItemId}/cancel`,
462
+ z.object({
463
+ orderItemId: z.string(),
464
+ refundAmount: z.number().int(),
465
+ refundedAt: z.string(),
466
+ }),
467
+ { body: cancelBySellerRequestSchema.parse(body), headers: idempotency(idempotencyKey) },
468
+ ),
469
+ updateMemo: (orderItemId: string, memo: string) =>
470
+ storeHttp.requestVoid("PUT", path`/order-items/${orderItemId}/memo`, { body: { memo } }),
471
+ requestPurchaseDecision: (orderItemId: string) =>
472
+ storeHttp.requestVoid("POST", path`/order-items/${orderItemId}/purchase-decision/request`),
473
+ extendPurchaseDecision: (orderItemId: string) =>
474
+ storeHttp.request(
475
+ "POST",
476
+ path`/order-items/${orderItemId}/purchase-decision/extend`,
477
+ z.object({
478
+ orderItemId: z.string(),
479
+ autoDecisionDate: z.string(),
480
+ extendCount: z.number().int().max(2),
481
+ }),
482
+ ),
483
+ dispatch: (orderItemId: string, body: DispatchRequest, idempotencyKey?: string) =>
484
+ storeHttp.request("POST", path`/order-items/${orderItemId}/dispatch`, deliveryInfoSchema, {
485
+ body: dispatchRequestSchema.parse(body),
486
+ headers: idempotency(idempotencyKey),
487
+ }),
488
+ dispatchBulk: (
489
+ dispatches: Array<{ orderItemId: string } & DispatchRequest>,
490
+ idempotencyKey?: string,
491
+ ) =>
492
+ storeHttp.request("POST", "/order-items/dispatch", bulkResultSchema, {
493
+ body: { dispatches },
494
+ headers: idempotency(idempotencyKey),
495
+ }),
496
+ updateTracking: (orderItemId: string, carrierCode: string, trackingNumber: string) =>
497
+ storeHttp.requestVoid("PUT", path`/order-items/${orderItemId}/dispatch/tracking`, {
498
+ body: { carrierCode, trackingNumber },
499
+ }),
500
+ getDelivery: (orderItemId: string) =>
501
+ storeHttp.request(
502
+ "GET",
503
+ path`/order-items/${orderItemId}/delivery`,
504
+ deliveryInfoSchema.extend({
505
+ trackingEvents: z
506
+ .array(z.object({ status: z.string(), location: z.string(), occurredAt: z.string() }))
507
+ .optional(),
508
+ }),
509
+ ),
510
+ },
511
+
512
+ carriers: {
513
+ list: () => storeHttp.request("GET", "/carriers", z.array(carrierSchema)),
514
+ },
515
+
516
+ claims: {
517
+ list: (params?: ClaimListParams) =>
518
+ storeHttp.request("GET", "/claims", pageSchema(claimSchema), { query: params as Query }),
519
+ get: (claimId: string) =>
520
+ storeHttp.request(
521
+ "GET",
522
+ path`/claims/${claimId}`,
523
+ claimSchema.extend({
524
+ histories: z
525
+ .array(
526
+ z.object({
527
+ status: claimStatusSchema,
528
+ actor: z.enum(["BUYER", "SELLER", "SYSTEM"]),
529
+ occurredAt: z.string(),
530
+ }),
531
+ )
532
+ .optional(),
533
+ }),
534
+ ),
535
+ approve: (claimId: string, deductAmount?: number, idempotencyKey?: string) =>
536
+ storeHttp.request("POST", path`/claims/${claimId}/approve`, approveClaimResultSchema, {
537
+ body: deductAmount !== undefined ? { deductAmount } : {},
538
+ headers: idempotency(idempotencyKey),
539
+ }),
540
+ reject: (claimId: string, body: RejectClaimRequest) =>
541
+ storeHttp.requestVoid("POST", path`/claims/${claimId}/reject`, {
542
+ body: rejectClaimRequestSchema.parse(body),
543
+ }),
544
+ registerCollect: (
545
+ claimId: string,
546
+ body: { collectMethod: "SELLER_PICKUP" | "BUYER_SEND"; carrierCode?: string },
547
+ ) => storeHttp.requestVoid("POST", path`/claims/${claimId}/collect`, { body }),
548
+ completeCollect: (claimId: string) =>
549
+ storeHttp.requestVoid("POST", path`/claims/${claimId}/collect/complete`),
550
+ registerRedelivery: (claimId: string, carrierCode: string, trackingNumber: string) =>
551
+ storeHttp.requestVoid("POST", path`/claims/${claimId}/redelivery`, {
552
+ body: { carrierCode, trackingNumber },
553
+ }),
554
+ hold: (claimId: string, reason: string) =>
555
+ storeHttp.request(
556
+ "POST",
557
+ path`/claims/${claimId}/hold`,
558
+ z.object({ claimId: z.string(), holdExpiresAt: z.string() }),
559
+ { body: { reason } },
560
+ ),
561
+ releaseHold: (claimId: string) =>
562
+ storeHttp.requestVoid("DELETE", path`/claims/${claimId}/hold`),
563
+ },
564
+
565
+ inquiries: {
566
+ list: (params?: PageParams & { answered?: boolean; productId?: string; from?: string }) =>
567
+ storeHttp.request("GET", "/inquiries", pageSchema(inquirySchema), {
568
+ query: params as Query,
569
+ }),
570
+ get: (inquiryId: string) =>
571
+ storeHttp.request("GET", path`/inquiries/${inquiryId}`, inquirySchema),
572
+ answer: (inquiryId: string, content: string) =>
573
+ storeHttp.requestVoid("POST", path`/inquiries/${inquiryId}/answer`, { body: { content } }),
574
+ updateAnswer: (inquiryId: string, content: string) =>
575
+ storeHttp.requestVoid("PUT", path`/inquiries/${inquiryId}/answer`, { body: { content } }),
576
+ },
577
+
578
+ customerInquiries: {
579
+ list: (
580
+ params?: PageParams & {
581
+ answered?: boolean;
582
+ category?: CustomerInquiryCategory;
583
+ from?: string;
584
+ },
585
+ ) =>
586
+ storeHttp.request("GET", "/customer-inquiries", pageSchema(sellerCustomerInquirySchema), {
587
+ query: params as Query,
588
+ }),
589
+ get: (inquiryId: string) =>
590
+ storeHttp.request(
591
+ "GET",
592
+ path`/customer-inquiries/${inquiryId}`,
593
+ sellerCustomerInquirySchema,
594
+ ),
595
+ answer: (inquiryId: string, content: string) =>
596
+ storeHttp.requestVoid("POST", path`/customer-inquiries/${inquiryId}/answer`, {
597
+ body: { content },
598
+ }),
599
+ updateAnswer: (inquiryId: string, content: string) =>
600
+ storeHttp.requestVoid("PUT", path`/customer-inquiries/${inquiryId}/answer`, {
601
+ body: { content },
602
+ }),
603
+ },
604
+
605
+ /**
606
+ * @deprecated ์ •์‚ฐ API๋Š” ํ๊ธฐ ์˜ˆ์ •์ด๋‹ค. ์ƒˆ ์ •์‚ฐ ๋ ˆ์ฝ”๋“œ๋Š” ๋” ์ด์ƒ ๋งŒ๋“ค์–ด์ง€์ง€ ์•Š๊ณ  ๊ธฐ์กด ๋ ˆ์ฝ”๋“œ ์กฐํšŒ๋งŒ
607
+ * ์œ ์ง€๋œ๋‹ค(์„œ๋ฒ„ ์‘๋‹ต ํ—ค๋” `Deprecation: true`). ๋Œ€์ฒด๋Š” {@link salesReport}(`GET /v1/sales-report`)๋‹ค.
608
+ */
609
+ settlements: {
610
+ /** @deprecated `salesReport.get`์„ ์“ด๋‹ค. ๊ธฐ์กด ์ •์‚ฐ ๋ ˆ์ฝ”๋“œ ์กฐํšŒ๋งŒ ์œ ์ง€๋œ๋‹ค */
611
+ list: (params: SettlementListParams) =>
612
+ storeHttp.request("GET", "/settlements", pageSchema(settlementSummarySchema), {
613
+ query: params as unknown as Query,
614
+ }),
615
+ /** @deprecated `salesReport.get`์„ ์“ด๋‹ค. ๊ธฐ์กด ์ •์‚ฐ ๋ ˆ์ฝ”๋“œ ์กฐํšŒ๋งŒ ์œ ์ง€๋œ๋‹ค */
616
+ get: (settlementId: string, params?: PageParams) =>
617
+ storeHttp.request(
618
+ "GET",
619
+ path`/settlements/${settlementId}`,
620
+ settlementSummarySchema.extend({ details: z.array(settlementDetailSchema).optional() }),
621
+ { query: params as Query },
622
+ ),
623
+ },
624
+
625
+ /**
626
+ * ๋งค์ถœ ๋ฆฌํฌํŠธ (์ •์‚ฐ API ๋Œ€์ฒด) โ€” ์Šค์ฝ”ํ”„ `settlement:r`. PG ์Šน์ธ ์‹œ๊ฐ ๊ธฐ์ค€ ๊ฒฐ์ œ, PG ์ทจ์†Œ ์™„๋ฃŒ ์‹œ๊ฐ ๊ธฐ์ค€
627
+ * ํ™˜๋ถˆ์„ Asia/Seoul ๋‚ ์งœ๋กœ ์ง‘๊ณ„ํ•œ๋‹ค. `rows`๋Š” ํ™œ๋™ ์—†๋Š” ๋‚ ์งœยทPGยท๊ฒฐ์ œ์ˆ˜๋‹จ์„ ์ƒ๋žตํ•˜๋ฏ€๋กœ ์ฐจํŠธ๋Š” ๋นˆ ๊ตฌ๊ฐ„์„ 0์œผ๋กœ ์ฑ„์šด๋‹ค.
628
+ */
629
+ salesReport: {
630
+ /**
631
+ * `GET /v1/sales-report`. fromยทto๋Š” ์–‘๋ ํฌํ•จ, ์ตœ๋Œ€ 92์ผ. ํ…Œ์ŠคํŠธ ๊ฒฐ์ œ(์ƒŒ๋“œ๋ฐ•์Šค)์˜ ์Šน์ธยทํ™˜๋ถˆ์€ ๊ธฐ๋ณธ ์ œ์™ธ์ด๊ณ 
632
+ * `includeTest: "true"`๋ฉด ํฌํ•จํ•œ๋‹ค(์‘๋‹ต `includeTest`์— ์ ์šฉ ๊ฐ’). 400 `VALIDATION_FAILED`(ํ˜•์‹),
633
+ * 400 `INVALID_PARAMETER`(์—†๋Š” ๋‚ ์งœยทto<from), 400 `RANGE_TOO_WIDE`(๊ธฐ๊ฐ„ ์ดˆ๊ณผ)
634
+ */
635
+ get: (params: SalesReportParams) =>
636
+ storeHttp.request("GET", "/sales-report", salesReportSchema, {
637
+ query: salesReportQuerySchema.parse(params),
638
+ }),
639
+ },
640
+
641
+ /**
642
+ * ๊ฒฐ์ œ ๋‚ด์—ญ โ€” ์Šค์ฝ”ํ”„ `settlement:r`. ๊ฒฐ์ œ ๊ฑด(์„ธ์…˜) ๋‹จ์œ„์ด๊ณ  ์ฃผ๋ฌธ ๋ชฉ๋ก๊ณผ ๋‹ค๋ฅด๋‹ค. ์ฃผ๋ฌธ์ด ๋งŒ๋“ค์–ด์ง€์ง€ ์•Š์€
643
+ * ๊ฒฐ์ œ(์นด๋“œ ๊ฑฐ์ ˆยท๊ฒฐ์ œ์ฐฝ ์ดํƒˆยท์Šน์ธ ๊ฒฐ๊ณผ ํ™•์ธ ์ค‘)๋„ ๊ทธ๋Œ€๋กœ ๋‚˜์˜จ๋‹ค.
644
+ */
645
+ payments: {
646
+ /**
647
+ * `GET /v1/payments`. ๊ธฐ๊ฐ„ ๊ธฐ์ค€์€ ๊ฒฐ์ œ ์‹œ์ž‘ ์‹œ๊ฐ, ์ตœ๋Œ€ 92์ผ์ด๊ณ  ์ƒ๋žตํ•˜๋ฉด ์ตœ๊ทผ 30์ผ์ด๋‹ค. ํ…Œ์ŠคํŠธ ๊ฒฐ์ œ๋Š”
648
+ * `includeTest: true`์ผ ๋•Œ๋งŒ ํฌํ•จํ•œ๋‹ค. 400 `INVALID_PARAMETER`(์‹œ๊ฐ ํ˜•์‹ยทto<from), 400 `RANGE_TOO_WIDE`
649
+ */
650
+ list: (params: PaymentListParams = {}) =>
651
+ storeHttp.request("GET", "/payments", paymentRecordPageSchema, {
652
+ query: params as unknown as Query,
653
+ }),
654
+ /** `GET /v1/payments/{paymentId}` โ€” ์‹œ๋„ยทํ™˜๋ถˆ ์ด๋ ฅ ํฌํ•จ. 404 `PAYMENT_NOT_FOUND` */
655
+ get: (paymentId: string) =>
656
+ storeHttp.request("GET", path`/payments/${paymentId}`, paymentRecordDetailSchema),
657
+ },
658
+
659
+ /**
660
+ * ๊ฒฐ์ œ(PG) ์„ค์ • โ€” **์ฝ˜์†” OWNER ์ „์šฉ**. ADMINยทSTAFFยทPATยทM2Mยท3rd-party ์•ฑ ํ† ํฐ์€ ์Šค์ฝ”ํ”„๊ฐ€ ์žˆ์–ด๋„
661
+ * 403 `INSUFFICIENT_ROLE`์ด๋‹ค(์กฐํšŒ ํฌํ•จ). OWNER ์—ฌ๋ถ€๋Š” ์š”์ฒญ ์‹œ์ ์˜ ์Šคํ† ์–ด ๋ฉค๋ฒ„์‹ญ์œผ๋กœ ๋‹ค์‹œ ํ™•์ธํ•˜๋ฏ€๋กœ
662
+ * ์–‘๋„ยท๊ฐ•๋“ฑ ์งํ›„์˜ ์˜› storeToken๋„ 403์ด๋‹ค(ํ† ํฐ์„ ์žฌ๋ฐœ๊ธ‰๋ฐ›๋Š”๋‹ค). ๋น„๋ฐ€ ๊ฐ’(์‹œํฌ๋ฆฟ ํ‚คยทAPI Secretยท์›นํ›… ์‹œํฌ๋ฆฟ)์€ ์š”์ฒญ์œผ๋กœ๋งŒ ๋ณด๋‚ด๊ณ 
663
+ * ์‘๋‹ต์—๋Š” `secrets[].masked`(์•ž 4์ž๋ฆฌ ๋งˆ์Šคํ‚น)๋งŒ ๋‚˜์˜จ๋‹ค.
664
+ */
665
+ paymentSettings: {
666
+ /** `GET /v1/store/payment-settings` โ€” ๋ผ์šฐํŒ… ๋ชจ๋“œ์™€ ์ง€์› PG ์ „๋ถ€(์—ฐ๊ฒฐ๋œ ๊ฒƒ์€ ์šฐ์„ ์ˆœ์œ„์ˆœ) */
667
+ get: () => storeHttp.request("GET", "/store/payment-settings", paymentSettingsSchema),
668
+ /**
669
+ * `PUT /v1/store/payment-settings` โ€” `priority`์—๋Š” ์—ฐ๊ฒฐ๋œ PG๋ฅผ ๊ฐœ์ˆ˜์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๋ชจ๋‘ ํ•œ ๋ฒˆ์”ฉ ๋‚˜์—ดํ•œ๋‹ค
670
+ * (๋น ์ง€๊ฑฐ๋‚˜ ์ค‘๋ณต๋˜๋ฉด 400 `INVALID_PRIORITY`). ์ง„ํ–‰ ์ค‘์ธ ๊ฒฐ์ œ์—๋Š” ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.
671
+ */
672
+ update: (body: UpdatePaymentSettingsRequest) =>
673
+ storeHttp.request("PUT", "/store/payment-settings", paymentSettingsSchema, {
674
+ body: updatePaymentSettingsRequestSchema.parse(body),
675
+ }),
676
+ /**
677
+ * `PUT /v1/store/payment-providers/tosspayments` โ€” ์ž๊ฒฉ ์ฆ๋ช… ์„ธํŠธ ์ €์žฅ. `environment`๋Š” **์ €์žฅํ•  ์„ธํŠธ**(`TEST`ยท`LIVE`)์ด๊ณ 
678
+ * ๊ฒฐ์ œ์— ์“ฐ๋Š” ์„ธํŠธ๋Š” ์ƒŒ๋“œ๋ฐ•์Šค ์Šค์œ„์น˜(`setSandboxMode`)๊ฐ€ ์ •ํ•œ๋‹ค. ์ฒ˜์Œ ์—ฐ๊ฒฐํ•˜๋ฉด `enabled: false`ยท`sandboxMode: true`๋กœ ๋งŒ๋“ค์–ด์ง„๋‹ค.
679
+ * ๊ฐ™์€ ๊ฐ€๋งน์ (`clientKey`)์ด๋ฉด ๊ทธ ์„ธํŠธ๋ฅผ ๊ฐฑ์‹ ํ•˜๊ณ , ๊ฐ€๋งน์ ์ด ๋ฐ”๋€Œ๋ฉด ์ƒˆ ์„ธํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ์ด์ „ ์„ธํŠธ๋Š” ๋ณด๊ด€ํ•œ๋‹ค
680
+ * (`retiredCredentialCount` +1, ์ด๋ฏธ ๋ฐ›์€ ๊ฒฐ์ œ์˜ ํ™˜๋ถˆยท๋Œ€์‚ฌ์— ๊ณ„์† ์“ฐ์ธ๋‹ค). `secretKey`ยท`webhookSecret`์„ ์ƒ๋žตํ•˜๋ฉด
681
+ * ํ˜„์žฌ ์„ธํŠธ์˜ ๊ฐ’์„ ์ด์–ด๋ฐ›๊ณ  `webhookSecret: null`์€ ์‚ญ์ œ๋‹ค. ์ €์žฅํ•˜๋ฉด ๊ทธ ์„ธํŠธ์˜ `lastVerification`์ด ์ดˆ๊ธฐํ™”๋œ๋‹ค.
682
+ *
683
+ * ์—๋Ÿฌ: 400 `PAYMENT_PROVIDER_INCOMPLETE`(์‹œํฌ๋ฆฟ ํ‚ค ์—†์Œ) ยท 400 `INVALID_CREDENTIALS`(ํ˜•์‹ยทํ™˜๊ฒฝ ๋ถˆ์ผ์น˜) ยท
684
+ * 409 `PAYMENT_PROVIDER_VERIFICATION_FAILED`(๋ผ์ด๋ธŒ ์‚ฌ์šฉ ์ค‘์ธ PG์˜ LIVE ์„ธํŠธ ๊ฐ€๋งน์ ์„ ๋ฐ”๊พธ๋ ค๋Š”๋ฐ ์ €์žฅ ์ „ ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ ์‹คํŒจ.
685
+ * ์ €์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ฐ™์€ ๊ฐ€๋งน์ ์˜ ๋น„๋ฐ€ ๊ฐ’ ๊ต์ฒด๋Š” ๊ฒ€์‚ฌํ•˜์ง€ ์•Š๋Š”๋‹ค)
686
+ */
687
+ upsertTossPayments: (body: UpsertTossPaymentsProviderRequest) =>
688
+ storeHttp.request("PUT", "/store/payment-providers/tosspayments", paymentProviderSchema, {
689
+ body: upsertTossPaymentsProviderRequestSchema.parse(body),
690
+ }),
691
+ /**
692
+ * `PUT /v1/store/payment-providers/portone` โ€” ์ž๊ฒฉ ์ฆ๋ช… ์„ธํŠธ ์ €์žฅ. ๊ทœ์น™ยท์—๋Ÿฌ๋Š” `upsertTossPayments`์™€ ๊ฐ™๋‹ค
693
+ * (๊ฐ€๋งน์  ์‹๋ณ„์ž๋Š” `storeId`, ๋น„๋ฐ€ ํ•„๋“œ๋Š” `apiSecret`ยท`webhookSecret`). 400 `INVALID_CREDENTIALS`์—๋Š” ์ฑ„๋„ ํ‚ค ์ค‘๋ณต์ด ํฌํ•จ๋œ๋‹ค.
694
+ */
695
+ upsertPortOne: (body: UpsertPortOneProviderRequest) =>
696
+ storeHttp.request("PUT", "/store/payment-providers/portone", paymentProviderSchema, {
697
+ body: upsertPortOneProviderRequestSchema.parse(body),
698
+ }),
699
+ /**
700
+ * `PATCH /v1/store/payment-providers/{provider}` โ€” ์‚ฌ์šฉ ์—ฌ๋ถ€(`enabled`)ยท์ƒŒ๋“œ๋ฐ•์Šค ์Šค์œ„์น˜(`sandboxMode`) ๋ณ€๊ฒฝ.
701
+ * ํ•˜๋‚˜ ์ด์ƒ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค(์•„๋‹ˆ๋ฉด ์š”์ฒญ ์ „์— ZodError). ๊ฒ€์ฆ์„ ๋ชจ๋‘ ํ†ต๊ณผํ•ด์•ผ ํ•œ ๋ฒˆ์— ์ ์šฉ๋œ๋‹ค(๋ถ€๋ถ„ ์ ์šฉ ์—†์Œ).
702
+ * ๋ˆ PG๋กœ ์Šน์ธ๋œ ๊ฒฐ์ œ์˜ ํ™˜๋ถˆ์€ ๊ณ„์† ๊ทธ PG๋กœ ๊ฐ„๋‹ค.
703
+ *
704
+ * ์—๋Ÿฌ
705
+ * - 404 `PAYMENT_PROVIDER_NOT_FOUND`: ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์€ PG
706
+ * - 409 `PAYMENT_PROVIDER_INCOMPLETE`: ์ผœ๋ ค๋Š”๋ฐ ํ™œ์„ฑ ํ™˜๊ฒฝ(์ƒŒ๋“œ๋ฐ•์Šค๋ฉด TEST, ์•„๋‹ˆ๋ฉด LIVE) ์„ธํŠธ๊ฐ€ ์—†๊ฑฐ๋‚˜ ๋ถˆ์™„์ „
707
+ * - 409 `LIVE_GATE_NOT_MET`: `sandboxMode: false`(๋ผ์ด๋ธŒ ์ „ํ™˜)์ธ๋ฐ ๊ฐœ๋ฐฉ ์กฐ๊ฑด ๋ฏธ์ถฉ์กฑ. `ApiError.details`๊ฐ€
708
+ * `{ conditions: LiveGateCondition[] }`(์กฐ๊ฑด๋ณ„ `met`)์ด๋‹ค. `liveGateErrorDetailsSchema`๋กœ ํŒŒ์‹ฑํ•œ๋‹ค
709
+ * - 409 `LIVE_PAYMENTS_DISABLED`: ํ”Œ๋žซํผ ์‹ค๊ฒฐ์ œ ์Šค์œ„์น˜๋งŒ ๋ฏธ์ถฉ์กฑ. `details`๋Š” ์œ„์™€ ๊ฐ™๋‹ค
710
+ *
711
+ * `sandboxMode: true`๋Š” ํ•ญ์ƒ ํ—ˆ์šฉ๋˜๊ณ  ์ฆ‰์‹œ ์‹ค๊ฒฐ์ œ๊ฐ€ ๋ฉˆ์ถ˜๋‹ค.
712
+ */
713
+ updateProvider: updatePaymentProvider,
714
+ /**
715
+ * ์ƒŒ๋“œ๋ฐ•์Šค ์Šค์œ„์น˜๋งŒ ๋ฐ”๊พผ๋‹ค โ€” `updateProvider(provider, { sandboxMode })`์™€ ๊ฐ™๋‹ค.
716
+ * `false`(๋ผ์ด๋ธŒ ์ „ํ™˜)๋Š” 409 `LIVE_GATE_NOT_MET`ยท`LIVE_PAYMENTS_DISABLED`(๋‘˜ ๋‹ค `ApiError.details.conditions`)๊ฐ€ ๋‚  ์ˆ˜ ์žˆ๋‹ค.
717
+ */
718
+ setSandboxMode: (provider: PgProvider, sandboxMode: boolean) =>
719
+ updatePaymentProvider(provider, { sandboxMode }),
720
+ /**
721
+ * `DELETE /v1/store/payment-providers/{provider}` (204) โ€” ์—ฐ๊ฒฐ ํ•ด์ œ. ๊ฒฐ์ œ ์ด๋ ฅ๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ ํ—ˆ์šฉ๋œ๋‹ค.
722
+ * ๋ชจ๋“  ์„ธํŠธ๋Š” ๋ณด๊ด€๋˜๊ณ  PG ์„ค์ •์—์„œ ์‚ฌ๋ผ์ง„๋‹ค. ์ด๋ฏธ ๋ฐ›์€ ๊ฒฐ์ œ์˜ ์ทจ์†Œยทํ™˜๋ถˆยท๋Œ€์‚ฌ๋Š” ๋ณด๊ด€๋œ ์ž๊ฒฉ ์ฆ๋ช…์œผ๋กœ ๊ณ„์† ์ฒ˜๋ฆฌ๋œ๋‹ค.
723
+ * 404 `PAYMENT_PROVIDER_NOT_FOUND`.
724
+ */
725
+ disconnect: (provider: PgProvider) =>
726
+ storeHttp.requestVoid(
727
+ "DELETE",
728
+ path`/store/payment-providers/${pgProviderSchema.parse(provider)}`,
729
+ ),
730
+ /**
731
+ * `DELETE /v1/store/payment-providers/{provider}/credentials/{TEST|LIVE}` (200) โ€” ํ™œ์„ฑ ํ™˜๊ฒฝ์ด ์•„๋‹Œ ์„ธํŠธ ํ•˜๋‚˜๋ฅผ ๋ณด๊ด€ ์ฒ˜๋ฆฌํ•˜๊ณ 
732
+ * ๊ฐฑ์‹ ๋œ `PaymentProvider`๋ฅผ ๋Œ๋ ค์ค€๋‹ค. ๋ณด๊ด€๋œ ์„ธํŠธ๋Š” ๊ทธ ์„ธํŠธ๋กœ ๋ฐ›์€ ๊ฒฐ์ œ์˜ ํ™˜๋ถˆ์— ๊ณ„์† ์“ฐ์ธ๋‹ค.
733
+ *
734
+ * ์—๋Ÿฌ: 404 `PAYMENT_PROVIDER_NOT_FOUND`(๋ฏธ์—ฐ๊ฒฐ PG) ยท 404 `PAYMENT_CREDENTIAL_NOT_FOUND`(๋“ฑ๋ก๋˜์ง€ ์•Š์€ ์„ธํŠธ) ยท
735
+ * 409 `PAYMENT_CREDENTIAL_ACTIVE`(์ง€๊ธˆ ๊ฒฐ์ œ์— ์“ฐ๋Š” ์„ธํŠธ. ๋จผ์ € `setSandboxMode`๋กœ ์Šค์œ„์น˜๋ฅผ ๋ฐ”๊พผ๋‹ค)
736
+ */
737
+ removeCredentialSet: (provider: PgProvider, environment: PgEnvironment) =>
738
+ storeHttp.request(
739
+ "DELETE",
740
+ path`/store/payment-providers/${pgProviderSchema.parse(provider)}/credentials/${pgEnvironmentSchema.parse(environment)}`,
741
+ paymentProviderSchema,
742
+ ),
743
+ /**
744
+ * `POST /v1/store/payment-providers/{provider}/verify` โ€” ์ €์žฅ๋œ ์„ธํŠธ๋กœ ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๊ทธ ์„ธํŠธ์— ์ €์žฅํ•œ๋‹ค.
745
+ * `environment`๋ฅผ ์ƒ๋žตํ•˜๋ฉด ํ™œ์„ฑ ํ™˜๊ฒฝ ์„ธํŠธ๋ฅผ ํ…Œ์ŠคํŠธํ•œ๋‹ค. ์‹คํŒจํ•ด๋„ 200์ด๊ณ  `result: "FAILED"`์™€ `message`์— ์‚ฌ์œ ๊ฐ€ ์˜จ๋‹ค.
746
+ * ๋ผ์ด๋ธŒ ๊ฐœ๋ฐฉ ์กฐ๊ฑด `TEST_CONNECTION`ยท`LIVE_CONNECTION`์€ ์ด ๊ฒฐ๊ณผ๋กœ ํŒ์ •ํ•œ๋‹ค. 404 `PAYMENT_PROVIDER_NOT_FOUND`.
747
+ */
748
+ verify: (provider: PgProvider, body?: VerifyPaymentProviderRequest) =>
749
+ storeHttp.request(
750
+ "POST",
751
+ path`/store/payment-providers/${pgProviderSchema.parse(provider)}/verify`,
752
+ paymentProviderVerificationSchema,
753
+ { body: verifyPaymentProviderRequestSchema.parse(body ?? {}) },
754
+ ),
755
+ },
756
+
757
+ webhooks: {
758
+ list: () => storeHttp.request("GET", "/webhooks", z.array(webhookSchema)),
759
+ /** ์„œ๋ช… ์‹œํฌ๋ฆฟ ํ‰๋ฌธ์€ ์ด ์‘๋‹ต์—์„œ๋งŒ ๋…ธ์ถœ๋œ๋‹ค */
760
+ create: (body: WebhookRequest) =>
761
+ storeHttp.request("POST", "/webhooks", webhookCreatedSchema, {
762
+ body: webhookRequestSchema.parse(body),
763
+ }),
764
+ get: (webhookId: string) =>
765
+ storeHttp.request("GET", path`/webhooks/${webhookId}`, webhookSchema),
766
+ /** SUSPENDED ๊ตฌ๋…์€ ์ˆ˜์ •ํ•˜๋ฉด ACTIVE๋กœ ๋Œ์•„๊ฐ„๋‹ค. ์ˆ˜์ •๋œ ๊ตฌ๋…์„ ๋Œ๋ ค์ค€๋‹ค */
767
+ update: (webhookId: string, body: WebhookRequest) =>
768
+ storeHttp.request("PUT", path`/webhooks/${webhookId}`, webhookSchema, {
769
+ body: webhookRequestSchema.parse(body),
770
+ }),
771
+ remove: (webhookId: string) => storeHttp.requestVoid("DELETE", path`/webhooks/${webhookId}`),
772
+ rotateSecret: (webhookId: string) =>
773
+ storeHttp.request(
774
+ "POST",
775
+ path`/webhooks/${webhookId}/rotate-secret`,
776
+ webhookSecretRotatedSchema,
777
+ ),
778
+ /** SUSPENDED ๊ตฌ๋…์„ ACTIVE๋กœ ์žฌ๊ฐœํ•˜๊ณ  ์—ฐ์† ์‹คํŒจ ๊ธฐ๋ก์„ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. ์ด๋ฏธ ACTIVE๋ฉด ๊ทธ๋Œ€๋กœ ๋Œ๋ ค์ค€๋‹ค(๋ฉฑ๋“ฑ) */
779
+ resume: (webhookId: string) =>
780
+ storeHttp.request("POST", path`/webhooks/${webhookId}/resume`, webhookSchema),
781
+ /** ์ „์†ก ๊ธฐ๋ก (์ตœ์‹  ์ ์žฌ์ˆœ, ๊ธฐ๋ณธ 50๊ฑดยท์ตœ๋Œ€ 100๊ฑด, ๋ณด๊ด€ 30์ผ) */
782
+ listDeliveries: (webhookId: string, params?: WebhookDeliveryListParams) =>
783
+ storeHttp.request(
784
+ "GET",
785
+ path`/webhooks/${webhookId}/deliveries`,
786
+ z.array(webhookDeliverySchema),
787
+ { query: params as Query },
788
+ ),
789
+ /**
790
+ * FAILED ์ „์†ก ๊ธฐ๋ก ํ•œ ๊ฑด์„ ๊ฐ™์€ eventId๋กœ ๋‹ค์‹œ ๋ณด๋‚ธ๋‹ค(202). ์ƒˆ๋กœ ์ ์žฌ๋œ ๊ธฐ๋ก์„ ๋Œ๋ ค์ฃผ๋ฉฐ
791
+ * `redeliveryOf`๊ฐ€ ์›๋ณธ deliveryId๋‹ค. ์›๋ณธ์ด FAILED๊ฐ€ ์•„๋‹ˆ๋ฉด 409 `DELIVERY_NOT_FAILED`,
792
+ * ์›๋ณธ์ด ์ด๋ฏธ ์žฌ์ „์†ก๋์œผ๋ฉด 409 `DELIVERY_ALREADY_REDELIVERED`(์›๋ณธ๋‹น 1ํšŒ, ์›๋ณธ์˜ `redeliveredAs` ์ฐธ๊ณ ),
793
+ * ๊ตฌ๋…์ด SUSPENDED๋ฉด 409 `WEBHOOK_SUSPENDED`.
794
+ */
795
+ redeliver: (webhookId: string, deliveryId: string) =>
796
+ storeHttp.request(
797
+ "POST",
798
+ path`/webhooks/${webhookId}/deliveries/${deliveryId}/redeliver`,
799
+ webhookDeliverySchema,
800
+ ),
801
+ /**
802
+ * ์ตœ๊ทผ 72์‹œ๊ฐ„์˜ FAILED ์ค‘ ์•„์ง ์žฌ์ „์†ก๋˜์ง€ ์•Š์€ ๊ฑด ์ „์ฒด(์ตœ๋Œ€ 500๊ฑด)๋ฅผ ๋‹ค์‹œ ๋ณด๋‚ธ๋‹ค(202).
803
+ * ๊ตฌ๋…์ด SUSPENDED๋ฉด 409 `WEBHOOK_SUSPENDED` โ€” ๋จผ์ € `resume`ํ•œ๋‹ค.
804
+ */
805
+ redeliverFailed: (webhookId: string) =>
806
+ storeHttp.request(
807
+ "POST",
808
+ path`/webhooks/${webhookId}/deliveries/redeliver-failed`,
809
+ webhookRedeliverFailedResultSchema,
810
+ ),
811
+ /**
812
+ * ์ง€์ •ํ•œ ์ด๋ฒคํŠธ ํƒ€์ž…์˜ ์ƒ˜ํ”Œ ํŽ˜์ด๋กœ๋“œ(์ด๋ฒคํŠธ id `evt_test_โ€ฆ`)๋ฅผ ์‹ค์ œ๋กœ ๋ณด๋‚ด๊ณ  ์ˆ˜์‹  ์„œ๋ฒ„ ์‘๋‹ต์„
813
+ * ๋™๊ธฐ๋กœ ๋Œ๋ ค์ค€๋‹ค. ์žฌ์‹œ๋„ยท์ •์ง€ ํŒ์ •ยท์ „์†ก ๊ธฐ๋ก์ด ์—†๋‹ค.
814
+ */
815
+ sendTest: (webhookId: string, body: WebhookTestRequest) =>
816
+ storeHttp.request("POST", path`/webhooks/${webhookId}/test`, webhookTestResultSchema, {
817
+ body: webhookTestRequestSchema.parse(body),
818
+ }),
819
+ },
820
+
821
+ /** API ๋กœ๊ทธ (๋ณด๊ด€ 7์ผ, ์ฝ˜์†” OWNER/ADMIN) */
822
+ apiLogs: {
823
+ /** ์ตœ์‹ ์ˆœ ์ปค์„œ ํŽ˜์ด์ง€๋„ค์ด์…˜. ๋‹ค์Œ ํŽ˜์ด์ง€๋Š” ์‘๋‹ต์˜ `nextCursor`๋ฅผ `cursor`๋กœ ๋„˜๊ธด๋‹ค */
824
+ list: (params?: ApiLogListOptions) =>
825
+ storeHttp.request("GET", "/api-logs", apiLogPageSchema, {
826
+ query: params && {
827
+ ...params,
828
+ principalType: Array.isArray(params.principalType)
829
+ ? params.principalType.join(",") || undefined
830
+ : params.principalType,
831
+ },
832
+ }),
833
+ /** ์ตœ๊ทผ 24์‹œ๊ฐ„ ์š”์ฒญ ์ˆ˜ยท์—๋Ÿฌ ์ˆ˜ */
834
+ summary: () => storeHttp.request("GET", "/api-logs/summary", apiLogSummarySchema),
835
+ },
836
+
837
+ reviews: {
838
+ list: (params?: ReviewListParams) =>
839
+ storeHttp.request("GET", "/reviews", pageSchema(reviewSchema), { query: params as Query }),
840
+ get: (reviewId: string) => storeHttp.request("GET", path`/reviews/${reviewId}`, reviewSchema),
841
+ createReply: (reviewId: string, content: string) =>
842
+ storeHttp.requestVoid("POST", path`/reviews/${reviewId}/reply`, { body: { content } }),
843
+ updateReply: (reviewId: string, content: string) =>
844
+ storeHttp.requestVoid("PUT", path`/reviews/${reviewId}/reply`, { body: { content } }),
845
+ deleteReply: (reviewId: string) =>
846
+ storeHttp.requestVoid("DELETE", path`/reviews/${reviewId}/reply`),
847
+ },
848
+
849
+ sellerAddresses: {
850
+ list: (type?: AddressType) =>
851
+ storeHttp.request("GET", "/seller-addresses", z.array(sellerAddressSchema), {
852
+ query: { type },
853
+ }),
854
+ create: (body: SellerAddressRequest) =>
855
+ storeHttp.requestVoid("POST", "/seller-addresses", {
856
+ body: sellerAddressRequestSchema.parse(body),
857
+ }),
858
+ update: (addressId: string, body: SellerAddressRequest) =>
859
+ storeHttp.requestVoid("PUT", path`/seller-addresses/${addressId}`, {
860
+ body: sellerAddressRequestSchema.parse(body),
861
+ }),
862
+ remove: (addressId: string) =>
863
+ storeHttp.requestVoid("DELETE", path`/seller-addresses/${addressId}`),
864
+ setDefault: (addressId: string) =>
865
+ storeHttp.requestVoid("POST", path`/seller-addresses/${addressId}/default`),
866
+ },
867
+
868
+ store: {
869
+ get: () => storeHttp.request("GET", "/store", storeSchema),
870
+ listMembers: () => storeHttp.request("GET", "/store/members", z.array(storeMemberSchema)),
871
+ listInvitations: () =>
872
+ storeHttp.request("GET", "/store/invitations", z.array(invitationSchema)),
873
+ },
874
+ };
875
+ }
876
+
877
+ export type StoreClient = ReturnType<typeof createStoreClient>;