@sayren/store-sdk 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -8,9 +8,13 @@ export declare class ApiError extends Error {
8
8
  readonly body?: unknown | undefined;
9
9
  /** 엔벨로프 `$.error.details` — 에러 코드별 보조 데이터. 없으면 undefined */
10
10
  readonly details?: Record<string, unknown> | undefined;
11
+ /** 엔벨로프 `$.error.docs` — 이 에러 코드를 설명하는 문서 URL. 매핑이 없으면 undefined */
12
+ readonly docs?: string | undefined;
11
13
  constructor(status: number, code: string, message: string, traceId?: string | undefined, body?: unknown | undefined,
12
14
  /** 엔벨로프 `$.error.details` — 에러 코드별 보조 데이터. 없으면 undefined */
13
- details?: Record<string, unknown> | undefined);
15
+ details?: Record<string, unknown> | undefined,
16
+ /** 엔벨로프 `$.error.docs` — 이 에러 코드를 설명하는 문서 URL. 매핑이 없으면 undefined */
17
+ docs?: string | undefined);
14
18
  }
15
19
  type TokenSource = string | null | undefined | (() => string | null | undefined);
16
20
  type Query = Record<string, string | number | boolean | undefined>;
@@ -619,6 +623,7 @@ export declare const apiLogListQuerySchema: z.ZodObject<{
619
623
  "5xx": "5xx";
620
624
  }>>;
621
625
  q: z.ZodOptional<z.ZodString>;
626
+ traceId: z.ZodOptional<z.ZodString>;
622
627
  cursor: z.ZodOptional<z.ZodString>;
623
628
  limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
624
629
  }, z.core.$strip>;
@@ -1956,6 +1961,34 @@ export declare const webhookDeliveryErrorCodeSchema: z.ZodEnum<{
1956
1961
  WEBHOOK_SUSPENDED: "WEBHOOK_SUSPENDED";
1957
1962
  }>;
1958
1963
  export type WebhookDeliveryErrorCode = z.infer<typeof webhookDeliveryErrorCodeSchema>;
1964
+ /**
1965
+ * 실패 원인 분류의 **현재** 값. 응답 스키마(`webhookFailureReasonSchema`)는 문자열로 열려 있다 —
1966
+ * 서버가 값을 더해도 이전 SDK가 목록 파싱에 실패하지 않게 한다(택배 추적 `stage`와 같은 이유).
1967
+ * 모르는 값은 `errorCode`로 판단한다.
1968
+ */
1969
+ export declare const WEBHOOK_FAILURE_REASONS: readonly ["DNS", "TLS", "TIMEOUT", "CONNECTION", "HTTP_4XX", "HTTP_5XX", "HTTP_OTHER", "SSRF_BLOCKED", "SUSPENDED"];
1970
+ /** 알려진 실패 원인. 응답에는 이 밖의 문자열이 올 수 있다 */
1971
+ export type WebhookFailureReason = (typeof WEBHOOK_FAILURE_REASONS)[number];
1972
+ export declare const webhookFailureReasonSchema: z.ZodString;
1973
+ /**
1974
+ * 대상 리소스 종류의 **현재** 값. 응답 스키마는 문자열로 열려 있다(새 이벤트가 새 리소스를 가리킬 수 있다).
1975
+ */
1976
+ export declare const WEBHOOK_RESOURCE_TYPES: readonly ["ORDER", "CLAIM", "PRODUCT", "REVIEW", "INQUIRY"];
1977
+ /** 알려진 리소스 종류. 응답에는 이 밖의 문자열이 올 수 있다 */
1978
+ export type WebhookResourceType = (typeof WEBHOOK_RESOURCE_TYPES)[number];
1979
+ export declare const webhookResourceTypeSchema: z.ZodString;
1980
+ /** 이벤트가 가리키는 대상 리소스 — 전송 기록을 주문·상품과 잇는 키 */
1981
+ export declare const webhookDeliveryResourceSchema: z.ZodObject<{
1982
+ type: z.ZodString;
1983
+ id: z.ZodString;
1984
+ }, z.core.$strip>;
1985
+ export type WebhookDeliveryResource = z.infer<typeof webhookDeliveryResourceSchema>;
1986
+ /** 발송 요청 헤더 한 줄 (값 마스킹) */
1987
+ export declare const webhookRequestHeaderSchema: z.ZodObject<{
1988
+ name: z.ZodString;
1989
+ value: z.ZodNullable<z.ZodString>;
1990
+ }, z.core.$strip>;
1991
+ export type WebhookRequestHeader = z.infer<typeof webhookRequestHeaderSchema>;
1959
1992
  /** 웹훅 전송 기록 한 건 (전송 기록 조회 항목) */
