@sayren/store-sdk 0.23.0 → 0.28.1

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/server.d.mts CHANGED
@@ -1,47 +1,38 @@
1
1
  import { a as WEBHOOK_HEADERS, i as WEBHOOK_EVENT_TYPE_HEADER, n as WEBHOOK_DELIVERY_ID_HEADER, o as WEBHOOK_SIGNATURE_HEADER, r as WEBHOOK_EVENT_ID_HEADER, s as WEBHOOK_USER_AGENT, t as WEBHOOK_DEFAULT_TOLERANCE_SEC } from "./webhook-headers-D6QmPdEJ.mjs";
2
2
  //#region src/member-delegation.d.ts
3
3
  /**
4
- * 구매자 위임 — 자체 회원 시스템을 가진 셀러 서버가 로그인한 사용자를 sayren 구매자 토큰으로 바꾼다
5
- * (token-exchange의 `member-uid`·`member-ref` 확장, auth-spec §12-10). 클라이언트 시크릿을 쓰므로 **서버에서만** 부른다.
4
+ * 구매자 위임 — 자체 회원 시스템을 가진 셀러 서버가 로그인한 사용자를 sayren 구매자 토큰으로 바꾼다.
5
+ * 계정 토큰(개인 액세스 토큰 등)을 쓰므로 **서버에서만** 부른다.
6
6
  *
7
- * 스토어에 바인딩된 confidential 클라이언트 + `member:delegate` 스코프가 필요하다. 응답은 구매자 액세스 토큰(30분)이고
8
- * refresh는 없다 — 만료되면 같은 호출로 다시 받는다.
7
+ * 응답은 구매자 액세스 토큰(30분)이고 refresh는 없다 — 만료되면 같은 호출로 다시 받는다.
9
8
  */
10
9
  interface DelegatedMemberToken {
11
10
  accessToken: string;
12
11
  expiresIn: number;
13
12
  memberId: string;
14
13
  }
15
- interface DelegationOptions {
16
- /** api 주소(예: `https://api.sayren.app`) — `/v1/oauth2/token`을 붙인다 */
17
- baseUrl: string;
18
- clientId: string;
19
- clientSecret: string;
20
- fetch?: typeof globalThis.fetch;
21
- }
22
- /** 셀러 자체 인증의 사용자 id로 지목한다. 처음 보는 id면 구매자를 만든다(이메일·이름은 그때만 쓴다) */
23
- export declare function delegateMemberByUid(options: DelegationOptions & {
24
- uid: string;
25
- email?: string;
26
- name?: string;
27
- /**
28
- * 사용자가 내 서비스에서 실제로 로그인한 시각. 주면 구매자 토큰의 인증 시각이 되고(탈퇴처럼 최근 인증이 필요한 동작에
29
- * 쓴다), 주지 않으면 인증 시각이 없는 토큰이 나온다
30
- */
31
- authenticatedAt?: Date;
32
- }): Promise<DelegatedMemberToken>;
33
- /** 이메일로 지목한다. 처음 보는 이메일이면 구매자를 만든다 */
34
- export declare function delegateMemberByEmail(options: DelegationOptions & {
35
- email: string;
36
- name?: string;
37
- }): Promise<DelegatedMemberToken>;
38
- /** 위임 실패 — `error`는 OAuth 오류 코드(`invalid_client`·`invalid_scope`·`invalid_request` 등) */
14
+ /** 위임 실패 — `error`는 api 오류 코드(엔벨로프 `error.code`, 없으면 `http_{status}`) */
39
15
  export declare class MemberDelegationError extends Error {
40
16
  readonly error: string;
41
17
  readonly description: string;
42
18
  readonly status: number;
43
19
  constructor(error: string, description: string, status: number);
44
20
  }
21
+ /**
22
+ * 계정 토큰으로 구매자 위임(`POST /v1/customers/delegations`) — 계정 토큰(개인 액세스 토큰 등)과 상점 id를 쓴다.
23
+ * `customer:rw`가 필요하다. `uid`(셀러 자체 인증의 사용자 id) 또는 `email`로 지목하고, 처음 보는 사용자면 구매자를 만든다.
24
+ */
25
+ export declare function delegateMember(options: {
26
+ /** api 주소(예: `https://api.sayren.app`) */
27
+ baseUrl: string;
28
+ accessToken: string;
29
+ storeId: string;
30
+ uid?: string;
31
+ email?: string;
32
+ name?: string;
33
+ authenticatedAt?: Date;
34
+ fetch?: typeof globalThis.fetch;
35
+ }): Promise<DelegatedMemberToken>;
45
36
  //#endregion
46
37
  //#region src/webhook-signature.d.ts
