@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
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 감사 로그와 승인 요청 — `/v1/audit-logs`, `/v1/approval-requests` (이슈 #36).
|
|
5
|
+
*
|
|
6
|
+
* 관리 API 쓰기(`/v1/**`의 GET 외 요청)는 모두 요청 단위 기록(`source: REQUEST`) 한 줄을 남긴다. 환불·직권취소 같은
|
|
7
|
+
* 도메인 동작은 그 안에서 변경 전후를 담은 기록(`source: DOMAIN`)을 따로 남기고 `requestId`로 요청 기록과 이어진다.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const auditActorTypeSchema = z
|
|
11
|
+
.enum(["USER", "AGENT", "APP", "TOKEN", "SYSTEM"])
|
|
12
|
+
.describe(
|
|
13
|
+
"행위자 유형. `USER`=콘솔 사용자, `AGENT`=AI 에이전트(MCP·에이전트용 API 토큰), `APP`=앱(OAuth 클라이언트·M2M), " +
|
|
14
|
+
"`TOKEN`=API 토큰, `SYSTEM`=자동화(주기 잡)",
|
|
15
|
+
);
|
|
16
|
+
export type AuditActorType = z.infer<typeof auditActorTypeSchema>;
|
|
17
|
+
|
|
18
|
+
export const auditActorSchema = z.object({
|
|
19
|
+
type: auditActorTypeSchema,
|
|
20
|
+
id: z
|
|
21
|
+
.string()
|
|
22
|
+
.describe(
|
|
23
|
+
"행위자 ID. `USER`=userId, `TOKEN`·`AGENT`(API 토큰)=`pat:{tokenId}`, `APP`=`client:{clientId}`, `SYSTEM`=`system`. " +
|
|
24
|
+
"사람이 앱에 위임한 호출은 `APP`이고 위임한 사람은 `metadata.onBehalfOf`에 있다",
|
|
25
|
+
),
|
|
26
|
+
name: z.string().nullable().describe("기록 시점의 표시 이름. 알 수 없으면 null"),
|
|
27
|
+
});
|
|
28
|
+
export type AuditActor = z.infer<typeof auditActorSchema>;
|
|
29
|
+
|
|
30
|
+
export const auditLogSourceSchema = z
|
|
31
|
+
.enum(["REQUEST", "DOMAIN"])
|
|
32
|
+
.describe(
|
|
33
|
+
"기록 단위. `REQUEST`=관리 API 쓰기 요청 한 건, `DOMAIN`=환불·취소 같은 도메인 동작의 상세",
|
|
34
|
+
);
|
|
35
|
+
export type AuditLogSource = z.infer<typeof auditLogSourceSchema>;
|
|
36
|
+
|
|
37
|
+
export const auditLogOutcomeSchema = z
|
|
38
|
+
.enum(["SUCCEEDED", "FAILED", "PENDING_APPROVAL", "DENIED", "DRY_RUN"])
|
|
39
|
+
.describe(
|
|
40
|
+
"요청 결과. `PENDING_APPROVAL`=승인 대기로 보류(202), `DENIED`=에이전트 정책으로 거부(403 `AGENT_OPERATION_DENIED`), " +
|
|
41
|
+
"`DRY_RUN`=드라이런(`x-sayren-dry-run: 1`)이라 실행하지 않음",
|
|
42
|
+
);
|
|
43
|
+
export type AuditLogOutcome = z.infer<typeof auditLogOutcomeSchema>;
|
|
44
|
+
|
|
45
|
+
export const auditLogSchema = z.object({
|
|
46
|
+
auditId: z.string().describe("감사 로그 ID"),
|
|
47
|
+
occurredAt: z.string().describe("기록 일시 (ISO 8601)"),
|
|
48
|
+
source: auditLogSourceSchema,
|
|
49
|
+
action: z
|
|
50
|
+
.string()
|
|
51
|
+
.describe(
|
|
52
|
+
"동작. `REQUEST`는 오퍼레이션 ID(OpenAPI `operationId`, 예: `ProductsController_patch`), `DOMAIN`은 도메인 코드(예: `ORDER_ITEM_CANCELED_BY_SELLER`)",
|
|
53
|
+
),
|
|
54
|
+
actor: auditActorSchema,
|
|
55
|
+
actorRole: z
|
|
56
|
+
.string()
|
|
57
|
+
.nullable()
|
|
58
|
+
.describe("요청 시점의 스토어 역할. 역할이 없는 토큰·자동화는 null"),
|
|
59
|
+
target: z.object({
|
|
60
|
+
type: z.string().describe("대상 리소스 유형 (예: `products`, `order-items`, `claim`)"),
|
|
61
|
+
id: z
|
|
62
|
+
.string()
|
|
63
|
+
.nullable()
|
|
64
|
+
.describe("대상 리소스 ID. 목록 대상 요청처럼 하나로 정할 수 없으면 null"),
|
|
65
|
+
}),
|
|
66
|
+
requestId: z
|
|
67
|
+
.string()
|
|
68
|
+
.nullable()
|
|
69
|
+
.describe(
|
|
70
|
+
"요청 ID. 같은 요청에서 나온 `REQUEST`·`DOMAIN` 기록이 같은 값을 가진다. 자동화는 null",
|
|
71
|
+
),
|
|
72
|
+
method: z.string().nullable().describe("HTTP 메서드 (`REQUEST`만)"),
|
|
73
|
+
path: z
|
|
74
|
+
.string()
|
|
75
|
+
.nullable()
|
|
76
|
+
.describe("요청 경로와 쿼리 (`REQUEST`만). 민감한 쿼리 값은 `***`로 가린다"),
|
|
77
|
+
statusCode: z.number().int().nullable().describe("응답 HTTP 상태 코드 (`REQUEST`만)"),
|
|
78
|
+
outcome: auditLogOutcomeSchema.nullable().describe("요청 결과 (`REQUEST`만)"),
|
|
79
|
+
errorCode: z.string().nullable().describe("에러 응답의 `error.code` (실패한 요청만)"),
|
|
80
|
+
traceId: z.string().nullable().describe("추적 ID. 에러 응답의 `error.traceId`와 같다"),
|
|
81
|
+
idempotencyKey: z.string().nullable().describe("요청의 멱등키"),
|
|
82
|
+
approvalId: z
|
|
83
|
+
.string()
|
|
84
|
+
.nullable()
|
|
85
|
+
.describe("승인 요청 ID. 승인 대기로 보류된 요청과 승인 뒤 실행된 요청에 실린다"),
|
|
86
|
+
metadata: z
|
|
87
|
+
.record(z.string(), z.unknown())
|
|
88
|
+
.describe(
|
|
89
|
+
"부가 정보. 도메인 기록은 변경 전후 값·사유를, 승인 실행 기록은 승인자(`approvedBy`)를 싣는다",
|
|
90
|
+
),
|
|
91
|
+
});
|
|
92
|
+
export type AuditLog = z.infer<typeof auditLogSchema>;
|
|
93
|
+
|
|
94
|
+
/** 쉼표 구분 목록 쿼리 → 배열 */
|
|
95
|
+
function commaList<T extends z.ZodType<string, string>>(item: T) {
|
|
96
|
+
return z
|
|
97
|
+
.union([z.string(), z.array(z.string())])
|
|
98
|
+
.transform((value) =>
|
|
99
|
+
(Array.isArray(value) ? value : [value])
|
|
100
|
+
.flatMap((part) => part.split(","))
|
|
101
|
+
.map((part) => part.trim())
|
|
102
|
+
.filter((part) => part.length > 0),
|
|
103
|
+
)
|
|
104
|
+
.pipe(z.array(item).min(1));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** 감사 로그 조회 쿼리 — `GET /audit-logs` */
|
|
108
|
+
export const auditLogListQuerySchema = z.object({
|
|
109
|
+
from: z.string().optional().describe("조회 시작 일시 (ISO 8601, 포함)"),
|
|
110
|
+
to: z.string().optional().describe("조회 종료 일시 (ISO 8601, 미포함)"),
|
|
111
|
+
actorType: commaList(auditActorTypeSchema)
|
|
112
|
+
.optional()
|
|
113
|
+
.describe("행위자 유형 필터. 여러 개는 쉼표로 구분한다 (예: `AGENT,APP`)"),
|
|
114
|
+
actorId: z.string().optional().describe("행위자 ID 필터 (정확히 일치)"),
|
|
115
|
+
action: z.string().optional().describe("동작 필터 (정확히 일치)"),
|
|
116
|
+
targetType: z.string().optional().describe("대상 유형 필터 (정확히 일치)"),
|
|
117
|
+
targetId: z.string().optional().describe("대상 ID 필터 (정확히 일치)"),
|
|
118
|
+
source: auditLogSourceSchema.optional(),
|
|
119
|
+
outcome: auditLogOutcomeSchema.optional(),
|
|
120
|
+
requestId: z
|
|
121
|
+
.string()
|
|
122
|
+
.optional()
|
|
123
|
+
.describe("요청 ID 필터 — 한 요청의 요청·도메인 기록을 함께 본다"),
|
|
124
|
+
approvalId: z.string().optional().describe("승인 요청 ID 필터"),
|
|
125
|
+
cursor: z.string().optional().describe("이전 응답의 `nextCursor`. 생략하면 가장 최신부터"),
|
|
126
|
+
limit: z.coerce.number().int().min(1).max(100).optional().describe("최대 건수 (1~100, 기본 50)"),
|
|
127
|
+
});
|
|
128
|
+
/** 파싱된 조회 쿼리 (서버 측). `actorType`은 항상 배열이다 */
|
|
129
|
+
export type AuditLogListParams = z.infer<typeof auditLogListQuerySchema>;
|
|
130
|
+
|
|
131
|
+
/** 클라이언트 `auditLogs.list` 인자. `actorType`은 단일 값·배열 모두 받고 쉼표로 직렬화한다 */
|
|
132
|
+
export type AuditLogListOptions = Omit<AuditLogListParams, "actorType"> & {
|
|
133
|
+
actorType?: AuditActorType | AuditActorType[];
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const auditLogPageSchema = z.object({
|
|
137
|
+
items: z.array(auditLogSchema),
|
|
138
|
+
nextCursor: z.string().nullable().describe("다음 페이지 커서. 마지막 페이지면 null"),
|
|
139
|
+
});
|
|
140
|
+
export type AuditLogPage = z.infer<typeof auditLogPageSchema>;
|
|
141
|
+
|
|
142
|
+
// ─── 에이전트 쓰기 정책·영향 미리보기 ──────────────────────────
|
|
143
|
+
|
|
144
|
+
export const agentPolicyDecisionSchema = z
|
|
145
|
+
.enum(["ALLOW", "APPROVAL_REQUIRED", "DENIED"])
|
|
146
|
+
.describe(
|
|
147
|
+
"에이전트 쓰기 판정. `ALLOW`=바로 실행, `APPROVAL_REQUIRED`=콘솔 승인 뒤 실행, `DENIED`=에이전트는 실행할 수 없음",
|
|
148
|
+
);
|
|
149
|
+
export type AgentPolicyDecision = z.infer<typeof agentPolicyDecisionSchema>;
|
|
150
|
+
|
|
151
|
+
const previewValueSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
|
|
152
|
+
|
|
153
|
+
export const writePreviewSchema = z.object({
|
|
154
|
+
operationId: z.string().describe("오퍼레이션 ID"),
|
|
155
|
+
method: z.string(),
|
|
156
|
+
path: z.string(),
|
|
157
|
+
target: z.object({ type: z.string(), id: z.string().nullable() }),
|
|
158
|
+
changes: z
|
|
159
|
+
.array(
|
|
160
|
+
z.object({
|
|
161
|
+
field: z.string().describe("바뀌는 필드"),
|
|
162
|
+
before: z.unknown().describe("현재 값. 새로 만드는 요청이거나 읽을 수 없으면 null"),
|
|
163
|
+
after: z.unknown().describe("요청이 넣을 값"),
|
|
164
|
+
}),
|
|
165
|
+
)
|
|
166
|
+
.describe("값이 달라지는 필드. 서버가 현재 값과 비교할 수 있는 오퍼레이션만 채운다"),
|
|
167
|
+
facts: z
|
|
168
|
+
.record(z.string(), previewValueSchema)
|
|
169
|
+
.describe("영향 요약 (예: 취소 수량·주문상품 금액·예상 환불액). 오퍼레이션마다 다르다"),
|
|
170
|
+
});
|
|
171
|
+
export type WritePreview = z.infer<typeof writePreviewSchema>;
|
|
172
|
+
|
|
173
|
+
/** `x-sayren-dry-run: 1`을 실은 쓰기 요청의 응답(200) — 실행하지 않고 판정과 영향만 돌려준다 */
|
|
174
|
+
export const writeDryRunResultSchema = z.object({
|
|
175
|
+
dryRun: z.literal(true),
|
|
176
|
+
decision: agentPolicyDecisionSchema,
|
|
177
|
+
reason: z.string().nullable().describe("승인 필요·거부 사유. `ALLOW`면 null"),
|
|
178
|
+
preview: writePreviewSchema,
|
|
179
|
+
});
|
|
180
|
+
export type WriteDryRunResult = z.infer<typeof writeDryRunResultSchema>;
|
|
181
|
+
|
|
182
|
+
// ─── 승인 요청 ───────────────────────────────────────────────
|
|
183
|
+
|
|
184
|
+
export const approvalStatusSchema = z
|
|
185
|
+
.enum(["PENDING", "EXECUTING", "SUCCEEDED", "FAILED", "UNKNOWN", "REJECTED", "EXPIRED"])
|
|
186
|
+
.describe(
|
|
187
|
+
"승인 요청 상태. `PENDING` 승인 대기 → 승인하면 `EXECUTING`(원 요청 실행 중) → `SUCCEEDED`·`FAILED`(원 요청의 응답 기준), " +
|
|
188
|
+
"거절하면 `REJECTED`, 기한이 지나면 `EXPIRED`. 실행 응답을 받지 못했으면(타임아웃·서버 종료) `UNKNOWN`이다 — " +
|
|
189
|
+
"서버가 감사 로그로 결과를 확정하면 `SUCCEEDED`·`FAILED`로 바뀌고, 확정하지 못하면 `UNKNOWN`에 남는다. " +
|
|
190
|
+
"`UNKNOWN`은 다시 실행하지 않는다(이미 실행됐을 수 있다)",
|
|
191
|
+
);
|
|
192
|
+
export type ApprovalStatus = z.infer<typeof approvalStatusSchema>;
|
|
193
|
+
|
|
194
|
+
export const approvalRequestSchema = z.object({
|
|
195
|
+
approvalId: z.string().describe("승인 요청 ID"),
|
|
196
|
+
status: approvalStatusSchema,
|
|
197
|
+
operationId: z.string().describe("보류된 오퍼레이션 ID"),
|
|
198
|
+
method: z.string().describe("HTTP 메서드"),
|
|
199
|
+
path: z.string().describe("요청 경로와 쿼리"),
|
|
200
|
+
body: z.unknown().describe("요청 본문. 승인하면 이 본문 그대로 실행한다. 없으면 null"),
|
|
201
|
+
idempotencyKey: z.string().nullable().describe("원 요청의 멱등키"),
|
|
202
|
+
reason: z.string().describe("승인이 필요한 이유"),
|
|
203
|
+
actor: auditActorSchema.describe("요청한 행위자"),
|
|
204
|
+
preview: writePreviewSchema.nullable().describe("요청 시점에 서버가 계산한 영향 미리보기"),
|
|
205
|
+
createdAt: z.string().describe("요청 일시 (ISO 8601)"),
|
|
206
|
+
expiresAt: z.string().describe("승인 기한 (ISO 8601). 지나면 `EXPIRED`가 된다"),
|
|
207
|
+
decidedAt: z.string().nullable().describe("승인·거절 일시"),
|
|
208
|
+
decidedBy: z
|
|
209
|
+
.object({ id: z.string(), name: z.string().nullable() })
|
|
210
|
+
.nullable()
|
|
211
|
+
.describe("승인·거절한 콘솔 사용자"),
|
|
212
|
+
decisionNote: z.string().nullable().describe("승인·거절 메모"),
|
|
213
|
+
executedAt: z.string().nullable().describe("원 요청 실행을 마친 일시"),
|
|
214
|
+
result: z
|
|
215
|
+
.object({
|
|
216
|
+
statusCode: z.number().int().describe("원 요청의 응답 상태 코드"),
|
|
217
|
+
data: z.unknown().describe("성공 응답의 `data` (큰 응답은 잘린다)"),
|
|
218
|
+
error: z
|
|
219
|
+
.object({ code: z.string(), message: z.string() })
|
|
220
|
+
.nullable()
|
|
221
|
+
.describe("실패 응답의 `error` 요약"),
|
|
222
|
+
})
|
|
223
|
+
.nullable()
|
|
224
|
+
.describe(
|
|
225
|
+
"실행 결과. `SUCCEEDED`·`FAILED`·`UNKNOWN`에서 값이 있다. 감사 로그로 확정한 결과는 `data`가 null이다",
|
|
226
|
+
),
|
|
227
|
+
approvalUrl: z
|
|
228
|
+
.string()
|
|
229
|
+
.nullable()
|
|
230
|
+
.describe("셀러 콘솔의 승인 화면 주소. 이 배포의 콘솔 주소를 알 수 없으면 null"),
|
|
231
|
+
});
|
|
232
|
+
export type ApprovalRequest = z.infer<typeof approvalRequestSchema>;
|
|
233
|
+
|
|
234
|
+
export const approvalListQuerySchema = z.object({
|
|
235
|
+
status: commaList(approvalStatusSchema)
|
|
236
|
+
.optional()
|
|
237
|
+
.describe("상태 필터. 여러 개는 쉼표로 구분한다 (예: `PENDING,EXECUTING`)"),
|
|
238
|
+
cursor: z.string().optional().describe("이전 응답의 `nextCursor`. 생략하면 가장 최신부터"),
|
|
239
|
+
limit: z.coerce.number().int().min(1).max(100).optional().describe("최대 건수 (1~100, 기본 50)"),
|
|
240
|
+
});
|
|
241
|
+
export type ApprovalListParams = z.infer<typeof approvalListQuerySchema>;
|
|
242
|
+
export type ApprovalListOptions = Omit<ApprovalListParams, "status"> & {
|
|
243
|
+
status?: ApprovalStatus | ApprovalStatus[];
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
export const approvalPageSchema = z.object({
|
|
247
|
+
items: z.array(approvalRequestSchema),
|
|
248
|
+
nextCursor: z.string().nullable().describe("다음 페이지 커서. 마지막 페이지면 null"),
|
|
249
|
+
});
|
|
250
|
+
export type ApprovalPage = z.infer<typeof approvalPageSchema>;
|
|
251
|
+
|
|
252
|
+
export const approvalDecisionRequestSchema = z.object({
|
|
253
|
+
note: z.string().trim().max(500).optional().describe("승인·거절 메모 (최대 500자)"),
|
|
254
|
+
});
|
|
255
|
+
export type ApprovalDecisionRequest = z.infer<typeof approvalDecisionRequestSchema>;
|
package/src/schemas/auth.ts
CHANGED
|
@@ -65,6 +65,12 @@ export const apiTokenSchema = z.object({
|
|
|
65
65
|
createdAt: z.string(),
|
|
66
66
|
expiresAt: z.string().nullable(),
|
|
67
67
|
lastUsedAt: z.string().nullable(),
|
|
68
|
+
agent: z
|
|
69
|
+
.boolean()
|
|
70
|
+
.default(false)
|
|
71
|
+
.describe(
|
|
72
|
+
"AI 에이전트(MCP)용 토큰인가. true면 이 토큰의 쓰기는 행위자 `AGENT`로 기록되고 에이전트 쓰기 정책(승인 필요·거부)을 따른다",
|
|
73
|
+
),
|
|
68
74
|
});
|
|
69
75
|
export type ApiToken = z.infer<typeof apiTokenSchema>;
|
|
70
76
|
|
|
@@ -77,5 +83,7 @@ export const apiTokenCreateRequestSchema = z.object({
|
|
|
77
83
|
scopes: z.array(z.string()).min(1),
|
|
78
84
|
/** 일 단위 만료. null = 무기한, 생략 = 90일 */
|
|
79
85
|
expiresInDays: z.number().int().nullable().optional(),
|
|
86
|
+
/** AI 에이전트(MCP)용 토큰. 생략 = false. 발급 뒤에는 바꿀 수 없다 */
|
|
87
|
+
agent: z.boolean().optional(),
|
|
80
88
|
});
|
|
81
89
|
export type ApiTokenCreateRequest = z.infer<typeof apiTokenCreateRequestSchema>;
|
package/src/schemas/order.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const orderSummarySchema = z.object({
|
|
|
12
12
|
testPayment: z
|
|
13
13
|
.boolean()
|
|
14
14
|
.describe(
|
|
15
|
-
"테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지
|
|
15
|
+
"테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지 않았습니다. 상품을 발송하지 않도록 주의하십시오",
|
|
16
16
|
),
|
|
17
17
|
});
|
|
18
18
|
|
|
@@ -61,7 +61,7 @@ export const orderItemSchema = z.object({
|
|
|
61
61
|
testPayment: z
|
|
62
62
|
.boolean()
|
|
63
63
|
.describe(
|
|
64
|
-
"소속 주문이 테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지
|
|
64
|
+
"소속 주문이 테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지 않았습니다",
|
|
65
65
|
),
|
|
66
66
|
});
|
|
67
67
|
|
|
@@ -118,7 +118,7 @@ export const orderSchema = z.object({
|
|
|
118
118
|
testPayment: z
|
|
119
119
|
.boolean()
|
|
120
120
|
.describe(
|
|
121
|
-
"테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지
|
|
121
|
+
"테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지 않았습니다. 상품을 발송하지 않도록 주의하십시오",
|
|
122
122
|
),
|
|
123
123
|
items: z.array(orderItemSchema),
|
|
124
124
|
});
|
|
@@ -137,7 +137,7 @@ const testPaymentQuery = z
|
|
|
137
137
|
.enum(["true", "false"])
|
|
138
138
|
.optional()
|
|
139
139
|
.describe(
|
|
140
|
-
"테스트 결제(샌드박스 결제) 필터. `true`면 테스트 결제만, `false`면 실결제만, 생략하면
|
|
140
|
+
"테스트 결제(샌드박스 결제) 필터. `true`면 테스트 결제만, `false`면 실결제만, 생략하면 전부입니다",
|
|
141
141
|
);
|
|
142
142
|
|
|
143
143
|
const pageQuery = {
|
package/src/schemas/payment.ts
CHANGED
|
@@ -59,7 +59,7 @@ export const TOSS_WIDGET_CLIENT_KEY_PATTERN = /^(test|live)_gck_/;
|
|
|
59
59
|
/** 토스페이먼츠 결제위젯 시크릿 키 접두사. {@link TOSS_WIDGET_CLIENT_KEY_PATTERN} 참고 */
|
|
60
60
|
export const TOSS_WIDGET_SECRET_KEY_PATTERN = /^(test|live)_gsk_/;
|
|
61
61
|
/** 결제위젯 키를 거부할 때의 필드 메시지 */
|
|
62
|
-
export const TOSS_WIDGET_KEY_MESSAGE = "결제위젯 키가 아니라 API 개별 연동 키를
|
|
62
|
+
export const TOSS_WIDGET_KEY_MESSAGE = "결제위젯 키가 아니라 API 개별 연동 키를 입력해주십시오";
|
|
63
63
|
|
|
64
64
|
export const tossPaymentsCredentialsInputSchema = z
|
|
65
65
|
.object({
|
|
@@ -187,7 +187,7 @@ export const updatePaymentProviderRequestSchema = z
|
|
|
187
187
|
body.enabled !== undefined ||
|
|
188
188
|
body.sandboxMode !== undefined ||
|
|
189
189
|
body.easyPayProviders !== undefined,
|
|
190
|
-
{ message: "enabled·sandboxMode·easyPayProviders 중 하나는 보내야
|
|
190
|
+
{ message: "enabled·sandboxMode·easyPayProviders 중 하나는 보내야 합니다" },
|
|
191
191
|
);
|
|
192
192
|
export type UpdatePaymentProviderRequest = z.infer<typeof updatePaymentProviderRequestSchema>;
|
|
193
193
|
|
|
@@ -221,7 +221,7 @@ export const updatePaymentSettingsRequestSchema = z
|
|
|
221
221
|
),
|
|
222
222
|
})
|
|
223
223
|
.refine((body) => body.priority !== undefined || body.checkoutOrigins !== undefined, {
|
|
224
|
-
message: "priority 또는 checkoutOrigins 중 하나는 보내야
|
|
224
|
+
message: "priority 또는 checkoutOrigins 중 하나는 보내야 합니다",
|
|
225
225
|
});
|
|
226
226
|
export type UpdatePaymentSettingsRequest = z.infer<typeof updatePaymentSettingsRequestSchema>;
|
|
227
227
|
|
package/src/schemas/product.ts
CHANGED
|
@@ -284,7 +284,7 @@ const ADDRESS_DESCRIPTIONS = {
|
|
|
284
284
|
export const productCreateRequestSchema = z
|
|
285
285
|
.object({
|
|
286
286
|
name: z.string().min(1).max(100),
|
|
287
|
-
categoryId: z.string().min(1, "카테고리를
|
|
287
|
+
categoryId: z.string().min(1, "카테고리를 선택해주십시오"),
|
|
288
288
|
salePrice: z
|
|
289
289
|
.number()
|
|
290
290
|
.int()
|
package/src/schemas/scope.ts
CHANGED
|
@@ -14,7 +14,7 @@ export const scopeAccessSchema = z
|
|
|
14
14
|
.describe("접근 수준. `r`=읽기, `rw`=읽기+쓰기(r을 함의)");
|
|
15
15
|
export type ScopeAccess = z.infer<typeof scopeAccessSchema>;
|
|
16
16
|
|
|
17
|
-
/** 리소스별로 부여 가능한 접근 수준. settlement·analytics는 쓰기 API가 없어 `r`만 존재한다 */
|
|
17
|
+
/** 리소스별로 부여 가능한 접근 수준. settlement·analytics·audit는 쓰기 API가 없어 `r`만 존재한다 */
|
|
18
18
|
export const STORE_SCOPE_RESOURCES = {
|
|
19
19
|
product: ["r", "rw"],
|
|
20
20
|
category: ["r", "rw"],
|
|
@@ -28,6 +28,7 @@ export const STORE_SCOPE_RESOURCES = {
|
|
|
28
28
|
store: ["r", "rw"],
|
|
29
29
|
member: ["r", "rw"],
|
|
30
30
|
customer: ["r", "rw"],
|
|
31
|
+
audit: ["r"],
|
|
31
32
|
} as const satisfies Record<string, readonly ScopeAccess[]>;
|
|
32
33
|
|
|
33
34
|
export type StoreScopeResource = keyof typeof STORE_SCOPE_RESOURCES;
|
|
@@ -63,6 +64,7 @@ export const STORE_ROLE_SCOPES = {
|
|
|
63
64
|
"store:rw",
|
|
64
65
|
"member:rw",
|
|
65
66
|
"customer:rw",
|
|
67
|
+
"audit:r",
|
|
66
68
|
],
|
|
67
69
|
ADMIN: [
|
|
68
70
|
"product:rw",
|
|
@@ -76,6 +78,7 @@ export const STORE_ROLE_SCOPES = {
|
|
|
76
78
|
"store:rw",
|
|
77
79
|
"member:rw",
|
|
78
80
|
"customer:rw",
|
|
81
|
+
"audit:r",
|
|
79
82
|
],
|
|
80
83
|
STAFF: [
|
|
81
84
|
"product:rw",
|
package/src/schemas/store.ts
CHANGED
|
@@ -68,11 +68,11 @@ export const storeCodeSchema = z
|
|
|
68
68
|
.string()
|
|
69
69
|
.regex(
|
|
70
70
|
STORE_CODE_PATTERN,
|
|
71
|
-
"소문자·숫자·하이픈 3~30자여야 하고 하이픈으로 시작하거나 끝날 수
|
|
71
|
+
"소문자·숫자·하이픈 3~30자여야 하고 하이픈으로 시작하거나 끝날 수 없습니다",
|
|
72
72
|
)
|
|
73
73
|
.refine(
|
|
74
74
|
(code) => !isReservedStoreCode(code),
|
|
75
|
-
"플랫폼이 쓰는 이름이라 스토어 코드로 쓸 수
|
|
75
|
+
"플랫폼이 쓰는 이름이라 스토어 코드로 쓸 수 없습니다",
|
|
76
76
|
);
|
|
77
77
|
|
|
78
78
|
/** `https://shop.example.com`처럼 경로·쿼리 없는 http(s) 오리진인가 */
|
|
@@ -98,7 +98,7 @@ export const analyticsOriginSchema = z
|
|
|
98
98
|
.string()
|
|
99
99
|
.trim()
|
|
100
100
|
.max(255)
|
|
101
|
-
.refine(isWebOrigin, "https://shop.example.com 형식(경로 없이)으로 입력해
|
|
101
|
+
.refine(isWebOrigin, "https://shop.example.com 형식(경로 없이)으로 입력해 주십시오")
|
|
102
102
|
.describe("스토어프론트 오리진. 예: `https://shop.example.com`");
|
|
103
103
|
|
|
104
104
|
/** 허용 오리진 상한 */
|
|
@@ -125,7 +125,7 @@ export const policyVersionSchema = z
|
|
|
125
125
|
export const policyDocumentUrlSchema = z
|
|
126
126
|
.url()
|
|
127
127
|
.max(500)
|
|
128
|
-
.refine((value) => value.startsWith("https://"), "https 주소만 쓸 수
|
|
128
|
+
.refine((value) => value.startsWith("https://"), "https 주소만 쓸 수 있습니다")
|
|
129
129
|
.nullable();
|
|
130
130
|
|
|
131
131
|
const autoDeliverDaysDescription =
|
package/src/session.ts
CHANGED
|
@@ -203,10 +203,10 @@ export function createStoreSession(options: StoreSessionOptions): StoreSession {
|
|
|
203
203
|
async function handleCallback(code: string, state: string): Promise<void> {
|
|
204
204
|
const raw = storage.get(STORE_SESSION_KEYS.pkce);
|
|
205
205
|
storage.remove(STORE_SESSION_KEYS.pkce); // 1회용
|
|
206
|
-
if (!raw) throw new Error("로그인 세션이 만료되었습니다. 다시
|
|
206
|
+
if (!raw) throw new Error("로그인 세션이 만료되었습니다. 다시 시도해주십시오");
|
|
207
207
|
const saved = JSON.parse(raw) as PkceBlob;
|
|
208
208
|
if (saved.state !== state) {
|
|
209
|
-
throw new Error("state가 일치하지 않습니다. 다시
|
|
209
|
+
throw new Error("state가 일치하지 않습니다. 다시 시도해주십시오");
|
|
210
210
|
}
|
|
211
211
|
const tokens = await exchangeAuthorizationCode({
|
|
212
212
|
baseUrl,
|