Commit 9a313fcf authored by ThinhNC's avatar ThinhNC

Merge branch 'feat/zalo-phone-auth' into 'develop'

feat(auth): streamline login with Zalo SDK and Claymorphism UI

See merge request !32
parents 653c0d50 e693bcf4
import { useState } from "react";
import { getAccessToken, getPhoneNumber } from "zmp-sdk";
import { useNavigate, useSnackbar } from "zmp-ui";
import { authService } from "@/services/auth.service";
import { useAuthStore } from "@/stores/auth-store";
import { getErrorMessage } from "@/lib/error-message";
import { useI18n } from "@/i18n";
interface UseZaloLoginReturn {
handleZaloLogin: () => Promise<void>;
isLoading: boolean;
}
export function useZaloLogin(): UseZaloLoginReturn {
const [isLoading, setIsLoading] = useState(false);
const navigate = useNavigate();
const { openSnackbar } = useSnackbar();
const setAuth = useAuthStore((state) => state.setAuth);
const { t } = useI18n();
const handleZaloLogin = async () => {
setIsLoading(true);
try {
// 1. Lấy Zalo access token (string trực tiếp)
const accessToken = await getAccessToken();
if (!accessToken) {
throw new Error(t("auth.zalo.tokenFailed"));
}
// 2. Lấy số điện thoại từ Zalo SDK
// `number` là field deprecated nhưng trả về SĐT thực trên Mini App
// `token` là flow mới: cần gửi lên server Zalo để đổi lấy SĐT
const phoneResult = await getPhoneNumber();
const phoneNumber = phoneResult.number;
if (!phoneNumber) {
openSnackbar({
type: "warning",
text: t("auth.zalo.phoneDenied"),
});
return;
}
// 3. Gửi lên backend để xác thực + đăng nhập/tạo tài khoản
const response = await authService.loginWithZalo({ accessToken, phoneNumber });
if (response.success && response.data) {
setAuth(
response.data.user,
response.data.accessToken || "",
response.data.refreshToken || ""
);
openSnackbar({
type: "success",
text: t("auth.zalo.loginSuccess"),
});
navigate("/", { replace: true });
} else {
throw new Error(t("auth.zalo.loginFailed"));
}
} catch (error: unknown) {
openSnackbar({
type: "error",
text: getErrorMessage(error, t("auth.zalo.loginFailed")),
});
} finally {
setIsLoading(false);
}
};
return { handleZaloLogin, isLoading };
}
...@@ -160,7 +160,21 @@ ...@@ -160,7 +160,21 @@
"registerNow": "Sign up now", "registerNow": "Sign up now",
"success": "Signed in successfully!", "success": "Signed in successfully!",
"failed": "Sign-in failed", "failed": "Sign-in failed",
"failedDetail": "Sign-in failed. Please check your details and try again." "failedDetail": "Sign-in failed. Please check your details and try again.",
"feature": {
"wallet": "Manage Wallets",
"report": "Reports",
"ai": "AI Assistant"
}
},
"zalo": {
"loginButton": "Sign in with Zalo",
"loggingIn": "Signing in...",
"loginSuccess": "Signed in successfully!",
"loginFailed": "Sign-in failed, please try again.",
"tokenFailed": "Could not retrieve Zalo info, please try again.",
"phoneDenied": "Phone number permission is required to sign in.",
"termsNote": "By signing in, you agree to FinWise's terms of service."
}, },
"register": { "register": {
"header": "Sign Up", "header": "Sign Up",
......
...@@ -160,7 +160,21 @@ ...@@ -160,7 +160,21 @@
"registerNow": "Đăng ký ngay", "registerNow": "Đăng ký ngay",
"success": "Đăng nhập thành công! 🎉", "success": "Đăng nhập thành công! 🎉",
"failed": "Đăng nhập thất bại", "failed": "Đăng nhập thất bại",
"failedDetail": "Đăng nhập thất bại, vui lòng kiểm tra lại thông tin." "failedDetail": "Đăng nhập thất bại, vui lòng kiểm tra lại thông tin.",
"feature": {
"wallet": "Quản lý ví",
"report": "Báo cáo",
"ai": "Trợ lý AI"
}
},
"zalo": {
"loginButton": "Đăng nhập bằng Zalo",
"loggingIn": "Đang đăng nhập...",
"loginSuccess": "Đăng nhập thành công!",
"loginFailed": "Đăng nhập thất bại, vui lòng thử lại.",
"tokenFailed": "Không thể lấy thông tin Zalo, vui lòng thử lại.",
"phoneDenied": "Bạn cần cấp quyền số điện thoại để đăng nhập.",
"termsNote": "Bằng cách đăng nhập, bạn đồng ý với điều khoản sử dụng của FinWise."
}, },
"register": { "register": {
"header": "Đăng Ký", "header": "Đăng Ký",
......
This diff is collapsed.
...@@ -71,4 +71,12 @@ export const authService = { ...@@ -71,4 +71,12 @@ export const authService = {
const response = await apiClient.delete("/auth/sessions"); const response = await apiClient.delete("/auth/sessions");
return response.data; return response.data;
}, },
async loginWithZalo(data: {
accessToken: string;
phoneNumber: string;
}): Promise<ApiResponse<{ user: User; accessToken?: string; refreshToken?: string }>> {
const response = await apiClient.post("/auth/zalo-login", data);
return response.data;
},
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment