@sayren/storefront-sdk 0.4.0 → 0.6.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/auth/index.d.mts +40 -2
- package/dist/auth/index.mjs +56 -2
- package/dist/{client-DHISrvXq.mjs → client-Do9-LG5X.mjs} +67 -3
- package/dist/{http-CxqZxTfE.d.mts → http-CqAR0_w4.d.mts} +26 -2
- package/dist/index.d.mts +47 -5
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
- package/src/auth/index.ts +81 -0
- package/src/client.ts +22 -0
- package/src/index.ts +1 -0
- package/src/schemas/auth.ts +34 -1
- package/src/schemas/member.ts +2 -1
- package/src/schemas/store.ts +23 -0
package/dist/auth/index.d.mts
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
|
-
import { a as AnonymousSession, c as TokenPair,
|
|
1
|
+
import { a as AnonymousSession, c as LoginRequest, d as TokenPair, f as WithdrawRequest, l as SignupRequest, p as WithdrawalBlockedDetails, t as ApiError, u as SocialProvider } from "../http-CqAR0_w4.mjs";
|
|
2
2
|
//#region src/auth/index.d.ts
|
|
3
|
+
/** 소셜 로그인 시작 결과 — `codeVerifier`와 `state`는 돌아올 때까지 서버 세션(httpOnly 쿠키 등)에 보관한다 */
|
|
4
|
+
export interface IdpStart {
|
|
5
|
+
/** 구매자를 보낼 공급자 로그인 주소 */
|
|
6
|
+
url: string;
|
|
7
|
+
state: string;
|
|
8
|
+
codeVerifier: string;
|
|
9
|
+
}
|
|
10
|
+
export interface IdpCallbackInput {
|
|
11
|
+
/** 돌아온 주소의 `code`(성공) — 실패면 `error`가 온다 */
|
|
12
|
+
code: string | null;
|
|
13
|
+
/** 돌아온 주소의 `state` */
|
|
14
|
+
state: string | null;
|
|
15
|
+
/** `idp()`가 준 값 — 보관해 둔 것 */
|
|
16
|
+
expectedState: string;
|
|
17
|
+
codeVerifier: string;
|
|
18
|
+
/** 비회원 장바구니 토큰 — 주면 회원 장바구니로 합친다 */
|
|
19
|
+
cartToken?: string;
|
|
20
|
+
}
|
|
21
|
+
/** 소셜 로그인 실패 — 구매자가 취소했거나(`access_denied`) state가 맞지 않는다 */
|
|
22
|
+
export declare class IdpCallbackError extends Error {
|
|
23
|
+
readonly reason: "access_denied" | "state_mismatch" | "missing_code" | string;
|
|
24
|
+
constructor(reason: "access_denied" | "state_mismatch" | "missing_code" | string);
|
|
25
|
+
}
|
|
3
26
|
export interface StorefrontAuthOptions {
|
|
4
27
|
/** 예: https://api.sayren.app/storefront/v1 */
|
|
5
28
|
baseUrl: string;
|
|
@@ -30,6 +53,21 @@ export declare function createStorefrontAuth(options: StorefrontAuthOptions): {
|
|
|
30
53
|
session(body: LoginRequest): Promise<TokenPair>;
|
|
31
54
|
/** 회원가입 — 가입과 동시에 로그인 토큰을 받는다. `cartToken`을 주면 장바구니를 병합한다 */
|
|
32
55
|
signUp(body: SignupRequest): Promise<TokenPair>;
|
|
56
|
+
/**
|
|
57
|
+
* 소셜 로그인 시작 — PKCE(S256)와 state를 만들고 공급자 로그인 주소를 받는다. 반환한 `state`·`codeVerifier`를
|
|
58
|
+
* 서버 세션에 보관하고 구매자를 `url`로 보낸다. 로그인을 마치면 `redirectUri`로 `?code=&state=`가 붙어 돌아온다.
|
|
59
|
+
* `redirectUri`는 스토어프런트 도메인 목록 안이어야 한다
|
|
60
|
+
*/
|
|
61
|
+
idp(provider: SocialProvider, options: {
|
|
62
|
+
redirectUri: string;
|
|
63
|
+
}): Promise<IdpStart>;
|
|
64
|
+
/**
|
|
65
|
+
* 소셜 로그인 마무리 — 돌아온 `code`를 구매자 토큰으로 바꾼다. state가 다르거나 구매자가 취소했으면
|
|
66
|
+
* `IdpCallbackError`. code는 1회용이고 1분 안에 바꿔야 한다
|
|
67
|
+
*/
|
|
68
|
+
idpCallback(input: IdpCallbackInput & {
|
|
69
|
+
error?: string | null;
|
|
70
|
+
}): Promise<TokenPair>;
|
|
33
71
|
/** 토큰 갱신 — refreshToken은 한 번 쓰면 폐기되고 새 쌍이 온다(회전) */
|
|
34
72
|
refresh(refreshToken: string): Promise<TokenPair>;
|
|
35
73
|
/**
|
|
@@ -46,4 +84,4 @@ export declare function createStorefrontAuth(options: StorefrontAuthOptions): {
|
|
|
46
84
|
};
|
|
47
85
|
export type StorefrontAuth = ReturnType<typeof createStorefrontAuth>;
|
|
48
86
|
//#endregion
|
|
49
|
-
export { type AnonymousSession, ApiError, type LoginRequest, type SignupRequest, type TokenPair, type WithdrawRequest, type WithdrawalBlockedDetails };
|
|
87
|
+
export { type AnonymousSession, ApiError, type LoginRequest, type SignupRequest, type SocialProvider, type TokenPair, type WithdrawRequest, type WithdrawalBlockedDetails };
|
package/dist/auth/index.mjs
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ct as ApiError, t as createStorefrontClient } from "../client-Do9-LG5X.mjs";
|
|
2
2
|
//#region src/auth/index.ts
|
|
3
|
+
/** 소셜 로그인 실패 — 구매자가 취소했거나(`access_denied`) state가 맞지 않는다 */
|
|
4
|
+
var IdpCallbackError = class extends Error {
|
|
5
|
+
reason;
|
|
6
|
+
constructor(reason) {
|
|
7
|
+
super(`소셜 로그인을 마치지 못했어요 (${reason})`);
|
|
8
|
+
this.reason = reason;
|
|
9
|
+
this.name = "IdpCallbackError";
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
function base64url(bytes) {
|
|
13
|
+
let binary = "";
|
|
14
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
15
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
16
|
+
}
|
|
17
|
+
function randomToken(bytes = 32) {
|
|
18
|
+
return base64url(globalThis.crypto.getRandomValues(new Uint8Array(bytes)));
|
|
19
|
+
}
|
|
20
|
+
async function s256(verifier) {
|
|
21
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
|
|
22
|
+
return base64url(new Uint8Array(digest));
|
|
23
|
+
}
|
|
3
24
|
/**
|
|
4
25
|
* 구매자 인증 — 화면은 스토어프런트가 그리고, 계정 저장과 인증 처리는 sayren이 한다.
|
|
5
26
|
* 어느 방식으로 로그인해도 결과는 같은 구매자 토큰(`TokenPair`)이고 `/me/*`·장바구니·주문 API에 그대로 쓴다.
|
|
@@ -36,6 +57,39 @@ function createStorefrontAuth(options) {
|
|
|
36
57
|
signUp(body) {
|
|
37
58
|
return client().auth.signup(body);
|
|
38
59
|
},
|
|
60
|
+
/**
|
|
61
|
+
* 소셜 로그인 시작 — PKCE(S256)와 state를 만들고 공급자 로그인 주소를 받는다. 반환한 `state`·`codeVerifier`를
|
|
62
|
+
* 서버 세션에 보관하고 구매자를 `url`로 보낸다. 로그인을 마치면 `redirectUri`로 `?code=&state=`가 붙어 돌아온다.
|
|
63
|
+
* `redirectUri`는 스토어프런트 도메인 목록 안이어야 한다
|
|
64
|
+
*/
|
|
65
|
+
async idp(provider, options) {
|
|
66
|
+
const state = randomToken(24);
|
|
67
|
+
const codeVerifier = randomToken(32);
|
|
68
|
+
const { url } = await client().auth.idpAuthorize(provider, {
|
|
69
|
+
redirectUri: options.redirectUri,
|
|
70
|
+
codeChallenge: await s256(codeVerifier),
|
|
71
|
+
state
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
url,
|
|
75
|
+
state,
|
|
76
|
+
codeVerifier
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
/**
|
|
80
|
+
* 소셜 로그인 마무리 — 돌아온 `code`를 구매자 토큰으로 바꾼다. state가 다르거나 구매자가 취소했으면
|
|
81
|
+
* `IdpCallbackError`. code는 1회용이고 1분 안에 바꿔야 한다
|
|
82
|
+
*/
|
|
83
|
+
idpCallback(input) {
|
|
84
|
+
if (input.error) return Promise.reject(new IdpCallbackError(input.error));
|
|
85
|
+
if (!input.state || input.state !== input.expectedState) return Promise.reject(new IdpCallbackError("state_mismatch"));
|
|
86
|
+
if (!input.code) return Promise.reject(new IdpCallbackError("missing_code"));
|
|
87
|
+
return client().auth.idpToken({
|
|
88
|
+
code: input.code,
|
|
89
|
+
codeVerifier: input.codeVerifier,
|
|
90
|
+
cartToken: input.cartToken
|
|
91
|
+
});
|
|
92
|
+
},
|
|
39
93
|
/** 토큰 갱신 — refreshToken은 한 번 쓰면 폐기되고 새 쌍이 온다(회전) */
|
|
40
94
|
refresh(refreshToken) {
|
|
41
95
|
return client().auth.refresh(refreshToken);
|
|
@@ -63,4 +117,4 @@ function createStorefrontAuth(options) {
|
|
|
63
117
|
};
|
|
64
118
|
}
|
|
65
119
|
//#endregion
|
|
66
|
-
export { ApiError, createStorefrontAuth };
|
|
120
|
+
export { ApiError, IdpCallbackError, createStorefrontAuth };
|
|
@@ -155,7 +155,12 @@ const anonymousSessionSchema = z.object({
|
|
|
155
155
|
* 진행 중인 주문(결제 대기~배송 중)·클레임·결제가 있으면 409 `WITHDRAWAL_BLOCKED`, 비밀번호가 틀리면
|
|
156
156
|
* 403 `PASSWORD_MISMATCH`다(로그인과 같은 5회 잠금).
|
|
157
157
|
*/
|
|
158
|
-
const withdrawRequestSchema = z.object({
|
|
158
|
+
const withdrawRequestSchema = z.object({
|
|
159
|
+
/**
|
|
160
|
+
* 비밀번호로 가입한 구매자는 필수. 소셜 로그인으로만 가입한 구매자는 비밀번호가 없어 생략하고, 대신 최근 10분 안에
|
|
161
|
+
* 로그인한 액세스 토큰이어야 한다(아니면 403 `REAUTH_REQUIRED` — 다시 로그인한 뒤 호출)
|
|
162
|
+
*/
|
|
163
|
+
password: z.string().min(1).optional() });
|
|
159
164
|
/** `WITHDRAWAL_BLOCKED`의 `error.details` — 탈퇴를 막는 항목 수 */
|
|
160
165
|
const withdrawalBlockedDetailsSchema = z.object({
|
|
161
166
|
/** 결제 대기~배송 중인 주문 상품 */
|
|
@@ -165,6 +170,31 @@ const withdrawalBlockedDetailsSchema = z.object({
|
|
|
165
170
|
/** 승인 중이거나 결제창이 열려 있을 수 있는 결제(결제창은 만료되면 풀린다) */
|
|
166
171
|
pendingPayments: z.number().int()
|
|
167
172
|
});
|
|
173
|
+
/** 소셜 로그인 공급자 */
|
|
174
|
+
const socialProviderSchema = z.enum([
|
|
175
|
+
"kakao",
|
|
176
|
+
"naver",
|
|
177
|
+
"google"
|
|
178
|
+
]);
|
|
179
|
+
/** `POST /auth/idp/{provider}/authorize` 본문 — PKCE(S256)와 state는 SDK가 만든다 */
|
|
180
|
+
const idpAuthorizeRequestSchema = z.object({
|
|
181
|
+
/** 로그인을 마치고 돌아올 스토어프런트 주소. 스토어프런트 도메인 목록 안이어야 한다 */
|
|
182
|
+
redirectUri: z.url(),
|
|
183
|
+
/** base64url(SHA-256(codeVerifier)) */
|
|
184
|
+
codeChallenge: z.string().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
185
|
+
/** 스토어프런트가 만든 값 — 돌아올 때 그대로 붙어 온다. 세션에 저장해 두고 비교한다 */
|
|
186
|
+
state: z.string().min(16).max(128)
|
|
187
|
+
});
|
|
188
|
+
const idpAuthorizeResponseSchema = z.object({
|
|
189
|
+
/** 구매자를 보낼 공급자 로그인 주소 */
|
|
190
|
+
url: z.url() });
|
|
191
|
+
/** `POST /auth/idp/token` 본문 — 돌아온 1회용 code를 구매자 토큰으로 바꾼다 */
|
|
192
|
+
const idpTokenRequestSchema = z.object({
|
|
193
|
+
code: z.string().min(1),
|
|
194
|
+
codeVerifier: z.string().regex(/^[A-Za-z0-9._~-]{43,128}$/),
|
|
195
|
+
/** 비회원 장바구니 토큰 — 주면 회원 장바구니로 합친다 */
|
|
196
|
+
cartToken: z.string().optional()
|
|
197
|
+
});
|
|
168
198
|
//#endregion
|
|
169
199
|
//#region src/schemas/cart.ts
|
|
170
200
|
const cartItemSchema = z.object({
|
|
@@ -512,7 +542,8 @@ const createInquiryRequestSchema = z.object({
|
|
|
512
542
|
//#region src/schemas/member.ts
|
|
513
543
|
const memberSchema = z.object({
|
|
514
544
|
memberId: z.string(),
|
|
515
|
-
|
|
545
|
+
/** 소셜 가입에서 공급자가 이메일을 주지 않았으면 null */
|
|
546
|
+
email: z.email().nullable(),
|
|
516
547
|
name: z.string(),
|
|
517
548
|
phone: z.string().nullable(),
|
|
518
549
|
point: z.number().int(),
|
|
@@ -642,6 +673,32 @@ const updateReviewRequestSchema = z.object({
|
|
|
642
673
|
images: z.array(z.url()).max(5).optional()
|
|
643
674
|
}).refine((value) => Object.values(value).some((v) => v !== void 0), { message: "최소 1개 필드가 필요합니다" });
|
|
644
675
|
//#endregion
|
|
676
|
+
//#region src/schemas/store.ts
|
|
677
|
+
/**
|
|
678
|
+
* 스토어 공개 설정 — `GET /store`. 스토어프런트가 화면을 그릴 때 읽는 값만 담는다.
|
|
679
|
+
*/
|
|
680
|
+
const storefrontStoreSchema = z.object({
|
|
681
|
+
storeCode: z.string(),
|
|
682
|
+
name: z.string(),
|
|
683
|
+
logoUrl: z.string().nullable(),
|
|
684
|
+
customerCenterPhone: z.string().nullable(),
|
|
685
|
+
businessHours: z.string().nullable(),
|
|
686
|
+
/**
|
|
687
|
+
* 비회원 주문 허용. false면 로그인하지 않은 구매자의 주문서 생성·결제 요청이 403 `GUEST_CHECKOUT_DISABLED`다.
|
|
688
|
+
* 장바구니 담기는 된다 — 주문 버튼을 로그인 안내로 바꾸면 된다
|
|
689
|
+
*/
|
|
690
|
+
guestCheckout: z.boolean(),
|
|
691
|
+
/**
|
|
692
|
+
* 켜진 로그인 방법 — 화면에 이 순서대로 버튼을 그리면 된다. `password`가 없으면 이메일·비밀번호 가입·로그인이 꺼져 있다
|
|
693
|
+
*/
|
|
694
|
+
loginMethods: z.array(z.enum([
|
|
695
|
+
"password",
|
|
696
|
+
"kakao",
|
|
697
|
+
"naver",
|
|
698
|
+
"google"
|
|
699
|
+
]))
|
|
700
|
+
});
|
|
701
|
+
//#endregion
|
|
645
702
|
//#region src/client.ts
|
|
646
703
|
const productPageSchema = pageSchema(productCardSchema).extend({ facets: productFacetsSchema.optional() });
|
|
647
704
|
const reviewPageSchema = pageSchema(publicReviewSchema).extend({ summary: reviewSummarySchema.optional() });
|
|
@@ -695,9 +752,16 @@ function createStorefrontClient(options) {
|
|
|
695
752
|
login: (body) => http.request("POST", "/auth/login", tokenPairSchema, { body: loginRequestSchema.parse(body) }),
|
|
696
753
|
refresh: (refreshToken) => http.request("POST", "/auth/token/refresh", tokenPairSchema, { body: { refreshToken } }),
|
|
697
754
|
logout: () => http.requestVoid("POST", "/auth/logout"),
|
|
755
|
+
/** 소셜 로그인 시작 — 공급자 로그인 주소를 받는다. PKCE·state는 `@sayren/storefront-sdk/auth`의 `idp()`가 만든다 */
|
|
756
|
+
idpAuthorize: (provider, body) => http.request("POST", path`/auth/idp/${provider}/authorize`, idpAuthorizeResponseSchema, { body: idpAuthorizeRequestSchema.parse(body) }),
|
|
757
|
+
/** 소셜 로그인 마무리 — 돌아온 1회용 code + codeVerifier를 구매자 토큰으로 바꾼다 */
|
|
758
|
+
idpToken: (body) => http.request("POST", "/auth/idp/token", tokenPairSchema, { body: idpTokenRequestSchema.parse(body) }),
|
|
698
759
|
/** 구매자 탈퇴 — 로그인 상태(accessToken)에서 비밀번호로 본인 확인. 성공하면 토큰은 모두 무효 */
|
|
699
760
|
withdraw: (body) => http.requestVoid("POST", "/auth/withdraw", { body: withdrawRequestSchema.parse(body) })
|
|
700
761
|
},
|
|
762
|
+
store: {
|
|
763
|
+
/** 스토어 공개 설정 — 이름·고객센터·비회원 주문 허용 여부 */
|
|
764
|
+
get: () => http.request("GET", "/store", storefrontStoreSchema) },
|
|
701
765
|
catalog: {
|
|
702
766
|
listCategories: () => http.request("GET", "/categories", z.array(categoryNodeSchema)),
|
|
703
767
|
searchProducts: (params) => http.request("GET", "/products", productPageSchema, { query: params }),
|
|
@@ -790,4 +854,4 @@ getStatus: (paymentId) => http.request("GET", path`/payments/${paymentId}`, paym
|
|
|
790
854
|
};
|
|
791
855
|
}
|
|
792
856
|
//#endregion
|
|
793
|
-
export {
|
|
857
|
+
export { idpAuthorizeRequestSchema as $, pageSchema as A, pgParamsSchema as B, claimTypeSchema as C, claimStatusSchema as D, myClaimSchema as E, createCheckoutRequestSchema as F, productDetailSchema as G, shippingAddressInputSchema as H, guestInfoSchema as I, addCartItemRequestSchema as J, productFacetsSchema as K, paymentMethodSchema as L, checkoutSessionSchema as M, confirmPaymentRequestSchema as N, orderItemStatusSchema as O, confirmPaymentResultSchema as P, anonymousSessionSchema as Q, paymentParamsSchema as R, claimReasonSchema as S, createClaimResultSchema as T, categoryNodeSchema as U, requestPaymentRequestSchema as V, productCardSchema as W, cartSchema as X, cartItemSchema as Y, updateCartItemRequestSchema as Z, createInquiryRequestSchema as _, publicReviewSchema as a, tokenPairSchema as at, customerInquiryCategorySchema as b, writableReviewSchema as c, ApiError as ct, myOrderSchema as d, unwrapData as dt, idpAuthorizeResponseSchema as et, orderShippingAddressSchema as f, updateProfileRequestSchema as g, memberSchema as h, createReviewResultSchema as i, socialProviderSchema as it, PAYMENT_STATUS_VALUES as j, pageInfoSchema as k, deliveryTrackingSchema as l, buildQuery as lt, memberAddressSchema as m, storefrontStoreSchema as n, loginRequestSchema as nt, reviewSummarySchema as o, withdrawRequestSchema as ot, memberAddressRequestSchema as p, productSortSchema as q, createReviewRequestSchema as r, signupRequestSchema as rt, updateReviewRequestSchema as s, withdrawalBlockedDetailsSchema as st, createStorefrontClient as t, idpTokenRequestSchema as tt, myOrderItemSchema as u, toApiError as ut, publicInquirySchema as v, createClaimRequestSchema as w, customerInquirySchema as x, createCustomerInquiryRequestSchema as y, paymentStatusSchema as z };
|
|
@@ -41,7 +41,7 @@ type AnonymousSession = z.infer<typeof anonymousSessionSchema>;
|
|
|
41
41
|
* 403 `PASSWORD_MISMATCH`다(로그인과 같은 5회 잠금).
|
|
42
42
|
*/
|
|
43
43
|
declare const withdrawRequestSchema: z.ZodObject<{
|
|
44
|
-
password: z.ZodString
|
|
44
|
+
password: z.ZodOptional<z.ZodString>;
|
|
45
45
|
}, z.core.$strip>;
|
|
46
46
|
type WithdrawRequest = z.infer<typeof withdrawRequestSchema>;
|
|
47
47
|
/** `WITHDRAWAL_BLOCKED`의 `error.details` — 탈퇴를 막는 항목 수 */
|
|
@@ -51,6 +51,30 @@ declare const withdrawalBlockedDetailsSchema: z.ZodObject<{
|
|
|
51
51
|
pendingPayments: z.ZodNumber;
|
|
52
52
|
}, z.core.$strip>;
|
|
53
53
|
type WithdrawalBlockedDetails = z.infer<typeof withdrawalBlockedDetailsSchema>;
|
|
54
|
+
/** 소셜 로그인 공급자 */
|
|
55
|
+
declare const socialProviderSchema: z.ZodEnum<{
|
|
56
|
+
kakao: "kakao";
|
|
57
|
+
naver: "naver";
|
|
58
|
+
google: "google";
|
|
59
|
+
}>;
|
|
60
|
+
type SocialProvider = z.infer<typeof socialProviderSchema>;
|
|
61
|
+
/** `POST /auth/idp/{provider}/authorize` 본문 — PKCE(S256)와 state는 SDK가 만든다 */
|
|
62
|
+
declare const idpAuthorizeRequestSchema: z.ZodObject<{
|
|
63
|
+
redirectUri: z.ZodURL;
|
|
64
|
+
codeChallenge: z.ZodString;
|
|
65
|
+
state: z.ZodString;
|
|
66
|
+
}, z.core.$strip>;
|
|
67
|
+
type IdpAuthorizeRequest = z.infer<typeof idpAuthorizeRequestSchema>;
|
|
68
|
+
declare const idpAuthorizeResponseSchema: z.ZodObject<{
|
|
69
|
+
url: z.ZodURL;
|
|
70
|
+
}, z.core.$strip>;
|
|
71
|
+
/** `POST /auth/idp/token` 본문 — 돌아온 1회용 code를 구매자 토큰으로 바꾼다 */
|
|
72
|
+
declare const idpTokenRequestSchema: z.ZodObject<{
|
|
73
|
+
code: z.ZodString;
|
|
74
|
+
codeVerifier: z.ZodString;
|
|
75
|
+
cartToken: z.ZodOptional<z.ZodString>;
|
|
76
|
+
}, z.core.$strip>;
|
|
77
|
+
type IdpTokenRequest = z.infer<typeof idpTokenRequestSchema>;
|
|
54
78
|
//#endregion
|
|
55
79
|
//#region src/http.d.ts
|
|
56
80
|
declare class ApiError extends Error {
|
|
@@ -68,4 +92,4 @@ type TokenSource = string | null | undefined | (() => string | null | undefined)
|
|
|
68
92
|
type Query = Record<string, string | number | boolean | undefined>;
|
|
69
93
|
declare function buildQuery(query?: Query): string;
|
|
70
94
|
//#endregion
|
|
71
|
-
export { AnonymousSession as a,
|
|
95
|
+
export { withdrawalBlockedDetailsSchema as C, withdrawRequestSchema as S, idpTokenRequestSchema as _, AnonymousSession as a, socialProviderSchema as b, LoginRequest as c, TokenPair as d, WithdrawRequest as f, idpAuthorizeResponseSchema as g, idpAuthorizeRequestSchema as h, buildQuery as i, SignupRequest as l, anonymousSessionSchema as m, Query as n, IdpAuthorizeRequest as o, WithdrawalBlockedDetails as p, TokenSource as r, IdpTokenRequest as s, ApiError as t, SocialProvider as u, loginRequestSchema as v, tokenPairSchema as x, signupRequestSchema as y };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as AnonymousSession, c as
|
|
1
|
+
import { C as withdrawalBlockedDetailsSchema, S as withdrawRequestSchema, _ as idpTokenRequestSchema, a as AnonymousSession, b as socialProviderSchema, c as LoginRequest, d as TokenPair, f as WithdrawRequest, g as idpAuthorizeResponseSchema, h as idpAuthorizeRequestSchema, i as buildQuery, l as SignupRequest, m as anonymousSessionSchema, n as Query, o as IdpAuthorizeRequest, p as WithdrawalBlockedDetails, r as TokenSource, s as IdpTokenRequest, t as ApiError, u as SocialProvider, v as loginRequestSchema, x as tokenPairSchema, y as signupRequestSchema } from "./http-CqAR0_w4.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/analytics-ids.d.ts
|
|
4
4
|
/**
|
|
@@ -489,7 +489,7 @@ export type CreateInquiryRequest = z.infer<typeof createInquiryRequestSchema>;
|
|
|
489
489
|
//#region src/schemas/member.d.ts
|
|
490
490
|
export declare const memberSchema: z.ZodObject<{
|
|
491
491
|
memberId: z.ZodString;
|
|
492
|
-
email: z.ZodEmail
|
|
492
|
+
email: z.ZodNullable<z.ZodEmail>;
|
|
493
493
|
name: z.ZodString;
|
|
494
494
|
phone: z.ZodNullable<z.ZodString>;
|
|
495
495
|
point: z.ZodNumber;
|
|
@@ -672,7 +672,7 @@ export declare function createStorefrontClient(options: StorefrontClientOptions)
|
|
|
672
672
|
/** session.userinfo() 위임 — session 미전달 시 에러 */
|
|
673
673
|
userinfo(): Promise<{
|
|
674
674
|
memberId: string;
|
|
675
|
-
email: string;
|
|
675
|
+
email: string | null;
|
|
676
676
|
name: string;
|
|
677
677
|
phone: string | null;
|
|
678
678
|
point: number;
|
|
@@ -701,9 +701,31 @@ export declare function createStorefrontClient(options: StorefrontClientOptions)
|
|
|
701
701
|
expiresIn: number;
|
|
702
702
|
}>;
|
|
703
703
|
logout: () => Promise<void>;
|
|
704
|
+
/** 소셜 로그인 시작 — 공급자 로그인 주소를 받는다. PKCE·state는 `@sayren/storefront-sdk/auth`의 `idp()`가 만든다 */
|
|
705
|
+
idpAuthorize: (provider: SocialProvider, body: IdpAuthorizeRequest) => Promise<{
|
|
706
|
+
url: string;
|
|
707
|
+
}>;
|
|
708
|
+
/** 소셜 로그인 마무리 — 돌아온 1회용 code + codeVerifier를 구매자 토큰으로 바꾼다 */
|
|
709
|
+
idpToken: (body: IdpTokenRequest) => Promise<{
|
|
710
|
+
accessToken: string;
|
|
711
|
+
refreshToken: string;
|
|
712
|
+
expiresIn: number;
|
|
713
|
+
}>;
|
|
704
714
|
/** 구매자 탈퇴 — 로그인 상태(accessToken)에서 비밀번호로 본인 확인. 성공하면 토큰은 모두 무효 */
|
|
705
715
|
withdraw: (body: WithdrawRequest) => Promise<void>;
|
|
706
716
|
};
|
|
717
|
+
store: {
|
|
718
|
+
/** 스토어 공개 설정 — 이름·고객센터·비회원 주문 허용 여부 */
|
|
719
|
+
get: () => Promise<{
|
|
720
|
+
storeCode: string;
|
|
721
|
+
name: string;
|
|
722
|
+
logoUrl: string | null;
|
|
723
|
+
customerCenterPhone: string | null;
|
|
724
|
+
businessHours: string | null;
|
|
725
|
+
guestCheckout: boolean;
|
|
726
|
+
loginMethods: ("password" | "kakao" | "naver" | "google")[];
|
|
727
|
+
}>;
|
|
728
|
+
};
|
|
707
729
|
catalog: {
|
|
708
730
|
listCategories: () => Promise<CategoryNode[]>;
|
|
709
731
|
searchProducts: (params?: ProductSearchParams) => Promise<{
|
|
@@ -1129,7 +1151,7 @@ export declare function createStorefrontClient(options: StorefrontClientOptions)
|
|
|
1129
1151
|
member: {
|
|
1130
1152
|
me: () => Promise<{
|
|
1131
1153
|
memberId: string;
|
|
1132
|
-
email: string;
|
|
1154
|
+
email: string | null;
|
|
1133
1155
|
name: string;
|
|
1134
1156
|
phone: string | null;
|
|
1135
1157
|
point: number;
|
|
@@ -1485,4 +1507,24 @@ export declare const deliveryTrackingSchema: z.ZodObject<{
|
|
|
1485
1507
|
}, z.core.$strip>;
|
|
1486
1508
|
export type DeliveryTracking = z.infer<typeof deliveryTrackingSchema>;
|
|
1487
1509
|
//#endregion
|
|
1488
|
-
|
|
1510
|
+
//#region src/schemas/store.d.ts
|
|
1511
|
+
/**
|
|
1512
|
+
* 스토어 공개 설정 — `GET /store`. 스토어프런트가 화면을 그릴 때 읽는 값만 담는다.
|
|
1513
|
+
*/
|
|
1514
|
+
export declare const storefrontStoreSchema: z.ZodObject<{
|
|
1515
|
+
storeCode: z.ZodString;
|
|
1516
|
+
name: z.ZodString;
|
|
1517
|
+
logoUrl: z.ZodNullable<z.ZodString>;
|
|
1518
|
+
customerCenterPhone: z.ZodNullable<z.ZodString>;
|
|
1519
|
+
businessHours: z.ZodNullable<z.ZodString>;
|
|
1520
|
+
guestCheckout: z.ZodBoolean;
|
|
1521
|
+
loginMethods: z.ZodArray<z.ZodEnum<{
|
|
1522
|
+
password: "password";
|
|
1523
|
+
kakao: "kakao";
|
|
1524
|
+
naver: "naver";
|
|
1525
|
+
google: "google";
|
|
1526
|
+
}>>;
|
|
1527
|
+
}, z.core.$strip>;
|
|
1528
|
+
export type StorefrontStore = z.infer<typeof storefrontStoreSchema>;
|
|
1529
|
+
//#endregion
|
|
1530
|
+
export { AnonymousSession, ApiError, IdpAuthorizeRequest, IdpTokenRequest, LoginRequest, type Query, type SessionStorage, SignupRequest, SocialProvider, type StorefrontClient, type StorefrontClientOptions, type StorefrontSession, type StorefrontSessionOptions, TokenPair, type TokenSource, WithdrawRequest, WithdrawalBlockedDetails, anonymousSessionSchema, buildQuery, idpAuthorizeRequestSchema, idpAuthorizeResponseSchema, idpTokenRequestSchema, loginRequestSchema, signupRequestSchema, socialProviderSchema, tokenPairSchema, withdrawRequestSchema, withdrawalBlockedDetailsSchema };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as ANALYTICS_VISITOR_HEADER, c as analyticsClientEventSchema, i as ANALYTICS_VISITOR_COOKIE, l as analyticsIdSchema, n as ANALYTICS_SESSION_COOKIE, o as analyticsBatchSchema, r as ANALYTICS_SESSION_HEADER, s as analyticsClientEventNameSchema, t as ANALYTICS_MAX_BATCH_BYTES, u as analyticsServerEventNameSchema } from "./analytics-CgD70OqH.mjs";
|
|
2
2
|
import { t as analyticsIdsFromCookie } from "./analytics-ids-COElZiI3.mjs";
|
|
3
|
-
import { $ as
|
|
3
|
+
import { $ as idpAuthorizeRequestSchema, A as pageSchema, B as pgParamsSchema, C as claimTypeSchema, D as claimStatusSchema, E as myClaimSchema, F as createCheckoutRequestSchema, G as productDetailSchema, H as shippingAddressInputSchema, I as guestInfoSchema, J as addCartItemRequestSchema, K as productFacetsSchema, L as paymentMethodSchema, M as checkoutSessionSchema, N as confirmPaymentRequestSchema, O as orderItemStatusSchema, P as confirmPaymentResultSchema, Q as anonymousSessionSchema, R as paymentParamsSchema, S as claimReasonSchema, T as createClaimResultSchema, U as categoryNodeSchema, V as requestPaymentRequestSchema, W as productCardSchema, X as cartSchema, Y as cartItemSchema, Z as updateCartItemRequestSchema, _ as createInquiryRequestSchema, a as publicReviewSchema, at as tokenPairSchema, b as customerInquiryCategorySchema, c as writableReviewSchema, ct as ApiError, d as myOrderSchema, dt as unwrapData, et as idpAuthorizeResponseSchema, f as orderShippingAddressSchema, g as updateProfileRequestSchema, h as memberSchema, i as createReviewResultSchema, it as socialProviderSchema, j as PAYMENT_STATUS_VALUES, k as pageInfoSchema, l as deliveryTrackingSchema, lt as buildQuery, m as memberAddressSchema, n as storefrontStoreSchema, nt as loginRequestSchema, o as reviewSummarySchema, ot as withdrawRequestSchema, p as memberAddressRequestSchema, q as productSortSchema, r as createReviewRequestSchema, rt as signupRequestSchema, s as updateReviewRequestSchema, st as withdrawalBlockedDetailsSchema, t as createStorefrontClient, tt as idpTokenRequestSchema, u as myOrderItemSchema, ut as toApiError, v as publicInquirySchema, w as createClaimRequestSchema, x as customerInquirySchema, y as createCustomerInquiryRequestSchema, z as paymentStatusSchema } from "./client-Do9-LG5X.mjs";
|
|
4
4
|
//#region src/session.ts
|
|
5
5
|
/** 세션 키 상수 — SDK가 소유한다. store-sdk와 겹치지 않는 별도 네임스페이스. */
|
|
6
6
|
const STOREFRONT_SESSION_KEYS = {
|
|
@@ -124,4 +124,4 @@ function createStorefrontSession(options) {
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
//#endregion
|
|
127
|
-
export { ANALYTICS_MAX_BATCH_BYTES, ANALYTICS_SESSION_COOKIE, ANALYTICS_SESSION_HEADER, ANALYTICS_VISITOR_COOKIE, ANALYTICS_VISITOR_HEADER, ApiError, PAYMENT_STATUS_VALUES, STOREFRONT_SESSION_KEYS, addCartItemRequestSchema, analyticsBatchSchema, analyticsClientEventNameSchema, analyticsClientEventSchema, analyticsIdSchema, analyticsIdsFromCookie, analyticsServerEventNameSchema, anonymousSessionSchema, buildQuery, cartItemSchema, cartSchema, categoryNodeSchema, checkoutSessionSchema, claimReasonSchema, claimStatusSchema, claimTypeSchema, confirmPaymentRequestSchema, confirmPaymentResultSchema, createCheckoutRequestSchema, createClaimRequestSchema, createClaimResultSchema, createCustomerInquiryRequestSchema, createInquiryRequestSchema, createReviewRequestSchema, createReviewResultSchema, createStorefrontClient, createStorefrontSession, customerInquiryCategorySchema, customerInquirySchema, deliveryTrackingSchema, guestInfoSchema, loginRequestSchema, memberAddressRequestSchema, memberAddressSchema, memberSchema, myClaimSchema, myOrderItemSchema, myOrderSchema, orderItemStatusSchema, orderShippingAddressSchema, pageInfoSchema, pageSchema, paymentMethodSchema, paymentParamsSchema, paymentStatusSchema, pgParamsSchema, productCardSchema, productDetailSchema, productFacetsSchema, productSortSchema, publicInquirySchema, publicReviewSchema, requestPaymentRequestSchema, reviewSummarySchema, shippingAddressInputSchema, signupRequestSchema, tokenPairSchema, updateCartItemRequestSchema, updateProfileRequestSchema, updateReviewRequestSchema, withdrawRequestSchema, withdrawalBlockedDetailsSchema, writableReviewSchema };
|
|
127
|
+
export { ANALYTICS_MAX_BATCH_BYTES, ANALYTICS_SESSION_COOKIE, ANALYTICS_SESSION_HEADER, ANALYTICS_VISITOR_COOKIE, ANALYTICS_VISITOR_HEADER, ApiError, PAYMENT_STATUS_VALUES, STOREFRONT_SESSION_KEYS, addCartItemRequestSchema, analyticsBatchSchema, analyticsClientEventNameSchema, analyticsClientEventSchema, analyticsIdSchema, analyticsIdsFromCookie, analyticsServerEventNameSchema, anonymousSessionSchema, buildQuery, cartItemSchema, cartSchema, categoryNodeSchema, checkoutSessionSchema, claimReasonSchema, claimStatusSchema, claimTypeSchema, confirmPaymentRequestSchema, confirmPaymentResultSchema, createCheckoutRequestSchema, createClaimRequestSchema, createClaimResultSchema, createCustomerInquiryRequestSchema, createInquiryRequestSchema, createReviewRequestSchema, createReviewResultSchema, createStorefrontClient, createStorefrontSession, customerInquiryCategorySchema, customerInquirySchema, deliveryTrackingSchema, guestInfoSchema, idpAuthorizeRequestSchema, idpAuthorizeResponseSchema, idpTokenRequestSchema, loginRequestSchema, memberAddressRequestSchema, memberAddressSchema, memberSchema, myClaimSchema, myOrderItemSchema, myOrderSchema, orderItemStatusSchema, orderShippingAddressSchema, pageInfoSchema, pageSchema, paymentMethodSchema, paymentParamsSchema, paymentStatusSchema, pgParamsSchema, productCardSchema, productDetailSchema, productFacetsSchema, productSortSchema, publicInquirySchema, publicReviewSchema, requestPaymentRequestSchema, reviewSummarySchema, shippingAddressInputSchema, signupRequestSchema, socialProviderSchema, storefrontStoreSchema, tokenPairSchema, updateCartItemRequestSchema, updateProfileRequestSchema, updateReviewRequestSchema, withdrawRequestSchema, withdrawalBlockedDetailsSchema, writableReviewSchema };
|
package/package.json
CHANGED
package/src/auth/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
AnonymousSession,
|
|
5
5
|
LoginRequest,
|
|
6
6
|
SignupRequest,
|
|
7
|
+
SocialProvider,
|
|
7
8
|
TokenPair,
|
|
8
9
|
WithdrawRequest,
|
|
9
10
|
} from "../schemas/auth";
|
|
@@ -14,11 +15,60 @@ export type {
|
|
|
14
15
|
AnonymousSession,
|
|
15
16
|
LoginRequest,
|
|
16
17
|
SignupRequest,
|
|
18
|
+
SocialProvider,
|
|
17
19
|
TokenPair,
|
|
18
20
|
WithdrawalBlockedDetails,
|
|
19
21
|
WithdrawRequest,
|
|
20
22
|
} from "../schemas/auth";
|
|
21
23
|
|
|
24
|
+
/** 소셜 로그인 시작 결과 — `codeVerifier`와 `state`는 돌아올 때까지 서버 세션(httpOnly 쿠키 등)에 보관한다 */
|
|
25
|
+
export interface IdpStart {
|
|
26
|
+
/** 구매자를 보낼 공급자 로그인 주소 */
|
|
27
|
+
url: string;
|
|
28
|
+
state: string;
|
|
29
|
+
codeVerifier: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface IdpCallbackInput {
|
|
33
|
+
/** 돌아온 주소의 `code`(성공) — 실패면 `error`가 온다 */
|
|
34
|
+
code: string | null;
|
|
35
|
+
/** 돌아온 주소의 `state` */
|
|
36
|
+
state: string | null;
|
|
37
|
+
/** `idp()`가 준 값 — 보관해 둔 것 */
|
|
38
|
+
expectedState: string;
|
|
39
|
+
codeVerifier: string;
|
|
40
|
+
/** 비회원 장바구니 토큰 — 주면 회원 장바구니로 합친다 */
|
|
41
|
+
cartToken?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** 소셜 로그인 실패 — 구매자가 취소했거나(`access_denied`) state가 맞지 않는다 */
|
|
45
|
+
export class IdpCallbackError extends Error {
|
|
46
|
+
constructor(
|
|
47
|
+
public readonly reason: "access_denied" | "state_mismatch" | "missing_code" | string,
|
|
48
|
+
) {
|
|
49
|
+
super(`소셜 로그인을 마치지 못했어요 (${reason})`);
|
|
50
|
+
this.name = "IdpCallbackError";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function base64url(bytes: Uint8Array): string {
|
|
55
|
+
let binary = "";
|
|
56
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
57
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function randomToken(bytes = 32): string {
|
|
61
|
+
return base64url(globalThis.crypto.getRandomValues(new Uint8Array(bytes)));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function s256(verifier: string): Promise<string> {
|
|
65
|
+
const digest = await globalThis.crypto.subtle.digest(
|
|
66
|
+
"SHA-256",
|
|
67
|
+
new TextEncoder().encode(verifier),
|
|
68
|
+
);
|
|
69
|
+
return base64url(new Uint8Array(digest));
|
|
70
|
+
}
|
|
71
|
+
|
|
22
72
|
export interface StorefrontAuthOptions {
|
|
23
73
|
/** 예: https://api.sayren.app/storefront/v1 */
|
|
24
74
|
baseUrl: string;
|
|
@@ -65,6 +115,37 @@ export function createStorefrontAuth(options: StorefrontAuthOptions) {
|
|
|
65
115
|
signUp(body: SignupRequest): Promise<TokenPair> {
|
|
66
116
|
return client().auth.signup(body);
|
|
67
117
|
},
|
|
118
|
+
/**
|
|
119
|
+
* 소셜 로그인 시작 — PKCE(S256)와 state를 만들고 공급자 로그인 주소를 받는다. 반환한 `state`·`codeVerifier`를
|
|
120
|
+
* 서버 세션에 보관하고 구매자를 `url`로 보낸다. 로그인을 마치면 `redirectUri`로 `?code=&state=`가 붙어 돌아온다.
|
|
121
|
+
* `redirectUri`는 스토어프런트 도메인 목록 안이어야 한다
|
|
122
|
+
*/
|
|
123
|
+
async idp(provider: SocialProvider, options: { redirectUri: string }): Promise<IdpStart> {
|
|
124
|
+
const state = randomToken(24);
|
|
125
|
+
const codeVerifier = randomToken(32);
|
|
126
|
+
const { url } = await client().auth.idpAuthorize(provider, {
|
|
127
|
+
redirectUri: options.redirectUri,
|
|
128
|
+
codeChallenge: await s256(codeVerifier),
|
|
129
|
+
state,
|
|
130
|
+
});
|
|
131
|
+
return { url, state, codeVerifier };
|
|
132
|
+
},
|
|
133
|
+
/**
|
|
134
|
+
* 소셜 로그인 마무리 — 돌아온 `code`를 구매자 토큰으로 바꾼다. state가 다르거나 구매자가 취소했으면
|
|
135
|
+
* `IdpCallbackError`. code는 1회용이고 1분 안에 바꿔야 한다
|
|
136
|
+
*/
|
|
137
|
+
idpCallback(input: IdpCallbackInput & { error?: string | null }): Promise<TokenPair> {
|
|
138
|
+
if (input.error) return Promise.reject(new IdpCallbackError(input.error));
|
|
139
|
+
if (!input.state || input.state !== input.expectedState) {
|
|
140
|
+
return Promise.reject(new IdpCallbackError("state_mismatch"));
|
|
141
|
+
}
|
|
142
|
+
if (!input.code) return Promise.reject(new IdpCallbackError("missing_code"));
|
|
143
|
+
return client().auth.idpToken({
|
|
144
|
+
code: input.code,
|
|
145
|
+
codeVerifier: input.codeVerifier,
|
|
146
|
+
cartToken: input.cartToken,
|
|
147
|
+
});
|
|
148
|
+
},
|
|
68
149
|
/** 토큰 갱신 — refreshToken은 한 번 쓰면 폐기되고 새 쌍이 온다(회전) */
|
|
69
150
|
refresh(refreshToken: string): Promise<TokenPair> {
|
|
70
151
|
return client().auth.refresh(refreshToken);
|
package/src/client.ts
CHANGED
|
@@ -4,7 +4,13 @@ import { ANALYTICS_SESSION_HEADER, ANALYTICS_VISITOR_HEADER } from "./schemas/an
|
|
|
4
4
|
import type { LoginRequest, SignupRequest } from "./schemas/auth";
|
|
5
5
|
import {
|
|
6
6
|
anonymousSessionSchema,
|
|
7
|
+
type IdpAuthorizeRequest,
|
|
8
|
+
type IdpTokenRequest,
|
|
9
|
+
idpAuthorizeRequestSchema,
|
|
10
|
+
idpAuthorizeResponseSchema,
|
|
11
|
+
idpTokenRequestSchema,
|
|
7
12
|
loginRequestSchema,
|
|
13
|
+
type SocialProvider,
|
|
8
14
|
signupRequestSchema,
|
|
9
15
|
tokenPairSchema,
|
|
10
16
|
type WithdrawRequest,
|
|
@@ -73,6 +79,7 @@ import {
|
|
|
73
79
|
updateReviewRequestSchema,
|
|
74
80
|
writableReviewSchema,
|
|
75
81
|
} from "./schemas/review";
|
|
82
|
+
import { storefrontStoreSchema } from "./schemas/store";
|
|
76
83
|
import type { StorefrontSession } from "./session";
|
|
77
84
|
|
|
78
85
|
export interface StorefrontClientOptions {
|
|
@@ -183,11 +190,26 @@ export function createStorefrontClient(options: StorefrontClientOptions) {
|
|
|
183
190
|
refresh: (refreshToken: string) =>
|
|
184
191
|
http.request("POST", "/auth/token/refresh", tokenPairSchema, { body: { refreshToken } }),
|
|
185
192
|
logout: () => http.requestVoid("POST", "/auth/logout"),
|
|
193
|
+
/** 소셜 로그인 시작 — 공급자 로그인 주소를 받는다. PKCE·state는 `@sayren/storefront-sdk/auth`의 `idp()`가 만든다 */
|
|
194
|
+
idpAuthorize: (provider: SocialProvider, body: IdpAuthorizeRequest) =>
|
|
195
|
+
http.request("POST", path`/auth/idp/${provider}/authorize`, idpAuthorizeResponseSchema, {
|
|
196
|
+
body: idpAuthorizeRequestSchema.parse(body),
|
|
197
|
+
}),
|
|
198
|
+
/** 소셜 로그인 마무리 — 돌아온 1회용 code + codeVerifier를 구매자 토큰으로 바꾼다 */
|
|
199
|
+
idpToken: (body: IdpTokenRequest) =>
|
|
200
|
+
http.request("POST", "/auth/idp/token", tokenPairSchema, {
|
|
201
|
+
body: idpTokenRequestSchema.parse(body),
|
|
202
|
+
}),
|
|
186
203
|
/** 구매자 탈퇴 — 로그인 상태(accessToken)에서 비밀번호로 본인 확인. 성공하면 토큰은 모두 무효 */
|
|
187
204
|
withdraw: (body: WithdrawRequest) =>
|
|
188
205
|
http.requestVoid("POST", "/auth/withdraw", { body: withdrawRequestSchema.parse(body) }),
|
|
189
206
|
},
|
|
190
207
|
|
|
208
|
+
store: {
|
|
209
|
+
/** 스토어 공개 설정 — 이름·고객센터·비회원 주문 허용 여부 */
|
|
210
|
+
get: () => http.request("GET", "/store", storefrontStoreSchema),
|
|
211
|
+
},
|
|
212
|
+
|
|
191
213
|
catalog: {
|
|
192
214
|
listCategories: () => http.request("GET", "/categories", z.array(categoryNodeSchema)),
|
|
193
215
|
searchProducts: (params?: ProductSearchParams) =>
|
package/src/index.ts
CHANGED
package/src/schemas/auth.ts
CHANGED
|
@@ -61,7 +61,11 @@ export type AnonymousSession = z.infer<typeof anonymousSessionSchema>;
|
|
|
61
61
|
* 403 `PASSWORD_MISMATCH`다(로그인과 같은 5회 잠금).
|
|
62
62
|
*/
|
|
63
63
|
export const withdrawRequestSchema = z.object({
|
|
64
|
-
|
|
64
|
+
/**
|
|
65
|
+
* 비밀번호로 가입한 구매자는 필수. 소셜 로그인으로만 가입한 구매자는 비밀번호가 없어 생략하고, 대신 최근 10분 안에
|
|
66
|
+
* 로그인한 액세스 토큰이어야 한다(아니면 403 `REAUTH_REQUIRED` — 다시 로그인한 뒤 호출)
|
|
67
|
+
*/
|
|
68
|
+
password: z.string().min(1).optional(),
|
|
65
69
|
});
|
|
66
70
|
|
|
67
71
|
export type WithdrawRequest = z.infer<typeof withdrawRequestSchema>;
|
|
@@ -77,3 +81,32 @@ export const withdrawalBlockedDetailsSchema = z.object({
|
|
|
77
81
|
});
|
|
78
82
|
|
|
79
83
|
export type WithdrawalBlockedDetails = z.infer<typeof withdrawalBlockedDetailsSchema>;
|
|
84
|
+
|
|
85
|
+
/** 소셜 로그인 공급자 */
|
|
86
|
+
export const socialProviderSchema = z.enum(["kakao", "naver", "google"]);
|
|
87
|
+
export type SocialProvider = z.infer<typeof socialProviderSchema>;
|
|
88
|
+
|
|
89
|
+
/** `POST /auth/idp/{provider}/authorize` 본문 — PKCE(S256)와 state는 SDK가 만든다 */
|
|
90
|
+
export const idpAuthorizeRequestSchema = z.object({
|
|
91
|
+
/** 로그인을 마치고 돌아올 스토어프런트 주소. 스토어프런트 도메인 목록 안이어야 한다 */
|
|
92
|
+
redirectUri: z.url(),
|
|
93
|
+
/** base64url(SHA-256(codeVerifier)) */
|
|
94
|
+
codeChallenge: z.string().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
95
|
+
/** 스토어프런트가 만든 값 — 돌아올 때 그대로 붙어 온다. 세션에 저장해 두고 비교한다 */
|
|
96
|
+
state: z.string().min(16).max(128),
|
|
97
|
+
});
|
|
98
|
+
export type IdpAuthorizeRequest = z.infer<typeof idpAuthorizeRequestSchema>;
|
|
99
|
+
|
|
100
|
+
export const idpAuthorizeResponseSchema = z.object({
|
|
101
|
+
/** 구매자를 보낼 공급자 로그인 주소 */
|
|
102
|
+
url: z.url(),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
/** `POST /auth/idp/token` 본문 — 돌아온 1회용 code를 구매자 토큰으로 바꾼다 */
|
|
106
|
+
export const idpTokenRequestSchema = z.object({
|
|
107
|
+
code: z.string().min(1),
|
|
108
|
+
codeVerifier: z.string().regex(/^[A-Za-z0-9._~-]{43,128}$/),
|
|
109
|
+
/** 비회원 장바구니 토큰 — 주면 회원 장바구니로 합친다 */
|
|
110
|
+
cartToken: z.string().optional(),
|
|
111
|
+
});
|
|
112
|
+
export type IdpTokenRequest = z.infer<typeof idpTokenRequestSchema>;
|
package/src/schemas/member.ts
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 스토어 공개 설정 — `GET /store`. 스토어프런트가 화면을 그릴 때 읽는 값만 담는다.
|
|
5
|
+
*/
|
|
6
|
+
export const storefrontStoreSchema = z.object({
|
|
7
|
+
storeCode: z.string(),
|
|
8
|
+
name: z.string(),
|
|
9
|
+
logoUrl: z.string().nullable(),
|
|
10
|
+
customerCenterPhone: z.string().nullable(),
|
|
11
|
+
businessHours: z.string().nullable(),
|
|
12
|
+
/**
|
|
13
|
+
* 비회원 주문 허용. false면 로그인하지 않은 구매자의 주문서 생성·결제 요청이 403 `GUEST_CHECKOUT_DISABLED`다.
|
|
14
|
+
* 장바구니 담기는 된다 — 주문 버튼을 로그인 안내로 바꾸면 된다
|
|
15
|
+
*/
|
|
16
|
+
guestCheckout: z.boolean(),
|
|
17
|
+
/**
|
|
18
|
+
* 켜진 로그인 방법 — 화면에 이 순서대로 버튼을 그리면 된다. `password`가 없으면 이메일·비밀번호 가입·로그인이 꺼져 있다
|
|
19
|
+
*/
|
|
20
|
+
loginMethods: z.array(z.enum(["password", "kakao", "naver", "google"])),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export type StorefrontStore = z.infer<typeof storefrontStoreSchema>;
|