Storefront (người mua) #
FE bán hàng (web, app, Zalo Mini App): StorefrontClient (sdk/storefront.ts),
token khách, /api/v1/storefront/*; spec riêng GET /storefront/openapi.json.
Phần chung (khởi tạo client, lỗi, danh sách, ngôn ngữ):
common.md. Giỏ, thanh toán, đơn hàng: FE.md của module (cart,
payments, checkout…).
1. Client và store (ADR-0027) #
- FE bán hàng dùng
StorefrontClient(sdk/storefront.ts), KHÔNG dùngXforgeClient: tài khoản khách (storefrontRegister/storefrontLogin, token khách), xem hàng không cần đăng nhập. Store xác định theo tên miền của store (hoặcx-storekhi dev). Token khách không dùng được ở API quản trị và ngược lại. - Thông tin công khai của store đang gọi:
getStorefrontStore()(GET /api/v1/storefront/store, không cần đăng nhập) →{ code, name, timezone, currency, defaultLang, languages }. Nút đổi ngôn ngữ dựng từlanguages(luôn chứadefaultLang) — KHÔNG ghi cứng danh sách ngôn ngữ trong FE; chọn xong gửix-lang(common §3.1/common §3.2). Store tắt / chủ bị tạm khoá → 403store_inactivenhư mọi route storefront (hiện trang "cửa hàng tạm đóng").
2. Tài khoản khách #
- Tài khoản khách theo cấu hình của store (
getStorefrontConfigs()→core.customer_auth):signupfalse → ẩn đăng ký (server trả 403signup_disabled);requireEmailVerification→ sau đăng ký có thư xác minh, route khách trả 403email_not_verifiedtới khi bấm link (/mevẫn mở để FE hiện trạng thái; gửi lại:storefrontRequestEmailVerification). - Quên mật khẩu khách:
storefrontForgotPassword(luôn 202) → thư có link<tên miền store><resetPath>→storefrontResetPassword({ token, password }). Xác minh:storefrontConfirmEmail({ token }). Trang storefront phải có đúng pathresetPath/verifyPathcủa store (mặc định/account/reset-password?token=và/account/verify-email?token=). storefrontChangePassword/storefrontRevokeAllSessionstrả token mới → lưu ngay.- Xoá tài khoản (bắt buộc với app có đăng ký trên App Store): nút "Xoá tài
khoản" →
storefrontRequestAccountDeletion(202, cần token khách) → báo "kiểm tra email". Thư có link<tên miền store><deletePath>(mặc định/account/delete?token=, hiệu lực 30 phút); trang đó xác nhận lần cuối rồi gọistorefrontDeleteAccount({ token })(204) — xoá token đã lưu và đăng xuất. 401delete_token_invalid→ link sai / đã dùng / hết hạn, gửi lại yêu cầu.core.customer_auth.deletionGraceDays= 0 (mặc định) → xoá NGAY, không hoàn tác. > 0 → chỉ hẹn xoá sau N ngày: mọi phiên chết ngay, khách nhận thư có ngày xoá; ĐĂNG NHẬP LẠI trước ngày đó là huỷ (có thư báo) — trang xác nhận nên nói rõ "đăng nhập lại trong N ngày để giữ tài khoản".
Số điện thoại + mã SMS (ADR-0104):
- Xác minh số (khách đã đăng nhập):
storefrontRequestPhoneVerification({ phone })(202, nhắn mã 6 số, hạn 5 phút) →storefrontConfirmPhone({ code })→{ phone }./mecóphoneVerifiedAt. 409phone_taken= số đã xác minh ở tài khoản khác. Đăng nhập / đăng ký bằng số (store bật
core.customer_auth.phoneLogin, xemgetStorefrontConfigs(); tắt → 403phone_login_disabled):storefrontRequestPhoneCode({ phone }) ← luôn 202; nút "gửi lại" sau 60 giây storefrontPhoneLogin({ phone, code }) ├─ data = { …tokens, created: false } ← số đã có tài khoản ├─ 400 phone_signup_details_required ← số mới: hỏi email + họ tên, │ storefrontPhoneLogin({ phone, code, email, name }) (CÙNG mã) │ → { …tokens, created: true } ├─ 409 email_taken ← email đã có tài khoản: đăng nhập │ bằng email rồi xác minh số └─ 401 otp_invalid ← sai / hết hạn (sai 5 lần → xin mã mới)
- Số nhập tự do (
0912…,+84 912…); server chuẩn hoá thành84912345678. 400phone_invalid, 503sms_unavailable(store chưa có cổng SMS — ẩn nút), 429 khi xin mã quá nhiều.
- Xác minh số (khách đã đăng nhập):
3. Đăng nhập mạng xã hội (ADR-0048) #
storefrontSocialProviders() → nút cho từng provider (clientId công khai cho
SDK). Kết quả đăng nhập giống storefrontLogin + outcome (created /
signed_in / linked).
- Native / SDK (app, Google One Tap, Apple JS popup): Google / Apple — gọi
storefrontSocialNoncetrước, đưanoncevào SDK (app iOS đưa SHA-256 hex cho Apple như thường lệ), gửiidTokentớistorefrontSocialSignIn(Apple: kèmnameở lần đầu, và LUÔN kèmauthorizationCodecủa SDK — server đổi lấy token để thu hồi khi khách xoá tài khoản, Apple bắt buộc). Facebook / Zalo gửiaccessToken. - Facebook Limited Login (iOS,
tracking: .limited):noncecủastorefrontSocialNoncevàoLoginConfiguration(nonce:), gửiAuthenticationToken.tokenStringlàmidToken(providerfacebook). Cùng người với đăng nhập Facebook thường → cùng tài khoản.nonce: trueở danh sách provider của Facebook nghĩa là "id_token cần nonce", access token thì không. - TikTok: web dùng luồng redirect (provider
tiktok). App: SDK Login Kit trảauthCode+codeVerifier(PKCE của SDK) → gửi{ authorizationCode, codeVerifier, redirectUri }(đúng redirect URI https SDK đã dùng) tớistorefrontSocialSignIn. TikTok không cho email → luôn quasocial_email_requiredở lần đầu. - Zalo Mini App: provider
zalo_miniapp(không có redirect). GửiaccessTokencủagetAccessToken(); muốn lấy số điện thoại thì xin quyềngetPhoneNumber()rồi gửi kèmphoneTokenNGAY (dùng một lần, hết hạn 2 phút; 401social_phone_token_invalid→ xin lại hoặc gửi không kèm). Số chỉ điền khi tài khoản chưa có số — không ghi đè. Danh tính hiện làzaloởstorefrontListIdentities(cùng người với Zalo Login trên web nếu store dùng chung Zalo App). Không email →social_email_requiredở lần đầu. - Danh sách provider có thể thêm giá trị mới bất cứ lúc nào — bỏ qua provider không biết.
- Redirect (web):
storefrontSocialStart({ redirectUri })→ lưuflowKeyvào sessionStorage, chuyển trình duyệt tớiurl. TrangredirectUri(phải nằm trên tên miền đã đăng ký của store, và khai với provider) lấycode,state→storefrontSocialExchange({ state, code, flowKey }). Apple POST form vềredirectUri(trang phải nhận POST) — web nên dùng Apple JS popup. - 409
social_email_required(Zalo / TikTok / Facebook không cho email): hỏi email →storefrontSocialComplete({ ticket: error.details.ticket, email }).email_taken→ mời đăng nhập tài khoản đó rồi nối; vé vẫn dùng được. - 409
social_link_required: email đã có tài khoản mà provider không xác minh → đăng nhập bằng mật khẩu rồi nối. - Trang tài khoản:
storefrontListIdentities, nối bằngstorefrontSocialLink(native) hoặcstorefrontSocialLinkStart/storefrontSocialLinkExchange(redirect, cần token khách), gỡ bằngstorefrontUnlinkIdentity(409social_last_login_methodkhi là cách đăng nhập cuối mà chưa có mật khẩu → gợi ý "quên mật khẩu" để đặt).