1960
1993
  export declare const webhookDeliverySchema: z.ZodObject<{
1961
1994
  deliveryId: z.ZodString;
@@ -1992,6 +2025,11 @@ export declare const webhookDeliverySchema: z.ZodObject<{
1992
2025
  NON_2XX: "NON_2XX";
1993
2026
  WEBHOOK_SUSPENDED: "WEBHOOK_SUSPENDED";
1994
2027
  }>>;
2028
+ failureReason: z.ZodNullable<z.ZodString>;
2029
+ resource: z.ZodNullable<z.ZodObject<{
2030
+ type: z.ZodString;
2031
+ id: z.ZodString;
2032
+ }, z.core.$strip>>;
1995
2033
  attemptCount: z.ZodNumber;
1996
2034
  createdAt: z.ZodString;
1997
2035
  attemptedAt: z.ZodNullable<z.ZodString>;
@@ -2000,6 +2038,64 @@ export declare const webhookDeliverySchema: z.ZodObject<{
2000
2038
  redeliveredAs: z.ZodNullable<z.ZodString>;
2001
2039
  }, z.core.$strip>;
2002
2040
  export type WebhookDelivery = z.infer<typeof webhookDeliverySchema>;
2041
+ /** 발송 본문 최대 노출 길이(바이트). 넘으면 잘라 주고 `payloadTruncated`가 true다 */
2042
+ export declare const WEBHOOK_PAYLOAD_EXCERPT_MAX_BYTES: number;
2043
+ /** 웹훅 전송 기록 상세 — `GET /webhooks/{id}/deliveries/{deliveryId}`. 목록 항목 + 요청 본문·헤더 */
2044
+ export declare const webhookDeliveryDetailSchema: z.ZodObject<{
2045
+ deliveryId: z.ZodString;
2046
+ eventId: z.ZodString;
2047
+ eventType: z.ZodEnum<{
2048
+ "ORDER.PAID": "ORDER.PAID";
2049
+ "ORDER.CONFIRMED": "ORDER.CONFIRMED";
2050
+ "ORDER.DISPATCHED": "ORDER.DISPATCHED";
2051
+ "ORDER.DELIVERED": "ORDER.DELIVERED";
2052
+ "ORDER.PURCHASE_DECIDED": "ORDER.PURCHASE_DECIDED";
2053
+ "ORDER.ADDRESS_CHANGE_REQUESTED": "ORDER.ADDRESS_CHANGE_REQUESTED";
2054
+ "CLAIM.CANCEL_REQUESTED": "CLAIM.CANCEL_REQUESTED";
2055
+ "CLAIM.RETURN_REQUESTED": "CLAIM.RETURN_REQUESTED";
2056
+ "CLAIM.EXCHANGE_REQUESTED": "CLAIM.EXCHANGE_REQUESTED";
2057
+ "CLAIM.COMPLETED": "CLAIM.COMPLETED";
2058
+ "INQUIRY.CREATED": "INQUIRY.CREATED";
2059
+ "REVIEW.CREATED": "REVIEW.CREATED";
2060
+ "PRODUCT.OUT_OF_STOCK": "PRODUCT.OUT_OF_STOCK";
2061
+ }>;
2062
+ url: z.ZodString;
2063
+ status: z.ZodEnum<{
2064
+ PENDING: "PENDING";
2065
+ PENDING_RETRY: "PENDING_RETRY";
2066
+ SUCCESS: "SUCCESS";
2067
+ FAILED: "FAILED";
2068
+ }>;
2069
+ responseStatusCode: z.ZodNullable<z.ZodNumber>;
2070
+ responseTimeMs: z.ZodNullable<z.ZodNumber>;
2071
+ responseBodyExcerpt: z.ZodNullable<z.ZodString>;
2072
+ errorCode: z.ZodNullable<z.ZodEnum<{
2073
+ TIMEOUT: "TIMEOUT";
2074
+ CONNECTION_FAILED: "CONNECTION_FAILED";
2075
+ BLOCKED_ADDRESS: "BLOCKED_ADDRESS";
2076
+ NON_2XX: "NON_2XX";
2077
+ WEBHOOK_SUSPENDED: "WEBHOOK_SUSPENDED";
2078
+ }>>;
2079
+ failureReason: z.ZodNullable<z.ZodString>;
2080
+ resource: z.ZodNullable<z.ZodObject<{
2081
+ type: z.ZodString;
2082
+ id: z.ZodString;
2083
+ }, z.core.$strip>>;
2084
+ attemptCount: z.ZodNumber;
2085
+ createdAt: z.ZodString;
2086
+ attemptedAt: z.ZodNullable<z.ZodString>;
2087
+ nextAttemptAt: z.ZodNullable<z.ZodString>;
2088
+ redeliveryOf: z.ZodNullable<z.ZodString>;
2089
+ redeliveredAs: z.ZodNullable<z.ZodString>;
2090
+ payload: z.ZodString;
2091
+ payloadTruncated: z.ZodBoolean;
2092
+ payloadMasked: z.ZodBoolean;
2093
+ requestHeaders: z.ZodArray<z.ZodObject<{
2094
+ name: z.ZodString;
2095
+ value: z.ZodNullable<z.ZodString>;
2096
+ }, z.core.$strip>>;
2097
+ }, z.core.$strip>;
2098
+ export type WebhookDeliveryDetail = z.infer<typeof webhookDeliveryDetailSchema>;
2003
2099
  /** 전송 기록 조회 쿼리 — `GET /webhooks/{id}/deliveries` */
2004
2100
  export declare const webhookDeliveryListQuerySchema: z.ZodObject<{
2005
2101
  status: z.ZodOptional<z.ZodEnum<{
@@ -2008,6 +2104,22 @@ export declare const webhookDeliveryListQuerySchema: z.ZodObject<{
2008
2104
  SUCCESS: "SUCCESS";
2009
2105
  FAILED: "FAILED";
2010
2106
  }>>;
2107
+ eventType: z.ZodOptional<z.ZodEnum<{
2108
+ "ORDER.PAID": "ORDER.PAID";
2109
+ "ORDER.CONFIRMED": "ORDER.CONFIRMED";
2110
+ "ORDER.DISPATCHED": "ORDER.DISPATCHED";
2111
+ "ORDER.DELIVERED": "ORDER.DELIVERED";
2112
+ "ORDER.PURCHASE_DECIDED": "ORDER.PURCHASE_DECIDED";
2113
+ "ORDER.ADDRESS_CHANGE_REQUESTED": "ORDER.ADDRESS_CHANGE_REQUESTED";
2114
+ "CLAIM.CANCEL_REQUESTED": "CLAIM.CANCEL_REQUESTED";
2115
+ "CLAIM.RETURN_REQUESTED": "CLAIM.RETURN_REQUESTED";
2116
+ "CLAIM.EXCHANGE_REQUESTED": "CLAIM.EXCHANGE_REQUESTED";
2117
+ "CLAIM.COMPLETED": "CLAIM.COMPLETED";
2118
+ "INQUIRY.CREATED": "INQUIRY.CREATED";
2119
+ "REVIEW.CREATED": "REVIEW.CREATED";
2120
+ "PRODUCT.OUT_OF_STOCK": "PRODUCT.OUT_OF_STOCK";
2121
+ }>>;
2122
+ resourceId: z.ZodOptional<z.ZodString>;
2011
2123
  limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
2012
2124
  }, z.core.$strip>;
2013
2125
  export type WebhookDeliveryListParams = z.infer<typeof webhookDeliveryListQuerySchema>;
@@ -2045,6 +2157,13 @@ export declare const webhookTestResultSchema: z.ZodObject<{
2045
2157
  NON_2XX: "NON_2XX";
2046
2158
  WEBHOOK_SUSPENDED: "WEBHOOK_SUSPENDED";
2047
2159
  }>>;
2160
+ failureReason: z.ZodNullable<z.ZodString>;
2161
+ responseBodyExcerpt: z.ZodNullable<z.ZodString>;
2162
+ requestHeaders: z.ZodArray<z.ZodObject<{
2163
+ name: z.ZodString;
2164
+ value: z.ZodNullable<z.ZodString>;
2165
+ }, z.core.$strip>>;
2166
+ payload: z.ZodString;
2048
2167
  }, z.core.$strip>;
2049
2168
  export type WebhookTestResult = z.infer<typeof webhookTestResultSchema>;
2050
2169
  export declare const webhookRequestSchema: z.ZodObject<{
@@ -6924,6 +7043,11 @@ export declare function createStoreClient(options: StoreClientOptions): {
6924
7043
  responseTimeMs: number | null;
6925
7044
  responseBodyExcerpt: string | null;
6926
7045
  errorCode: "TIMEOUT" | "CONNECTION_FAILED" | "BLOCKED_ADDRESS" | "NON_2XX" | "WEBHOOK_SUSPENDED" | null;
7046
+ failureReason: string | null;
7047
+ resource: {
7048
+ type: string;
7049
+ id: string;
7050
+ } | null;
6927
7051
  attemptCount: number;
6928
7052
  createdAt: string;
6929
7053
  attemptedAt: string | null;
@@ -6931,6 +7055,40 @@ export declare function createStoreClient(options: StoreClientOptions): {
6931
7055
  redeliveryOf: string | null;
6932
7056
  redeliveredAs: string | null;
6933
7057
  }[]>;
7058
+ /**
7059
+ * 전송 기록 한 건의 상세. 목록 항목에 보낸 요청 본문(최대 16KB)과 요청 헤더가 더해진다.
7060
+ * 실패 원인 분류(`failureReason`)·응답 본문(`responseBodyExcerpt`)으로 원인을 찾고,
7061
+ * `resource.id`를 API 로그 조회의 `q`로 넘겨 같은 리소스의 API 요청과 맞춰 본다.
7062
+ */
7063
+ getDelivery: (webhookId: string, deliveryId: string) => Promise<{
7064
+ deliveryId: string;
7065
+ eventId: string;
7066
+ eventType: "ORDER.PAID" | "ORDER.CONFIRMED" | "ORDER.DISPATCHED" | "ORDER.DELIVERED" | "ORDER.PURCHASE_DECIDED" | "ORDER.ADDRESS_CHANGE_REQUESTED" | "CLAIM.CANCEL_REQUESTED" | "CLAIM.RETURN_REQUESTED" | "CLAIM.EXCHANGE_REQUESTED" | "CLAIM.COMPLETED" | "INQUIRY.CREATED" | "REVIEW.CREATED" | "PRODUCT.OUT_OF_STOCK";
7067
+ url: string;
7068
+ status: "PENDING" | "PENDING_RETRY" | "SUCCESS" | "FAILED";
7069
+ responseStatusCode: number | null;
7070
+ responseTimeMs: number | null;
7071
+ responseBodyExcerpt: string | null;
7072
+ errorCode: "TIMEOUT" | "CONNECTION_FAILED" | "BLOCKED_ADDRESS" | "NON_2XX" | "WEBHOOK_SUSPENDED" | null;
7073
+ failureReason: string | null;
7074
+ resource: {
7075
+ type: string;
7076
+ id: string;
7077
+ } | null;
7078
+ attemptCount: number;
7079
+ createdAt: string;
7080
+ attemptedAt: string | null;
7081
+ nextAttemptAt: string | null;
7082
+ redeliveryOf: string | null;
7083
+ redeliveredAs: string | null;
7084
+ payload: string;
7085
+ payloadTruncated: boolean;
7086
+ payloadMasked: boolean;
7087
+ requestHeaders: {
7088
+ name: string;
7089
+ value: string | null;
7090
+ }[];
7091
+ }>;
6934
7092
  /**
6935
7093
  * FAILED 전송 기록 한 건을 같은 eventId로 다시 보낸다(202). 새로 적재된 기록을 돌려주며
6936
7094
  * `redeliveryOf`가 원본 deliveryId다. 원본이 FAILED가 아니면 409 `DELIVERY_NOT_FAILED`,
@@ -6947,6 +7105,11 @@ export declare function createStoreClient(options: StoreClientOptions): {
6947
7105
  responseTimeMs: number | null;
6948
7106
  responseBodyExcerpt: string | null;
6949
7107
  errorCode: "TIMEOUT" | "CONNECTION_FAILED" | "BLOCKED_ADDRESS" | "NON_2XX" | "WEBHOOK_SUSPENDED" | null;
7108
+ failureReason: string | null;
7109
+ resource: {
7110
+ type: string;
7111
+ id: string;
7112
+ } | null;
6950
7113
  attemptCount: number;
6951
7114
  createdAt: string;
6952
7115
  attemptedAt: string | null;
@@ -6969,6 +7132,13 @@ export declare function createStoreClient(options: StoreClientOptions): {
6969
7132
  responseStatusCode: number | null;
6970
7133
  responseTimeMs: number | null;
6971
7134
  errorCode: "TIMEOUT" | "CONNECTION_FAILED" | "BLOCKED_ADDRESS" | "NON_2XX" | "WEBHOOK_SUSPENDED" | null;
7135
+ failureReason: string | null;
7136
+ responseBodyExcerpt: string | null;
7137
+ requestHeaders: {
7138
+ name: string;
7139
+ value: string | null;
7140
+ }[];
7141
+ payload: string;
6972
7142
  }>;
6973
7143
  };
6974
7144
  /** API 로그 (보관 7일, 콘솔 OWNER/ADMIN) */
package/dist/index.mjs CHANGED
@@ -34,6 +34,11 @@ const errorDetailSchema = z.object({
34
34
  message: z.string(),
35
35
  traceId: z.string().optional(),
36
36
  /**
37
+ * 이 에러 코드를 설명하는 문서 URL(선택). 서버가 코드별 매핑을 가진 코드에만 실린다 —
38
+ * 없으면 필드가 아예 없으므로 소비측은 있을 때만 쓴다
39
+ */
40
+ docs: z.url().optional().catch(void 0),
41
+ /**
37
42
  * 에러 코드별 보조 데이터(선택). 예: 관리 API `LIVE_GATE_NOT_MET`·`LIVE_PAYMENTS_DISABLED`의
38
43
  * `{ conditions: LiveGateCondition[] }`. 형태는 에러 코드마다 다르므로 소비측이 코드별 스키마로 다시 파싱한다
39
44
  */
@@ -69,13 +74,15 @@ var ApiError = class extends Error {
69
74
  traceId;
70
75
  body;
71
76
  details;
72
- constructor(status, code, message, traceId, body, details) {
77
+ docs;
78
+ constructor(status, code, message, traceId, body, details, docs) {
73
79
  super(message);
74
80
  this.status = status;
75
81
  this.code = code;
76
82
  this.traceId = traceId;
77
83
  this.body = body;
78
84
  this.details = details;
85
+ this.docs = docs;
79
86
  this.name = "ApiError";
80
87
  }
81
88
  };
@@ -88,7 +95,7 @@ var ApiError = class extends Error {
88
95
  function toApiError(status, raw) {
89
96
  const envelope = envelopeSchema.safeParse(raw);
90
97
  const detail = envelope.success ? envelope.data.error : errorDetailSchema.safeParse(raw).data ?? null;
91
- if (detail) return new ApiError(status, detail.code, detail.message, detail.traceId, raw, detail.details);
98
+ if (detail) return new ApiError(status, detail.code, detail.message, detail.traceId, raw, detail.details, detail.docs);
92
99
  const oauth = envelope.success ? null : oauthErrorSchema.safeParse(raw).data;
93
100
  if (oauth) return new ApiError(status, oauth.error, oauth.error_description ?? oauth.error, void 0, raw);
94
101
  return new ApiError(status, "UNKNOWN", `HTTP ${status}`, void 0, raw);
@@ -467,7 +474,8 @@ const apiLogListQuerySchema = z.object({
467
474
  principalType: 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(apiLogPrincipalTypeSchema).min(1)).optional().describe("호출 주체 유형 필터. 여러 개는 쉼표로 구분한다 (예: `PAT,CLIENT`). 파싱 결과는 배열로 정규화된다"),
468
475
  principalId: z.string().optional().describe("호출 주체 ID 필터"),
469
476
  status: apiLogStatusFilterSchema.optional(),
470
- q: z.string().optional().describe("경로 부분 일치 검색어"),
477
+ q: z.string().optional().describe("경로 부분 일치 검색어. 리소스 id(예: `ord_…`)를 넣으면 그 리소스를 건드린 요청만 남으므로 웹훅 전송 기록의 `resource.id`와 짝지어 쓴다"),
478
+ traceId: z.string().optional().describe("추적 ID 필터(정확히 일치). 에러 응답 `error.traceId` 값을 그대로 넣어 그 요청 한 건을 찾는다"),
471
479
  cursor: z.string().optional().describe("이전 응답의 `nextCursor`. 생략하면 가장 최신부터"),
472
480
  limit: z.coerce.number().int().min(1).max(100).optional().describe("최대 건수 (1~100, 기본 50)")
473
481
  });
@@ -1180,6 +1188,44 @@ const webhookDeliveryErrorCodeSchema = z.enum([
1180
1188
  "NON_2XX",
1181
1189
  "WEBHOOK_SUSPENDED"
1182
1190
  ]).describe("전송 실패 사유. `TIMEOUT`=10초 안에 응답 없음, `CONNECTION_FAILED`=연결 실패(DNS·TLS 포함), `BLOCKED_ADDRESS`=사설·루프백 등 전송이 허용되지 않는 주소, `NON_2XX`=2xx가 아닌 응답(리다이렉트 포함), `WEBHOOK_SUSPENDED`=구독 정지로 대기 중인 전송이 종료됨");
1191
+ /**
1192
+ * 실패 원인 분류의 **현재** 값. 응답 스키마(`webhookFailureReasonSchema`)는 문자열로 열려 있다 —
1193
+ * 서버가 값을 더해도 이전 SDK가 목록 파싱에 실패하지 않게 한다(택배 추적 `stage`와 같은 이유).
1194
+ * 모르는 값은 `errorCode`로 판단한다.
1195
+ */
1196
+ const WEBHOOK_FAILURE_REASONS = [
1197
+ "DNS",
1198
+ "TLS",
1199
+ "TIMEOUT",
1200
+ "CONNECTION",
1201
+ "HTTP_4XX",
1202
+ "HTTP_5XX",
1203
+ "HTTP_OTHER",
1204
+ "SSRF_BLOCKED",
1205
+ "SUSPENDED"
1206
+ ];
1207
+ const webhookFailureReasonSchema = z.string().describe("실패 원인 분류(진단용). 현재 값은 `DNS`=호스트 이름을 주소로 바꾸지 못함, `TLS`=인증서·핸드셰이크 실패, `TIMEOUT`=10초 안에 응답 없음, `CONNECTION`=연결 거부·끊김, `HTTP_4XX`·`HTTP_5XX`=그 대역의 응답, `HTTP_OTHER`=2xx·4xx·5xx가 아닌 응답(리다이렉트 등), `SSRF_BLOCKED`=사설·루프백 등 전송이 허용되지 않는 주소, `SUSPENDED`=구독 정지로 대기 중인 전송이 종료됨. `errorCode`보다 잘게 나눈 값이며 둘은 함께 쓸 수 있다. 값이 추가될 수 있으므로 모르는 값은 `errorCode`로 판단한다");
1208
+ /**
1209
+ * 대상 리소스 종류의 **현재** 값. 응답 스키마는 문자열로 열려 있다(새 이벤트가 새 리소스를 가리킬 수 있다).
1210
+ */
1211
+ const WEBHOOK_RESOURCE_TYPES = [
1212
+ "ORDER",
1213
+ "CLAIM",
1214
+ "PRODUCT",
1215
+ "REVIEW",
1216
+ "INQUIRY"
1217
+ ];
1218
+ const webhookResourceTypeSchema = z.string().describe("이벤트가 가리키는 리소스 종류. 현재 값은 `ORDER`·`CLAIM`·`PRODUCT`·`REVIEW`·`INQUIRY`이고 값이 추가될 수 있다");
1219
+ /** 이벤트가 가리키는 대상 리소스 — 전송 기록을 주문·상품과 잇는 키 */
1220
+ const webhookDeliveryResourceSchema = z.object({
1221
+ type: webhookResourceTypeSchema,
1222
+ id: z.string().describe("리소스 ID. 전송 기록 조회의 `resourceId`, API 로그 조회의 `q`로 그대로 넣을 수 있다")
1223
+ });
1224
+ /** 발송 요청 헤더 한 줄 (값 마스킹) */
1225
+ const webhookRequestHeaderSchema = z.object({
1226
+ name: z.string().describe("헤더 이름"),
1227
+ value: z.string().nullable().describe("헤더 값. 서명(`Sayren-Signature`)처럼 비밀이 섞이는 헤더는 이름만 주고 값은 null이다")
1228
+ });
1183
1229
  /** 웹훅 전송 기록 한 건 (전송 기록 조회 항목) */
1184
1230
  const webhookDeliverySchema = z.object({
1185
1231
  deliveryId: z.string().describe("전송 기록 ID. 수신 요청의 `Sayren-Delivery-Id` 헤더 값"),
@@ -1189,8 +1235,10 @@ const webhookDeliverySchema = z.object({
1189
1235
  status: webhookDeliveryStatusSchema,
1190
1236
  responseStatusCode: z.number().int().nullable().describe("마지막 시도의 HTTP 응답 코드. 미시도·연결 실패·타임아웃·차단이면 null"),
1191
1237
  responseTimeMs: z.number().int().nullable().describe("마지막 시도의 응답 시간(ms). 미시도·연결 실패면 null"),
1192
- responseBodyExcerpt: z.string().nullable().describe("마지막 시도의 응답 본문 앞부분(최대 1,024자). 응답이 없으면 null"),
1238
+ responseBodyExcerpt: z.string().nullable().describe("마지막 시도의 응답 본문 앞부분(최대 4,096자). 응답이 없으면 null. 가린 뒤에 자른다. 키 이름에 `authorization`·`token`·`secret`·`password`·`key`·`signature`·`credential`·`cookie`가 **들어 있으면**(부분 일치, 대소문자 무시. 예: `apiKey`·`access_token`·`keyword`) 그 값을 `***`로 가린다. 값이 객체·배열이면 통째로 가린다. 평문 `Authorization: …` 같은 헤더 줄, `Bearer <토큰>`, 쿼리 형식 `access_token=…`, 끝에서 잘려 닫히지 않은 값도 가린다"),
1193
1239
  errorCode: webhookDeliveryErrorCodeSchema.nullable().describe("마지막 시도의 실패 사유. 성공했거나 미시도면 null"),
1240
+ failureReason: webhookFailureReasonSchema.nullable().describe("마지막 시도의 실패 원인 분류. 성공했거나 미시도면 null. `errorCode`를 더 잘게 나눈 진단 값이다"),
1241
+ resource: webhookDeliveryResourceSchema.nullable().describe("이벤트가 가리키는 대상 리소스. 리소스를 특정할 수 없는 이벤트면 null. 이 값으로 전송 기록(`resourceId`)과 API 로그(`q`)를 오갈 수 있다"),
1194
1242
  attemptCount: z.number().int().min(0).describe("지금까지의 시도 횟수. 최초 시도 1회 + 재시도 최대 4회"),
1195
1243
  createdAt: z.string().describe("전송 기록이 적재된 일시 (ISO 8601)"),
1196
1244
  attemptedAt: z.string().nullable().describe("마지막 시도 일시 (ISO 8601). 미시도면 null"),
@@ -1198,9 +1246,20 @@ const webhookDeliverySchema = z.object({
1198
1246
  redeliveryOf: z.string().nullable().describe("수동 재전송으로 생긴 기록이면 원본 전송 기록 ID. 아니면 null"),
1199
1247
  redeliveredAs: z.string().nullable().describe("이 기록을 원본으로 수동 재전송해 생긴 기록의 전송 기록 ID. 아직 재전송하지 않았으면 null. 재전송은 원본당 1회다")
1200
1248
  });
1249
+ /** 발송 본문 최대 노출 길이(바이트). 넘으면 잘라 주고 `payloadTruncated`가 true다 */
1250
+ const WEBHOOK_PAYLOAD_EXCERPT_MAX_BYTES = 16384;
1251
+ /** 웹훅 전송 기록 상세 — `GET /webhooks/{id}/deliveries/{deliveryId}`. 목록 항목 + 요청 본문·헤더 */
1252
+ const webhookDeliveryDetailSchema = webhookDeliverySchema.extend({
1253
+ payload: z.string().describe(`수신 서버로 보낸 요청 본문 그대로(JSON 문자열). 서명은 이 문자열을 대상으로 계산한다. 최대 ${WEBHOOK_PAYLOAD_EXCERPT_MAX_BYTES / 1024}KB까지 주고 넘으면 잘라 준다`),
1254
+ payloadTruncated: z.boolean().describe("본문이 길어서 잘렸으면 true. true면 `payload`는 유효한 JSON이 아니다"),
1255
+ payloadMasked: z.boolean().describe("민감 키 마스킹(`responseBodyExcerpt`와 같은 키 이름 규칙)이 본문을 바꿨으면 true. true면 `payload`는 보낸 본문과 달라 서명을 맞춰 볼 수 없다. 이벤트 본문에는 보통 민감 키가 없어 false다"),
1256
+ requestHeaders: z.array(webhookRequestHeaderSchema).describe("발송 요청에 실은 헤더. 값은 전송 시점 그대로이나 서명 헤더는 이름만 주고 값은 null이다. `Content-Length`는 실제로 보낸 본문(마스킹·자르기 전)의 바이트 수다. 매 시도가 같은 헤더 집합을 쓰고 서명·타임스탬프만 달라진다")
1257
+ });
1201
1258
  /** 전송 기록 조회 쿼리 — `GET /webhooks/{id}/deliveries` */
1202
1259
  const webhookDeliveryListQuerySchema = z.object({
1203
1260
  status: webhookDeliveryStatusSchema.optional().describe("전송 상태 필터. 생략하면 전체"),
1261
+ eventType: eventTypeSchema.optional().describe("이벤트 타입 필터. 생략하면 전체"),
1262
+ resourceId: z.string().optional().describe("대상 리소스 ID 필터(정확히 일치). 주문·클레임·상품·리뷰·문의 id를 넣으면 그 리소스의 이벤트만 남는다. 같은 id로 API 로그를 `q`로 검색하면 그 리소스를 건드린 API 요청을 볼 수 있다"),
1204
1263
  limit: z.coerce.number().int().min(1).max(100).optional().describe("최대 건수 (1~100, 기본 50). 최신 적재순으로 반환하며 보관 기간은 30일이다")
1205
1264
  });
1206
1265
  /** 실패 건 일괄 재전송 응답 — `POST /webhooks/{id}/deliveries/redeliver-failed` */
@@ -1210,7 +1269,11 @@ const webhookTestRequestSchema = z.object({ eventType: eventTypeSchema.describe(
1210
1269
  const webhookTestResultSchema = z.object({
1211
1270
  responseStatusCode: z.number().int().nullable().describe("수신 서버의 HTTP 응답 코드. 연결 실패·타임아웃·차단이면 null"),
1212
1271
  responseTimeMs: z.number().int().nullable().describe("응답 시간(ms). 연결 실패면 null"),
1213
- errorCode: webhookDeliveryErrorCodeSchema.nullable().describe("실패 사유. 2xx 응답이면 null")
1272
+ errorCode: webhookDeliveryErrorCodeSchema.nullable().describe("실패 사유. 2xx 응답이면 null"),
1273
+ failureReason: webhookFailureReasonSchema.nullable().describe("실패 원인 분류. 2xx 응답이면 null"),
1274
+ responseBodyExcerpt: z.string().nullable().describe("응답 본문 앞부분(최대 4,096자). 규칙은 전송 기록의 `responseBodyExcerpt`와 같다. 응답이 없으면 null"),
1275
+ requestHeaders: z.array(webhookRequestHeaderSchema).describe("보낸 요청 헤더. 서명 헤더는 이름만 주고 값은 null이다"),
1276
+ payload: z.string().describe("보낸 요청 본문 그대로(JSON 문자열). 샘플 이벤트다")
1214
1277
  });
1215
1278
  const webhookRequestSchema = z.object({
1216
1279
  url: z.url().startsWith("https://", "웹훅 URL은 HTTPS만 허용됩니다"),
@@ -3392,6 +3455,12 @@ get: (params) => storeHttp.request("GET", "/sales-report", salesReportSchema, {
3392
3455
  /** 전송 기록 (최신 적재순, 기본 50건·최대 100건, 보관 30일) */
3393
3456
  listDeliveries: (webhookId, params) => storeHttp.request("GET", path`/webhooks/${webhookId}/deliveries`, z.array(webhookDeliverySchema), { query: params }),
3394
3457
  /**
3458
+ * 전송 기록 한 건의 상세. 목록 항목에 보낸 요청 본문(최대 16KB)과 요청 헤더가 더해진다.
3459
+ * 실패 원인 분류(`failureReason`)·응답 본문(`responseBodyExcerpt`)으로 원인을 찾고,
3460
+ * `resource.id`를 API 로그 조회의 `q`로 넘겨 같은 리소스의 API 요청과 맞춰 본다.
3461
+ */
3462
+ getDelivery: (webhookId, deliveryId) => storeHttp.request("GET", path`/webhooks/${webhookId}/deliveries/${deliveryId}`, webhookDeliveryDetailSchema),
3463
+ /**
3395
3464
  * FAILED 전송 기록 한 건을 같은 eventId로 다시 보낸다(202). 새로 적재된 기록을 돌려주며
3396
3465
  * `redeliveryOf`가 원본 deliveryId다. 원본이 FAILED가 아니면 409 `DELIVERY_NOT_FAILED`,
3397
3466
  * 원본이 이미 재전송됐으면 409 `DELIVERY_ALREADY_REDELIVERED`(원본당 1회, 원본의 `redeliveredAs` 참고),
@@ -3831,4 +3900,4 @@ function createStoreSession(options) {
3831
3900
  };
3832
3901
  }
3833
3902
  //#endregion
3834
- 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, 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_HEADERS, 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, 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, webhookDeliveryErrorCodeSchema, webhookDeliveryListQuerySchema, webhookDeliverySchema, webhookDeliveryStatusSchema, webhookEventSchema, webhookRedeliverFailedResultSchema, webhookRequestSchema, webhookSchema, webhookSecretRotatedSchema, webhookStatusSchema, webhookTestRequestSchema, webhookTestResultSchema, withdrawCustomerRequestSchema };
3903
+ 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, 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, 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sayren/store-sdk",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "main": "./dist/index.mjs",
5
5
  "types": "./dist/index.d.mts",
6
6
  "exports": {
package/src/client.ts CHANGED
@@ -101,6 +101,7 @@ import {
101
101
  type WebhookRequest,
102
102
  type WebhookTestRequest,
103
103
  webhookCreatedSchema,
104
+ webhookDeliveryDetailSchema,
104
105
  webhookDeliverySchema,
105
106
  webhookRedeliverFailedResultSchema,
106
107
  webhookRequestSchema,
@@ -984,6 +985,17 @@ export function createStoreClient(options: StoreClientOptions) {
984
985
  z.array(webhookDeliverySchema),
985
986
  { query: params as Query },
986
987
  ),
988
+ /**
989
+ * 전송 기록 한 건의 상세. 목록 항목에 보낸 요청 본문(최대 16KB)과 요청 헤더가 더해진다.
990
+ * 실패 원인 분류(`failureReason`)·응답 본문(`responseBodyExcerpt`)으로 원인을 찾고,
991
+ * `resource.id`를 API 로그 조회의 `q`로 넘겨 같은 리소스의 API 요청과 맞춰 본다.
992
+ */
993
+ getDelivery: (webhookId: string, deliveryId: string) =>
994
+ storeHttp.request(
995
+ "GET",
996
+ path`/webhooks/${webhookId}/deliveries/${deliveryId}`,
997
+ webhookDeliveryDetailSchema,
998
+ ),
987
999
  /**
988
1000
  * FAILED 전송 기록 한 건을 같은 eventId로 다시 보낸다(202). 새로 적재된 기록을 돌려주며
989
1001
  * `redeliveryOf`가 원본 deliveryId다. 원본이 FAILED가 아니면 409 `DELIVERY_NOT_FAILED`,
package/src/http.ts CHANGED
@@ -5,6 +5,11 @@ export const errorDetailSchema = z.object({
5
5
  code: z.string(),
6
6
  message: z.string(),
7
7
  traceId: z.string().optional(),
8
+ /**
9
+ * 이 에러 코드를 설명하는 문서 URL(선택). 서버가 코드별 매핑을 가진 코드에만 실린다 —
10
+ * 없으면 필드가 아예 없으므로 소비측은 있을 때만 쓴다
11
+ */
12
+ docs: z.url().optional().catch(undefined),
8
13
  /**
9
14
  * 에러 코드별 보조 데이터(선택). 예: 관리 API `LIVE_GATE_NOT_MET`·`LIVE_PAYMENTS_DISABLED`의
10
15
  * `{ conditions: LiveGateCondition[] }`. 형태는 에러 코드마다 다르므로 소비측이 코드별 스키마로 다시 파싱한다
@@ -50,6 +55,8 @@ export class ApiError extends Error {
50
55
  public readonly body?: unknown,
51
56
  /** 엔벨로프 `$.error.details` — 에러 코드별 보조 데이터. 없으면 undefined */
52
57
  public readonly details?: Record<string, unknown>,
58
+ /** 엔벨로프 `$.error.docs` — 이 에러 코드를 설명하는 문서 URL. 매핑이 없으면 undefined */
59
+ public readonly docs?: string,
53
60
  ) {
54
61
  super(message);
55
62
  this.name = "ApiError";
@@ -68,7 +75,15 @@ export function toApiError(status: number, raw: unknown): ApiError {
68
75
  ? envelope.data.error
69
76
  : (errorDetailSchema.safeParse(raw).data ?? null);
70
77
  if (detail) {
71
- return new ApiError(status, detail.code, detail.message, detail.traceId, raw, detail.details);
78
+ return new ApiError(
79
+ status,
80
+ detail.code,
81
+ detail.message,
82
+ detail.traceId,
83
+ raw,
84
+ detail.details,
85
+ detail.docs,
86
+ );
72
87
  }
73
88
  const oauth = envelope.success ? null : oauthErrorSchema.safeParse(raw).data;
74
89
  if (oauth) {
@@ -68,7 +68,18 @@ export const apiLogListQuerySchema = z.object({
68
68
  ),
69
69
  principalId: z.string().optional().describe("호출 주체 ID 필터"),
70
70
  status: apiLogStatusFilterSchema.optional(),
71
- q: z.string().optional().describe("경로 부분 일치 검색어"),
71
+ q: z
72
+ .string()
73
+ .optional()
74
+ .describe(
75
+ "경로 부분 일치 검색어. 리소스 id(예: `ord_…`)를 넣으면 그 리소스를 건드린 요청만 남으므로 웹훅 전송 기록의 `resource.id`와 짝지어 쓴다",
76
+ ),
77
+ traceId: z
78
+ .string()
79
+ .optional()
80
+ .describe(
81
+ "추적 ID 필터(정확히 일치). 에러 응답 `error.traceId` 값을 그대로 넣어 그 요청 한 건을 찾는다",
82
+ ),
72
83
  cursor: z.string().optional().describe("이전 응답의 `nextCursor`. 생략하면 가장 최신부터"),
73
84
  limit: z.coerce.number().int().min(1).max(100).optional().describe("최대 건수 (1~100, 기본 50)"),
74
85
  });
@@ -208,6 +208,71 @@ export const webhookDeliveryErrorCodeSchema = z
208
208
  );
209
209
  export type WebhookDeliveryErrorCode = z.infer<typeof webhookDeliveryErrorCodeSchema>;
210
210
 
211
+ /**
212
+ * 실패 원인 분류의 **현재** 값. 응답 스키마(`webhookFailureReasonSchema`)는 문자열로 열려 있다 —
213
+ * 서버가 값을 더해도 이전 SDK가 목록 파싱에 실패하지 않게 한다(택배 추적 `stage`와 같은 이유).
214
+ * 모르는 값은 `errorCode`로 판단한다.
215
+ */
216
+ export const WEBHOOK_FAILURE_REASONS = [
217
+ "DNS",
218
+ "TLS",
219
+ "TIMEOUT",
220
+ "CONNECTION",
221
+ "HTTP_4XX",
222
+ "HTTP_5XX",
223
+ "HTTP_OTHER",
224
+ "SSRF_BLOCKED",
225
+ "SUSPENDED",
226
+ ] as const;
227
+ /** 알려진 실패 원인. 응답에는 이 밖의 문자열이 올 수 있다 */
228
+ export type WebhookFailureReason = (typeof WEBHOOK_FAILURE_REASONS)[number];
229
+
230
+ export const webhookFailureReasonSchema = z
231
+ .string()
232
+ .describe(
233
+ "실패 원인 분류(진단용). 현재 값은 `DNS`=호스트 이름을 주소로 바꾸지 못함, `TLS`=인증서·핸드셰이크 실패, " +
234
+ "`TIMEOUT`=10초 안에 응답 없음, `CONNECTION`=연결 거부·끊김, `HTTP_4XX`·`HTTP_5XX`=그 대역의 응답, " +
235
+ "`HTTP_OTHER`=2xx·4xx·5xx가 아닌 응답(리다이렉트 등), `SSRF_BLOCKED`=사설·루프백 등 전송이 허용되지 않는 주소, " +
236
+ "`SUSPENDED`=구독 정지로 대기 중인 전송이 종료됨. `errorCode`보다 잘게 나눈 값이며 둘은 함께 쓸 수 있다. " +
237
+ "값이 추가될 수 있으므로 모르는 값은 `errorCode`로 판단한다",
238
+ );
239
+
240
+ /**
241
+ * 대상 리소스 종류의 **현재** 값. 응답 스키마는 문자열로 열려 있다(새 이벤트가 새 리소스를 가리킬 수 있다).
242
+ */
243
+ export const WEBHOOK_RESOURCE_TYPES = ["ORDER", "CLAIM", "PRODUCT", "REVIEW", "INQUIRY"] as const;
244
+ /** 알려진 리소스 종류. 응답에는 이 밖의 문자열이 올 수 있다 */
245
+ export type WebhookResourceType = (typeof WEBHOOK_RESOURCE_TYPES)[number];
246
+
247
+ export const webhookResourceTypeSchema = z
248
+ .string()
249
+ .describe(
250
+ "이벤트가 가리키는 리소스 종류. 현재 값은 `ORDER`·`CLAIM`·`PRODUCT`·`REVIEW`·`INQUIRY`이고 값이 추가될 수 있다",
251
+ );
252
+
253
+ /** 이벤트가 가리키는 대상 리소스 — 전송 기록을 주문·상품과 잇는 키 */
254
+ export const webhookDeliveryResourceSchema = z.object({
255
+ type: webhookResourceTypeSchema,
256
+ id: z
257
+ .string()
258
+ .describe(
259
+ "리소스 ID. 전송 기록 조회의 `resourceId`, API 로그 조회의 `q`로 그대로 넣을 수 있다",
260
+ ),
261
+ });
262
+ export type WebhookDeliveryResource = z.infer<typeof webhookDeliveryResourceSchema>;
263
+
264
+ /** 발송 요청 헤더 한 줄 (값 마스킹) */
265
+ export const webhookRequestHeaderSchema = z.object({
266
+ name: z.string().describe("헤더 이름"),
267
+ value: z
268
+ .string()
269
+ .nullable()
270
+ .describe(
271
+ "헤더 값. 서명(`Sayren-Signature`)처럼 비밀이 섞이는 헤더는 이름만 주고 값은 null이다",
272
+ ),
273
+ });
274
+ export type WebhookRequestHeader = z.infer<typeof webhookRequestHeaderSchema>;
275
+
211
276
  /** 웹훅 전송 기록 한 건 (전송 기록 조회 항목) */
212
277
  export const webhookDeliverySchema = z.object({
213
278
  deliveryId: z.string().describe("전송 기록 ID. 수신 요청의 `Sayren-Delivery-Id` 헤더 값"),
@@ -232,10 +297,27 @@ export const webhookDeliverySchema = z.object({
232
297
  responseBodyExcerpt: z
233
298
  .string()
234
299
  .nullable()
235
- .describe("마지막 시도의 응답 본문 앞부분(최대 1,024자). 응답이 없으면 null"),
300
+ .describe(
301
+ "마지막 시도의 응답 본문 앞부분(최대 4,096자). 응답이 없으면 null. 가린 뒤에 자른다. " +
302
+ "키 이름에 `authorization`·`token`·`secret`·`password`·`key`·`signature`·`credential`·`cookie`가 " +
303
+ "**들어 있으면**(부분 일치, 대소문자 무시. 예: `apiKey`·`access_token`·`keyword`) 그 값을 `***`로 가린다. " +
304
+ "값이 객체·배열이면 통째로 가린다. 평문 `Authorization: …` 같은 헤더 줄, `Bearer <토큰>`, " +
305
+ "쿼리 형식 `access_token=…`, 끝에서 잘려 닫히지 않은 값도 가린다",
306
+ ),
236
307
  errorCode: webhookDeliveryErrorCodeSchema
237
308
  .nullable()
238
309
  .describe("마지막 시도의 실패 사유. 성공했거나 미시도면 null"),
310
+ failureReason: webhookFailureReasonSchema
311
+ .nullable()
312
+ .describe(
313
+ "마지막 시도의 실패 원인 분류. 성공했거나 미시도면 null. `errorCode`를 더 잘게 나눈 진단 값이다",
314
+ ),
315
+ resource: webhookDeliveryResourceSchema
316
+ .nullable()
317
+ .describe(
318
+ "이벤트가 가리키는 대상 리소스. 리소스를 특정할 수 없는 이벤트면 null. " +
319
+ "이 값으로 전송 기록(`resourceId`)과 API 로그(`q`)를 오갈 수 있다",
320
+ ),
239
321
  attemptCount: z
240
322
  .number()
241
323
  .int()
@@ -260,9 +342,47 @@ export const webhookDeliverySchema = z.object({
260
342
  });
261
343
  export type WebhookDelivery = z.infer<typeof webhookDeliverySchema>;
262
344
 
345
+ /** 발송 본문 최대 노출 길이(바이트). 넘으면 잘라 주고 `payloadTruncated`가 true다 */
346
+ export const WEBHOOK_PAYLOAD_EXCERPT_MAX_BYTES = 16 * 1024;
347
+
348
+ /** 웹훅 전송 기록 상세 — `GET /webhooks/{id}/deliveries/{deliveryId}`. 목록 항목 + 요청 본문·헤더 */
349
+ export const webhookDeliveryDetailSchema = webhookDeliverySchema.extend({
350
+ payload: z
351
+ .string()
352
+ .describe(
353
+ "수신 서버로 보낸 요청 본문 그대로(JSON 문자열). 서명은 이 문자열을 대상으로 계산한다. " +
354
+ `최대 ${WEBHOOK_PAYLOAD_EXCERPT_MAX_BYTES / 1024}KB까지 주고 넘으면 잘라 준다`,
355
+ ),
356
+ payloadTruncated: z
357
+ .boolean()
358
+ .describe("본문이 길어서 잘렸으면 true. true면 `payload`는 유효한 JSON이 아니다"),
359
+ payloadMasked: z
360
+ .boolean()
361
+ .describe(
362
+ "민감 키 마스킹(`responseBodyExcerpt`와 같은 키 이름 규칙)이 본문을 바꿨으면 true. " +
363
+ "true면 `payload`는 보낸 본문과 달라 서명을 맞춰 볼 수 없다. 이벤트 본문에는 보통 민감 키가 없어 false다",
364
+ ),
365
+ requestHeaders: z
366
+ .array(webhookRequestHeaderSchema)
367
+ .describe(
368
+ "발송 요청에 실은 헤더. 값은 전송 시점 그대로이나 서명 헤더는 이름만 주고 값은 null이다. " +
369
+ "`Content-Length`는 실제로 보낸 본문(마스킹·자르기 전)의 바이트 수다. " +
370
+ "매 시도가 같은 헤더 집합을 쓰고 서명·타임스탬프만 달라진다",
371
+ ),
372
+ });
373
+ export type WebhookDeliveryDetail = z.infer<typeof webhookDeliveryDetailSchema>;
374
+
263
375
  /** 전송 기록 조회 쿼리 — `GET /webhooks/{id}/deliveries` */
264
376
  export const webhookDeliveryListQuerySchema = z.object({
265
377
  status: webhookDeliveryStatusSchema.optional().describe("전송 상태 필터. 생략하면 전체"),
378
+ eventType: eventTypeSchema.optional().describe("이벤트 타입 필터. 생략하면 전체"),
379
+ resourceId: z
380
+ .string()
381
+ .optional()
382
+ .describe(
383
+ "대상 리소스 ID 필터(정확히 일치). 주문·클레임·상품·리뷰·문의 id를 넣으면 그 리소스의 이벤트만 남는다. " +
384
+ "같은 id로 API 로그를 `q`로 검색하면 그 리소스를 건드린 API 요청을 볼 수 있다",
385
+ ),
266
386
  limit: z.coerce
267
387
  .number()
268
388
  .int()
@@ -299,6 +419,19 @@ export const webhookTestResultSchema = z.object({
299
419
  .describe("수신 서버의 HTTP 응답 코드. 연결 실패·타임아웃·차단이면 null"),
300
420
  responseTimeMs: z.number().int().nullable().describe("응답 시간(ms). 연결 실패면 null"),
301
421
  errorCode: webhookDeliveryErrorCodeSchema.nullable().describe("실패 사유. 2xx 응답이면 null"),
422
+ failureReason: webhookFailureReasonSchema
423
+ .nullable()
424
+ .describe("실패 원인 분류. 2xx 응답이면 null"),
425
+ responseBodyExcerpt: z
426
+ .string()
427
+ .nullable()
428
+ .describe(
429
+ "응답 본문 앞부분(최대 4,096자). 규칙은 전송 기록의 `responseBodyExcerpt`와 같다. 응답이 없으면 null",
430
+ ),
431
+ requestHeaders: z
432
+ .array(webhookRequestHeaderSchema)
433
+ .describe("보낸 요청 헤더. 서명 헤더는 이름만 주고 값은 null이다"),
434
+ payload: z.string().describe("보낸 요청 본문 그대로(JSON 문자열). 샘플 이벤트다"),
302
435
  });
303
436
  export type WebhookTestResult = z.infer<typeof webhookTestResultSchema>;
304
437