This commit is contained in:
eric
2026-03-13 04:41:51 -05:00
parent 37d0883ce9
commit a193fe4d45
22 changed files with 1119 additions and 468 deletions

View File

@@ -7,30 +7,29 @@ import { getStoredToken, getStoredUser } from "@/app/lib/pocketbase";
import AuthModal from "@/app/components/AuthModal";
import {
getPayEnv,
getRecommendedChannel,
mustUseWxPay,
redirectToPay,
getDeviceFromEnv,
} from "@/app/lib/payment";
import { useState } from "react";
export function CTASection() {
const { user, vip } = useAuthAndVip();
const { user, vip, refetch } = useAuthAndVip();
const paid = !!user && vip;
const [authOpen, setAuthOpen] = useState(false);
const [payRedirecting, setPayRedirecting] = useState(false);
const startPay = (userId: string) => {
const env = getPayEnv();
const channel = mustUseWxPay(env) ? "wxpay" : getRecommendedChannel(env);
const channel = "wxpay";
const device = getDeviceFromEnv(env);
const returnUrl = `${window.location.origin}/course/0/0`;
const returnUrl = `${window.location.origin}/`;
redirectToPay({
user_id: userId,
return_url: returnUrl,
channel,
device,
type: "video",
useJoinConfig: true,
});
};
@@ -40,14 +39,17 @@ export function CTASection() {
setPayRedirecting(true);
try {
if (user?.id) {
startPay(user.id);
return;
}
const meRes = await fetch("/api/auth/me", { credentials: "include" });
const meData = await meRes.json().catch(() => ({}));
// 先验证登录态
let meRes = await fetch("/api/auth/me", { credentials: "include" });
let meData = await meRes.json().catch(() => ({}));
if (meData?.user?.id) {
// 已登录:先校验 VIP已是 VIP 则无需支付
const vipRes = await fetch(`/api/vip/check?email=${encodeURIComponent(meData.user.email)}`, { credentials: "include" });
const vipData = await vipRes.json().catch(() => ({}));
if (vipData?.vip) {
await refetch();
return;
}
startPay(meData.user.id);
return;
}
@@ -61,8 +63,18 @@ export function CTASection() {
body: JSON.stringify({ token: storedToken, record: storedUser }),
credentials: "include",
}).catch(() => null);
startPay(storedUser.id);
return;
meRes = await fetch("/api/auth/me", { credentials: "include" });
meData = await meRes.json().catch(() => ({}));
if (meData?.user?.id) {
const vipRes = await fetch(`/api/vip/check?email=${encodeURIComponent(meData.user.email)}`, { credentials: "include" });
const vipData = await vipRes.json().catch(() => ({}));
if (vipData?.vip) {
await refetch();
return;
}
startPay(meData.user.id);
return;
}
}
setAuthOpen(true);
@@ -110,7 +122,7 @@ export function CTASection() {
onClose={() => setAuthOpen(false)}
onSuccess={() => {
setAuthOpen(false);
void handleEnroll();
void refetch();
}}
/>
</section>

View File

@@ -9,8 +9,6 @@ import { getLastWatched, getProgress } from "@/app/lib/learning";
import AuthModal from "@/app/components/AuthModal";
import {
getPayEnv,
getRecommendedChannel,
mustUseWxPay,
redirectToPay,
getDeviceFromEnv,
} from "@/app/lib/payment";
@@ -18,7 +16,7 @@ import {
const TOTAL_LESSONS = CURRICULUM_CONFIG.modules.reduce((s, m) => s + m.lessons.length, 0);
export function CourseHero() {
const { user, vip } = useAuthAndVip();
const { user, vip, refetch } = useAuthAndVip();
const [last, setLast] = useState<ReturnType<typeof getLastWatched>>(null);
const [progress, setProgress] = useState({ completed: 0, percent: 0 });
const [authOpen, setAuthOpen] = useState(false);
@@ -39,15 +37,16 @@ export function CourseHero() {
const startPay = (userId: string) => {
const env = getPayEnv();
const channel = mustUseWxPay(env) ? "wxpay" : getRecommendedChannel(env);
const channel = "wxpay";
const device = getDeviceFromEnv(env);
const returnUrl = `${window.location.origin}/course/0/0`;
const returnUrl = `${window.location.origin}/`;
redirectToPay({
user_id: userId,
return_url: returnUrl,
channel,
device,
type: "video",
useJoinConfig: true,
});
};
@@ -57,14 +56,17 @@ export function CourseHero() {
setPayRedirecting(true);
try {
if (user?.id) {
startPay(user.id);
return;
}
const meRes = await fetch("/api/auth/me", { credentials: "include" });
const meData = await meRes.json().catch(() => ({}));
// 先验证登录态
let meRes = await fetch("/api/auth/me", { credentials: "include" });
let meData = await meRes.json().catch(() => ({}));
if (meData?.user?.id) {
// 已登录:先校验 VIP已是 VIP 则无需支付
const vipRes = await fetch(`/api/vip/check?email=${encodeURIComponent(meData.user.email)}`, { credentials: "include" });
const vipData = await vipRes.json().catch(() => ({}));
if (vipData?.vip) {
await refetch();
return;
}
startPay(meData.user.id);
return;
}
@@ -78,8 +80,18 @@ export function CourseHero() {
body: JSON.stringify({ token: storedToken, record: storedUser }),
credentials: "include",
}).catch(() => null);
startPay(storedUser.id);
return;
meRes = await fetch("/api/auth/me", { credentials: "include" });
meData = await meRes.json().catch(() => ({}));
if (meData?.user?.id) {
const vipRes = await fetch(`/api/vip/check?email=${encodeURIComponent(meData.user.email)}`, { credentials: "include" });
const vipData = await vipRes.json().catch(() => ({}));
if (vipData?.vip) {
await refetch();
return;
}
startPay(meData.user.id);
return;
}
}
setAuthOpen(true);
@@ -149,7 +161,7 @@ export function CourseHero() {
onClose={() => setAuthOpen(false)}
onSuccess={() => {
setAuthOpen(false);
void handleEnroll();
void refetch();
}}
/>
</section>