@sayren/store-sdk 0.13.0 → 0.14.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/index.d.mts +742 -98
- package/dist/index.mjs +256 -22
- package/package.json +1 -1
- package/src/client.ts +59 -0
- package/src/http.ts +54 -1
- package/src/index.ts +8 -1
- package/src/schemas/audit.ts +255 -0
- package/src/schemas/auth.ts +8 -0
- package/src/schemas/order.ts +4 -4
- package/src/schemas/payment.ts +3 -3
- package/src/schemas/product.ts +1 -1
- package/src/schemas/scope.ts +4 -1
- package/src/schemas/store.ts +4 -4
- package/src/session.ts +2 -2
package/dist/index.mjs
CHANGED
|
@@ -27,6 +27,162 @@ function base64Utf8(text) {
|
|
|
27
27
|
return base64FromBytes(new TextEncoder().encode(text));
|
|
28
28
|
}
|
|
29
29
|
//#endregion
|
|
30
|
+
//#region src/schemas/audit.ts
|
|
31
|
+
/**
|
|
32
|
+
* 감사 로그와 승인 요청 — `/v1/audit-logs`, `/v1/approval-requests` (이슈 #36).
|
|
33
|
+
*
|
|
34
|
+
* 관리 API 쓰기(`/v1/**`의 GET 외 요청)는 모두 요청 단위 기록(`source: REQUEST`) 한 줄을 남긴다. 환불·직권취소 같은
|
|
35
|
+
* 도메인 동작은 그 안에서 변경 전후를 담은 기록(`source: DOMAIN`)을 따로 남기고 `requestId`로 요청 기록과 이어진다.
|
|
36
|
+
*/
|
|
37
|
+
const auditActorTypeSchema = z.enum([
|
|
38
|
+
"USER",
|
|
39
|
+
"AGENT",
|
|
40
|
+
"APP",
|
|
41
|
+
"TOKEN",
|
|
42
|
+
"SYSTEM"
|
|
43
|
+
]).describe("행위자 유형. `USER`=콘솔 사용자, `AGENT`=AI 에이전트(MCP·에이전트용 API 토큰), `APP`=앱(OAuth 클라이언트·M2M), `TOKEN`=API 토큰, `SYSTEM`=자동화(주기 잡)");
|
|
44
|
+
const auditActorSchema = z.object({
|
|
45
|
+
type: auditActorTypeSchema,
|
|
46
|
+
id: z.string().describe("행위자 ID. `USER`=userId, `TOKEN`·`AGENT`(API 토큰)=`pat:{tokenId}`, `APP`=`client:{clientId}`, `SYSTEM`=`system`. 사람이 앱에 위임한 호출은 `APP`이고 위임한 사람은 `metadata.onBehalfOf`에 있다"),
|
|
47
|
+
name: z.string().nullable().describe("기록 시점의 표시 이름. 알 수 없으면 null")
|
|
48
|
+
});
|
|
49
|
+
const auditLogSourceSchema = z.enum(["REQUEST", "DOMAIN"]).describe("기록 단위. `REQUEST`=관리 API 쓰기 요청 한 건, `DOMAIN`=환불·취소 같은 도메인 동작의 상세");
|
|
50
|
+
const auditLogOutcomeSchema = z.enum([
|
|
51
|
+
"SUCCEEDED",
|
|
52
|
+
"FAILED",
|
|
53
|
+
"PENDING_APPROVAL",
|
|
54
|
+
"DENIED",
|
|
55
|
+
"DRY_RUN"
|
|
56
|
+
]).describe("요청 결과. `PENDING_APPROVAL`=승인 대기로 보류(202), `DENIED`=에이전트 정책으로 거부(403 `AGENT_OPERATION_DENIED`), `DRY_RUN`=드라이런(`x-sayren-dry-run: 1`)이라 실행하지 않음");
|
|
57
|
+
const auditLogSchema = z.object({
|
|
58
|
+
auditId: z.string().describe("감사 로그 ID"),
|
|
59
|
+
occurredAt: z.string().describe("기록 일시 (ISO 8601)"),
|
|
60
|
+
source: auditLogSourceSchema,
|
|
61
|
+
action: z.string().describe("동작. `REQUEST`는 오퍼레이션 ID(OpenAPI `operationId`, 예: `ProductsController_patch`), `DOMAIN`은 도메인 코드(예: `ORDER_ITEM_CANCELED_BY_SELLER`)"),
|
|
62
|
+
actor: auditActorSchema,
|
|
63
|
+
actorRole: z.string().nullable().describe("요청 시점의 스토어 역할. 역할이 없는 토큰·자동화는 null"),
|
|
64
|
+
target: z.object({
|
|
65
|
+
type: z.string().describe("대상 리소스 유형 (예: `products`, `order-items`, `claim`)"),
|
|
66
|
+
id: z.string().nullable().describe("대상 리소스 ID. 목록 대상 요청처럼 하나로 정할 수 없으면 null")
|
|
67
|
+
}),
|
|
68
|
+
requestId: z.string().nullable().describe("요청 ID. 같은 요청에서 나온 `REQUEST`·`DOMAIN` 기록이 같은 값을 가진다. 자동화는 null"),
|
|
69
|
+
method: z.string().nullable().describe("HTTP 메서드 (`REQUEST`만)"),
|
|
70
|
+
path: z.string().nullable().describe("요청 경로와 쿼리 (`REQUEST`만). 민감한 쿼리 값은 `***`로 가린다"),
|
|
71
|
+
statusCode: z.number().int().nullable().describe("응답 HTTP 상태 코드 (`REQUEST`만)"),
|
|
72
|
+
outcome: auditLogOutcomeSchema.nullable().describe("요청 결과 (`REQUEST`만)"),
|
|
73
|
+
errorCode: z.string().nullable().describe("에러 응답의 `error.code` (실패한 요청만)"),
|
|
74
|
+
traceId: z.string().nullable().describe("추적 ID. 에러 응답의 `error.traceId`와 같다"),
|
|
75
|
+
idempotencyKey: z.string().nullable().describe("요청의 멱등키"),
|
|
76
|
+
approvalId: z.string().nullable().describe("승인 요청 ID. 승인 대기로 보류된 요청과 승인 뒤 실행된 요청에 실린다"),
|
|
77
|
+
metadata: z.record(z.string(), z.unknown()).describe("부가 정보. 도메인 기록은 변경 전후 값·사유를, 승인 실행 기록은 승인자(`approvedBy`)를 싣는다")
|
|
78
|
+
});
|
|
79
|
+
/** 쉼표 구분 목록 쿼리 → 배열 */
|
|
80
|
+
function commaList(item) {
|
|
81
|
+
return z.union([z.string(), z.array(z.string())]).transform((value) => (Array.isArray(value) ? value : [value]).flatMap((part) => part.split(",")).map((part) => part.trim()).filter((part) => part.length > 0)).pipe(z.array(item).min(1));
|
|
82
|
+
}
|
|
83
|
+
/** 감사 로그 조회 쿼리 — `GET /audit-logs` */
|
|
84
|
+
const auditLogListQuerySchema = z.object({
|
|
85
|
+
from: z.string().optional().describe("조회 시작 일시 (ISO 8601, 포함)"),
|
|
86
|
+
to: z.string().optional().describe("조회 종료 일시 (ISO 8601, 미포함)"),
|
|
87
|
+
actorType: commaList(auditActorTypeSchema).optional().describe("행위자 유형 필터. 여러 개는 쉼표로 구분한다 (예: `AGENT,APP`)"),
|
|
88
|
+
actorId: z.string().optional().describe("행위자 ID 필터 (정확히 일치)"),
|
|
89
|
+
action: z.string().optional().describe("동작 필터 (정확히 일치)"),
|
|
90
|
+
targetType: z.string().optional().describe("대상 유형 필터 (정확히 일치)"),
|
|
91
|
+
targetId: z.string().optional().describe("대상 ID 필터 (정확히 일치)"),
|
|
92
|
+
source: auditLogSourceSchema.optional(),
|
|
93
|
+
outcome: auditLogOutcomeSchema.optional(),
|
|
94
|
+
requestId: z.string().optional().describe("요청 ID 필터 — 한 요청의 요청·도메인 기록을 함께 본다"),
|
|
95
|
+
approvalId: z.string().optional().describe("승인 요청 ID 필터"),
|
|
96
|
+
cursor: z.string().optional().describe("이전 응답의 `nextCursor`. 생략하면 가장 최신부터"),
|
|
97
|
+
limit: z.coerce.number().int().min(1).max(100).optional().describe("최대 건수 (1~100, 기본 50)")
|
|
98
|
+
});
|
|
99
|
+
const auditLogPageSchema = z.object({
|
|
100
|
+
items: z.array(auditLogSchema),
|
|
101
|
+
nextCursor: z.string().nullable().describe("다음 페이지 커서. 마지막 페이지면 null")
|
|
102
|
+
});
|
|
103
|
+
const agentPolicyDecisionSchema = z.enum([
|
|
104
|
+
"ALLOW",
|
|
105
|
+
"APPROVAL_REQUIRED",
|
|
106
|
+
"DENIED"
|
|
107
|
+
]).describe("에이전트 쓰기 판정. `ALLOW`=바로 실행, `APPROVAL_REQUIRED`=콘솔 승인 뒤 실행, `DENIED`=에이전트는 실행할 수 없음");
|
|
108
|
+
const previewValueSchema = z.union([
|
|
109
|
+
z.string(),
|
|
110
|
+
z.number(),
|
|
111
|
+
z.boolean(),
|
|
112
|
+
z.null()
|
|
113
|
+
]);
|
|
114
|
+
const writePreviewSchema = z.object({
|
|
115
|
+
operationId: z.string().describe("오퍼레이션 ID"),
|
|
116
|
+
method: z.string(),
|
|
117
|
+
path: z.string(),
|
|
118
|
+
target: z.object({
|
|
119
|
+
type: z.string(),
|
|
120
|
+
id: z.string().nullable()
|
|
121
|
+
}),
|
|
122
|
+
changes: z.array(z.object({
|
|
123
|
+
field: z.string().describe("바뀌는 필드"),
|
|
124
|
+
before: z.unknown().describe("현재 값. 새로 만드는 요청이거나 읽을 수 없으면 null"),
|
|
125
|
+
after: z.unknown().describe("요청이 넣을 값")
|
|
126
|
+
})).describe("값이 달라지는 필드. 서버가 현재 값과 비교할 수 있는 오퍼레이션만 채운다"),
|
|
127
|
+
facts: z.record(z.string(), previewValueSchema).describe("영향 요약 (예: 취소 수량·주문상품 금액·예상 환불액). 오퍼레이션마다 다르다")
|
|
128
|
+
});
|
|
129
|
+
/** `x-sayren-dry-run: 1`을 실은 쓰기 요청의 응답(200) — 실행하지 않고 판정과 영향만 돌려준다 */
|
|
130
|
+
const writeDryRunResultSchema = z.object({
|
|
131
|
+
dryRun: z.literal(true),
|
|
132
|
+
decision: agentPolicyDecisionSchema,
|
|
133
|
+
reason: z.string().nullable().describe("승인 필요·거부 사유. `ALLOW`면 null"),
|
|
134
|
+
preview: writePreviewSchema
|
|
135
|
+
});
|
|
136
|
+
const approvalStatusSchema = z.enum([
|
|
137
|
+
"PENDING",
|
|
138
|
+
"EXECUTING",
|
|
139
|
+
"SUCCEEDED",
|
|
140
|
+
"FAILED",
|
|
141
|
+
"UNKNOWN",
|
|
142
|
+
"REJECTED",
|
|
143
|
+
"EXPIRED"
|
|
144
|
+
]).describe("승인 요청 상태. `PENDING` 승인 대기 → 승인하면 `EXECUTING`(원 요청 실행 중) → `SUCCEEDED`·`FAILED`(원 요청의 응답 기준), 거절하면 `REJECTED`, 기한이 지나면 `EXPIRED`. 실행 응답을 받지 못했으면(타임아웃·서버 종료) `UNKNOWN`이다 — 서버가 감사 로그로 결과를 확정하면 `SUCCEEDED`·`FAILED`로 바뀌고, 확정하지 못하면 `UNKNOWN`에 남는다. `UNKNOWN`은 다시 실행하지 않는다(이미 실행됐을 수 있다)");
|
|
145
|
+
const approvalRequestSchema = z.object({
|
|
146
|
+
approvalId: z.string().describe("승인 요청 ID"),
|
|
147
|
+
status: approvalStatusSchema,
|
|
148
|
+
operationId: z.string().describe("보류된 오퍼레이션 ID"),
|
|
149
|
+
method: z.string().describe("HTTP 메서드"),
|
|
150
|
+
path: z.string().describe("요청 경로와 쿼리"),
|
|
151
|
+
body: z.unknown().describe("요청 본문. 승인하면 이 본문 그대로 실행한다. 없으면 null"),
|
|
152
|
+
idempotencyKey: z.string().nullable().describe("원 요청의 멱등키"),
|
|
153
|
+
reason: z.string().describe("승인이 필요한 이유"),
|
|
154
|
+
actor: auditActorSchema.describe("요청한 행위자"),
|
|
155
|
+
preview: writePreviewSchema.nullable().describe("요청 시점에 서버가 계산한 영향 미리보기"),
|
|
156
|
+
createdAt: z.string().describe("요청 일시 (ISO 8601)"),
|
|
157
|
+
expiresAt: z.string().describe("승인 기한 (ISO 8601). 지나면 `EXPIRED`가 된다"),
|
|
158
|
+
decidedAt: z.string().nullable().describe("승인·거절 일시"),
|
|
159
|
+
decidedBy: z.object({
|
|
160
|
+
id: z.string(),
|
|
161
|
+
name: z.string().nullable()
|
|
162
|
+
}).nullable().describe("승인·거절한 콘솔 사용자"),
|
|
163
|
+
decisionNote: z.string().nullable().describe("승인·거절 메모"),
|
|
164
|
+
executedAt: z.string().nullable().describe("원 요청 실행을 마친 일시"),
|
|
165
|
+
result: z.object({
|
|
166
|
+
statusCode: z.number().int().describe("원 요청의 응답 상태 코드"),
|
|
167
|
+
data: z.unknown().describe("성공 응답의 `data` (큰 응답은 잘린다)"),
|
|
168
|
+
error: z.object({
|
|
169
|
+
code: z.string(),
|
|
170
|
+
message: z.string()
|
|
171
|
+
}).nullable().describe("실패 응답의 `error` 요약")
|
|
172
|
+
}).nullable().describe("실행 결과. `SUCCEEDED`·`FAILED`·`UNKNOWN`에서 값이 있다. 감사 로그로 확정한 결과는 `data`가 null이다"),
|
|
173
|
+
approvalUrl: z.string().nullable().describe("셀러 콘솔의 승인 화면 주소. 이 배포의 콘솔 주소를 알 수 없으면 null")
|
|
174
|
+
});
|
|
175
|
+
const approvalListQuerySchema = z.object({
|
|
176
|
+
status: commaList(approvalStatusSchema).optional().describe("상태 필터. 여러 개는 쉼표로 구분한다 (예: `PENDING,EXECUTING`)"),
|
|
177
|
+
cursor: z.string().optional().describe("이전 응답의 `nextCursor`. 생략하면 가장 최신부터"),
|
|
178
|
+
limit: z.coerce.number().int().min(1).max(100).optional().describe("최대 건수 (1~100, 기본 50)")
|
|
179
|
+
});
|
|
180
|
+
const approvalPageSchema = z.object({
|
|
181
|
+
items: z.array(approvalRequestSchema),
|
|
182
|
+
nextCursor: z.string().nullable().describe("다음 페이지 커서. 마지막 페이지면 null")
|
|
183
|
+
});
|
|
184
|
+
const approvalDecisionRequestSchema = z.object({ note: z.string().trim().max(500).optional().describe("승인·거절 메모 (최대 500자)") });
|
|
185
|
+
//#endregion
|
|
30
186
|
//#region src/http.ts
|
|
31
187
|
/** 표준 엔벨로프의 $.error 상세 */
|
|
32
188
|
const errorDetailSchema = z.object({
|
|
@@ -86,6 +242,47 @@ var ApiError = class extends Error {
|
|
|
86
242
|
this.name = "ApiError";
|
|
87
243
|
}
|
|
88
244
|
};
|
|
245
|
+
/** 에이전트 쓰기가 승인 대기로 보류됐다(202) */
|
|
246
|
+
const APPROVAL_REQUIRED_CODE = "APPROVAL_REQUIRED";
|
|
247
|
+
/** 같은 멱등키의 승인 요청이 이미 끝났다(200) — 결과는 승인 요청의 `status`·`result`다 */
|
|
248
|
+
const APPROVAL_ALREADY_DECIDED_CODE = "APPROVAL_ALREADY_DECIDED";
|
|
249
|
+
/**
|
|
250
|
+
* 쓰기 요청이 실행되지 않고 승인 흐름에 걸렸다 (이슈 #36). 응답 `data`가 요청한 리소스가 아니라 승인 요청이라
|
|
251
|
+
* 성공 값으로 돌려주지 않는다 — 돌려주면 호출부가 승인 요청을 상품·주문으로 읽는다.
|
|
252
|
+
*
|
|
253
|
+
* - `code = APPROVAL_REQUIRED`(202): 에이전트의 고위험 쓰기가 보류됐다. 아직 아무것도 바뀌지 않았다.
|
|
254
|
+
* 셀러가 콘솔(`approval.approvalUrl`)에서 승인하면 서버가 원 요청을 실행한다. 같은 요청을 다시 보내지 말고
|
|
255
|
+
* `approvalRequests.get(approvalId)`로 결과를 확인한다.
|
|
256
|
+
* - `code = APPROVAL_ALREADY_DECIDED`(200): 같은 멱등키로 보낸 요청의 승인이 이미 끝났다. `status`가
|
|
257
|
+
* `SUCCEEDED`·`FAILED`·`UNKNOWN`이면 실행 결과(`approval.result`)를 보고, `REJECTED`·`EXPIRED`면 새 멱등키로 다시 요청한다.
|
|
258
|
+
*/
|
|
259
|
+
var ApprovalPendingError = class extends Error {
|
|
260
|
+
code;
|
|
261
|
+
httpStatus;
|
|
262
|
+
approval;
|
|
263
|
+
approvalId;
|
|
264
|
+
/** 승인 요청 상태 */
|
|
265
|
+
status;
|
|
266
|
+
constructor(code, httpStatus, approval, message) {
|
|
267
|
+
super(message);
|
|
268
|
+
this.code = code;
|
|
269
|
+
this.httpStatus = httpStatus;
|
|
270
|
+
this.approval = approval;
|
|
271
|
+
this.name = "ApprovalPendingError";
|
|
272
|
+
this.approvalId = approval.approvalId;
|
|
273
|
+
this.status = approval.status;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
/** 성공 엔벨로프가 승인 흐름 응답이면 ApprovalPendingError. 그 밖(다른 202 등)은 null */
|
|
277
|
+
function approvalErrorOf(status, json) {
|
|
278
|
+
const envelope = envelopeSchema.safeParse(json);
|
|
279
|
+
if (!envelope.success) return null;
|
|
280
|
+
const { code, message } = envelope.data.meta;
|
|
281
|
+
if (code !== "APPROVAL_REQUIRED" && code !== "APPROVAL_ALREADY_DECIDED") return null;
|
|
282
|
+
const approval = approvalRequestSchema.safeParse(envelope.data.data);
|
|
283
|
+
if (!approval.success) return null;
|
|
284
|
+
return new ApprovalPendingError(code, status, approval.data, message);
|
|
285
|
+
}
|
|
89
286
|
/**
|
|
90
287
|
* 에러 응답 본문 → ApiError 구성 (에러 형태의 단일 원천).
|
|
91
288
|
* 표준 엔벨로프($.error) 우선, 구형 평면 에러({ code, message }), OAuth 평면 에러
|
|
@@ -144,11 +341,18 @@ function createHttp(options) {
|
|
|
144
341
|
}
|
|
145
342
|
return {
|
|
146
343
|
async request(method, path, schema, request) {
|
|
147
|
-
const
|
|
344
|
+
const response = await send(method, path, request);
|
|
345
|
+
const json = await response.json();
|
|
346
|
+
const pending = approvalErrorOf(response.status, json);
|
|
347
|
+
if (pending) throw pending;
|
|
148
348
|
return schema.parse(unwrapData(json));
|
|
149
349
|
},
|
|
150
350
|
async requestVoid(method, path, request) {
|
|
151
|
-
await send(method, path, request);
|
|
351
|
+
const response = await send(method, path, request);
|
|
352
|
+
if (response.status === 204) return;
|
|
353
|
+
const json = await response.json().catch(() => null);
|
|
354
|
+
const pending = approvalErrorOf(response.status, json);
|
|
355
|
+
if (pending) throw pending;
|
|
152
356
|
}
|
|
153
357
|
};
|
|
154
358
|
}
|
|
@@ -551,7 +755,8 @@ const apiTokenSchema = z.object({
|
|
|
551
755
|
createdByEmail: z.string().nullable().describe("발급자 이메일 (표시용). 계정이 없으면 null"),
|
|
552
756
|
createdAt: z.string(),
|
|
553
757
|
expiresAt: z.string().nullable(),
|
|
554
|
-
lastUsedAt: z.string().nullable()
|
|
758
|
+
lastUsedAt: z.string().nullable(),
|
|
759
|
+
agent: z.boolean().default(false).describe("AI 에이전트(MCP)용 토큰인가. true면 이 토큰의 쓰기는 행위자 `AGENT`로 기록되고 에이전트 쓰기 정책(승인 필요·거부)을 따른다")
|
|
555
760
|
});
|
|
556
761
|
/** 생성 응답 — 평문 토큰은 이 응답에서 단 한 번만 노출된다 */
|
|
557
762
|
const apiTokenCreatedSchema = apiTokenSchema.extend({ token: z.string() });
|
|
@@ -559,7 +764,9 @@ const apiTokenCreateRequestSchema = z.object({
|
|
|
559
764
|
name: z.string().min(1).max(50),
|
|
560
765
|
scopes: z.array(z.string()).min(1),
|
|
561
766
|
/** 일 단위 만료. null = 무기한, 생략 = 90일 */
|
|
562
|
-
expiresInDays: z.number().int().nullable().optional()
|
|
767
|
+
expiresInDays: z.number().int().nullable().optional(),
|
|
768
|
+
/** AI 에이전트(MCP)용 토큰. 생략 = false. 발급 뒤에는 바꿀 수 없다 */
|
|
769
|
+
agent: z.boolean().optional()
|
|
563
770
|
});
|
|
564
771
|
//#endregion
|
|
565
772
|
//#region src/schemas/category.ts
|
|
@@ -1370,7 +1577,7 @@ const TOSS_WIDGET_CLIENT_KEY_PATTERN = /^(test|live)_gck_/;
|
|
|
1370
1577
|
/** 토스페이먼츠 결제위젯 시크릿 키 접두사. {@link TOSS_WIDGET_CLIENT_KEY_PATTERN} 참고 */
|
|
1371
1578
|
const TOSS_WIDGET_SECRET_KEY_PATTERN = /^(test|live)_gsk_/;
|
|
1372
1579
|
/** 결제위젯 키를 거부할 때의 필드 메시지 */
|
|
1373
|
-
const TOSS_WIDGET_KEY_MESSAGE = "결제위젯 키가 아니라 API 개별 연동 키를
|
|
1580
|
+
const TOSS_WIDGET_KEY_MESSAGE = "결제위젯 키가 아니라 API 개별 연동 키를 입력해주십시오";
|
|
1374
1581
|
const tossPaymentsCredentialsInputSchema = z.object({
|
|
1375
1582
|
clientKey: z.string().trim().min(8).max(200).describe("클라이언트 키 (`test_ck_…`/`live_ck_…`). 공개 값 — 결제창에서 쓰인다"),
|
|
1376
1583
|
secretKey: secretInput().optional().describe("시크릿 키 (`test_sk_…`/`live_sk_…`). 비밀 값. 최초 연결 시 필수, 수정 시 생략하면 기존 값을 유지한다"),
|
|
@@ -1417,12 +1624,12 @@ const updatePaymentProviderRequestSchema = z.object({
|
|
|
1417
1624
|
enabled: z.boolean().optional().describe("사용 여부. 켜려면 활성 환경(샌드박스면 TEST, 아니면 LIVE) 자격 증명이 모두 저장돼 있어야 한다(`PAYMENT_PROVIDER_INCOMPLETE`)"),
|
|
1418
1625
|
sandboxMode: z.boolean().optional().describe("샌드박스 스위치. `true`면 새 결제에 TEST 세트를 쓴다(항상 허용). `false`(라이브 전환)는 라이브 개방 조건을 모두 충족해야 한다(`LIVE_GATE_NOT_MET`)"),
|
|
1419
1626
|
easyPayProviders: z.array(easyPayProviderSchema).max(easyPayProviderSchema.options.length).optional().describe("이 PG로 받는 간편결제사 — 통째로 바꾼다. PG 계약(포트원은 채널)에 추가한 간편결제만 켠다. 켠 간편결제사마다 스토어프론트 결제 옵션이 하나씩 생긴다(포트원은 간편결제를 받는 채널이 있어야 한다)")
|
|
1420
|
-
}).refine((body) => body.enabled !== void 0 || body.sandboxMode !== void 0 || body.easyPayProviders !== void 0, { message: "enabled·sandboxMode·easyPayProviders 중 하나는 보내야
|
|
1627
|
+
}).refine((body) => body.enabled !== void 0 || body.sandboxMode !== void 0 || body.easyPayProviders !== void 0, { message: "enabled·sandboxMode·easyPayProviders 중 하나는 보내야 합니다" });
|
|
1421
1628
|
const verifyPaymentProviderRequestSchema = z.object({ environment: pgEnvironmentSchema.optional().describe("연결 테스트할 자격 증명 세트. 생략하면 활성 환경(샌드박스면 TEST, 아니면 LIVE)") });
|
|
1422
1629
|
const updatePaymentSettingsRequestSchema = z.object({
|
|
1423
1630
|
priority: z.array(pgProviderSchema).min(1).max(50).optional().describe("PG 우선순위(앞이 1순위) — 스토어프론트 결제 옵션 목록의 PG 순서다. 연결된 PG 수만큼 **모두 한 번씩** 나열한다(길이 상한 50은 안전장치다). 빠지거나 중복되거나 연결되지 않은 PG가 있으면 `INVALID_PRIORITY`"),
|
|
1424
1631
|
checkoutOrigins: z.array(z.string().max(200)).max(20).optional().describe("결제 도메인 — 통째로 바꾼다. 결제를 마친 구매자가 돌아올 스토어프론트 origin(`https://shop.example.com`, https·경로 없음). 결제 시작의 `returnUrl`은 이 목록 안이어야 한다. 비어 있으면 실결제를 시작할 수 없다(테스트 결제는 localhost 허용). 같은 도메인을 PG 가맹 정보에도 등록한다. 형식이 틀리면 `INVALID_CHECKOUT_ORIGIN`")
|
|
1425
|
-
}).refine((body) => body.priority !== void 0 || body.checkoutOrigins !== void 0, { message: "priority 또는 checkoutOrigins 중 하나는 보내야
|
|
1632
|
+
}).refine((body) => body.priority !== void 0 || body.checkoutOrigins !== void 0, { message: "priority 또는 checkoutOrigins 중 하나는 보내야 합니다" });
|
|
1426
1633
|
const maskedSecretSchema = z.object({
|
|
1427
1634
|
field: z.string().describe("비밀 필드 이름 (`secretKey`·`apiSecret`·`webhookSecret`)"),
|
|
1428
1635
|
configured: z.boolean().describe("값이 저장돼 있는가"),
|
|
@@ -1681,7 +1888,7 @@ const orderSummarySchema = z.object({
|
|
|
1681
1888
|
totalAmount: z.number().int(),
|
|
1682
1889
|
representativeStatus: orderItemStatusSchema,
|
|
1683
1890
|
orderedAt: z.string(),
|
|
1684
|
-
testPayment: z.boolean().describe("테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지
|
|
1891
|
+
testPayment: z.boolean().describe("테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지 않았습니다. 상품을 발송하지 않도록 주의하십시오")
|
|
1685
1892
|
});
|
|
1686
1893
|
const shippingAddressSchema = z.object({
|
|
1687
1894
|
receiverName: z.string(),
|
|
@@ -1721,7 +1928,7 @@ const orderItemSchema = z.object({
|
|
|
1721
1928
|
orderedAt: z.string(),
|
|
1722
1929
|
lastChangedAt: z.string(),
|
|
1723
1930
|
lastChangedType: changeTypeSchema,
|
|
1724
|
-
testPayment: z.boolean().describe("소속 주문이 테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지
|
|
1931
|
+
testPayment: z.boolean().describe("소속 주문이 테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지 않았습니다")
|
|
1725
1932
|
});
|
|
1726
1933
|
const orderSchema = z.object({
|
|
1727
1934
|
orderId: z.string(),
|
|
@@ -1750,14 +1957,14 @@ const orderSchema = z.object({
|
|
|
1750
1957
|
paidAt: z.string().nullable(),
|
|
1751
1958
|
cashReceipt: cashReceiptSchema.nullable().optional().default(null).describe("현금영수증. 구매자가 계좌이체·가상계좌 결제에서 신청했을 때만 있고 그 밖에는 null. 서버는 항상 싣는다")
|
|
1752
1959
|
}),
|
|
1753
|
-
testPayment: z.boolean().describe("테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지
|
|
1960
|
+
testPayment: z.boolean().describe("테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지 않았습니다. 상품을 발송하지 않도록 주의하십시오"),
|
|
1754
1961
|
items: z.array(orderItemSchema)
|
|
1755
1962
|
});
|
|
1756
1963
|
/** 주문 목록 응답 — `GET /v1/orders` */
|
|
1757
1964
|
const orderSummaryPageSchema = pageSchema(orderSummarySchema);
|
|
1758
1965
|
/** 주문상품 변경 목록 응답 — `GET /v1/order-items` */
|
|
1759
1966
|
const orderItemPageSchema = pageSchema(orderItemSchema);
|
|
1760
|
-
const testPaymentQuery = z.enum(["true", "false"]).optional().describe("테스트 결제(샌드박스 결제) 필터. `true`면 테스트 결제만, `false`면 실결제만, 생략하면
|
|
1967
|
+
const testPaymentQuery = z.enum(["true", "false"]).optional().describe("테스트 결제(샌드박스 결제) 필터. `true`면 테스트 결제만, `false`면 실결제만, 생략하면 전부입니다");
|
|
1761
1968
|
const pageQuery = { page: z.string().optional().describe("페이지 번호 (1부터, 기본 1)") };
|
|
1762
1969
|
/**
|
|
1763
1970
|
* `GET /v1/orders` 쿼리 — OpenAPI 문서용 형태(값은 쿼리 문자열). 검증 규칙은 서버가 적용한다:
|
|
@@ -2317,7 +2524,7 @@ const ADDRESS_DESCRIPTIONS = {
|
|
|
2317
2524
|
};
|
|
2318
2525
|
const productCreateRequestSchema = z.object({
|
|
2319
2526
|
name: z.string().min(1).max(100),
|
|
2320
|
-
categoryId: z.string().min(1, "카테고리를
|
|
2527
|
+
categoryId: z.string().min(1, "카테고리를 선택해주십시오"),
|
|
2321
2528
|
salePrice: z.number().int().min(10).multipleOf(10, "판매가는 10원 단위여야 합니다 (INVALID_PRICE_UNIT)").describe("판매가. 원(KRW) 단위 정수, 최소 10원, 10원 단위"),
|
|
2322
2529
|
originalPrice: z.number().int().optional().describe("정가(할인 전 표시 가격). 원 단위"),
|
|
2323
2530
|
description: z.string().optional(),
|
|
@@ -2873,7 +3080,7 @@ const RESERVED_STORE_CODES = [
|
|
|
2873
3080
|
function isReservedStoreCode(code) {
|
|
2874
3081
|
return RESERVED_STORE_CODES.includes(code.toLowerCase());
|
|
2875
3082
|
}
|
|
2876
|
-
const storeCodeSchema = z.string().regex(STORE_CODE_PATTERN, "소문자·숫자·하이픈 3~30자여야 하고 하이픈으로 시작하거나 끝날 수
|
|
3083
|
+
const storeCodeSchema = z.string().regex(STORE_CODE_PATTERN, "소문자·숫자·하이픈 3~30자여야 하고 하이픈으로 시작하거나 끝날 수 없습니다").refine((code) => !isReservedStoreCode(code), "플랫폼이 쓰는 이름이라 스토어 코드로 쓸 수 없습니다");
|
|
2877
3084
|
/** `https://shop.example.com`처럼 경로·쿼리 없는 http(s) 오리진인가 */
|
|
2878
3085
|
function isWebOrigin(value) {
|
|
2879
3086
|
try {
|
|
@@ -2884,7 +3091,7 @@ function isWebOrigin(value) {
|
|
|
2884
3091
|
}
|
|
2885
3092
|
}
|
|
2886
3093
|
/** 방문 분석을 보낼 수 있는 스토어프론트 오리진 — 스킴·호스트(·포트)만. 서버가 소문자·끝 슬래시 제거로 정규화한다 */
|
|
2887
|
-
const analyticsOriginSchema = z.string().trim().max(255).refine(isWebOrigin, "https://shop.example.com 형식(경로 없이)으로 입력해
|
|
3094
|
+
const analyticsOriginSchema = z.string().trim().max(255).refine(isWebOrigin, "https://shop.example.com 형식(경로 없이)으로 입력해 주십시오").describe("스토어프론트 오리진. 예: `https://shop.example.com`");
|
|
2888
3095
|
/** 허용 오리진 상한 */
|
|
2889
3096
|
const MAX_ANALYTICS_ALLOWED_ORIGINS = 20;
|
|
2890
3097
|
/** 자동 배송완료·자동 구매확정 대기일 상한 */
|
|
@@ -2898,7 +3105,7 @@ const policyVersionSchema = z.string().trim().min(1).max(20).describe("약관
|
|
|
2898
3105
|
* 약관 문서 주소 — 구매자에게 보여 주는 약관 본문의 주소다. 동의 시점 값이 이력 행에 복사되므로
|
|
2899
3106
|
* 나중에 주소를 바꿔도 "무엇에 동의했는지"가 남는다. `null`로 지울 수 있다.
|
|
2900
3107
|
*/
|
|
2901
|
-
const policyDocumentUrlSchema = z.url().max(500).refine((value) => value.startsWith("https://"), "https 주소만 쓸 수
|
|
3108
|
+
const policyDocumentUrlSchema = z.url().max(500).refine((value) => value.startsWith("https://"), "https 주소만 쓸 수 있습니다").nullable();
|
|
2902
3109
|
const autoDeliverDaysDescription = "발송 후 자동 배송완료까지의 일수(0~30, 기본 7). 발송(`DISPATCHED`)·배송중(`DELIVERING`) 주문상품을 이 기간이 지나면 배송완료(`DELIVERED`)로 바꾼다. `0`이면 자동 배송완료를 하지 않는다";
|
|
2903
3110
|
const autoConfirmDaysDescription = "배송완료 후 자동 구매확정까지의 일수(0~30, 기본 7). 배송완료 시점에 구매확정 예정일(`autoDecisionDate`)을 이 값으로 잡고, 그 날짜가 지나면 구매확정(`PURCHASE_DECIDED`)으로 바꾼다. 진행 중인 클레임이 있으면 건너뛴다. `0`이면 자동 구매확정을 하지 않는다";
|
|
2904
3111
|
/** `PATCH /v1/store` 본문 — 바꿀 필드만 보낸다 */
|
|
@@ -3488,6 +3695,30 @@ get: (params) => storeHttp.request("GET", "/sales-report", salesReportSchema, {
|
|
|
3488
3695
|
/** 최근 24시간 요청 수·에러 수 */
|
|
3489
3696
|
summary: () => storeHttp.request("GET", "/api-logs/summary", apiLogSummarySchema)
|
|
3490
3697
|
},
|
|
3698
|
+
/** 감사 로그 (관리 API 쓰기의 행위자·대상·결과, 콘솔 OWNER/ADMIN, `audit:r`) */
|
|
3699
|
+
auditLogs: {
|
|
3700
|
+
/** 최신순 커서 페이지네이션. 다음 페이지는 응답의 `nextCursor`를 `cursor`로 넘긴다 */
|
|
3701
|
+
list: (params) => storeHttp.request("GET", "/audit-logs", auditLogPageSchema, { query: params && {
|
|
3702
|
+
...params,
|
|
3703
|
+
actorType: Array.isArray(params.actorType) ? params.actorType.join(",") || void 0 : params.actorType
|
|
3704
|
+
} }) },
|
|
3705
|
+
/**
|
|
3706
|
+
* 승인 요청 — 에이전트의 고위험 쓰기가 202로 보류된 건. 목록·승인·거절은 콘솔 OWNER/ADMIN,
|
|
3707
|
+
* 단건 조회는 요청한 토큰도 할 수 있다(상태 확인용).
|
|
3708
|
+
*/
|
|
3709
|
+
approvalRequests: {
|
|
3710
|
+
list: (params) => storeHttp.request("GET", "/approval-requests", approvalPageSchema, { query: params && {
|
|
3711
|
+
...params,
|
|
3712
|
+
status: Array.isArray(params.status) ? params.status.join(",") || void 0 : params.status
|
|
3713
|
+
} }),
|
|
3714
|
+
get: (approvalId) => storeHttp.request("GET", path`/approval-requests/${approvalId}`, approvalRequestSchema),
|
|
3715
|
+
/**
|
|
3716
|
+
* 승인하면 서버가 원 요청을 그대로 한 번 실행한다. 응답은 실행을 기다리지 않은 `EXECUTING` 상태이고,
|
|
3717
|
+
* 결과(`SUCCEEDED`·`FAILED`·`UNKNOWN`)는 `get`으로 확인한다. 두 번 눌러도 한 번만 실행한다
|
|
3718
|
+
*/
|
|
3719
|
+
approve: (approvalId, body = {}) => storeHttp.request("POST", path`/approval-requests/${approvalId}/approve`, approvalRequestSchema, { body: approvalDecisionRequestSchema.parse(body) }),
|
|
3720
|
+
reject: (approvalId, body = {}) => storeHttp.request("POST", path`/approval-requests/${approvalId}/reject`, approvalRequestSchema, { body: approvalDecisionRequestSchema.parse(body) })
|
|
3721
|
+
},
|
|
3491
3722
|
reviews: {
|
|
3492
3723
|
list: (params) => storeHttp.request("GET", "/reviews", pageSchema(reviewSchema), { query: params }),
|
|
3493
3724
|
get: (reviewId) => storeHttp.request("GET", path`/reviews/${reviewId}`, reviewSchema),
|
|
@@ -3602,7 +3833,7 @@ async function exchangeStoreTokenViaOAuth(options) {
|
|
|
3602
3833
|
* 서버 인가가 갈라지지 않는다.
|
|
3603
3834
|
*/
|
|
3604
3835
|
const scopeAccessSchema = z.enum(["r", "rw"]).describe("접근 수준. `r`=읽기, `rw`=읽기+쓰기(r을 함의)");
|
|
3605
|
-
/** 리소스별로 부여 가능한 접근 수준. settlement·analytics는 쓰기 API가 없어 `r`만 존재한다 */
|
|
3836
|
+
/** 리소스별로 부여 가능한 접근 수준. settlement·analytics·audit는 쓰기 API가 없어 `r`만 존재한다 */
|
|
3606
3837
|
const STORE_SCOPE_RESOURCES = {
|
|
3607
3838
|
product: ["r", "rw"],
|
|
3608
3839
|
category: ["r", "rw"],
|
|
@@ -3615,7 +3846,8 @@ const STORE_SCOPE_RESOURCES = {
|
|
|
3615
3846
|
webhook: ["r", "rw"],
|
|
3616
3847
|
store: ["r", "rw"],
|
|
3617
3848
|
member: ["r", "rw"],
|
|
3618
|
-
customer: ["r", "rw"]
|
|
3849
|
+
customer: ["r", "rw"],
|
|
3850
|
+
audit: ["r"]
|
|
3619
3851
|
};
|
|
3620
3852
|
const storeScopeResourceSchema = z.enum(Object.keys(STORE_SCOPE_RESOURCES)).describe("스코프 리소스");
|
|
3621
3853
|
/** 카탈로그상 존재하는 스코프 문자열인가 (예: `settlement:rw`는 false) */
|
|
@@ -3642,7 +3874,8 @@ const STORE_ROLE_SCOPES = {
|
|
|
3642
3874
|
"webhook:rw",
|
|
3643
3875
|
"store:rw",
|
|
3644
3876
|
"member:rw",
|
|
3645
|
-
"customer:rw"
|
|
3877
|
+
"customer:rw",
|
|
3878
|
+
"audit:r"
|
|
3646
3879
|
],
|
|
3647
3880
|
ADMIN: [
|
|
3648
3881
|
"product:rw",
|
|
@@ -3655,7 +3888,8 @@ const STORE_ROLE_SCOPES = {
|
|
|
3655
3888
|
"webhook:rw",
|
|
3656
3889
|
"store:rw",
|
|
3657
3890
|
"member:rw",
|
|
3658
|
-
"customer:rw"
|
|
3891
|
+
"customer:rw",
|
|
3892
|
+
"audit:r"
|
|
3659
3893
|
],
|
|
3660
3894
|
STAFF: [
|
|
3661
3895
|
"product:rw",
|
|
@@ -3896,9 +4130,9 @@ function createStoreSession(options) {
|
|
|
3896
4130
|
async function handleCallback(code, state) {
|
|
3897
4131
|
const raw = storage.get(STORE_SESSION_KEYS.pkce);
|
|
3898
4132
|
storage.remove(STORE_SESSION_KEYS.pkce);
|
|
3899
|
-
if (!raw) throw new Error("로그인 세션이 만료되었습니다. 다시
|
|
4133
|
+
if (!raw) throw new Error("로그인 세션이 만료되었습니다. 다시 시도해주십시오");
|
|
3900
4134
|
const saved = JSON.parse(raw);
|
|
3901
|
-
if (saved.state !== state) throw new Error("state가 일치하지 않습니다. 다시
|
|
4135
|
+
if (saved.state !== state) throw new Error("state가 일치하지 않습니다. 다시 시도해주십시오");
|
|
3902
4136
|
const tokens = await exchangeAuthorizationCode({
|
|
3903
4137
|
baseUrl,
|
|
3904
4138
|
clientId,
|
|
@@ -3938,4 +4172,4 @@ function createStoreSession(options) {
|
|
|
3938
4172
|
};
|
|
3939
4173
|
}
|
|
3940
4174
|
//#endregion
|
|
3941
|
-
export { ApiError, CASH_RECEIPT_STATUS_VALUES, MAX_ANALYTICS_ALLOWED_ORIGINS, MAX_AUTO_TRANSITION_DAYS, MAX_OPTION_GROUPS, MAX_PRODUCT_IMAGES, MAX_VALUES_PER_GROUP, MAX_VARIANTS, MCP_TELEMETRY_MAX_RESULTS, PAYMENT_ROUTING_EXCLUSION_CODES, PORTONE_CHANNEL_NAME_MAX_LENGTH, PORTONE_MAX_CHANNELS, PORTONE_MIN_CHANNELS, REFUND_POLICY_DEFAULTS, REMOTE_AREA_ZIP_RANGES, RESERVED_STORE_CODES, SHIPPING_POLICY_DEFAULTS, STORE_CODE_PATTERN, STORE_ROLE_SCOPES, STORE_SCOPE_RESOURCES, STORE_SESSION_KEYS, TOSS_WIDGET_CLIENT_KEY_PATTERN, TOSS_WIDGET_KEY_MESSAGE, TOSS_WIDGET_SECRET_KEY_PATTERN, UPLOAD_PURPOSE_LIMITS, VARIANT_STOCK_EXCLUSIVE_MESSAGE, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_FAILURE_REASONS, WEBHOOK_HEADERS, WEBHOOK_PAYLOAD_EXCERPT_MAX_BYTES, WEBHOOK_RESOURCE_TYPES, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, addressChangeRequestListSchema, addressTypeSchema, analyticsChannelSchema, analyticsCheckoutOutcomeSchema, analyticsCheckoutSchema, analyticsDeviceRowSchema, analyticsDevicesSchema, analyticsFunnelStepSchema, analyticsOriginSchema, analyticsOverviewSchema, analyticsPagesQuerySchema, analyticsPagesSchema, analyticsProductRowSchema, analyticsProductSortSchema, analyticsProductsQuerySchema, analyticsProductsSchema, analyticsRangeQuerySchema, analyticsRealtimeSchema, analyticsSearchQuerySchema, analyticsSearchSchema, 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, cashReceiptSchema, categoryAttributeSchema, categoryCreateRequestSchema, categoryDeleteQuerySchema, categoryDeleteStrategySchema, categoryListQuerySchema, categoryPatchRequestSchema, categoryProductSortSchema, categorySchema, categorySearchResultSchema, categoryTreeNodeSchema, categoryWithPathSchema, changeTypeSchema, claimDetailSchema, claimHoldResultSchema, claimListQuerySchema, claimReasonSchema, claimReturnAddressSchema, claimSchema, claimStatusSchema, claimTypeSchema, claimWebhookEventDataSchema, claimWebhookEventSchema, clientCredentialsTokenResponseSchema, consentActionSchema, consentKindSchema, consentSourceSchema, createPkcePair, createStoreClient, createStoreSession, customerAddressSchema, customerConsentListQuerySchema, customerConsentSchema, customerConsentStateSchema, customerConsentsSchema, customerDetailSchema, customerInquiryCategorySchema, customerInquiryListQuerySchema, customerListQuerySchema, customerOrderSchema, customerSortSchema, customerStatsSchema, customerSummarySchema, customerWithdrawalBlockedDetailsSchema, deliveryBundleKeyOf, deliveryFeeWaiverSchema, deliveryInfoSchema, deliveryMethodSchema, deliveryStatusSchema, deliveryTypeSchema, discountRequestSchema, discountSchema, dispatchRequestSchema, easyPayProviderResponseSchema, easyPayProviderSchema, eventTypeSchema, exchangeAuthorizationCode, exchangeStoreTokenViaOAuth, holdOrderItemRequestSchema, inquiryListQuerySchema, inquirySchema, inquiryWebhookEventDataSchema, inquiryWebhookEventSchema, invitationListQuerySchema, invitationSchema, isReservedStoreCode, isStoreScope, isWebOrigin, liveGateConditionCodeSchema, liveGateConditionSchema, liveGateErrorDetailsSchema, liveGateSchema, loginProviderSchema, loginProviderStatusSchema, loginProviderTestSchema, loginSettingsSchema, maskedSecretSchema, mcpRuleResultSchema, mcpToolEventSchema, 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, paymentSettingsSchema, pgEnvironmentSchema, pgPaymentMethodSchema, pgProviderSchema, pgVerificationResultSchema, policyDocumentUrlSchema, policyVersionSchema, portOneChannelSchema, portOneCredentialsInputSchema, productCreateRequestSchema, productImageRejection, productImageUrlSchema, productListQuerySchema, productOutOfStockWebhookEventDataSchema, productOutOfStockWebhookEventSchema, productPatchRequestSchema, productRefundPolicyOverride, productSchema, productSortSchema, productStatusSchema, productSummarySchema, productVariantRequestSchema, productVariantSchema, proratedRefundBase, purchaseDecisionExtendResultSchema, purchaseDecisionRequestResultSchema, quoteDelivery, refreshUserToken, refundPolicyPatchSchema, refundPolicySchema, rejectClaimRequestSchema, remoteAreaLabelOf, remoteAreaOf, remoteAreaSchema, remoteSurchargeOf, resolveRefundPolicy, resolveShippingPolicy, reviewListQuerySchema, reviewReportResultSchema, reviewSchema, reviewSortSchema, reviewWebhookEventDataSchema, reviewWebhookEventSchema, salesReportGroupBySchema, salesReportQuerySchema, salesReportRowSchema, salesReportSchema, salesTotalsSchema, saveLoginProviderRequestSchema, scopeAccessSchema, sellerAddressListQuerySchema, sellerAddressRequestSchema, sellerAddressSchema, sellerCustomerInquirySchema, settlementDetailQuerySchema, settlementDetailSchema, settlementListQuerySchema, settlementStatusSchema, settlementSummarySchema, settlementWithDetailsSchema, shippingAddressSchema, shippingPolicyPatchSchema, shippingPolicySchema, storeCodeSchema, storeMemberRoleUpdatedSchema, storeMemberSchema, storeRoleSchema, storeSchema, storeScopeResourceSchema, storeTokenResponseSchema, tossPaymentsCredentialsInputSchema, trackingStageResponseSchema, trackingUpdateResultSchema, updateLoginProviderRequestSchema, updateLoginSettingsRequestSchema, updatePaymentProviderRequestSchema, updatePaymentSettingsRequestSchema, updateStoreRequestSchema, uploadExtensionOf, uploadPurposeSchema, uploadRejection, uploadRequestSchema, uploadTicketSchema, upsertPortOneProviderRequestSchema, upsertTossPaymentsProviderRequestSchema, userTokenResponseSchema, variantStockRequestSchema, variantStockSchema, verifyPaymentProviderRequestSchema, webhookCreatedSchema, webhookDeliveryDetailSchema, webhookDeliveryErrorCodeSchema, webhookDeliveryListQuerySchema, webhookDeliveryResourceSchema, webhookDeliverySchema, webhookDeliveryStatusSchema, webhookEventSchema, webhookFailureReasonSchema, webhookRedeliverFailedResultSchema, webhookRequestHeaderSchema, webhookRequestSchema, webhookResourceTypeSchema, webhookSchema, webhookSecretRotatedSchema, webhookStatusSchema, webhookTestRequestSchema, webhookTestResultSchema, withdrawCustomerRequestSchema };
|
|
4175
|
+
export { APPROVAL_ALREADY_DECIDED_CODE, APPROVAL_REQUIRED_CODE, ApiError, ApprovalPendingError, CASH_RECEIPT_STATUS_VALUES, MAX_ANALYTICS_ALLOWED_ORIGINS, MAX_AUTO_TRANSITION_DAYS, MAX_OPTION_GROUPS, MAX_PRODUCT_IMAGES, MAX_VALUES_PER_GROUP, MAX_VARIANTS, MCP_TELEMETRY_MAX_RESULTS, PAYMENT_ROUTING_EXCLUSION_CODES, PORTONE_CHANNEL_NAME_MAX_LENGTH, PORTONE_MAX_CHANNELS, PORTONE_MIN_CHANNELS, REFUND_POLICY_DEFAULTS, REMOTE_AREA_ZIP_RANGES, RESERVED_STORE_CODES, SHIPPING_POLICY_DEFAULTS, STORE_CODE_PATTERN, STORE_ROLE_SCOPES, STORE_SCOPE_RESOURCES, STORE_SESSION_KEYS, TOSS_WIDGET_CLIENT_KEY_PATTERN, TOSS_WIDGET_KEY_MESSAGE, TOSS_WIDGET_SECRET_KEY_PATTERN, UPLOAD_PURPOSE_LIMITS, VARIANT_STOCK_EXCLUSIVE_MESSAGE, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_FAILURE_REASONS, WEBHOOK_HEADERS, WEBHOOK_PAYLOAD_EXCERPT_MAX_BYTES, WEBHOOK_RESOURCE_TYPES, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, addressChangeRequestListSchema, addressTypeSchema, agentPolicyDecisionSchema, analyticsChannelSchema, analyticsCheckoutOutcomeSchema, analyticsCheckoutSchema, analyticsDeviceRowSchema, analyticsDevicesSchema, analyticsFunnelStepSchema, analyticsOriginSchema, analyticsOverviewSchema, analyticsPagesQuerySchema, analyticsPagesSchema, analyticsProductRowSchema, analyticsProductSortSchema, analyticsProductsQuerySchema, analyticsProductsSchema, analyticsRangeQuerySchema, analyticsRealtimeSchema, analyticsSearchQuerySchema, analyticsSearchSchema, analyticsSourceGroupBySchema, analyticsSourceRowSchema, analyticsSourcesQuerySchema, analyticsSourcesSchema, analyticsTotalsSchema, apiClientListQuerySchema, apiClientSchema, apiClientSecretRotatedSchema, apiClientStatusSchema, apiClientTypeSchema, apiLogErrorSchema, apiLogListQuerySchema, apiLogPageSchema, apiLogPrincipalSchema, apiLogPrincipalTypeSchema, apiLogSchema, apiLogStatusFilterSchema, apiLogSummarySchema, apiTokenCreateRequestSchema, apiTokenCreatedSchema, apiTokenSchema, approvalDecisionRequestSchema, approvalListQuerySchema, approvalPageSchema, approvalRequestSchema, approvalStatusSchema, approveClaimResultSchema, auditActorSchema, auditActorTypeSchema, auditLogListQuerySchema, auditLogOutcomeSchema, auditLogPageSchema, auditLogSchema, auditLogSourceSchema, buildAuthorizeUrl, buildQuery, bulkResultSchema, cancelBySellerRequestSchema, cancelBySellerResultSchema, carrierSchema, cashReceiptSchema, categoryAttributeSchema, categoryCreateRequestSchema, categoryDeleteQuerySchema, categoryDeleteStrategySchema, categoryListQuerySchema, categoryPatchRequestSchema, categoryProductSortSchema, categorySchema, categorySearchResultSchema, categoryTreeNodeSchema, categoryWithPathSchema, changeTypeSchema, claimDetailSchema, claimHoldResultSchema, claimListQuerySchema, claimReasonSchema, claimReturnAddressSchema, claimSchema, claimStatusSchema, claimTypeSchema, claimWebhookEventDataSchema, claimWebhookEventSchema, clientCredentialsTokenResponseSchema, consentActionSchema, consentKindSchema, consentSourceSchema, createPkcePair, createStoreClient, createStoreSession, customerAddressSchema, customerConsentListQuerySchema, customerConsentSchema, customerConsentStateSchema, customerConsentsSchema, customerDetailSchema, customerInquiryCategorySchema, customerInquiryListQuerySchema, customerListQuerySchema, customerOrderSchema, customerSortSchema, customerStatsSchema, customerSummarySchema, customerWithdrawalBlockedDetailsSchema, deliveryBundleKeyOf, deliveryFeeWaiverSchema, deliveryInfoSchema, deliveryMethodSchema, deliveryStatusSchema, deliveryTypeSchema, discountRequestSchema, discountSchema, dispatchRequestSchema, easyPayProviderResponseSchema, easyPayProviderSchema, eventTypeSchema, exchangeAuthorizationCode, exchangeStoreTokenViaOAuth, holdOrderItemRequestSchema, inquiryListQuerySchema, inquirySchema, inquiryWebhookEventDataSchema, inquiryWebhookEventSchema, invitationListQuerySchema, invitationSchema, isReservedStoreCode, isStoreScope, isWebOrigin, liveGateConditionCodeSchema, liveGateConditionSchema, liveGateErrorDetailsSchema, liveGateSchema, loginProviderSchema, loginProviderStatusSchema, loginProviderTestSchema, loginSettingsSchema, maskedSecretSchema, mcpRuleResultSchema, mcpToolEventSchema, 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, paymentSettingsSchema, pgEnvironmentSchema, pgPaymentMethodSchema, pgProviderSchema, pgVerificationResultSchema, policyDocumentUrlSchema, policyVersionSchema, portOneChannelSchema, portOneCredentialsInputSchema, productCreateRequestSchema, productImageRejection, productImageUrlSchema, productListQuerySchema, productOutOfStockWebhookEventDataSchema, productOutOfStockWebhookEventSchema, productPatchRequestSchema, productRefundPolicyOverride, productSchema, productSortSchema, productStatusSchema, productSummarySchema, productVariantRequestSchema, productVariantSchema, proratedRefundBase, purchaseDecisionExtendResultSchema, purchaseDecisionRequestResultSchema, quoteDelivery, refreshUserToken, refundPolicyPatchSchema, refundPolicySchema, rejectClaimRequestSchema, remoteAreaLabelOf, remoteAreaOf, remoteAreaSchema, remoteSurchargeOf, resolveRefundPolicy, resolveShippingPolicy, reviewListQuerySchema, reviewReportResultSchema, reviewSchema, reviewSortSchema, reviewWebhookEventDataSchema, reviewWebhookEventSchema, salesReportGroupBySchema, salesReportQuerySchema, salesReportRowSchema, salesReportSchema, salesTotalsSchema, saveLoginProviderRequestSchema, scopeAccessSchema, sellerAddressListQuerySchema, sellerAddressRequestSchema, sellerAddressSchema, sellerCustomerInquirySchema, settlementDetailQuerySchema, settlementDetailSchema, settlementListQuerySchema, settlementStatusSchema, settlementSummarySchema, settlementWithDetailsSchema, shippingAddressSchema, shippingPolicyPatchSchema, shippingPolicySchema, storeCodeSchema, storeMemberRoleUpdatedSchema, storeMemberSchema, storeRoleSchema, storeSchema, storeScopeResourceSchema, storeTokenResponseSchema, tossPaymentsCredentialsInputSchema, trackingStageResponseSchema, trackingUpdateResultSchema, updateLoginProviderRequestSchema, updateLoginSettingsRequestSchema, updatePaymentProviderRequestSchema, updatePaymentSettingsRequestSchema, updateStoreRequestSchema, uploadExtensionOf, uploadPurposeSchema, uploadRejection, uploadRequestSchema, uploadTicketSchema, upsertPortOneProviderRequestSchema, upsertTossPaymentsProviderRequestSchema, userTokenResponseSchema, variantStockRequestSchema, variantStockSchema, verifyPaymentProviderRequestSchema, webhookCreatedSchema, webhookDeliveryDetailSchema, webhookDeliveryErrorCodeSchema, webhookDeliveryListQuerySchema, webhookDeliveryResourceSchema, webhookDeliverySchema, webhookDeliveryStatusSchema, webhookEventSchema, webhookFailureReasonSchema, webhookRedeliverFailedResultSchema, webhookRequestHeaderSchema, webhookRequestSchema, webhookResourceTypeSchema, webhookSchema, webhookSecretRotatedSchema, webhookStatusSchema, webhookTestRequestSchema, webhookTestResultSchema, withdrawCustomerRequestSchema, writeDryRunResultSchema, writePreviewSchema };
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -38,6 +38,15 @@ import {
|
|
|
38
38
|
oauthClientRegistrationResponseSchema,
|
|
39
39
|
} from "./schemas/api-client";
|
|
40
40
|
import { type ApiLogListOptions, apiLogPageSchema, apiLogSummarySchema } from "./schemas/api-log";
|
|
41
|
+
import {
|
|
42
|
+
type ApprovalDecisionRequest,
|
|
43
|
+
type ApprovalListOptions,
|
|
44
|
+
type AuditLogListOptions,
|
|
45
|
+
approvalDecisionRequestSchema,
|
|
46
|
+
approvalPageSchema,
|
|
47
|
+
approvalRequestSchema,
|
|
48
|
+
auditLogPageSchema,
|
|
49
|
+
} from "./schemas/audit";
|
|
41
50
|
import {
|
|
42
51
|
type ApiTokenCreateRequest,
|
|
43
52
|
apiTokenCreatedSchema,
|
|
@@ -1044,6 +1053,56 @@ export function createStoreClient(options: StoreClientOptions) {
|
|
|
1044
1053
|
summary: () => storeHttp.request("GET", "/api-logs/summary", apiLogSummarySchema),
|
|
1045
1054
|
},
|
|
1046
1055
|
|
|
1056
|
+
/** 감사 로그 (관리 API 쓰기의 행위자·대상·결과, 콘솔 OWNER/ADMIN, `audit:r`) */
|
|
1057
|
+
auditLogs: {
|
|
1058
|
+
/** 최신순 커서 페이지네이션. 다음 페이지는 응답의 `nextCursor`를 `cursor`로 넘긴다 */
|
|
1059
|
+
list: (params?: AuditLogListOptions) =>
|
|
1060
|
+
storeHttp.request("GET", "/audit-logs", auditLogPageSchema, {
|
|
1061
|
+
query: params && {
|
|
1062
|
+
...params,
|
|
1063
|
+
actorType: Array.isArray(params.actorType)
|
|
1064
|
+
? params.actorType.join(",") || undefined
|
|
1065
|
+
: params.actorType,
|
|
1066
|
+
},
|
|
1067
|
+
}),
|
|
1068
|
+
},
|
|
1069
|
+
|
|
1070
|
+
/**
|
|
1071
|
+
* 승인 요청 — 에이전트의 고위험 쓰기가 202로 보류된 건. 목록·승인·거절은 콘솔 OWNER/ADMIN,
|
|
1072
|
+
* 단건 조회는 요청한 토큰도 할 수 있다(상태 확인용).
|
|
1073
|
+
*/
|
|
1074
|
+
approvalRequests: {
|
|
1075
|
+
list: (params?: ApprovalListOptions) =>
|
|
1076
|
+
storeHttp.request("GET", "/approval-requests", approvalPageSchema, {
|
|
1077
|
+
query: params && {
|
|
1078
|
+
...params,
|
|
1079
|
+
status: Array.isArray(params.status)
|
|
1080
|
+
? params.status.join(",") || undefined
|
|
1081
|
+
: params.status,
|
|
1082
|
+
},
|
|
1083
|
+
}),
|
|
1084
|
+
get: (approvalId: string) =>
|
|
1085
|
+
storeHttp.request("GET", path`/approval-requests/${approvalId}`, approvalRequestSchema),
|
|
1086
|
+
/**
|
|
1087
|
+
* 승인하면 서버가 원 요청을 그대로 한 번 실행한다. 응답은 실행을 기다리지 않은 `EXECUTING` 상태이고,
|
|
1088
|
+
* 결과(`SUCCEEDED`·`FAILED`·`UNKNOWN`)는 `get`으로 확인한다. 두 번 눌러도 한 번만 실행한다
|
|
1089
|
+
*/
|
|
1090
|
+
approve: (approvalId: string, body: ApprovalDecisionRequest = {}) =>
|
|
1091
|
+
storeHttp.request(
|
|
1092
|
+
"POST",
|
|
1093
|
+
path`/approval-requests/${approvalId}/approve`,
|
|
1094
|
+
approvalRequestSchema,
|
|
1095
|
+
{ body: approvalDecisionRequestSchema.parse(body) },
|
|
1096
|
+
),
|
|
1097
|
+
reject: (approvalId: string, body: ApprovalDecisionRequest = {}) =>
|
|
1098
|
+
storeHttp.request(
|
|
1099
|
+
"POST",
|
|
1100
|
+
path`/approval-requests/${approvalId}/reject`,
|
|
1101
|
+
approvalRequestSchema,
|
|
1102
|
+
{ body: approvalDecisionRequestSchema.parse(body) },
|
|
1103
|
+
),
|
|
1104
|
+
},
|
|
1105
|
+
|
|
1047
1106
|
reviews: {
|
|
1048
1107
|
list: (params?: ReviewListParams) =>
|
|
1049
1108
|
storeHttp.request("GET", "/reviews", pageSchema(reviewSchema), { query: params as Query }),
|
package/src/http.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { type ApprovalRequest, type ApprovalStatus, approvalRequestSchema } from "./schemas/audit";
|
|
2
3
|
|
|
3
4
|
/** 표준 엔벨로프의 $.error 상세 */
|
|
4
5
|
export const errorDetailSchema = z.object({
|
|
@@ -63,6 +64,51 @@ export class ApiError extends Error {
|
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
/** 에이전트 쓰기가 승인 대기로 보류됐다(202) */
|
|
68
|
+
export const APPROVAL_REQUIRED_CODE = "APPROVAL_REQUIRED";
|
|
69
|
+
/** 같은 멱등키의 승인 요청이 이미 끝났다(200) — 결과는 승인 요청의 `status`·`result`다 */
|
|
70
|
+
export const APPROVAL_ALREADY_DECIDED_CODE = "APPROVAL_ALREADY_DECIDED";
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 쓰기 요청이 실행되지 않고 승인 흐름에 걸렸다 (이슈 #36). 응답 `data`가 요청한 리소스가 아니라 승인 요청이라
|
|
74
|
+
* 성공 값으로 돌려주지 않는다 — 돌려주면 호출부가 승인 요청을 상품·주문으로 읽는다.
|
|
75
|
+
*
|
|
76
|
+
* - `code = APPROVAL_REQUIRED`(202): 에이전트의 고위험 쓰기가 보류됐다. 아직 아무것도 바뀌지 않았다.
|
|
77
|
+
* 셀러가 콘솔(`approval.approvalUrl`)에서 승인하면 서버가 원 요청을 실행한다. 같은 요청을 다시 보내지 말고
|
|
78
|
+
* `approvalRequests.get(approvalId)`로 결과를 확인한다.
|
|
79
|
+
* - `code = APPROVAL_ALREADY_DECIDED`(200): 같은 멱등키로 보낸 요청의 승인이 이미 끝났다. `status`가
|
|
80
|
+
* `SUCCEEDED`·`FAILED`·`UNKNOWN`이면 실행 결과(`approval.result`)를 보고, `REJECTED`·`EXPIRED`면 새 멱등키로 다시 요청한다.
|
|
81
|
+
*/
|
|
82
|
+
export class ApprovalPendingError extends Error {
|
|
83
|
+
readonly approvalId: string;
|
|
84
|
+
/** 승인 요청 상태 */
|
|
85
|
+
readonly status: ApprovalStatus;
|
|
86
|
+
|
|
87
|
+
constructor(
|
|
88
|
+
public readonly code: typeof APPROVAL_REQUIRED_CODE | typeof APPROVAL_ALREADY_DECIDED_CODE,
|
|
89
|
+
/** HTTP 상태 코드 (202·200) */
|
|
90
|
+
public readonly httpStatus: number,
|
|
91
|
+
public readonly approval: ApprovalRequest,
|
|
92
|
+
message: string,
|
|
93
|
+
) {
|
|
94
|
+
super(message);
|
|
95
|
+
this.name = "ApprovalPendingError";
|
|
96
|
+
this.approvalId = approval.approvalId;
|
|
97
|
+
this.status = approval.status;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** 성공 엔벨로프가 승인 흐름 응답이면 ApprovalPendingError. 그 밖(다른 202 등)은 null */
|
|
102
|
+
function approvalErrorOf(status: number, json: unknown): ApprovalPendingError | null {
|
|
103
|
+
const envelope = envelopeSchema.safeParse(json);
|
|
104
|
+
if (!envelope.success) return null;
|
|
105
|
+
const { code, message } = envelope.data.meta;
|
|
106
|
+
if (code !== APPROVAL_REQUIRED_CODE && code !== APPROVAL_ALREADY_DECIDED_CODE) return null;
|
|
107
|
+
const approval = approvalRequestSchema.safeParse(envelope.data.data);
|
|
108
|
+
if (!approval.success) return null;
|
|
109
|
+
return new ApprovalPendingError(code, status, approval.data, message);
|
|
110
|
+
}
|
|
111
|
+
|
|
66
112
|
/**
|
|
67
113
|
* 에러 응답 본문 → ApiError 구성 (에러 형태의 단일 원천).
|
|
68
114
|
* 표준 엔벨로프($.error) 우선, 구형 평면 에러({ code, message }), OAuth 평면 에러
|
|
@@ -200,10 +246,17 @@ export function createHttp(options: HttpOptions): Http {
|
|
|
200
246
|
): Promise<T> {
|
|
201
247
|
const response = await send(method, path, request);
|
|
202
248
|
const json: unknown = await response.json();
|
|
249
|
+
const pending = approvalErrorOf(response.status, json);
|
|
250
|
+
if (pending) throw pending;
|
|
203
251
|
return schema.parse(unwrapData(json));
|
|
204
252
|
},
|
|
205
253
|
async requestVoid(method: string, path: string, request?: RequestOptions): Promise<void> {
|
|
206
|
-
await send(method, path, request);
|
|
254
|
+
const response = await send(method, path, request);
|
|
255
|
+
if (response.status === 204) return;
|
|
256
|
+
// 본문 없는 쓰기도 에이전트면 보류(202)될 수 있다 — 성공으로 삼키지 않는다
|
|
257
|
+
const json: unknown = await response.json().catch(() => null);
|
|
258
|
+
const pending = approvalErrorOf(response.status, json);
|
|
259
|
+
if (pending) throw pending;
|
|
207
260
|
},
|
|
208
261
|
};
|
|
209
262
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
export type { StoreClient, StoreClientOptions } from "./client";
|
|
2
2
|
export { createStoreClient } from "./client";
|
|
3
3
|
export type { Query, TokenSource } from "./http";
|
|
4
|
-
export {
|
|
4
|
+
export {
|
|
5
|
+
APPROVAL_ALREADY_DECIDED_CODE,
|
|
6
|
+
APPROVAL_REQUIRED_CODE,
|
|
7
|
+
ApiError,
|
|
8
|
+
ApprovalPendingError,
|
|
9
|
+
buildQuery,
|
|
10
|
+
} from "./http";
|
|
5
11
|
export {
|
|
6
12
|
buildAuthorizeUrl,
|
|
7
13
|
createPkcePair,
|
|
@@ -14,6 +20,7 @@ export {
|
|
|
14
20
|
export * from "./schemas/analytics";
|
|
15
21
|
export * from "./schemas/api-client";
|
|
16
22
|
export * from "./schemas/api-log";
|
|
23
|
+
export * from "./schemas/audit";
|
|
17
24
|
export * from "./schemas/auth";
|
|
18
25
|
export * from "./schemas/category";
|
|
19
26
|
export * from "./schemas/claim";
|