47
38
  interface SignWebhookPayloadOptions {
package/dist/server.mjs CHANGED
@@ -1,46 +1,6 @@
1
1
  import { a as WEBHOOK_HEADERS, i as WEBHOOK_EVENT_TYPE_HEADER, n as WEBHOOK_DELIVERY_ID_HEADER, o as WEBHOOK_SIGNATURE_HEADER, r as WEBHOOK_EVENT_ID_HEADER, s as WEBHOOK_USER_AGENT, t as WEBHOOK_DEFAULT_TOLERANCE_SEC } from "./webhook-headers-ap_exsb9.mjs";
2
2
  //#region src/member-delegation.ts
3
- /** 셀러 자체 인증의 사용자 id로 지목한다. 처음 보는 id면 구매자를 만든다(이메일·이름은 그때만 쓴다) */
4
- function delegateMemberByUid(options) {
5
- return delegate(options, {
6
- subject_token_type: "urn:sayren:params:oauth:token-type:member-uid",
7
- subject_token: options.uid,
8
- ...options.authenticatedAt ? { subject_auth_time: String(Math.floor(options.authenticatedAt.getTime() / 1e3)) } : {},
9
- ...options.email ? { subject_email: options.email } : {},
10
- ...options.name ? { subject_name: options.name } : {}
11
- });
12
- }
13
- /** 이메일로 지목한다. 처음 보는 이메일이면 구매자를 만든다 */
14
- function delegateMemberByEmail(options) {
15
- return delegate(options, {
16
- subject_token_type: "urn:sayren:params:oauth:token-type:member-ref",
17
- subject_token: options.email,
18
- ...options.name ? { subject_name: options.name } : {}
19
- });
20
- }
21
- async function delegate(options, subject) {
22
- const doFetch = options.fetch ?? globalThis.fetch;
23
- const basic = btoa(`${options.clientId}:${options.clientSecret}`);
24
- const res = await doFetch(`${options.baseUrl.replace(/\/+$/, "")}/v1/oauth2/token`, {
25
- method: "POST",
26
- headers: {
27
- "content-type": "application/x-www-form-urlencoded",
28
- authorization: `Basic ${basic}`
29
- },
30
- body: new URLSearchParams({
31
- grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
32
- ...subject
33
- }).toString()
34
- });
35
- const body = await res.json().catch(() => ({}));
36
- if (!res.ok || typeof body.access_token !== "string") throw new MemberDelegationError(typeof body.error === "string" ? body.error : `http_${res.status}`, typeof body.error_description === "string" ? body.error_description : "", res.status);
37
- return {
38
- accessToken: body.access_token,
39
- expiresIn: Number(body.expires_in),
40
- memberId: String(body.memberId)
41
- };
42
- }
43
- /** 위임 실패 — `error`는 OAuth 오류 코드(`invalid_client`·`invalid_scope`·`invalid_request` 등) */
3
+ /** 위임 실패 — `error`는 api 오류 코드(엔벨로프 `error.code`, 없으면 `http_{status}`) */
44
4
  var MemberDelegationError = class extends Error {
45
5
  error;
46
6
  description;
@@ -53,6 +13,33 @@ var MemberDelegationError = class extends Error {
53
13
  this.name = "MemberDelegationError";
54
14
  }
55
15
  };
16
+ /**
17
+ * 계정 토큰으로 구매자 위임(`POST /v1/customers/delegations`) — 계정 토큰(개인 액세스 토큰 등)과 상점 id를 쓴다.
18
+ * `customer:rw`가 필요하다. `uid`(셀러 자체 인증의 사용자 id) 또는 `email`로 지목하고, 처음 보는 사용자면 구매자를 만든다.
19
+ */
20
+ async function delegateMember(options) {
21
+ const res = await (options.fetch ?? globalThis.fetch)(`${options.baseUrl.replace(/\/+$/, "")}/v1/customers/delegations`, {
22
+ method: "POST",
23
+ headers: {
24
+ "content-type": "application/json",
25
+ authorization: `Bearer ${options.accessToken}`,
26
+ "x-store-id": options.storeId
27
+ },
28
+ body: JSON.stringify({
29
+ ...options.uid ? { uid: options.uid } : {},
30
+ ...options.email ? { email: options.email } : {},
31
+ ...options.name ? { name: options.name } : {},
32
+ ...options.authenticatedAt ? { authenticatedAt: options.authenticatedAt.toISOString() } : {}
33
+ })
34
+ });
35
+ const body = await res.json().catch(() => ({}));
36
+ if (!res.ok || typeof body.data?.accessToken !== "string") throw new MemberDelegationError(typeof body.error?.code === "string" ? body.error.code : `http_${res.status}`, typeof body.error?.message === "string" ? body.error.message : "", res.status);
37
+ return {
38
+ accessToken: body.data.accessToken,
39
+ expiresIn: Number(body.data.expiresIn),
40
+ memberId: String(body.data.memberId)
41
+ };
42
+ }
56
43
  //#endregion
57
44
  //#region src/webhook-signature.ts
58
45
  /**
@@ -177,4 +164,4 @@ async function verifyWebhookSignature(options) {
177
164
  };
178
165
  }
179
166
  //#endregion
180
- export { MemberDelegationError, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_HEADERS, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, delegateMemberByEmail, delegateMemberByUid, parseWebhookSignatureHeader, signWebhookPayload, verifyWebhookSignature };
167
+ export { MemberDelegationError, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_HEADERS, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, delegateMember, parseWebhookSignatureHeader, signWebhookPayload, verifyWebhookSignature };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sayren/store-sdk",
3
- "version": "0.23.0",
3
+ "version": "0.28.1",
4
4
  "main": "./dist/index.mjs",
5
5
  "types": "./dist/index.d.mts",
6
6
  "exports": {
package/src/client.ts CHANGED
@@ -1,14 +1,5 @@
1
1
  import { z } from "zod";
2
- import { base64Utf8 } from "./base64";
3
- import {
4
- ApiError,
5
- createHttp,
6
- path,
7
- type Query,
8
- resolveToken,
9
- type TokenSource,
10
- unwrapData,
11
- } from "./http";
2
+ import { ApiError, createHttp, path, type Query, resolveToken, type TokenSource } from "./http";
12
3
  import {
13
4
  type AnalyticsPagesParams,
14
5
  type AnalyticsProductsParams,
@@ -29,14 +20,6 @@ import {
29
20
  analyticsSourcesQuerySchema,
30
21
  analyticsSourcesSchema,
31
22
  } from "./schemas/analytics";
32
- import {
33
- type ApiClientListParams,
34
- apiClientSchema,
35
- apiClientSecretRotatedSchema,
36
- type OAuthClientRegistrationRequest,
37
- oauthClientRegistrationRequestSchema,
38
- oauthClientRegistrationResponseSchema,
39
- } from "./schemas/api-client";
40
23
  import { type ApiLogListOptions, apiLogPageSchema, apiLogSummarySchema } from "./schemas/api-log";
41
24
  import {
42
25
  type ApprovalDecisionRequest,
@@ -47,15 +30,7 @@ import {
47
30
  approvalRequestSchema,
48
31
  auditLogPageSchema,
49
32
  } from "./schemas/audit";
50
- import {
51
- type ApiTokenCreateRequest,
52
- apiTokenCreatedSchema,
53
- apiTokenCreateRequestSchema,
54
- apiTokenSchema,
55
- clientCredentialsTokenResponseSchema,
56
- myAccountSchema,
57
- myStoreSchema,
58
- } from "./schemas/auth";
33
+ import { myAccountSchema, myStoreSchema } from "./schemas/auth";
59
34
  import {
60
35
  type CategoryCreateRequest,
61
36
  type CategoryDeleteStrategy,
@@ -292,6 +267,7 @@ import {
292
267
  type WriteStorefrontFilesRequest,
293
268
  writeStorefrontFilesRequestSchema,
294
269
  } from "./schemas/storefront-hosting";
270
+ import { publicPlansSchema, storeSubscriptionSchema } from "./schemas/subscription";
295
271
  import {
296
272
  type UploadPurpose,
297
273
  type UploadRequest,
@@ -305,20 +281,16 @@ import type { AuthorizeOptions, StoreSession } from "./session";
305
281
  export interface StoreClientOptions {
306
282
  /** 예: https://api.example.com/v1 */
307
283
  baseUrl: string;
308
- /** 유저 토큰 (1단계). 인증/스토어 목록 API에 사용 */
309
- userToken?: TokenSource;
310
- /** 스토어 토큰 (3단계). 테넌트 리소스 API에 사용 */
311
- storeToken?: TokenSource;
312
- /** 테넌트 storeId — storeHttp(테넌트 리소스) 요청에 X-Store-Id로 실린다(userToken 직접 인가). */
313
- storeId?: TokenSource;
314
284
  /**
315
- * 토큰 주입 모드 — PAT/storeToken급(테넌트 바인딩, role 없음)을 storeHttp Bearer로 싣는다.
316
- * userToken이 아니다(비인터랙티브 M2M/CLI 경로용). storeToken과 함께 주면 storeToken이 우선한다.
285
+ * 계정 토큰(accounts가 발급한 JWT — 상점 플랫폼 로그인, 개인 액세스 토큰, 연결된 앱의 토큰).
286
+ * 계정 API(`/me/*`)와 상점 API 모두에 Bearer로 싣는다. `session`을 주면 생략해도 세션 토큰을 쓴다.
317
287
  */
318
- auth?: { apiToken?: string };
288
+ accessToken?: TokenSource;
289
+ /** 대상 상점 id — 상점 API 요청에 `X-Store-Id`로 싣는다. 계정 API(`/me/*`)에는 싣지 않는다 */
290
+ storeId?: TokenSource;
319
291
  /**
320
- * SDK 세션 매니저 — 전달하면 userToken(Bearer)·401 refresh·복구불가 시 clear를
321
- * 이 세션에서 자동 배선한다. 개별 옵션(userToken/refresh/onUnauthorized)을 명시하면 그쪽이 우선한다.
292
+ * SDK 세션 매니저 — 전달하면 Bearer 토큰·401 refresh·복구 불가 시 clear를
293
+ * 이 세션에서 자동 배선한다. 개별 옵션(accessToken/refresh/onUnauthorized)을 명시하면 그쪽이 우선한다.
322
294
  */
323
295
  session?: StoreSession;
324
296
  /** 401 시 세션 갱신 시도 — true면 요청을 1회 재시도한다(refresh 토큰 회전) */
@@ -328,44 +300,37 @@ export interface StoreClientOptions {
328
300
  fetch?: typeof globalThis.fetch;
329
301
  }
330
302
 
331
- function formBody(fields: Record<string, string>): string {
332
- return new URLSearchParams(fields).toString();
333
- }
334
-
335
303
  export function createStoreClient(options: StoreClientOptions) {
336
304
  const fetchFn = options.fetch ?? globalThis.fetch;
337
305
  const { session } = options;
338
306
 
339
- // session이 있으면 userToken/refresh/onUnauthorized의 기본값을 세션에서 배선한다.
340
- // 개별 옵션을 명시하면 그쪽이 우선(하위호환) — session 미전달 시 기존 동작 그대로.
341
- const userTokenSource: TokenSource =
342
- options.userToken ?? (session ? () => session.getToken() : undefined);
307
+ // session이 있으면 토큰/refresh/onUnauthorized의 기본값을 세션에서 배선한다. 개별 옵션을 명시하면 그쪽이 우선한다.
308
+ const tokenSource: TokenSource =
309
+ options.accessToken ?? (session ? () => session.getToken() : undefined);
343
310
  const refresh = options.refresh ?? (session ? () => session.refresh() : undefined);
344
311
  const onUnauthorized = options.onUnauthorized ?? (session ? () => session.clear() : undefined);
345
- // storeToken(명시 옵션)이 auth.apiToken(PAT/storeToken급 주입)보다 우선한다.
346
- const storeTokenSource: TokenSource = options.storeToken ?? options.auth?.apiToken;
347
312
 
313
+ function bearer(): Record<string, string> {
314
+ const token = resolveToken(tokenSource);
315
+ return token ? { authorization: `Bearer ${token}` } : {};
316
+ }
317
+
318
+ /** 계정 API(`/me/*`) — 상점을 고르지 않는다 */
348
319
  const userHttp = createHttp({
349
320
  baseUrl: options.baseUrl,
350
321
  fetch: fetchFn,
351
- headers: () => {
352
- const token = resolveToken(userTokenSource);
353
- return token ? { authorization: `Bearer ${token}` } : ({} as Record<string, string>);
354
- },
322
+ headers: bearer,
355
323
  refresh,
356
324
  onUnauthorized,
357
325
  });
358
326
 
327
+ /** 상점 API — 같은 계정 토큰 + `X-Store-Id` */
359
328
  const storeHttp = createHttp({
360
329
  baseUrl: options.baseUrl,
361
330
  fetch: fetchFn,
362
331
  headers: () => {
363
- const token = resolveToken(storeTokenSource);
364
332
  const storeId = resolveToken(options.storeId);
365
- return {
366
- ...(token ? { authorization: `Bearer ${token}` } : {}),
367
- ...(storeId ? { "x-store-id": storeId } : {}),
368
- };
333
+ return { ...bearer(), ...(storeId ? { "x-store-id": storeId } : {}) };
369
334
  },
370
335
  refresh,
371
336
  onUnauthorized,
@@ -391,7 +356,7 @@ export function createStoreClient(options: StoreClientOptions) {
391
356
  }
392
357
 
393
358
  return {
394
- /** session의 현재 userToken (session 미배선 시 null — 던지지 않음) */
359
+ /** session의 현재 액세스 토큰 (session 미배선 시 null — 던지지 않음) */
395
360
  get accessToken(): string | null {
396
361
  return session ? session.accessToken : null;
397
362
  },
@@ -405,90 +370,11 @@ export function createStoreClient(options: StoreClientOptions) {
405
370
  userinfo: () => requireSession().userinfo(),
406
371
 
407
372
  auth: {
408
- /**
409
- * 서버 간 연동(M2M) — client_credentials grant (client_secret_basic).
410
- * 클라이언트가 스토어에 바인딩되어 있어 **storeToken이 곧바로 발급**된다.
411
- * ⚠ client_secret은 서버 환경에서만 사용할 것 (브라우저 반입 금지)
412
- */
413
- async issueStoreTokenByClientCredentials(
414
- clientId: string,
415
- clientSecret: string,
416
- scope?: string,
417
- ) {
418
- // RFC 7617 client_secret_basic — UTF-8 바이트를 base64로 인코딩한다.
419
- const credentials = base64Utf8(`${clientId}:${clientSecret}`);
420
- const response = await fetchFn(`${options.baseUrl}/oauth2/token`, {
421
- method: "POST",
422
- headers: {
423
- "content-type": "application/x-www-form-urlencoded",
424
- authorization: `Basic ${credentials}`,
425
- },
426
- body: formBody({ grant_type: "client_credentials", ...(scope ? { scope } : {}) }),
427
- });
428
- if (!response.ok) {
429
- const body = (await response.json().catch(() => null)) as {
430
- error_description?: string;
431
- } | null;
432
- throw new Error(body?.error_description ?? `토큰 발급 실패 (HTTP ${response.status})`);
433
- }
434
- return clientCredentialsTokenResponseSchema.parse(unwrapData(await response.json()));
435
- },
436
- /** 2단계 — 소속 스토어 목록 */
373
+ /** 소속 상점 목록 */
437
374
  listMyStores: () => userHttp.request("GET", "/me/stores", z.array(myStoreSchema)),
438
- /** RFC 7009 — token을 폐기한다 (public client는 client_id만 동반) */
439
- async revoke(token: string, clientId: string) {
440
- await fetchFn(`${options.baseUrl}/oauth2/revoke`, {
441
- method: "POST",
442
- headers: { "content-type": "application/x-www-form-urlencoded" },
443
- body: formBody({ token, client_id: clientId }),
444
- });
445
- },
446
375
  me: () => userHttp.request("GET", "/me", myAccountSchema),
447
376
  },
448
377
 
449
- /** API 토큰(PAT) 관리 — OWNER/ADMIN 전용. 발급된 토큰은 storeToken처럼 Bearer로 사용 */
450
- apiTokens: {
451
- create: (body: ApiTokenCreateRequest) =>
452
- storeHttp.request("POST", "/store/api-tokens", apiTokenCreatedSchema, {
453
- body: apiTokenCreateRequestSchema.parse(body),
454
- }),
455
- list: () => storeHttp.request("GET", "/store/api-tokens", z.array(apiTokenSchema)),
456
- revoke: (tokenId: string) =>
457
- storeHttp.requestVoid("DELETE", path`/store/api-tokens/${tokenId}`),
458
- },
459
-
460
- /**
461
- * 앱(OAuth 클라이언트) 관리 — OWNER/ADMIN storeToken 전용(PAT·M2M 거부). auth-spec §12, 부록 B-13.
462
- * 시크릿 평문은 register·rotateSecret 응답에서만 한 번 노출된다(apiTokens.create와 같은 성격).
463
- */
464
- apiClients: {
465
- /**
466
- * DCR 등록 (RFC 7591) — `POST /v1/oauth2/register`. OAuth 프로토콜 표면이라 응답이 평면 JSON이다.
467
- * 등록 오류(`invalid_client_metadata`·`invalid_redirect_uri`)는 ApiError(code=error,
468
- * message=error_description)로, Bearer 인증 오류는 엔벨로프 그대로 ApiError로 던진다.
469
- */
470
- register: (body: OAuthClientRegistrationRequest) =>
471
- storeHttp.request("POST", "/oauth2/register", oauthClientRegistrationResponseSchema, {
472
- body: oauthClientRegistrationRequestSchema.parse(body),
473
- }),
474
- list: (params?: ApiClientListParams) =>
475
- storeHttp.request("GET", "/store/api-clients", z.array(apiClientSchema), {
476
- query: params as Query,
477
- }),
478
- get: (clientId: string) =>
479
- storeHttp.request("GET", path`/store/api-clients/${clientId}`, apiClientSchema),
480
- /** confidential 전용. 기존 시크릿은 즉시 무효(유예 없음) */
481
- rotateSecret: (clientId: string) =>
482
- storeHttp.request(
483
- "POST",
484
- path`/store/api-clients/${clientId}/rotate-secret`,
485
- apiClientSecretRotatedSchema,
486
- ),
487
- /** REVOKED로 전환(되돌릴 수 없음). 이미 비활성이면 현재 상태를 그대로 돌려준다 */
488
- deactivate: (clientId: string) =>
489
- storeHttp.request("POST", path`/store/api-clients/${clientId}/deactivate`, apiClientSchema),
490
- },
491
-
492
378
  /**
493
379
  * 미디어 업로드 — 자리를 받아(`POST /uploads`) 파일 바이트는 호출자가
494
380
  * `uploadUrl`로 직접 PUT한다. 업로드가 끝나면 `url`이 공개 주소가 된다.
@@ -605,7 +491,7 @@ export function createStoreClient(options: StoreClientOptions) {
605
491
  }),
606
492
  get: (optionSetId: string) =>
607
493
  storeHttp.request("GET", path`/saved-option-sets/${optionSetId}`, savedOptionSetSchema),
608
- /** 이름이 겹치면 409 `OPTION_SET_NAME_TAKEN`, 스토어당 200개를 넘으면 409 `OPTION_SET_LIMIT_EXCEEDED` */
494
+ /** 이름이 겹치면 409 `OPTION_SET_NAME_TAKEN`, 상점당 200개를 넘으면 409 `OPTION_SET_LIMIT_EXCEEDED` */
609
495
  create: (body: SavedOptionSetRequestInput) =>
610
496
  storeHttp.request("POST", "/saved-option-sets", savedOptionSetSchema, {
611
497
  body: savedOptionSetRequestSchema.parse(body),
@@ -675,7 +561,7 @@ export function createStoreClient(options: StoreClientOptions) {
675
561
  get: (orderId: string) => storeHttp.request("GET", path`/orders/${orderId}`, orderSchema),
676
562
  /**
677
563
  * `POST /v1/orders/{orderId}/cash-receipt/reissue` — 실패했거나 재시도 대기 중인 현금영수증을 지금 다시 발급·취소한다.
678
- * 콘솔 OWNER·ADMIN 전용(PAT·M2M 403 `INSUFFICIENT_ROLE`). 404 `CASH_RECEIPT_NOT_FOUND`, 409 `CASH_RECEIPT_NOT_REISSUABLE`
564
+ * 상점 플랫폼 OWNER·ADMIN 전용(PAT·M2M 403 `INSUFFICIENT_ROLE`). 404 `CASH_RECEIPT_NOT_FOUND`, 409 `CASH_RECEIPT_NOT_REISSUABLE`
679
565
  */
680
566
  reissueCashReceipt: (orderId: string) =>
681
567
  storeHttp.request("POST", path`/orders/${orderId}/cash-receipt/reissue`, cashReceiptSchema),
@@ -738,7 +624,7 @@ export function createStoreClient(options: StoreClientOptions) {
738
624
  }),
739
625
  /**
740
626
  * 배송완료 처리 — `DISPATCHED`·`DELIVERING`만 넘어간다. 응답의 `autoDecisionDate`가 자동 구매확정 예정일이다
741
- * (스토어 설정 `autoConfirmDays`가 0이면 null).
627
+ * (상점 설정 `autoConfirmDays`가 0이면 null).
742
628
  */
743
629
  markDelivered: (orderItemId: string, idempotencyKey?: string) =>
744
630
  storeHttp.request("POST", path`/order-items/${orderItemId}/delivered`, orderItemSchema, {
@@ -827,7 +713,7 @@ export function createStoreClient(options: StoreClientOptions) {
827
713
  },
828
714
 
829
715
  /**
830
- * 구매자 로그인 설정 — 이메일·비밀번호 허용, 소셜 공급자 연결. 비밀 값을 다루므로 콘솔에 로그인한 OWNER만 호출할 수 있다
716
+ * 구매자 로그인 설정 — 이메일·비밀번호 허용, 소셜 공급자 연결. 비밀 값을 다루므로 상점 플랫폼에 로그인한 OWNER만 호출할 수 있다
831
717
  */
832
718
  loginSettings: {
833
719
  get: () => storeHttp.request("GET", "/store/login-settings", loginSettingsSchema),
@@ -855,7 +741,7 @@ export function createStoreClient(options: StoreClientOptions) {
855
741
  storeHttp.request("DELETE", path`/store/auth-providers/${provider}`, loginSettingsSchema),
856
742
  },
857
743
 
858
- /** 구매자 관리 — 조회 `customer:r`, 탈퇴 처리 `customer:rw`(콘솔 OWNER·ADMIN) */
744
+ /** 구매자 관리 — 조회 `customer:r`, 탈퇴 처리 `customer:rw`(상점 플랫폼 OWNER·ADMIN) */
859
745
  customers: {
860
746
  list: (
861
747
  params?: PageParams & Omit<z.input<typeof customerListQuerySchema>, "page" | "size">,
@@ -1069,9 +955,9 @@ export function createStoreClient(options: StoreClientOptions) {
1069
955
  },
1070
956
 
1071
957
  /**
1072
- * 결제(PG) 설정 — **콘솔 OWNER 전용**. ADMIN·STAFF·PAT·M2M·3rd-party 앱 토큰은 스코프가 있어도
1073
- * 403 `INSUFFICIENT_ROLE`이다(조회 포함). OWNER 여부는 요청 시점의 스토어 멤버십으로 다시 확인하므로
1074
- * 양도·강등 직후의 옛 storeToken도 403이다(토큰을 재발급받는다). 비밀 값(시크릿 키·API Secret·웹훅 시크릿)은 요청으로만 보내고
958
+ * 결제(PG) 설정 — **상점 플랫폼 OWNER 전용**. ADMIN·STAFF·PAT·M2M·3rd-party 앱 토큰은 스코프가 있어도
959
+ * 403 `INSUFFICIENT_ROLE`이다(조회 포함). OWNER 여부는 요청 시점의 상점 멤버십으로 다시 확인하므로
960
+ * 양도·강등 직후에도 바로 403이다. 비밀 값(시크릿 키·API Secret·웹훅 시크릿)은 요청으로만 보내고
1075
961
  * 응답에는 `secrets[].masked`(앞 4자리 마스킹)만 나온다.
1076
962
  */
1077
963
  paymentSettings: {
@@ -1241,7 +1127,7 @@ export function createStoreClient(options: StoreClientOptions) {
1241
1127
  }),
1242
1128
  },
1243
1129
 
1244
- /** API 로그 (보관 7일, 콘솔 OWNER/ADMIN) */
1130
+ /** API 로그 (보관 7일, 상점 플랫폼 OWNER/ADMIN) */
1245
1131
  apiLogs: {
1246
1132
  /** 최신순 커서 페이지네이션. 다음 페이지는 응답의 `nextCursor`를 `cursor`로 넘긴다 */
1247
1133
  list: (params?: ApiLogListOptions) =>
@@ -1257,7 +1143,7 @@ export function createStoreClient(options: StoreClientOptions) {
1257
1143
  summary: () => storeHttp.request("GET", "/api-logs/summary", apiLogSummarySchema),
1258
1144
  },
1259
1145
 
1260
- /** 감사 로그 (관리 API 쓰기의 행위자·대상·결과, 콘솔 OWNER/ADMIN, `audit:r`) */
1146
+ /** 감사 로그 (관리 API 쓰기의 행위자·대상·결과, 상점 플랫폼 OWNER/ADMIN, `audit:r`) */
1261
1147
  auditLogs: {
1262
1148
  /** 최신순 커서 페이지네이션. 다음 페이지는 응답의 `nextCursor`를 `cursor`로 넘긴다 */
1263
1149
  list: (params?: AuditLogListOptions) =>
@@ -1272,7 +1158,7 @@ export function createStoreClient(options: StoreClientOptions) {
1272
1158
  },
1273
1159
 
1274
1160
  /**
1275
- * 승인 요청 — 에이전트의 고위험 쓰기가 202로 보류된 건. 목록·승인·거절은 콘솔 OWNER/ADMIN,
1161
+ * 승인 요청 — 에이전트의 고위험 쓰기가 202로 보류된 건. 목록·승인·거절은 상점 플랫폼 OWNER/ADMIN,
1276
1162
  * 단건 조회는 요청한 토큰도 할 수 있다(상태 확인용).
1277
1163
  */
1278
1164
  approvalRequests: {
@@ -1361,7 +1247,7 @@ export function createStoreClient(options: StoreClientOptions) {
1361
1247
  ),
1362
1248
  /**
1363
1249
  * `POST /v1/storefront/site` (201) — 템플릿으로 사이트를 만들고 v1을 발행한다. 공개 상태는 오픈 준비 중이다.
1364
- * 사이트를 만드는 유일한 경로다(스토어를 만들어도 사이트는 생기지 않는다). 템플릿은 여기서만 고른다
1250
+ * 사이트를 만드는 유일한 경로다(상점을 만들어도 사이트는 생기지 않는다). 템플릿은 여기서만 고른다
1365
1251
  */
1366
1252
  create: (body: CreateStorefrontSiteRequest) =>
1367
1253
  storeHttp.request("POST", "/storefront/site", storefrontSiteSchema, {
@@ -1412,7 +1298,7 @@ export function createStoreClient(options: StoreClientOptions) {
1412
1298
  }),
1413
1299
  /**
1414
1300
  * `POST /v1/storefront/site/preview-link` — 오픈 준비 중인 사이트를 구성원이 볼 수 있는 5분짜리 링크.
1415
- * 콘솔에서 로그인한 OWNER·ADMIN만 받는다
1301
+ * 상점 플랫폼에서 로그인한 OWNER·ADMIN만 받는다
1416
1302
  */
1417
1303
  createPreviewLink: (body: CreateStorefrontPreviewLinkRequest = {}) =>
1418
1304
  storeHttp.request("POST", "/storefront/site/preview-link", storefrontPreviewLinkSchema, {
@@ -1470,7 +1356,7 @@ export function createStoreClient(options: StoreClientOptions) {
1470
1356
  storeHttp.request("DELETE", "/storefront/site/editing", storefrontEditingSchema),
1471
1357
  /**
1472
1358
  * `POST /v1/storefront/site/editing/preview-link` — 편집 미리보기(dev 서버) 5분짜리 링크.
1473
- * 콘솔에서 로그인한 OWNER·ADMIN만 받는다(에이전트 불가)
1359
+ * 상점 플랫폼에서 로그인한 OWNER·ADMIN만 받는다(에이전트 불가)
1474
1360
  */
1475
1361
  createEditPreviewLink: (body: CreateStorefrontEditPreviewLinkRequest = {}) =>
1476
1362
  storeHttp.request(
@@ -1583,6 +1469,22 @@ export function createStoreClient(options: StoreClientOptions) {
1583
1469
  }),
1584
1470
  },
1585
1471
 
1472
+ subscription: {
1473
+ /**
1474
+ * `GET /v1/store/subscription` — 스코프 `store:r`. 현재 요금제·상태·사용량 기간·한도·이번 기간 사용량.
1475
+ * 결제 수단·청구 금액은 싣지 않는다. 요금제 변경은 상점 플랫폼에서만 한다
1476
+ */
1477
+ get: () => storeHttp.request("GET", "/store/subscription", storeSubscriptionSchema),
1478
+ },
1479
+
1480
+ plans: {
1481
+ /**
1482
+ * `GET /v1/public/plans` — 인증 없음. 공개 요금제의 지금 버전(가격·기능 키 값). 값은 서버가 관리하고 언제든 바뀔 수 있어
1483
+ * 코드에 요금제 수치를 두지 말고 이 값을 읽는다
1484
+ */
1485
+ listPublic: () => storeHttp.request("GET", "/public/plans", publicPlansSchema),
1486
+ },
1487
+
1586
1488
  store: {
1587
1489
  get: () => storeHttp.request("GET", "/store", storeSchema),
1588
1490
  /**
@@ -1608,7 +1510,7 @@ export function createStoreClient(options: StoreClientOptions) {
1608
1510
  }),
1609
1511
  /**
1610
1512
  * `POST /v1/store/invitations` (201) — 스코프 `member:rw`. 초대 메일을 보낸다. 발송 결과는 응답의
1611
- * `deliveryStatus`다(발송이 실패해도 초대는 만들어진다). 스토어·초대한 사람·받는 주소별 발송 한도가 있다(429, `details.limit`).
1513
+ * `deliveryStatus`다(발송이 실패해도 초대는 만들어진다). 상점·초대한 사람·받는 주소별 발송 한도가 있다(429, `details.limit`).
1612
1514
  */
1613
1515
  invite: (body: InviteMemberRequest) =>
1614
1516
  storeHttp.request("POST", "/store/invitations", invitationSchema, {
package/src/http.ts CHANGED
@@ -74,7 +74,7 @@ export const APPROVAL_ALREADY_DECIDED_CODE = "APPROVAL_ALREADY_DECIDED";
74
74
  * 성공 값으로 돌려주지 않는다 — 돌려주면 호출부가 승인 요청을 상품·주문으로 읽는다.
75
75
  *
76
76
  * - `code = APPROVAL_REQUIRED`(202): 에이전트의 고위험 쓰기가 보류됐다. 아직 아무것도 바뀌지 않았다.
77
- * 셀러가 콘솔(`approval.approvalUrl`)에서 승인하면 서버가 원 요청을 실행한다. 같은 요청을 다시 보내지 말고
77
+ * 셀러가 상점 플랫폼(`approval.approvalUrl`)에서 승인하면 서버가 원 요청을 실행한다. 같은 요청을 다시 보내지 말고
78
78
  * `approvalRequests.get(approvalId)`로 결과를 확인한다.
79
79
  * - `code = APPROVAL_ALREADY_DECIDED`(200): 같은 멱등키로 보낸 요청의 승인이 이미 끝났다. `status`가
80
80
  * `SUCCEEDED`·`FAILED`·`UNKNOWN`이면 실행 결과(`approval.result`)를 보고, `REJECTED`·`EXPIRED`면 새 멱등키로 다시 요청한다.
package/src/index.ts CHANGED
@@ -12,13 +12,11 @@ export {
12
12
  buildAuthorizeUrl,
13
13
  createPkcePair,
14
14
  exchangeAuthorizationCode,
15
- exchangeStoreTokenViaOAuth,
16
15
  type OAuthTokenResponse,
17
16
  oauthTokenResponseSchema,
18
17
  refreshUserToken,
19
18
  } from "./oauth";
20
19
  export * from "./schemas/analytics";
21
- export * from "./schemas/api-client";
22
20
  export * from "./schemas/api-log";
23
21
  export * from "./schemas/audit";
24
22
  export * from "./schemas/auth";
@@ -27,6 +25,7 @@ export * from "./schemas/claim";
27
25
  export * from "./schemas/common";
28
26
  export * from "./schemas/coupon";
29
27
  export * from "./schemas/customer";
28
+ export * from "./schemas/customer-delegation";
30
29
  export * from "./schemas/fulfillment";
31
30
  export * from "./schemas/login";
32
31
  export * from "./schemas/misc";
@@ -40,6 +39,7 @@ export * from "./schemas/scope";
40
39
  export * from "./schemas/shipping-policy";
41
40
  export * from "./schemas/store";
42
41
  export * from "./schemas/storefront-hosting";
42
+ export * from "./schemas/subscription";
43
43
  export * from "./schemas/telemetry";
44
44
  export * from "./schemas/upload";
45
45
  export * from "./schemas/webhook-event";
@@ -48,6 +48,7 @@ export {
48
48
  createStoreSession,
49
49
  type SessionStorage,
50
50
  STORE_SESSION_KEYS,
51
+ type StoreAuthServer,
51
52
  type StoreSession,
52
53
  type StoreSessionOptions,
53
54
  } from "./session";