@sayren/store-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +5338 -0
- package/dist/index.mjs +2300 -0
- package/dist/server.d.mts +59 -0
- package/dist/server.mjs +125 -0
- package/dist/webhook-headers-D6QmPdEJ.d.mts +22 -0
- package/dist/webhook-headers-ap_exsb9.mjs +22 -0
- package/package.json +48 -0
- package/src/base64.ts +28 -0
- package/src/client.ts +877 -0
- package/src/http.ts +194 -0
- package/src/index.ts +35 -0
- package/src/oauth.ts +132 -0
- package/src/schemas/api-client.ts +121 -0
- package/src/schemas/api-log.ts +104 -0
- package/src/schemas/auth.ts +81 -0
- package/src/schemas/category.ts +98 -0
- package/src/schemas/claim.ts +89 -0
- package/src/schemas/common.ts +61 -0
- package/src/schemas/misc.ts +298 -0
- package/src/schemas/order.ts +233 -0
- package/src/schemas/payment.ts +582 -0
- package/src/schemas/product.ts +272 -0
- package/src/schemas/scope.ts +75 -0
- package/src/schemas/store.ts +143 -0
- package/src/schemas/upload.ts +23 -0
- package/src/schemas/webhook-event.ts +145 -0
- package/src/server.ts +24 -0
- package/src/session.ts +249 -0
- package/src/webhook-headers.ts +21 -0
- package/src/webhook-signature.ts +177 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const categoryProductSortSchema = z.enum([
|
|
4
|
+
"RECENT",
|
|
5
|
+
"POPULAR",
|
|
6
|
+
"PRICE_LOW",
|
|
7
|
+
"PRICE_HIGH",
|
|
8
|
+
"NAME",
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
export type CategoryProductSort = z.infer<typeof categoryProductSortSchema>;
|
|
12
|
+
|
|
13
|
+
export const categorySchema = z.object({
|
|
14
|
+
categoryId: z.string(),
|
|
15
|
+
storeId: z.string(),
|
|
16
|
+
name: z.string(),
|
|
17
|
+
parentId: z.string().nullable(),
|
|
18
|
+
path: z.string(),
|
|
19
|
+
depth: z.number().int().min(1).max(4),
|
|
20
|
+
leaf: z.boolean(),
|
|
21
|
+
sortOrder: z.number().int(),
|
|
22
|
+
visible: z.boolean(),
|
|
23
|
+
showPc: z.boolean(),
|
|
24
|
+
showMobile: z.boolean(),
|
|
25
|
+
productSort: categoryProductSortSchema,
|
|
26
|
+
metaTitle: z.string().nullable(),
|
|
27
|
+
metaDescription: z.string().nullable(),
|
|
28
|
+
metaKeywords: z.string().nullable(),
|
|
29
|
+
createdAt: z.string(),
|
|
30
|
+
updatedAt: z.string(),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export type Category = z.infer<typeof categorySchema>;
|
|
34
|
+
|
|
35
|
+
export type CategoryTreeNode = Category & { children: CategoryTreeNode[] };
|
|
36
|
+
export const categoryTreeNodeSchema: z.ZodType<CategoryTreeNode> = categorySchema.extend({
|
|
37
|
+
children: z.lazy(() => z.array(categoryTreeNodeSchema)),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export const categoryWithPathSchema = categorySchema.extend({
|
|
41
|
+
fullPath: z.string(),
|
|
42
|
+
ancestors: z.array(categorySchema),
|
|
43
|
+
childCount: z.number().int(),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export type CategoryWithPath = z.infer<typeof categoryWithPathSchema>;
|
|
47
|
+
|
|
48
|
+
export const categorySearchResultSchema = categorySchema.extend({
|
|
49
|
+
fullPath: z.string(),
|
|
50
|
+
ancestors: z.array(categorySchema),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export type CategorySearchResult = z.infer<typeof categorySearchResultSchema>;
|
|
54
|
+
|
|
55
|
+
export const categoryAttributeSchema = z.object({
|
|
56
|
+
attributeId: z.string(),
|
|
57
|
+
name: z.string(),
|
|
58
|
+
required: z.boolean(),
|
|
59
|
+
inputType: z.enum(["SELECT", "MULTI_SELECT", "TEXT", "NUMBER"]),
|
|
60
|
+
unit: z.string().nullable(),
|
|
61
|
+
allowedValues: z.array(z.string()).optional(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export type CategoryAttribute = z.infer<typeof categoryAttributeSchema>;
|
|
65
|
+
|
|
66
|
+
export const categoryCreateRequestSchema = z.object({
|
|
67
|
+
name: z.string().min(1).max(100),
|
|
68
|
+
parentId: z.string().nullable().optional(),
|
|
69
|
+
sortOrder: z.number().int().optional(),
|
|
70
|
+
visible: z.boolean().optional(),
|
|
71
|
+
showPc: z.boolean().optional(),
|
|
72
|
+
showMobile: z.boolean().optional(),
|
|
73
|
+
productSort: categoryProductSortSchema.optional(),
|
|
74
|
+
metaTitle: z.string().max(100).nullable().optional(),
|
|
75
|
+
metaDescription: z.string().max(300).nullable().optional(),
|
|
76
|
+
metaKeywords: z.string().max(200).nullable().optional(),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export type CategoryCreateRequest = z.infer<typeof categoryCreateRequestSchema>;
|
|
80
|
+
|
|
81
|
+
export const categoryPatchRequestSchema = z.object({
|
|
82
|
+
name: z.string().min(1).max(100).optional(),
|
|
83
|
+
parentId: z.string().nullable().optional(),
|
|
84
|
+
sortOrder: z.number().int().optional(),
|
|
85
|
+
visible: z.boolean().optional(),
|
|
86
|
+
showPc: z.boolean().optional(),
|
|
87
|
+
showMobile: z.boolean().optional(),
|
|
88
|
+
productSort: categoryProductSortSchema.optional(),
|
|
89
|
+
metaTitle: z.string().max(100).nullable().optional(),
|
|
90
|
+
metaDescription: z.string().max(300).nullable().optional(),
|
|
91
|
+
metaKeywords: z.string().max(200).nullable().optional(),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export type CategoryPatchRequest = z.infer<typeof categoryPatchRequestSchema>;
|
|
95
|
+
|
|
96
|
+
export const categoryDeleteStrategySchema = z.enum(["reparent", "cascade"]);
|
|
97
|
+
|
|
98
|
+
export type CategoryDeleteStrategy = z.infer<typeof categoryDeleteStrategySchema>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { PageParams } from "./common";
|
|
3
|
+
|
|
4
|
+
export const claimTypeSchema = z.enum(["CANCEL", "RETURN", "EXCHANGE"]);
|
|
5
|
+
|
|
6
|
+
export type ClaimType = z.infer<typeof claimTypeSchema>;
|
|
7
|
+
|
|
8
|
+
export const claimStatusSchema = z.enum([
|
|
9
|
+
"REQUESTED",
|
|
10
|
+
"HELD",
|
|
11
|
+
"COLLECTING",
|
|
12
|
+
"COLLECTED",
|
|
13
|
+
"REDELIVERING",
|
|
14
|
+
"APPROVED",
|
|
15
|
+
"REJECTED",
|
|
16
|
+
"COMPLETED",
|
|
17
|
+
"WITHDRAWN",
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
export type ClaimStatus = z.infer<typeof claimStatusSchema>;
|
|
21
|
+
|
|
22
|
+
export const claimReasonSchema = z.enum([
|
|
23
|
+
"CHANGE_OF_MIND",
|
|
24
|
+
"WRONG_ORDER",
|
|
25
|
+
"DEFECTIVE",
|
|
26
|
+
"WRONG_DELIVERY",
|
|
27
|
+
"DIFFERENT_FROM_PAGE",
|
|
28
|
+
"DELAY",
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
export type ClaimReason = z.infer<typeof claimReasonSchema>;
|
|
32
|
+
|
|
33
|
+
export const claimSchema = z.object({
|
|
34
|
+
claimId: z.string(),
|
|
35
|
+
type: claimTypeSchema,
|
|
36
|
+
status: claimStatusSchema,
|
|
37
|
+
orderId: z.string(),
|
|
38
|
+
orderItemId: z.string(),
|
|
39
|
+
productName: z.string(),
|
|
40
|
+
quantity: z.number().int(),
|
|
41
|
+
reason: claimReasonSchema,
|
|
42
|
+
reasonDetail: z.string().nullable(),
|
|
43
|
+
responsibility: z.enum(["BUYER", "SELLER"]),
|
|
44
|
+
claimDeliveryFee: z.number().int(),
|
|
45
|
+
expectedRefundAmount: z.number().int().nullable(),
|
|
46
|
+
exchangeOption: z.object({ optionId: z.string(), optionName: z.string() }).nullable(),
|
|
47
|
+
collect: z
|
|
48
|
+
.object({
|
|
49
|
+
method: z.enum(["SELLER_PICKUP", "BUYER_SEND"]),
|
|
50
|
+
carrierCode: z.string().nullable(),
|
|
51
|
+
trackingNumber: z.string().nullable(),
|
|
52
|
+
})
|
|
53
|
+
.nullable(),
|
|
54
|
+
requestedAt: z.string(),
|
|
55
|
+
completedAt: z.string().nullable(),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export type Claim = z.infer<typeof claimSchema>;
|
|
59
|
+
|
|
60
|
+
export interface ClaimListParams extends PageParams {
|
|
61
|
+
type?: ClaimType;
|
|
62
|
+
status?: ClaimStatus;
|
|
63
|
+
orderItemId?: string;
|
|
64
|
+
requestedFrom?: string;
|
|
65
|
+
requestedTo?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const rejectClaimRequestSchema = z.object({
|
|
69
|
+
reason: z.enum([
|
|
70
|
+
"USED_PRODUCT",
|
|
71
|
+
"MISSING_COMPONENT",
|
|
72
|
+
"PERIOD_EXPIRED",
|
|
73
|
+
"BUYER_FAULT_DAMAGE",
|
|
74
|
+
"ETC",
|
|
75
|
+
]),
|
|
76
|
+
detail: z.string().min(10).max(1000),
|
|
77
|
+
evidenceImages: z.array(z.url()).max(5).optional(),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
export type RejectClaimRequest = z.infer<typeof rejectClaimRequestSchema>;
|
|
81
|
+
|
|
82
|
+
export const approveClaimResultSchema = z.object({
|
|
83
|
+
claimId: z.string(),
|
|
84
|
+
refundAmount: z.number().int(),
|
|
85
|
+
deductAmount: z.number().int(),
|
|
86
|
+
refundedAt: z.string(),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export type ApproveClaimResult = z.infer<typeof approveClaimResultSchema>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const pageInfoSchema = z.object({
|
|
4
|
+
page: z.number().int(),
|
|
5
|
+
size: z.number().int(),
|
|
6
|
+
totalElements: z.number().int(),
|
|
7
|
+
totalPages: z.number().int(),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export type PageInfo = z.infer<typeof pageInfoSchema>;
|
|
11
|
+
|
|
12
|
+
export function pageSchema<T extends z.ZodType>(item: T) {
|
|
13
|
+
return pageInfoSchema.extend({ contents: z.array(item) });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PageParams {
|
|
17
|
+
page?: number;
|
|
18
|
+
size?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const bulkResultSchema = z.object({
|
|
22
|
+
succeeded: z.array(z.string()),
|
|
23
|
+
failed: z.array(
|
|
24
|
+
z.object({
|
|
25
|
+
id: z.string().optional(),
|
|
26
|
+
productId: z.string().optional(),
|
|
27
|
+
orderItemId: z.string().optional(),
|
|
28
|
+
code: z.string(),
|
|
29
|
+
message: z.string(),
|
|
30
|
+
}),
|
|
31
|
+
),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export type BulkResult = z.infer<typeof bulkResultSchema>;
|
|
35
|
+
|
|
36
|
+
export const orderItemStatusSchema = z.enum([
|
|
37
|
+
"PAYMENT_WAITING",
|
|
38
|
+
"PAID",
|
|
39
|
+
"CONFIRMED",
|
|
40
|
+
"DISPATCHED",
|
|
41
|
+
"DELIVERING",
|
|
42
|
+
"DELIVERED",
|
|
43
|
+
"PURCHASE_DECIDED",
|
|
44
|
+
"CANCELED",
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
export type OrderItemStatus = z.infer<typeof orderItemStatusSchema>;
|
|
48
|
+
|
|
49
|
+
export const changeTypeSchema = z.enum([
|
|
50
|
+
"ORDER.PAID",
|
|
51
|
+
"ORDER.CONFIRMED",
|
|
52
|
+
"ORDER.DISPATCHED",
|
|
53
|
+
"ORDER.DELIVERED",
|
|
54
|
+
"ORDER.PURCHASE_DECIDED",
|
|
55
|
+
"CLAIM.CANCEL_REQUESTED",
|
|
56
|
+
"CLAIM.RETURN_REQUESTED",
|
|
57
|
+
"CLAIM.EXCHANGE_REQUESTED",
|
|
58
|
+
"ORDER.ADDRESS_CHANGE_REQUESTED",
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
export type ChangeType = z.infer<typeof changeTypeSchema>;
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { PageParams } from "./common";
|
|
3
|
+
|
|
4
|
+
export const inquirySchema = z.object({
|
|
5
|
+
inquiryId: z.string(),
|
|
6
|
+
productId: z.string(),
|
|
7
|
+
productName: z.string(),
|
|
8
|
+
orderItemId: z.string().nullable(),
|
|
9
|
+
category: z.enum(["PRODUCT", "DELIVERY", "RETURN", "EXCHANGE", "ETC"]),
|
|
10
|
+
content: z.string(),
|
|
11
|
+
secret: z.boolean(),
|
|
12
|
+
writerMaskedName: z.string(),
|
|
13
|
+
answered: z.boolean(),
|
|
14
|
+
answer: z
|
|
15
|
+
.object({
|
|
16
|
+
content: z.string(),
|
|
17
|
+
answeredAt: z.string(),
|
|
18
|
+
modifiedCount: z.number().int().max(1),
|
|
19
|
+
})
|
|
20
|
+
.nullable(),
|
|
21
|
+
createdAt: z.string(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export type Inquiry = z.infer<typeof inquirySchema>;
|
|
25
|
+
|
|
26
|
+
export const customerInquiryCategorySchema = z.enum([
|
|
27
|
+
"DELIVERY",
|
|
28
|
+
"RETURN",
|
|
29
|
+
"EXCHANGE",
|
|
30
|
+
"PAYMENT",
|
|
31
|
+
"ETC",
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
export type CustomerInquiryCategory = z.infer<typeof customerInquiryCategorySchema>;
|
|
35
|
+
|
|
36
|
+
/** 셀러 뷰 1:1 문의 — 작성자 마스킹명 + 답변 수정횟수 포함 */
|
|
37
|
+
export const sellerCustomerInquirySchema = z.object({
|
|
38
|
+
inquiryId: z.string(),
|
|
39
|
+
memberMaskedName: z.string(),
|
|
40
|
+
category: customerInquiryCategorySchema,
|
|
41
|
+
title: z.string(),
|
|
42
|
+
content: z.string(),
|
|
43
|
+
orderItemId: z.string().nullable(),
|
|
44
|
+
answered: z.boolean(),
|
|
45
|
+
answer: z
|
|
46
|
+
.object({
|
|
47
|
+
content: z.string(),
|
|
48
|
+
answeredAt: z.string(),
|
|
49
|
+
modifiedCount: z.number().int().max(1),
|
|
50
|
+
})
|
|
51
|
+
.nullable(),
|
|
52
|
+
createdAt: z.string(),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export type SellerCustomerInquiry = z.infer<typeof sellerCustomerInquirySchema>;
|
|
56
|
+
|
|
57
|
+
export const settlementStatusSchema = z.enum(["SCHEDULED", "PAID", "HELD"]);
|
|
58
|
+
|
|
59
|
+
export type SettlementStatus = z.infer<typeof settlementStatusSchema>;
|
|
60
|
+
|
|
61
|
+
export const settlementSummarySchema = z.object({
|
|
62
|
+
settlementId: z.string(),
|
|
63
|
+
settlementDate: z.string(),
|
|
64
|
+
payoutDate: z.string().nullable(),
|
|
65
|
+
status: settlementStatusSchema,
|
|
66
|
+
totalPaymentAmount: z.number().int(),
|
|
67
|
+
totalCommission: z.number().int(),
|
|
68
|
+
totalDeliveryFee: z.number().int(),
|
|
69
|
+
adjustmentAmount: z.number().int(),
|
|
70
|
+
settlementAmount: z.number().int(),
|
|
71
|
+
itemCount: z.number().int(),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
export type SettlementSummary = z.infer<typeof settlementSummarySchema>;
|
|
75
|
+
|
|
76
|
+
export const settlementDetailSchema = z.object({
|
|
77
|
+
orderItemId: z.string(),
|
|
78
|
+
productName: z.string(),
|
|
79
|
+
settlementType: z.enum(["SALE", "REFUND_DEDUCTION", "ADJUSTMENT"]),
|
|
80
|
+
paymentAmount: z.number().int(),
|
|
81
|
+
salesCommission: z.number().int(),
|
|
82
|
+
paymentCommission: z.number().int(),
|
|
83
|
+
commissionRate: z.number(),
|
|
84
|
+
deliveryFee: z.number().int(),
|
|
85
|
+
settlementAmount: z.number().int(),
|
|
86
|
+
purchaseDecidedAt: z.string().nullable(),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export type SettlementDetail = z.infer<typeof settlementDetailSchema>;
|
|
90
|
+
|
|
91
|
+
export interface SettlementListParams extends PageParams {
|
|
92
|
+
from: string;
|
|
93
|
+
to?: string;
|
|
94
|
+
status?: SettlementStatus;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const eventTypeSchema = z.enum([
|
|
98
|
+
"ORDER.PAID",
|
|
99
|
+
"ORDER.CONFIRMED",
|
|
100
|
+
"ORDER.DISPATCHED",
|
|
101
|
+
"ORDER.DELIVERED",
|
|
102
|
+
"ORDER.PURCHASE_DECIDED",
|
|
103
|
+
"ORDER.ADDRESS_CHANGE_REQUESTED",
|
|
104
|
+
"CLAIM.CANCEL_REQUESTED",
|
|
105
|
+
"CLAIM.RETURN_REQUESTED",
|
|
106
|
+
"CLAIM.EXCHANGE_REQUESTED",
|
|
107
|
+
"CLAIM.COMPLETED",
|
|
108
|
+
"INQUIRY.CREATED",
|
|
109
|
+
"REVIEW.CREATED",
|
|
110
|
+
"PRODUCT.OUT_OF_STOCK",
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
export type EventType = z.infer<typeof eventTypeSchema>;
|
|
114
|
+
|
|
115
|
+
export const webhookStatusSchema = z
|
|
116
|
+
.enum(["ACTIVE", "SUSPENDED"])
|
|
117
|
+
.describe(
|
|
118
|
+
"구독 상태. `ACTIVE`=발송 대상, `SUSPENDED`=24시간 이상 연속 전송 실패로 발송이 정지된 상태. 정지 중 발생한 이벤트는 적재하지 않는다. 재개(`POST /webhooks/{id}/resume`)하거나 구독을 수정하면 ACTIVE로 돌아간다",
|
|
119
|
+
);
|
|
120
|
+
export type WebhookStatus = z.infer<typeof webhookStatusSchema>;
|
|
121
|
+
|
|
122
|
+
export const webhookSchema = z.object({
|
|
123
|
+
webhookId: z.string(),
|
|
124
|
+
url: z.url(),
|
|
125
|
+
eventTypes: z.array(eventTypeSchema),
|
|
126
|
+
status: webhookStatusSchema,
|
|
127
|
+
secretPrefix: z
|
|
128
|
+
.string()
|
|
129
|
+
.describe("서명 시크릿 앞부분(식별용, 서명 검증에 사용할 수 없음). 예: `whsec_1a2b`"),
|
|
130
|
+
suspendedAt: z.string().nullable().describe("발송이 정지된 일시 (ISO 8601). `ACTIVE`면 null"),
|
|
131
|
+
createdAt: z.string(),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
export type Webhook = z.infer<typeof webhookSchema>;
|
|
135
|
+
|
|
136
|
+
/** 등록 응답 — 서명 시크릿 평문은 이 응답에서만 노출된다 */
|
|
137
|
+
export const webhookCreatedSchema = webhookSchema.extend({
|
|
138
|
+
secret: z.string().describe("서명 시크릿 평문. 등록 응답에서만 노출되며 다시 조회할 수 없다"),
|
|
139
|
+
});
|
|
140
|
+
export type WebhookCreated = z.infer<typeof webhookCreatedSchema>;
|
|
141
|
+
|
|
142
|
+
/** 시크릿 교체 응답 */
|
|
143
|
+
export const webhookSecretRotatedSchema = z.object({
|
|
144
|
+
secret: z.string().describe("새 서명 시크릿 평문. 이 응답에서만 노출된다"),
|
|
145
|
+
oldSecretExpiresAt: z
|
|
146
|
+
.string()
|
|
147
|
+
.describe(
|
|
148
|
+
"기존 시크릿의 병행 유효 종료 일시 (ISO 8601). 이때까지는 기존 시크릿 서명도 함께 붙는다",
|
|
149
|
+
),
|
|
150
|
+
});
|
|
151
|
+
export type WebhookSecretRotated = z.infer<typeof webhookSecretRotatedSchema>;
|
|
152
|
+
|
|
153
|
+
export const webhookDeliveryStatusSchema = z
|
|
154
|
+
.enum(["PENDING", "PENDING_RETRY", "SUCCESS", "FAILED"])
|
|
155
|
+
.describe(
|
|
156
|
+
"전송 상태. `PENDING`=첫 시도 전, `PENDING_RETRY`=실패 후 재시도 대기, `SUCCESS`=2xx 응답 수신, `FAILED`=재시도를 모두 소진했거나 구독 정지로 종료",
|
|
157
|
+
);
|
|
158
|
+
export type WebhookDeliveryStatus = z.infer<typeof webhookDeliveryStatusSchema>;
|
|
159
|
+
|
|
160
|
+
export const webhookDeliveryErrorCodeSchema = z
|
|
161
|
+
.enum(["TIMEOUT", "CONNECTION_FAILED", "BLOCKED_ADDRESS", "NON_2XX", "WEBHOOK_SUSPENDED"])
|
|
162
|
+
.describe(
|
|
163
|
+
"전송 실패 사유. `TIMEOUT`=10초 안에 응답 없음, `CONNECTION_FAILED`=연결 실패(DNS·TLS 포함), `BLOCKED_ADDRESS`=사설·루프백 등 전송이 허용되지 않는 주소, `NON_2XX`=2xx가 아닌 응답(리다이렉트 포함), `WEBHOOK_SUSPENDED`=구독 정지로 대기 중인 전송이 종료됨",
|
|
164
|
+
);
|
|
165
|
+
export type WebhookDeliveryErrorCode = z.infer<typeof webhookDeliveryErrorCodeSchema>;
|
|
166
|
+
|
|
167
|
+
/** 웹훅 전송 기록 한 건 (전송 기록 조회 항목) */
|
|
168
|
+
export const webhookDeliverySchema = z.object({
|
|
169
|
+
deliveryId: z.string().describe("전송 기록 ID. 수신 요청의 `Sayren-Delivery-Id` 헤더 값"),
|
|
170
|
+
eventId: z
|
|
171
|
+
.string()
|
|
172
|
+
.describe(
|
|
173
|
+
"이벤트 ID. 재시도·재전송해도 같은 이벤트면 같은 값이다. 수신 측 중복 제거 키로 쓴다",
|
|
174
|
+
),
|
|
175
|
+
eventType: eventTypeSchema,
|
|
176
|
+
url: z.string().describe("전송 시점의 대상 URL"),
|
|
177
|
+
status: webhookDeliveryStatusSchema,
|
|
178
|
+
responseStatusCode: z
|
|
179
|
+
.number()
|
|
180
|
+
.int()
|
|
181
|
+
.nullable()
|
|
182
|
+
.describe("마지막 시도의 HTTP 응답 코드. 미시도·연결 실패·타임아웃·차단이면 null"),
|
|
183
|
+
responseTimeMs: z
|
|
184
|
+
.number()
|
|
185
|
+
.int()
|
|
186
|
+
.nullable()
|
|
187
|
+
.describe("마지막 시도의 응답 시간(ms). 미시도·연결 실패면 null"),
|
|
188
|
+
responseBodyExcerpt: z
|
|
189
|
+
.string()
|
|
190
|
+
.nullable()
|
|
191
|
+
.describe("마지막 시도의 응답 본문 앞부분(최대 1,024자). 응답이 없으면 null"),
|
|
192
|
+
errorCode: webhookDeliveryErrorCodeSchema
|
|
193
|
+
.nullable()
|
|
194
|
+
.describe("마지막 시도의 실패 사유. 성공했거나 미시도면 null"),
|
|
195
|
+
attemptCount: z
|
|
196
|
+
.number()
|
|
197
|
+
.int()
|
|
198
|
+
.min(0)
|
|
199
|
+
.describe("지금까지의 시도 횟수. 최초 시도 1회 + 재시도 최대 4회"),
|
|
200
|
+
createdAt: z.string().describe("전송 기록이 적재된 일시 (ISO 8601)"),
|
|
201
|
+
attemptedAt: z.string().nullable().describe("마지막 시도 일시 (ISO 8601). 미시도면 null"),
|
|
202
|
+
nextAttemptAt: z
|
|
203
|
+
.string()
|
|
204
|
+
.nullable()
|
|
205
|
+
.describe("다음 시도 예정 일시 (ISO 8601). `PENDING`·`PENDING_RETRY`일 때만 값이 있다"),
|
|
206
|
+
redeliveryOf: z
|
|
207
|
+
.string()
|
|
208
|
+
.nullable()
|
|
209
|
+
.describe("수동 재전송으로 생긴 기록이면 원본 전송 기록 ID. 아니면 null"),
|
|
210
|
+
redeliveredAs: z
|
|
211
|
+
.string()
|
|
212
|
+
.nullable()
|
|
213
|
+
.describe(
|
|
214
|
+
"이 기록을 원본으로 수동 재전송해 생긴 기록의 전송 기록 ID. 아직 재전송하지 않았으면 null. 재전송은 원본당 1회다",
|
|
215
|
+
),
|
|
216
|
+
});
|
|
217
|
+
export type WebhookDelivery = z.infer<typeof webhookDeliverySchema>;
|
|
218
|
+
|
|
219
|
+
/** 전송 기록 조회 쿼리 — `GET /webhooks/{id}/deliveries` */
|
|
220
|
+
export const webhookDeliveryListQuerySchema = z.object({
|
|
221
|
+
status: webhookDeliveryStatusSchema.optional().describe("전송 상태 필터. 생략하면 전체"),
|
|
222
|
+
limit: z.coerce
|
|
223
|
+
.number()
|
|
224
|
+
.int()
|
|
225
|
+
.min(1)
|
|
226
|
+
.max(100)
|
|
227
|
+
.optional()
|
|
228
|
+
.describe("최대 건수 (1~100, 기본 50). 최신 적재순으로 반환하며 보관 기간은 30일이다"),
|
|
229
|
+
});
|
|
230
|
+
export type WebhookDeliveryListParams = z.infer<typeof webhookDeliveryListQuerySchema>;
|
|
231
|
+
|
|
232
|
+
/** 실패 건 일괄 재전송 응답 — `POST /webhooks/{id}/deliveries/redeliver-failed` */
|
|
233
|
+
export const webhookRedeliverFailedResultSchema = z.object({
|
|
234
|
+
queued: z
|
|
235
|
+
.number()
|
|
236
|
+
.int()
|
|
237
|
+
.min(0)
|
|
238
|
+
.describe(
|
|
239
|
+
"재전송 대기열에 올린 건수. 최근 72시간의 `FAILED` 중 아직 재전송되지 않은 건이 대상이며 최대 500건이다",
|
|
240
|
+
),
|
|
241
|
+
});
|
|
242
|
+
export type WebhookRedeliverFailedResult = z.infer<typeof webhookRedeliverFailedResultSchema>;
|
|
243
|
+
|
|
244
|
+
export const webhookTestRequestSchema = z.object({
|
|
245
|
+
eventType: eventTypeSchema.describe("샘플 페이로드로 보낼 이벤트 타입"),
|
|
246
|
+
});
|
|
247
|
+
export type WebhookTestRequest = z.infer<typeof webhookTestRequestSchema>;
|
|
248
|
+
|
|
249
|
+
/** 테스트 이벤트 발송 결과 (동기 반환). 재시도·정지 판정·전송 기록이 없다 */
|
|
250
|
+
export const webhookTestResultSchema = z.object({
|
|
251
|
+
responseStatusCode: z
|
|
252
|
+
.number()
|
|
253
|
+
.int()
|
|
254
|
+
.nullable()
|
|
255
|
+
.describe("수신 서버의 HTTP 응답 코드. 연결 실패·타임아웃·차단이면 null"),
|
|
256
|
+
responseTimeMs: z.number().int().nullable().describe("응답 시간(ms). 연결 실패면 null"),
|
|
257
|
+
errorCode: webhookDeliveryErrorCodeSchema.nullable().describe("실패 사유. 2xx 응답이면 null"),
|
|
258
|
+
});
|
|
259
|
+
export type WebhookTestResult = z.infer<typeof webhookTestResultSchema>;
|
|
260
|
+
|
|
261
|
+
export const webhookRequestSchema = z.object({
|
|
262
|
+
url: z.url().startsWith("https://", "웹훅 URL은 HTTPS만 허용됩니다"),
|
|
263
|
+
eventTypes: z.array(eventTypeSchema).min(1),
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
export type WebhookRequest = z.infer<typeof webhookRequestSchema>;
|
|
267
|
+
|
|
268
|
+
export const reviewSchema = z.object({
|
|
269
|
+
reviewId: z.string(),
|
|
270
|
+
productId: z.string(),
|
|
271
|
+
productName: z.string(),
|
|
272
|
+
orderItemId: z.string(),
|
|
273
|
+
optionName: z.string().nullable(),
|
|
274
|
+
rating: z.number().int().min(1).max(5),
|
|
275
|
+
content: z.string(),
|
|
276
|
+
images: z.array(z.url()),
|
|
277
|
+
writerMaskedName: z.string(),
|
|
278
|
+
blinded: z.boolean(),
|
|
279
|
+
reply: z
|
|
280
|
+
.object({
|
|
281
|
+
content: z.string(),
|
|
282
|
+
repliedAt: z.string(),
|
|
283
|
+
updatedAt: z.string().nullable(),
|
|
284
|
+
})
|
|
285
|
+
.nullable(),
|
|
286
|
+
createdAt: z.string(),
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
export type Review = z.infer<typeof reviewSchema>;
|
|
290
|
+
|
|
291
|
+
export interface ReviewListParams extends PageParams {
|
|
292
|
+
productId?: string;
|
|
293
|
+
rating?: number;
|
|
294
|
+
replied?: boolean;
|
|
295
|
+
hasImage?: boolean;
|
|
296
|
+
from?: string;
|
|
297
|
+
sort?: "createdAt.desc" | "createdAt.asc" | "rating.asc" | "rating.desc";
|
|
298
|
+
}
|