@sayren/store-sdk 0.5.0 → 0.7.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 +296 -16
- package/dist/index.mjs +138 -29
- package/dist/server.d.mts +45 -1
- package/dist/server.mjs +56 -1
- package/package.json +1 -1
- package/src/client.ts +42 -3
- package/src/member-delegation.ts +98 -0
- package/src/schemas/analytics.ts +146 -1
- package/src/schemas/claim.ts +10 -2
- package/src/schemas/customer.ts +1 -1
- package/src/schemas/login.ts +1 -1
- package/src/schemas/product.ts +21 -10
- package/src/schemas/store.ts +6 -6
- package/src/server.ts +6 -0
package/dist/index.mjs
CHANGED
|
@@ -150,7 +150,7 @@ function createHttp(options) {
|
|
|
150
150
|
/**
|
|
151
151
|
* 방문 분석 조회 (관리 API `/v1/analytics/*`, 스코프 `analytics:r`).
|
|
152
152
|
*
|
|
153
|
-
*
|
|
153
|
+
* 스토어프론트가 `@sayren/storefront-sdk`로 수집한 이벤트를 서버가 날짜(Asia/Seoul)별로 집계한 값이다. 집계는 주기적으로
|
|
154
154
|
* 갱신되므로 오늘 값은 `lastComputedAt` 시점까지 반영돼 있다. 봇·자동화 브라우저의 세션은 모든 집계에서 빠진다.
|
|
155
155
|
*/
|
|
156
156
|
const reportDate$1 = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "날짜 형식은 YYYY-MM-DD 입니다");
|
|
@@ -204,7 +204,8 @@ const analyticsOverviewSchema = z.object({
|
|
|
204
204
|
...rangeMeta,
|
|
205
205
|
summary: analyticsTotalsSchema.describe("기간 합계"),
|
|
206
206
|
funnel: z.array(analyticsFunnelStepSchema).describe("세션 기준 구매 퍼널. 각 단계는 그 행동을 한 세션 수이고, 앞 단계를 거쳤는지는 따지지 않는다"),
|
|
207
|
-
rows: z.array(analyticsTotalsSchema.extend({ date: z.string().describe("일자 (YYYY-MM-DD)") })).describe("날짜별 값. 세션이 없는 날은 생략된다")
|
|
207
|
+
rows: z.array(analyticsTotalsSchema.extend({ date: z.string().describe("일자 (YYYY-MM-DD)") })).describe("날짜별 값. 세션이 없는 날은 생략된다"),
|
|
208
|
+
excludedBotSessions: z.number().int().describe("집계에서 뺀 봇 세션 수 — 봇 표시 이벤트가 하나라도 있는 세션은 모든 값에서 빠진다")
|
|
208
209
|
});
|
|
209
210
|
const analyticsProductSortSchema = z.enum([
|
|
210
211
|
"VIEWS",
|
|
@@ -227,7 +228,8 @@ const analyticsProductRowSchema = z.object({
|
|
|
227
228
|
purchases: z.number().int().describe("이 상품이 포함된 구매(주문) 수"),
|
|
228
229
|
quantity: z.number().int().describe("구매 수량"),
|
|
229
230
|
revenue: z.number().int().describe("이 상품의 구매 금액 — 주문 상품 금액(할인 반영, 배송비 제외) 합계. 이후 취소·환불은 빼지 않는다"),
|
|
230
|
-
purchaseRate: rate("구매율 = 구매 / 조회 세션. 조회 세션이 없으면 null")
|
|
231
|
+
purchaseRate: rate("구매율 = 구매 / 조회 세션. 조회 세션이 없으면 null"),
|
|
232
|
+
clickThroughRate: rate("목록 → 상세 전환율 = 상세 조회 / 목록 노출. 노출이 없으면 null(검색·외부 링크로 바로 들어온 조회는 노출 없이 잡힌다)")
|
|
231
233
|
});
|
|
232
234
|
const analyticsProductsSchema = z.object({
|
|
233
235
|
...rangeMeta,
|
|
@@ -275,6 +277,94 @@ const analyticsSourcesSchema = z.object({
|
|
|
275
277
|
groupBy: analyticsSourceGroupBySchema,
|
|
276
278
|
rows: z.array(analyticsSourceRowSchema).describe("세션 수 내림차순")
|
|
277
279
|
});
|
|
280
|
+
const analyticsRealtimeSchema = z.object({
|
|
281
|
+
asOf: z.string().describe("기준 시각(ISO 8601)"),
|
|
282
|
+
windowMinutes: z.literal(5).describe("최근 몇 분 동안 활동한 방문을 세는가"),
|
|
283
|
+
visitors: z.number().int().describe("최근 5분 안에 이벤트가 있는 방문자 수(봇 제외)"),
|
|
284
|
+
sessions: z.number().int(),
|
|
285
|
+
pageViews: z.number().int(),
|
|
286
|
+
pages: z.array(z.object({
|
|
287
|
+
path: z.string(),
|
|
288
|
+
visitors: z.number().int(),
|
|
289
|
+
pageViews: z.number().int()
|
|
290
|
+
})).describe("최근 5분 페이지뷰가 있는 경로. 방문자 수 내림차순 최대 10개")
|
|
291
|
+
});
|
|
292
|
+
const analyticsDeviceRowSchema = z.object({
|
|
293
|
+
device: z.enum([
|
|
294
|
+
"desktop",
|
|
295
|
+
"mobile",
|
|
296
|
+
"tablet",
|
|
297
|
+
"unknown"
|
|
298
|
+
]),
|
|
299
|
+
sessions: z.number().int(),
|
|
300
|
+
productViewSessions: z.number().int(),
|
|
301
|
+
addToCartSessions: z.number().int(),
|
|
302
|
+
beginCheckoutSessions: z.number().int(),
|
|
303
|
+
purchaseSessions: z.number().int(),
|
|
304
|
+
conversionRate: rate("전환율 = 구매 세션 / 세션")
|
|
305
|
+
});
|
|
306
|
+
const analyticsDevicesSchema = z.object({
|
|
307
|
+
...rangeMeta,
|
|
308
|
+
rows: z.array(analyticsDeviceRowSchema).describe("기기별 세션 기준 퍼널. 세션 수 내림차순")
|
|
309
|
+
});
|
|
310
|
+
const limitParam = z.coerce.number().int().min(1).max(100).optional().describe("최대 행 수. 기본 20, 최대 100");
|
|
311
|
+
const analyticsPagesQuerySchema = analyticsRangeQuerySchema.extend({ limit: limitParam });
|
|
312
|
+
const analyticsPagesSchema = z.object({
|
|
313
|
+
...rangeMeta,
|
|
314
|
+
landings: z.array(z.object({
|
|
315
|
+
path: z.string(),
|
|
316
|
+
entrances: z.number().int().describe("이 페이지로 시작한 세션 수"),
|
|
317
|
+
bounces: z.number().int().describe("이 페이지로 시작해 참여하지 않고 끝난 세션 수"),
|
|
318
|
+
bounceRate: rate("반송률 = 반송 / 진입")
|
|
319
|
+
})).describe("첫 페이지. 진입 수 내림차순"),
|
|
320
|
+
exits: z.array(z.object({
|
|
321
|
+
path: z.string(),
|
|
322
|
+
pageViews: z.number().int(),
|
|
323
|
+
exits: z.number().int().describe("이 페이지가 세션의 마지막 페이지뷰였던 수"),
|
|
324
|
+
exitRate: rate("이탈률 = 이탈 / 페이지뷰")
|
|
325
|
+
})).describe("이탈 페이지. 이탈 수 내림차순")
|
|
326
|
+
});
|
|
327
|
+
const analyticsSearchQuerySchema = analyticsRangeQuerySchema.extend({ limit: limitParam });
|
|
328
|
+
const analyticsSearchSchema = z.object({
|
|
329
|
+
...rangeMeta,
|
|
330
|
+
summary: z.object({
|
|
331
|
+
searches: z.number().int(),
|
|
332
|
+
zeroResultSearches: z.number().int().describe("결과가 0개였던 검색 수"),
|
|
333
|
+
searchSessions: z.number().int().describe("검색한 세션 수(세션당 한 번)"),
|
|
334
|
+
purchaseSessions: z.number().int().describe("검색한 뒤 같은 날 구매한 세션 수(세션당 한 번). 검색 전에 끝난 구매는 세지 않는다"),
|
|
335
|
+
conversionRate: rate("검색 후 구매율 = 구매 세션 / 검색 세션")
|
|
336
|
+
}),
|
|
337
|
+
terms: z.array(z.object({
|
|
338
|
+
term: z.string().describe("검색어 — 소문자, 공백 정리"),
|
|
339
|
+
searches: z.number().int(),
|
|
340
|
+
zeroResultSearches: z.number().int(),
|
|
341
|
+
searchSessions: z.number().int().describe("이 검색어로 검색한 세션 수"),
|
|
342
|
+
purchaseSessions: z.number().int().describe("이 검색어로 검색한 뒤 같은 날 구매한 세션 수"),
|
|
343
|
+
conversionRate: rate("검색 후 구매율 = 구매 세션 / 검색 세션")
|
|
344
|
+
})).describe("검색 수 내림차순. 세션 수는 검색어마다 세므로 여러 검색어를 쓴 세션은 검색어마다 잡힌다(더하면 요약보다 크다)"),
|
|
345
|
+
zeroResultTerms: z.array(z.object({
|
|
346
|
+
term: z.string(),
|
|
347
|
+
searches: z.number().int(),
|
|
348
|
+
zeroResultSearches: z.number().int()
|
|
349
|
+
})).describe("결과가 0개였던 적이 있는 검색어. 결과 없음 수 내림차순 — 검색 수 상위(`terms`)에 들지 않는 드문 검색어도 여기 잡힌다")
|
|
350
|
+
});
|
|
351
|
+
const analyticsCheckoutOutcomeSchema = z.enum([
|
|
352
|
+
"COMPLETED",
|
|
353
|
+
"NOT_OPENED",
|
|
354
|
+
"USER_CANCELED",
|
|
355
|
+
"DECLINED",
|
|
356
|
+
"PREPARE_FAILURE",
|
|
357
|
+
"OTHER",
|
|
358
|
+
"IN_PROGRESS"
|
|
359
|
+
]).describe("결제 세션의 최종 결과 — `COMPLETED`(결제 완료), `NOT_OPENED`(결제창을 열지 않고 끝남), `USER_CANCELED`(결제창에서 취소), `DECLINED`(카드 거절 등 승인 거절), `PREPARE_FAILURE`(결제창을 띄우기 전 장애), `OTHER`(그 밖의 이탈·만료), `IN_PROGRESS`(아직 진행 중 — 만료 전 대기·승인 중). 실패 분류는 세션의 마지막 결제 시도 기준이다");
|
|
360
|
+
const analyticsCheckoutSchema = z.object({
|
|
361
|
+
...rangeMeta,
|
|
362
|
+
sessions: z.number().int().describe("기간 안에 시작한 결제 세션 수(결제 요청 단위)"),
|
|
363
|
+
outcomes: z.array(z.object({
|
|
364
|
+
outcome: analyticsCheckoutOutcomeSchema,
|
|
365
|
+
sessions: z.number().int()
|
|
366
|
+
})).describe("결과별 결제 세션 수. 결과마다 한 행")
|
|
367
|
+
});
|
|
278
368
|
//#endregion
|
|
279
369
|
//#region src/schemas/api-client.ts
|
|
280
370
|
/**
|
|
@@ -654,12 +744,16 @@ const claimSchema = z.object({
|
|
|
654
744
|
trackingNumber: z.string().nullable()
|
|
655
745
|
}).nullable(),
|
|
656
746
|
requestedAt: z.string(),
|
|
657
|
-
completedAt: z.string().nullable()
|
|
747
|
+
completedAt: z.string().nullable(),
|
|
748
|
+
/** 보류 중이면 자동 해제 시각(ISO 8601), 아니면 null */
|
|
749
|
+
holdExpiresAt: z.string().nullable(),
|
|
750
|
+
/** 보류를 이미 썼는지 — 보류는 클레임당 1회라 해제·만료 뒤에도 true */
|
|
751
|
+
holdUsed: z.boolean()
|
|
658
752
|
});
|
|
659
753
|
/** `GET /v1/claims` 쿼리 — OpenAPI 문서용 형태(값은 쿼리 문자열) */
|
|
660
754
|
const claimListQuerySchema = z.object({
|
|
661
755
|
type: claimTypeSchema.optional().describe("클레임 유형 필터"),
|
|
662
|
-
status:
|
|
756
|
+
status: z.string().optional().describe("클레임 상태 필터. 쉼표로 여러 개를 줄 수 있다 (예: `REQUESTED,HELD,COLLECTED`)"),
|
|
663
757
|
orderItemId: z.string().optional().describe("주문상품 id 필터"),
|
|
664
758
|
requestedFrom: z.string().optional().describe("요청 시각 조회 시작 (ISO 8601, 포함)"),
|
|
665
759
|
requestedTo: z.string().optional().describe("요청 시각 조회 끝 (ISO 8601, 포함)"),
|
|
@@ -798,7 +892,7 @@ const withdrawCustomerRequestSchema = z.object({
|
|
|
798
892
|
]).describe("처리 사유 — 구매자 요청·장기 미이용·부정 이용·기타. 감사 로그에 남는다"),
|
|
799
893
|
memo: z.string().max(200).optional().describe("사유 보충(선택)")
|
|
800
894
|
});
|
|
801
|
-
/** `WITHDRAWAL_BLOCKED`의 `error.details` — 탈퇴를 막는 항목 수(
|
|
895
|
+
/** `WITHDRAWAL_BLOCKED`의 `error.details` — 탈퇴를 막는 항목 수(스토어프론트 탈퇴와 같다) */
|
|
802
896
|
const customerWithdrawalBlockedDetailsSchema = z.object({
|
|
803
897
|
inProgressOrderItems: z.number().int(),
|
|
804
898
|
openClaims: z.number().int(),
|
|
@@ -816,7 +910,7 @@ const loginProviderSchema = z.enum([
|
|
|
816
910
|
const loginProviderStatusSchema = z.object({
|
|
817
911
|
provider: loginProviderSchema,
|
|
818
912
|
connected: z.boolean(),
|
|
819
|
-
/**
|
|
913
|
+
/** 스토어프론트에서 사용 중인지. 연결 안 됐으면 false */
|
|
820
914
|
enabled: z.boolean(),
|
|
821
915
|
clientIdPrefix: z.string().nullable().describe("클라이언트 ID(카카오 REST API 키) 앞 4자리"),
|
|
822
916
|
secretPrefix: z.string().nullable(),
|
|
@@ -1600,10 +1694,10 @@ const productSummarySchema = z.object({
|
|
|
1600
1694
|
productId: z.string(),
|
|
1601
1695
|
name: z.string(),
|
|
1602
1696
|
status: productStatusSchema,
|
|
1603
|
-
salePrice: z.number().int(),
|
|
1604
|
-
discountedPrice: z.number().int().nullable(),
|
|
1697
|
+
salePrice: z.number().int().describe("판매가. 원(KRW) 단위 정수"),
|
|
1698
|
+
discountedPrice: z.number().int().nullable().describe("즉시 할인 적용가. 원 단위. 할인이 없으면 null"),
|
|
1605
1699
|
/** usable variant 재고 합계 (서버 계산) */
|
|
1606
|
-
stockQuantity: z.number().int(),
|
|
1700
|
+
stockQuantity: z.number().int().describe("판매 가능한 옵션 재고의 합계. 서버가 계산한다"),
|
|
1607
1701
|
categoryId: z.string().nullable(),
|
|
1608
1702
|
thumbnailUrl: z.url().optional(),
|
|
1609
1703
|
/** optionGroups.length > 0 */
|
|
@@ -1740,8 +1834,8 @@ const variantStockSchema = z.object({
|
|
|
1740
1834
|
const productCreateRequestSchema = z.object({
|
|
1741
1835
|
name: z.string().min(1).max(100),
|
|
1742
1836
|
categoryId: z.string().min(1, "카테고리를 선택해주세요"),
|
|
1743
|
-
salePrice: z.number().int().min(10).multipleOf(10, "판매가는 10원 단위여야 합니다 (INVALID_PRICE_UNIT)"),
|
|
1744
|
-
originalPrice: z.number().int().optional(),
|
|
1837
|
+
salePrice: z.number().int().min(10).multipleOf(10, "판매가는 10원 단위여야 합니다 (INVALID_PRICE_UNIT)").describe("판매가. 원(KRW) 단위 정수, 최소 10원, 10원 단위"),
|
|
1838
|
+
originalPrice: z.number().int().optional().describe("정가(할인 전 표시 가격). 원 단위"),
|
|
1745
1839
|
description: z.string().optional(),
|
|
1746
1840
|
/**
|
|
1747
1841
|
* 상세설명 에디터(@avarlabs/editor)의 직렬화 상태. 재편집 때 이 값으로 문서를 복원한다.
|
|
@@ -1753,11 +1847,11 @@ const productCreateRequestSchema = z.object({
|
|
|
1753
1847
|
attributeId: z.string(),
|
|
1754
1848
|
value: z.string()
|
|
1755
1849
|
})).optional(),
|
|
1756
|
-
deliveryType: deliveryTypeSchema.default("PAID"),
|
|
1757
|
-
deliveryFee: z.number().int().default(0),
|
|
1758
|
-
conditionalFreeAmount: z.number().int().optional(),
|
|
1759
|
-
returnFee: z.number().int().default(0),
|
|
1760
|
-
exchangeFee: z.number().int().default(0),
|
|
1850
|
+
deliveryType: deliveryTypeSchema.default("PAID").describe("배송비 유형. FREE 무료, PAID 유료, CONDITIONAL_FREE 조건부 무료"),
|
|
1851
|
+
deliveryFee: z.number().int().default(0).describe("배송비. 원 단위"),
|
|
1852
|
+
conditionalFreeAmount: z.number().int().optional().describe("CONDITIONAL_FREE일 때 무료 배송이 되는 주문 금액. 원 단위"),
|
|
1853
|
+
returnFee: z.number().int().default(0).describe("반품 배송비. 원 단위"),
|
|
1854
|
+
exchangeFee: z.number().int().default(0).describe("교환 배송비. 원 단위"),
|
|
1761
1855
|
shippingAddressId: z.string().optional(),
|
|
1762
1856
|
returnAddressId: z.string().optional()
|
|
1763
1857
|
}).extend(optionModelRequestSchema.shape);
|
|
@@ -1873,8 +1967,8 @@ function isWebOrigin(value) {
|
|
|
1873
1967
|
return false;
|
|
1874
1968
|
}
|
|
1875
1969
|
}
|
|
1876
|
-
/** 방문 분석을 보낼 수 있는
|
|
1877
|
-
const analyticsOriginSchema = z.string().trim().max(255).refine(isWebOrigin, "https://shop.example.com 형식(경로 없이)으로 입력해 주세요").describe("
|
|
1970
|
+
/** 방문 분석을 보낼 수 있는 스토어프론트 오리진 — 스킴·호스트(·포트)만. 서버가 소문자·끝 슬래시 제거로 정규화한다 */
|
|
1971
|
+
const analyticsOriginSchema = z.string().trim().max(255).refine(isWebOrigin, "https://shop.example.com 형식(경로 없이)으로 입력해 주세요").describe("스토어프론트 오리진. 예: `https://shop.example.com`");
|
|
1878
1972
|
/** 허용 오리진 상한 */
|
|
1879
1973
|
const MAX_ANALYTICS_ALLOWED_ORIGINS = 20;
|
|
1880
1974
|
/** `PATCH /v1/store` 본문 — 바꿀 필드만 보낸다 */
|
|
@@ -1883,8 +1977,8 @@ const updateStoreRequestSchema = z.object({
|
|
|
1883
1977
|
logoUrl: z.url().optional(),
|
|
1884
1978
|
customerCenterPhone: z.string().optional(),
|
|
1885
1979
|
businessHours: z.string().max(100).optional(),
|
|
1886
|
-
analyticsAllowedOrigins: z.array(analyticsOriginSchema).max(20).optional().describe("@deprecated `storefrontOrigins`를 쓴다(같은 목록). 방문 분석 수집을 받을
|
|
1887
|
-
storefrontOrigins: z.array(analyticsOriginSchema).max(20).optional().describe("
|
|
1980
|
+
analyticsAllowedOrigins: z.array(analyticsOriginSchema).max(20).optional().describe("@deprecated `storefrontOrigins`를 쓴다(같은 목록). 방문 분석 수집을 받을 스토어프론트 오리진(최대 20개). 비우면 모든 오리진에서 받는다. 넣으면 목록에 없는 오리진의 수집 요청은 403 `ORIGIN_NOT_ALLOWED`이고, 페이지 URL이 목록 밖인 이벤트는 버린다"),
|
|
1981
|
+
storefrontOrigins: z.array(analyticsOriginSchema).max(20).optional().describe("스토어프론트 도메인(최대 20개) — 소셜 로그인 뒤 돌아갈 주소와 방문 분석 수집을 함께 제한한다. `analyticsAllowedOrigins`와 같은 목록이다(둘 다 주면 이 값이 이긴다)"),
|
|
1888
1982
|
guestCheckoutEnabled: z.boolean().optional().describe("비회원 주문 허용. false(회원만)면 로그인하지 않은 구매자의 주문서 생성·결제 요청이 403 `GUEST_CHECKOUT_DISABLED`다. 장바구니 담기와 이미 받은 비회원 주문의 조회·취소는 막지 않는다")
|
|
1889
1983
|
}).refine((value) => Object.values(value).some((v) => v !== void 0), { message: "최소 1개 필드가 필요합니다" });
|
|
1890
1984
|
const storeSchema = z.object({
|
|
@@ -1900,8 +1994,8 @@ const storeSchema = z.object({
|
|
|
1900
1994
|
logoUrl: z.url().nullable(),
|
|
1901
1995
|
businessNumber: z.string().nullable(),
|
|
1902
1996
|
customerCenterPhone: z.string().nullable(),
|
|
1903
|
-
analyticsAllowedOrigins: z.array(z.string()).describe("방문 분석 수집을 받는
|
|
1904
|
-
storefrontOrigins: z.array(z.string()).describe("
|
|
1997
|
+
analyticsAllowedOrigins: z.array(z.string()).describe("방문 분석 수집을 받는 스토어프론트 오리진. 비어 있으면 모든 오리진에서 받는다"),
|
|
1998
|
+
storefrontOrigins: z.array(z.string()).describe("스토어프론트 도메인 — 소셜 로그인 리디렉트와 방문 분석 수집이 함께 쓴다(`analyticsAllowedOrigins`와 같은 값)"),
|
|
1905
1999
|
guestCheckoutEnabled: z.boolean().describe("비회원 주문 허용. false면 회원만 주문할 수 있다"),
|
|
1906
2000
|
memberCount: z.number().int(),
|
|
1907
2001
|
createdAt: z.string()
|
|
@@ -2188,7 +2282,10 @@ function createStoreClient(options) {
|
|
|
2188
2282
|
},
|
|
2189
2283
|
carriers: { list: () => storeHttp.request("GET", "/carriers", z.array(carrierSchema)) },
|
|
2190
2284
|
claims: {
|
|
2191
|
-
list: (params) => storeHttp.request("GET", "/claims", pageSchema(claimSchema), { query:
|
|
2285
|
+
list: (params) => storeHttp.request("GET", "/claims", pageSchema(claimSchema), { query: {
|
|
2286
|
+
...params,
|
|
2287
|
+
status: Array.isArray(params?.status) ? params.status.join(",") : params?.status
|
|
2288
|
+
} }),
|
|
2192
2289
|
get: (claimId) => storeHttp.request("GET", path`/claims/${claimId}`, claimDetailSchema),
|
|
2193
2290
|
approve: (claimId, deductAmount, idempotencyKey) => storeHttp.request("POST", path`/claims/${claimId}/approve`, approveClaimResultSchema, {
|
|
2194
2291
|
body: deductAmount !== void 0 ? { deductAmount } : {},
|
|
@@ -2202,7 +2299,9 @@ function createStoreClient(options) {
|
|
|
2202
2299
|
trackingNumber
|
|
2203
2300
|
} }),
|
|
2204
2301
|
hold: (claimId, reason) => storeHttp.request("POST", path`/claims/${claimId}/hold`, claimHoldResultSchema, { body: { reason } }),
|
|
2205
|
-
releaseHold: (claimId) => storeHttp.requestVoid("DELETE", path`/claims/${claimId}/hold`)
|
|
2302
|
+
releaseHold: (claimId) => storeHttp.requestVoid("DELETE", path`/claims/${claimId}/hold`),
|
|
2303
|
+
/** 교환 완료 — 재배송 중인 교환을 셀러가 명시적으로 끝낸다 */
|
|
2304
|
+
completeExchange: (claimId) => storeHttp.request("POST", path`/claims/${claimId}/complete-exchange`, claimSchema)
|
|
2206
2305
|
},
|
|
2207
2306
|
inquiries: {
|
|
2208
2307
|
list: (params) => storeHttp.request("GET", "/inquiries", pageSchema(inquirySchema), { query: params }),
|
|
@@ -2258,7 +2357,7 @@ function createStoreClient(options) {
|
|
|
2258
2357
|
*/
|
|
2259
2358
|
get: (params) => storeHttp.request("GET", "/sales-report", salesReportSchema, { query: salesReportQuerySchema.parse(params) }) },
|
|
2260
2359
|
/**
|
|
2261
|
-
* 방문 분석 — 스코프 `analytics:r`.
|
|
2360
|
+
* 방문 분석 — 스코프 `analytics:r`. 스토어프론트가 `@sayren/storefront-sdk`로 수집한 이벤트를 Asia/Seoul 날짜별로
|
|
2262
2361
|
* 집계한 값이다. 집계는 주기적으로 갱신되고 응답 `lastComputedAt`이 마지막 집계 시각이다. 봇 세션은 빠진다.
|
|
2263
2362
|
* 기간은 양끝 포함 최대 92일이고, 테스트 결제 구매는 `includeTest: "true"`일 때만 포함한다.
|
|
2264
2363
|
*/
|
|
@@ -2268,7 +2367,17 @@ get: (params) => storeHttp.request("GET", "/sales-report", salesReportSchema, {
|
|
|
2268
2367
|
/** `GET /v1/analytics/products` — 상품별 노출·조회·장바구니·구매 */
|
|
2269
2368
|
products: (params) => storeHttp.request("GET", "/analytics/products", analyticsProductsSchema, { query: analyticsProductsQuerySchema.parse(params) }),
|
|
2270
2369
|
/** `GET /v1/analytics/sources` — 채널·소스·캠페인·기기별 세션과 구매 */
|
|
2271
|
-
sources: (params) => storeHttp.request("GET", "/analytics/sources", analyticsSourcesSchema, { query: analyticsSourcesQuerySchema.parse(params) })
|
|
2370
|
+
sources: (params) => storeHttp.request("GET", "/analytics/sources", analyticsSourcesSchema, { query: analyticsSourcesQuerySchema.parse(params) }),
|
|
2371
|
+
/** `GET /v1/analytics/realtime` — 최근 5분 방문자·세션·페이지(원본 이벤트 기준, 봇 제외) */
|
|
2372
|
+
realtime: () => storeHttp.request("GET", "/analytics/realtime", analyticsRealtimeSchema),
|
|
2373
|
+
/** `GET /v1/analytics/devices` — 기기별 세션 퍼널 */
|
|
2374
|
+
devices: (params) => storeHttp.request("GET", "/analytics/devices", analyticsDevicesSchema, { query: analyticsRangeQuerySchema.parse(params) }),
|
|
2375
|
+
/** `GET /v1/analytics/pages` — 첫 페이지(진입·반송)와 이탈 페이지 */
|
|
2376
|
+
pages: (params) => storeHttp.request("GET", "/analytics/pages", analyticsPagesSchema, { query: analyticsPagesQuerySchema.parse(params) }),
|
|
2377
|
+
/** `GET /v1/analytics/search` — 검색어별 검색·결과 0개·검색 후 구매 */
|
|
2378
|
+
search: (params) => storeHttp.request("GET", "/analytics/search", analyticsSearchSchema, { query: analyticsSearchQuerySchema.parse(params) }),
|
|
2379
|
+
/** `GET /v1/analytics/checkout` — 결제 세션의 결과별 수(결제 단계 이탈 이유) */
|
|
2380
|
+
checkout: (params) => storeHttp.request("GET", "/analytics/checkout", analyticsCheckoutSchema, { query: analyticsRangeQuerySchema.parse(params) })
|
|
2272
2381
|
},
|
|
2273
2382
|
/**
|
|
2274
2383
|
* 결제 내역 — 스코프 `settlement:r`. 결제 건(세션) 단위이고 주문 목록과 다르다. 주문이 만들어지지 않은
|
|
@@ -2414,7 +2523,7 @@ get: (params) => storeHttp.request("GET", "/sales-report", salesReportSchema, {
|
|
|
2414
2523
|
get: () => storeHttp.request("GET", "/store", storeSchema),
|
|
2415
2524
|
/**
|
|
2416
2525
|
* `PATCH /v1/store` — 스코프 `store:rw`. 바꿀 필드만 보낸다. `analyticsAllowedOrigins`는 방문 분석 수집을 받을
|
|
2417
|
-
*
|
|
2526
|
+
* 스토어프론트 오리진 목록이다(빈 배열이면 모든 오리진 허용).
|
|
2418
2527
|
*/
|
|
2419
2528
|
update: (body) => storeHttp.request("PATCH", "/store", storeSchema, { body: updateStoreRequestSchema.parse(body) }),
|
|
2420
2529
|
listMembers: () => storeHttp.request("GET", "/store/members", z.array(storeMemberSchema)),
|
|
@@ -2803,4 +2912,4 @@ function createStoreSession(options) {
|
|
|
2803
2912
|
};
|
|
2804
2913
|
}
|
|
2805
2914
|
//#endregion
|
|
2806
|
-
export { ApiError, MAX_ANALYTICS_ALLOWED_ORIGINS, MAX_OPTION_GROUPS, MAX_VALUES_PER_GROUP, MAX_VARIANTS, PAYMENT_ROUTING_EXCLUSION_CODES, PORTONE_CHANNEL_NAME_MAX_LENGTH, PORTONE_MAX_CHANNELS, PORTONE_MIN_CHANNELS, RESERVED_STORE_CODES, 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, 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, analyticsFunnelStepSchema, analyticsOriginSchema, analyticsOverviewSchema, analyticsProductRowSchema, analyticsProductSortSchema, analyticsProductsQuerySchema, analyticsProductsSchema, analyticsRangeQuerySchema, 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, categoryAttributeSchema, categoryCreateRequestSchema, categoryDeleteQuerySchema, categoryDeleteStrategySchema, categoryListQuerySchema, categoryPatchRequestSchema, categoryProductSortSchema, categorySchema, categorySearchResultSchema, categoryTreeNodeSchema, categoryWithPathSchema, changeTypeSchema, claimDetailSchema, claimHoldResultSchema, claimListQuerySchema, claimReasonSchema, claimSchema, claimStatusSchema, claimTypeSchema, claimWebhookEventDataSchema, claimWebhookEventSchema, clientCredentialsTokenResponseSchema, createPkcePair, createStoreClient, createStoreSession, customerAddressSchema, customerDetailSchema, customerInquiryCategorySchema, customerInquiryListQuerySchema, customerListQuerySchema, customerOrderSchema, customerSortSchema, customerStatsSchema, customerSummarySchema, customerWithdrawalBlockedDetailsSchema, deliveryInfoSchema, deliveryMethodSchema, deliveryStatusSchema, deliveryTypeSchema, discountRequestSchema, discountSchema, dispatchRequestSchema, effectivePaymentRoutingModeSchema, 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, paymentRoutingModeSchema, paymentSettingsSchema, pgEnvironmentSchema, pgPaymentMethodSchema, pgProviderSchema, pgVerificationResultSchema, portOneChannelSchema, portOneCredentialsInputSchema, productCreateRequestSchema, productImageUploadResultSchema, productListQuerySchema, productOutOfStockWebhookEventDataSchema, productOutOfStockWebhookEventSchema, productPatchRequestSchema, productSchema, productSortSchema, productStatusSchema, productSummarySchema, productVariantRequestSchema, productVariantSchema, purchaseDecisionExtendResultSchema, purchaseDecisionRequestResultSchema, refreshUserToken, rejectClaimRequestSchema, reviewListQuerySchema, reviewReportResultSchema, reviewSchema, reviewSortSchema, reviewWebhookEventDataSchema, reviewWebhookEventSchema, salesReportGroupBySchema, salesReportQuerySchema, salesReportRowSchema, salesReportSchema, salesTotalsSchema, saveLoginProviderRequestSchema, scopeAccessSchema, sellerAddressListQuerySchema, sellerAddressRequestSchema, sellerAddressSchema, sellerCustomerInquirySchema, settlementDetailQuerySchema, settlementDetailSchema, settlementListQuerySchema, settlementStatusSchema, settlementSummarySchema, settlementWithDetailsSchema, shippingAddressSchema, storeCodeSchema, storeMemberRoleUpdatedSchema, storeMemberSchema, storeRoleSchema, storeSchema, storeScopeResourceSchema, storeTokenResponseSchema, tossPaymentsCredentialsInputSchema, trackingUpdateResultSchema, updateLoginProviderRequestSchema, updateLoginSettingsRequestSchema, updatePaymentProviderRequestSchema, updatePaymentSettingsRequestSchema, updateStoreRequestSchema, uploadRequestSchema, uploadTicketSchema, upsertPortOneProviderRequestSchema, upsertTossPaymentsProviderRequestSchema, userTokenResponseSchema, variantStockRequestSchema, variantStockSchema, verifyPaymentProviderRequestSchema, webhookCreatedSchema, webhookDeliveryErrorCodeSchema, webhookDeliveryListQuerySchema, webhookDeliverySchema, webhookDeliveryStatusSchema, webhookEventSchema, webhookRedeliverFailedResultSchema, webhookRequestSchema, webhookSchema, webhookSecretRotatedSchema, webhookStatusSchema, webhookTestRequestSchema, webhookTestResultSchema, withdrawCustomerRequestSchema };
|
|
2915
|
+
export { ApiError, MAX_ANALYTICS_ALLOWED_ORIGINS, MAX_OPTION_GROUPS, MAX_VALUES_PER_GROUP, MAX_VARIANTS, PAYMENT_ROUTING_EXCLUSION_CODES, PORTONE_CHANNEL_NAME_MAX_LENGTH, PORTONE_MAX_CHANNELS, PORTONE_MIN_CHANNELS, RESERVED_STORE_CODES, 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, 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, categoryAttributeSchema, categoryCreateRequestSchema, categoryDeleteQuerySchema, categoryDeleteStrategySchema, categoryListQuerySchema, categoryPatchRequestSchema, categoryProductSortSchema, categorySchema, categorySearchResultSchema, categoryTreeNodeSchema, categoryWithPathSchema, changeTypeSchema, claimDetailSchema, claimHoldResultSchema, claimListQuerySchema, claimReasonSchema, claimSchema, claimStatusSchema, claimTypeSchema, claimWebhookEventDataSchema, claimWebhookEventSchema, clientCredentialsTokenResponseSchema, createPkcePair, createStoreClient, createStoreSession, customerAddressSchema, customerDetailSchema, customerInquiryCategorySchema, customerInquiryListQuerySchema, customerListQuerySchema, customerOrderSchema, customerSortSchema, customerStatsSchema, customerSummarySchema, customerWithdrawalBlockedDetailsSchema, deliveryInfoSchema, deliveryMethodSchema, deliveryStatusSchema, deliveryTypeSchema, discountRequestSchema, discountSchema, dispatchRequestSchema, effectivePaymentRoutingModeSchema, 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, paymentRoutingModeSchema, paymentSettingsSchema, pgEnvironmentSchema, pgPaymentMethodSchema, pgProviderSchema, pgVerificationResultSchema, portOneChannelSchema, portOneCredentialsInputSchema, productCreateRequestSchema, productImageUploadResultSchema, productListQuerySchema, productOutOfStockWebhookEventDataSchema, productOutOfStockWebhookEventSchema, productPatchRequestSchema, productSchema, productSortSchema, productStatusSchema, productSummarySchema, productVariantRequestSchema, productVariantSchema, purchaseDecisionExtendResultSchema, purchaseDecisionRequestResultSchema, refreshUserToken, rejectClaimRequestSchema, reviewListQuerySchema, reviewReportResultSchema, reviewSchema, reviewSortSchema, reviewWebhookEventDataSchema, reviewWebhookEventSchema, salesReportGroupBySchema, salesReportQuerySchema, salesReportRowSchema, salesReportSchema, salesTotalsSchema, saveLoginProviderRequestSchema, scopeAccessSchema, sellerAddressListQuerySchema, sellerAddressRequestSchema, sellerAddressSchema, sellerCustomerInquirySchema, settlementDetailQuerySchema, settlementDetailSchema, settlementListQuerySchema, settlementStatusSchema, settlementSummarySchema, settlementWithDetailsSchema, shippingAddressSchema, storeCodeSchema, storeMemberRoleUpdatedSchema, storeMemberSchema, storeRoleSchema, storeSchema, storeScopeResourceSchema, storeTokenResponseSchema, tossPaymentsCredentialsInputSchema, trackingUpdateResultSchema, updateLoginProviderRequestSchema, updateLoginSettingsRequestSchema, updatePaymentProviderRequestSchema, updatePaymentSettingsRequestSchema, updateStoreRequestSchema, uploadRequestSchema, uploadTicketSchema, upsertPortOneProviderRequestSchema, upsertTossPaymentsProviderRequestSchema, userTokenResponseSchema, variantStockRequestSchema, variantStockSchema, verifyPaymentProviderRequestSchema, webhookCreatedSchema, webhookDeliveryErrorCodeSchema, webhookDeliveryListQuerySchema, webhookDeliverySchema, webhookDeliveryStatusSchema, webhookEventSchema, webhookRedeliverFailedResultSchema, webhookRequestSchema, webhookSchema, webhookSecretRotatedSchema, webhookStatusSchema, webhookTestRequestSchema, webhookTestResultSchema, withdrawCustomerRequestSchema };
|
package/dist/server.d.mts
CHANGED
|
@@ -1,4 +1,48 @@
|
|
|
1
1
|
import { a as WEBHOOK_HEADERS, i as WEBHOOK_EVENT_TYPE_HEADER, n as WEBHOOK_DELIVERY_ID_HEADER, o as WEBHOOK_SIGNATURE_HEADER, r as WEBHOOK_EVENT_ID_HEADER, s as WEBHOOK_USER_AGENT, t as WEBHOOK_DEFAULT_TOLERANCE_SEC } from "./webhook-headers-D6QmPdEJ.mjs";
|
|
2
|
+
//#region src/member-delegation.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* 구매자 위임 — 자체 회원 시스템을 가진 셀러 서버가 로그인한 사용자를 sayren 구매자 토큰으로 바꾼다
|
|
5
|
+
* (token-exchange의 `member-uid`·`member-ref` 확장, auth-spec §12-10). 클라이언트 시크릿을 쓰므로 **서버에서만** 부른다.
|
|
6
|
+
*
|
|
7
|
+
* 스토어에 바인딩된 confidential 클라이언트 + `member:delegate` 스코프가 필요하다. 응답은 구매자 액세스 토큰(30분)이고
|
|
8
|
+
* refresh는 없다 — 만료되면 같은 호출로 다시 받는다.
|
|
9
|
+
*/
|
|
10
|
+
interface DelegatedMemberToken {
|
|
11
|
+
accessToken: string;
|
|
12
|
+
expiresIn: number;
|
|
13
|
+
memberId: string;
|
|
14
|
+
}
|
|
15
|
+
interface DelegationOptions {
|
|
16
|
+
/** api 주소(예: `https://api.sayren.app`) — `/v1/oauth2/token`을 붙인다 */
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
clientId: string;
|
|
19
|
+
clientSecret: string;
|
|
20
|
+
fetch?: typeof globalThis.fetch;
|
|
21
|
+
}
|
|
22
|
+
/** 셀러 자체 인증의 사용자 id로 지목한다. 처음 보는 id면 구매자를 만든다(이메일·이름은 그때만 쓴다) */
|
|
23
|
+
export declare function delegateMemberByUid(options: DelegationOptions & {
|
|
24
|
+
uid: string;
|
|
25
|
+
email?: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
/**
|
|
28
|
+
* 사용자가 내 서비스에서 실제로 로그인한 시각. 주면 구매자 토큰의 인증 시각이 되고(탈퇴처럼 최근 인증이 필요한 동작에
|
|
29
|
+
* 쓴다), 주지 않으면 인증 시각이 없는 토큰이 나온다
|
|
30
|
+
*/
|
|
31
|
+
authenticatedAt?: Date;
|
|
32
|
+
}): Promise<DelegatedMemberToken>;
|
|
33
|
+
/** 이메일로 지목한다. 처음 보는 이메일이면 구매자를 만든다 */
|
|
34
|
+
export declare function delegateMemberByEmail(options: DelegationOptions & {
|
|
35
|
+
email: string;
|
|
36
|
+
name?: string;
|
|
37
|
+
}): Promise<DelegatedMemberToken>;
|
|
38
|
+
/** 위임 실패 — `error`는 OAuth 오류 코드(`invalid_client`·`invalid_scope`·`invalid_request` 등) */
|
|
39
|
+
export declare class MemberDelegationError extends Error {
|
|
40
|
+
readonly error: string;
|
|
41
|
+
readonly description: string;
|
|
42
|
+
readonly status: number;
|
|
43
|
+
constructor(error: string, description: string, status: number);
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
2
46
|
//#region src/webhook-signature.d.ts
|
|
3
47
|
interface SignWebhookPayloadOptions {
|
|
4
48
|
/** 요청 본문 원문. 전송하는 바이트와 정확히 같아야 한다 */
|
|
@@ -56,4 +100,4 @@ interface VerifyWebhookSignatureOptions {
|
|
|
56
100
|
*/
|
|
57
101
|
export declare function verifyWebhookSignature(options: VerifyWebhookSignatureOptions): Promise<WebhookSignatureVerification>;
|
|
58
102
|
//#endregion
|
|
59
|
-
export { type ParsedWebhookSignature, type SignWebhookPayloadOptions, type VerifyWebhookSignatureOptions, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_HEADERS, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, type WebhookSignatureFailureReason, type WebhookSignatureVerification };
|
|
103
|
+
export { type DelegatedMemberToken, type ParsedWebhookSignature, type SignWebhookPayloadOptions, type VerifyWebhookSignatureOptions, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_HEADERS, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, type WebhookSignatureFailureReason, type WebhookSignatureVerification };
|
package/dist/server.mjs
CHANGED
|
@@ -1,4 +1,59 @@
|
|
|
1
1
|
import { a as WEBHOOK_HEADERS, i as WEBHOOK_EVENT_TYPE_HEADER, n as WEBHOOK_DELIVERY_ID_HEADER, o as WEBHOOK_SIGNATURE_HEADER, r as WEBHOOK_EVENT_ID_HEADER, s as WEBHOOK_USER_AGENT, t as WEBHOOK_DEFAULT_TOLERANCE_SEC } from "./webhook-headers-ap_exsb9.mjs";
|
|
2
|
+
//#region src/member-delegation.ts
|
|
3
|
+
/** 셀러 자체 인증의 사용자 id로 지목한다. 처음 보는 id면 구매자를 만든다(이메일·이름은 그때만 쓴다) */
|
|
4
|
+
function delegateMemberByUid(options) {
|
|
5
|
+
return delegate(options, {
|
|
6
|
+
subject_token_type: "urn:sayren:params:oauth:token-type:member-uid",
|
|
7
|
+
subject_token: options.uid,
|
|
8
|
+
...options.authenticatedAt ? { subject_auth_time: String(Math.floor(options.authenticatedAt.getTime() / 1e3)) } : {},
|
|
9
|
+
...options.email ? { subject_email: options.email } : {},
|
|
10
|
+
...options.name ? { subject_name: options.name } : {}
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
/** 이메일로 지목한다. 처음 보는 이메일이면 구매자를 만든다 */
|
|
14
|
+
function delegateMemberByEmail(options) {
|
|
15
|
+
return delegate(options, {
|
|
16
|
+
subject_token_type: "urn:sayren:params:oauth:token-type:member-ref",
|
|
17
|
+
subject_token: options.email,
|
|
18
|
+
...options.name ? { subject_name: options.name } : {}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async function delegate(options, subject) {
|
|
22
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
23
|
+
const basic = btoa(`${options.clientId}:${options.clientSecret}`);
|
|
24
|
+
const res = await doFetch(`${options.baseUrl.replace(/\/+$/, "")}/v1/oauth2/token`, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: {
|
|
27
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
28
|
+
authorization: `Basic ${basic}`
|
|
29
|
+
},
|
|
30
|
+
body: new URLSearchParams({
|
|
31
|
+
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
32
|
+
...subject
|
|
33
|
+
}).toString()
|
|
34
|
+
});
|
|
35
|
+
const body = await res.json().catch(() => ({}));
|
|
36
|
+
if (!res.ok || typeof body.access_token !== "string") throw new MemberDelegationError(typeof body.error === "string" ? body.error : `http_${res.status}`, typeof body.error_description === "string" ? body.error_description : "", res.status);
|
|
37
|
+
return {
|
|
38
|
+
accessToken: body.access_token,
|
|
39
|
+
expiresIn: Number(body.expires_in),
|
|
40
|
+
memberId: String(body.memberId)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** 위임 실패 — `error`는 OAuth 오류 코드(`invalid_client`·`invalid_scope`·`invalid_request` 등) */
|
|
44
|
+
var MemberDelegationError = class extends Error {
|
|
45
|
+
error;
|
|
46
|
+
description;
|
|
47
|
+
status;
|
|
48
|
+
constructor(error, description, status) {
|
|
49
|
+
super(description ? `${error}: ${description}` : error);
|
|
50
|
+
this.error = error;
|
|
51
|
+
this.description = description;
|
|
52
|
+
this.status = status;
|
|
53
|
+
this.name = "MemberDelegationError";
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
//#endregion
|
|
2
57
|
//#region src/webhook-signature.ts
|
|
3
58
|
/**
|
|
4
59
|
* 웹훅 서명 (`Sayren-Signature`) 생성·파싱·검증.
|
|
@@ -122,4 +177,4 @@ async function verifyWebhookSignature(options) {
|
|
|
122
177
|
};
|
|
123
178
|
}
|
|
124
179
|
//#endregion
|
|
125
|
-
export { WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_HEADERS, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, parseWebhookSignatureHeader, signWebhookPayload, verifyWebhookSignature };
|
|
180
|
+
export { MemberDelegationError, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_HEADERS, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, delegateMemberByEmail, delegateMemberByUid, parseWebhookSignatureHeader, signWebhookPayload, verifyWebhookSignature };
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -2,13 +2,22 @@ import { z } from "zod";
|
|
|
2
2
|
import { base64Utf8 } from "./base64";
|
|
3
3
|
import { createHttp, path, type Query, resolveToken, type TokenSource, unwrapData } from "./http";
|
|
4
4
|
import {
|
|
5
|
+
type AnalyticsPagesParams,
|
|
5
6
|
type AnalyticsProductsParams,
|
|
6
7
|
type AnalyticsRangeParams,
|
|
8
|
+
type AnalyticsSearchParams,
|
|
7
9
|
type AnalyticsSourcesParams,
|
|
10
|
+
analyticsCheckoutSchema,
|
|
11
|
+
analyticsDevicesSchema,
|
|
8
12
|
analyticsOverviewSchema,
|
|
13
|
+
analyticsPagesQuerySchema,
|
|
14
|
+
analyticsPagesSchema,
|
|
9
15
|
analyticsProductsQuerySchema,
|
|
10
16
|
analyticsProductsSchema,
|
|
11
17
|
analyticsRangeQuerySchema,
|
|
18
|
+
analyticsRealtimeSchema,
|
|
19
|
+
analyticsSearchQuerySchema,
|
|
20
|
+
analyticsSearchSchema,
|
|
12
21
|
analyticsSourcesQuerySchema,
|
|
13
22
|
analyticsSourcesSchema,
|
|
14
23
|
} from "./schemas/analytics";
|
|
@@ -533,7 +542,12 @@ export function createStoreClient(options: StoreClientOptions) {
|
|
|
533
542
|
|
|
534
543
|
claims: {
|
|
535
544
|
list: (params?: ClaimListParams) =>
|
|
536
|
-
storeHttp.request("GET", "/claims", pageSchema(claimSchema), {
|
|
545
|
+
storeHttp.request("GET", "/claims", pageSchema(claimSchema), {
|
|
546
|
+
query: {
|
|
547
|
+
...params,
|
|
548
|
+
status: Array.isArray(params?.status) ? params.status.join(",") : params?.status,
|
|
549
|
+
} as Query,
|
|
550
|
+
}),
|
|
537
551
|
get: (claimId: string) =>
|
|
538
552
|
storeHttp.request("GET", path`/claims/${claimId}`, claimDetailSchema),
|
|
539
553
|
approve: (claimId: string, deductAmount?: number, idempotencyKey?: string) =>
|
|
@@ -561,6 +575,9 @@ export function createStoreClient(options: StoreClientOptions) {
|
|
|
561
575
|
}),
|
|
562
576
|
releaseHold: (claimId: string) =>
|
|
563
577
|
storeHttp.requestVoid("DELETE", path`/claims/${claimId}/hold`),
|
|
578
|
+
/** 교환 완료 — 재배송 중인 교환을 셀러가 명시적으로 끝낸다 */
|
|
579
|
+
completeExchange: (claimId: string) =>
|
|
580
|
+
storeHttp.request("POST", path`/claims/${claimId}/complete-exchange`, claimSchema),
|
|
564
581
|
},
|
|
565
582
|
|
|
566
583
|
inquiries: {
|
|
@@ -691,7 +708,7 @@ export function createStoreClient(options: StoreClientOptions) {
|
|
|
691
708
|
},
|
|
692
709
|
|
|
693
710
|
/**
|
|
694
|
-
* 방문 분석 — 스코프 `analytics:r`.
|
|
711
|
+
* 방문 분석 — 스코프 `analytics:r`. 스토어프론트가 `@sayren/storefront-sdk`로 수집한 이벤트를 Asia/Seoul 날짜별로
|
|
695
712
|
* 집계한 값이다. 집계는 주기적으로 갱신되고 응답 `lastComputedAt`이 마지막 집계 시각이다. 봇 세션은 빠진다.
|
|
696
713
|
* 기간은 양끝 포함 최대 92일이고, 테스트 결제 구매는 `includeTest: "true"`일 때만 포함한다.
|
|
697
714
|
*/
|
|
@@ -711,6 +728,28 @@ export function createStoreClient(options: StoreClientOptions) {
|
|
|
711
728
|
storeHttp.request("GET", "/analytics/sources", analyticsSourcesSchema, {
|
|
712
729
|
query: analyticsSourcesQuerySchema.parse(params),
|
|
713
730
|
}),
|
|
731
|
+
/** `GET /v1/analytics/realtime` — 최근 5분 방문자·세션·페이지(원본 이벤트 기준, 봇 제외) */
|
|
732
|
+
realtime: () => storeHttp.request("GET", "/analytics/realtime", analyticsRealtimeSchema),
|
|
733
|
+
/** `GET /v1/analytics/devices` — 기기별 세션 퍼널 */
|
|
734
|
+
devices: (params: AnalyticsRangeParams) =>
|
|
735
|
+
storeHttp.request("GET", "/analytics/devices", analyticsDevicesSchema, {
|
|
736
|
+
query: analyticsRangeQuerySchema.parse(params),
|
|
737
|
+
}),
|
|
738
|
+
/** `GET /v1/analytics/pages` — 첫 페이지(진입·반송)와 이탈 페이지 */
|
|
739
|
+
pages: (params: AnalyticsPagesParams) =>
|
|
740
|
+
storeHttp.request("GET", "/analytics/pages", analyticsPagesSchema, {
|
|
741
|
+
query: analyticsPagesQuerySchema.parse(params) as unknown as Query,
|
|
742
|
+
}),
|
|
743
|
+
/** `GET /v1/analytics/search` — 검색어별 검색·결과 0개·검색 후 구매 */
|
|
744
|
+
search: (params: AnalyticsSearchParams) =>
|
|
745
|
+
storeHttp.request("GET", "/analytics/search", analyticsSearchSchema, {
|
|
746
|
+
query: analyticsSearchQuerySchema.parse(params) as unknown as Query,
|
|
747
|
+
}),
|
|
748
|
+
/** `GET /v1/analytics/checkout` — 결제 세션의 결과별 수(결제 단계 이탈 이유) */
|
|
749
|
+
checkout: (params: AnalyticsRangeParams) =>
|
|
750
|
+
storeHttp.request("GET", "/analytics/checkout", analyticsCheckoutSchema, {
|
|
751
|
+
query: analyticsRangeQuerySchema.parse(params),
|
|
752
|
+
}),
|
|
714
753
|
},
|
|
715
754
|
|
|
716
755
|
/**
|
|
@@ -944,7 +983,7 @@ export function createStoreClient(options: StoreClientOptions) {
|
|
|
944
983
|
get: () => storeHttp.request("GET", "/store", storeSchema),
|
|
945
984
|
/**
|
|
946
985
|
* `PATCH /v1/store` — 스코프 `store:rw`. 바꿀 필드만 보낸다. `analyticsAllowedOrigins`는 방문 분석 수집을 받을
|
|
947
|
-
*
|
|
986
|
+
* 스토어프론트 오리진 목록이다(빈 배열이면 모든 오리진 허용).
|
|
948
987
|
*/
|
|
949
988
|
update: (body: UpdateStoreRequest) =>
|
|
950
989
|
storeHttp.request("PATCH", "/store", storeSchema, {
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 구매자 위임 — 자체 회원 시스템을 가진 셀러 서버가 로그인한 사용자를 sayren 구매자 토큰으로 바꾼다
|
|
3
|
+
* (token-exchange의 `member-uid`·`member-ref` 확장, auth-spec §12-10). 클라이언트 시크릿을 쓰므로 **서버에서만** 부른다.
|
|
4
|
+
*
|
|
5
|
+
* 스토어에 바인딩된 confidential 클라이언트 + `member:delegate` 스코프가 필요하다. 응답은 구매자 액세스 토큰(30분)이고
|
|
6
|
+
* refresh는 없다 — 만료되면 같은 호출로 다시 받는다.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface DelegatedMemberToken {
|
|
10
|
+
accessToken: string;
|
|
11
|
+
expiresIn: number;
|
|
12
|
+
memberId: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface DelegationOptions {
|
|
16
|
+
/** api 주소(예: `https://api.sayren.app`) — `/v1/oauth2/token`을 붙인다 */
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
clientId: string;
|
|
19
|
+
clientSecret: string;
|
|
20
|
+
fetch?: typeof globalThis.fetch;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** 셀러 자체 인증의 사용자 id로 지목한다. 처음 보는 id면 구매자를 만든다(이메일·이름은 그때만 쓴다) */
|
|
24
|
+
export function delegateMemberByUid(
|
|
25
|
+
options: DelegationOptions & {
|
|
26
|
+
uid: string;
|
|
27
|
+
email?: string;
|
|
28
|
+
name?: string;
|
|
29
|
+
/**
|
|
30
|
+
* 사용자가 내 서비스에서 실제로 로그인한 시각. 주면 구매자 토큰의 인증 시각이 되고(탈퇴처럼 최근 인증이 필요한 동작에
|
|
31
|
+
* 쓴다), 주지 않으면 인증 시각이 없는 토큰이 나온다
|
|
32
|
+
*/
|
|
33
|
+
authenticatedAt?: Date;
|
|
34
|
+
},
|
|
35
|
+
): Promise<DelegatedMemberToken> {
|
|
36
|
+
return delegate(options, {
|
|
37
|
+
subject_token_type: "urn:sayren:params:oauth:token-type:member-uid",
|
|
38
|
+
subject_token: options.uid,
|
|
39
|
+
...(options.authenticatedAt
|
|
40
|
+
? { subject_auth_time: String(Math.floor(options.authenticatedAt.getTime() / 1000)) }
|
|
41
|
+
: {}),
|
|
42
|
+
...(options.email ? { subject_email: options.email } : {}),
|
|
43
|
+
...(options.name ? { subject_name: options.name } : {}),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** 이메일로 지목한다. 처음 보는 이메일이면 구매자를 만든다 */
|
|
48
|
+
export function delegateMemberByEmail(
|
|
49
|
+
options: DelegationOptions & { email: string; name?: string },
|
|
50
|
+
): Promise<DelegatedMemberToken> {
|
|
51
|
+
return delegate(options, {
|
|
52
|
+
subject_token_type: "urn:sayren:params:oauth:token-type:member-ref",
|
|
53
|
+
subject_token: options.email,
|
|
54
|
+
...(options.name ? { subject_name: options.name } : {}),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function delegate(
|
|
59
|
+
options: DelegationOptions,
|
|
60
|
+
subject: Record<string, string>,
|
|
61
|
+
): Promise<DelegatedMemberToken> {
|
|
62
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
63
|
+
const basic = btoa(`${options.clientId}:${options.clientSecret}`);
|
|
64
|
+
const res = await doFetch(`${options.baseUrl.replace(/\/+$/, "")}/v1/oauth2/token`, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
headers: {
|
|
67
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
68
|
+
authorization: `Basic ${basic}`,
|
|
69
|
+
},
|
|
70
|
+
body: new URLSearchParams({
|
|
71
|
+
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
72
|
+
...subject,
|
|
73
|
+
}).toString(),
|
|
74
|
+
});
|
|
75
|
+
const body = (await res.json().catch(() => ({}))) as Record<string, unknown>;
|
|
76
|
+
if (!res.ok || typeof body.access_token !== "string") {
|
|
77
|
+
const error = typeof body.error === "string" ? body.error : `http_${res.status}`;
|
|
78
|
+
const description = typeof body.error_description === "string" ? body.error_description : "";
|
|
79
|
+
throw new MemberDelegationError(error, description, res.status);
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
accessToken: body.access_token,
|
|
83
|
+
expiresIn: Number(body.expires_in),
|
|
84
|
+
memberId: String(body.memberId),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** 위임 실패 — `error`는 OAuth 오류 코드(`invalid_client`·`invalid_scope`·`invalid_request` 등) */
|
|
89
|
+
export class MemberDelegationError extends Error {
|
|
90
|
+
constructor(
|
|
91
|
+
readonly error: string,
|
|
92
|
+
readonly description: string,
|
|
93
|
+
readonly status: number,
|
|
94
|
+
) {
|
|
95
|
+
super(description ? `${error}: ${description}` : error);
|
|
96
|
+
this.name = "MemberDelegationError";
|
|
97
|
+
}
|
|
98
|
+
}
|