对接获取积分

This commit is contained in:
海龙 2025-08-28 18:40:17 +08:00
parent 4b0ae8e32c
commit 506438dc7f
4 changed files with 79 additions and 13 deletions

View File

@ -10,7 +10,7 @@ export default function payCallback() {
const canceled = searchParams.get("canceled")||false;
useEffect(() => {
window.opener.postMessage(
window.opener?.postMessage(
{ type: "payment-callback", canceled, sessionId, userId },
"*"
);

View File

@ -93,7 +93,7 @@ function HomeModule5() {
if (!result.successful || !result.data) {
throw new Error("create checkout session failed");
}
window.opener.postMessage({ type: "waiting-payment" }, "*");
window.opener?.postMessage({ type: "waiting-payment" }, "*");
// 2. 直接跳转到Stripe托管页面就这么简单
window.location.href = result.data.checkout_url;
} catch (error) {

View File

@ -18,7 +18,7 @@ import ReactDOM from 'react-dom';
import { useRouter, usePathname } from 'next/navigation';
import React, { useRef, useEffect, useLayoutEffect, useState } from 'react';
import { logoutUser } from '@/lib/auth';
import { createPortalSession, redirectToPortal } from '@/lib/stripe';
import { createPortalSession, redirectToPortal, getUserSubscriptionInfo } from '@/lib/stripe';
interface User {
@ -46,6 +46,28 @@ export function TopBar({
const [isLogin, setIsLogin] = useState(false);
const pathname = usePathname();
const [isManagingSubscription, setIsManagingSubscription] = useState(false);
const [subscriptionStatus, setSubscriptionStatus] = useState<string>('');
const [credits, setCredits] = useState<number>(100);
const [isLoadingSubscription, setIsLoadingSubscription] = useState(false);
// 获取用户订阅信息
const fetchSubscriptionInfo = async () => {
if (!currentUser?.id) return;
setIsLoadingSubscription(true);
try {
const response = await getUserSubscriptionInfo(String(currentUser.id));
if (response.successful && response.data) {
setSubscriptionStatus(response.data.subscription_status);
setCredits(response.data.credits);
}
} catch (error) {
console.error('获取订阅信息失败:', error);
} finally {
setIsLoadingSubscription(false);
}
};
useEffect(() => {
const currentUser = localStorage.getItem("currentUser");
if (JSON.parse(currentUser || "{}")?.token) {
@ -213,6 +235,10 @@ export function TopBar({
size="sm"
onClick={() => {
console.log("Button clicked, current isOpen:", isOpen);
if (!isOpen) {
// 每次打开菜单时重新获取订阅信息
fetchSubscriptionInfo();
}
setIsOpen(!isOpen);
}}
data-alt="user-menu-trigger"
@ -270,7 +296,7 @@ export function TopBar({
<div className="flex items-center space-x-2">
<Sparkles className="h-4 w-4" />
<span className="text-white underline text-sm">
100 credits
{isLoadingSubscription ? '...' : `${credits} credits`}
</span>
</div>
<Button
@ -283,15 +309,17 @@ export function TopBar({
>
Upgrade
</Button>
<Button
variant="outline"
size="sm"
className="text-white border-white hover:bg-white/10 rounded-full px-3 py-1 text-[10px]"
onClick={handleManageSubscription}
disabled={isManagingSubscription}
>
Manage
</Button>
{subscriptionStatus === 'ACTIVE' && (
<Button
variant="outline"
size="sm"
className="text-white border-white hover:bg-white/10 rounded-full px-3 py-1 text-[10px]"
onClick={handleManageSubscription}
disabled={isManagingSubscription}
>
Manage
</Button>
)}
</div>
{/* Menu Items */}

View File

@ -138,6 +138,44 @@ export async function createPortalSession(
throw error;
}
}
/**
*
* @param {string} userId - ID
* @returns {Promise<UserSubscriptionInfoResponse>} -
* @throws {Error} -
*/
export async function getUserSubscriptionInfo(
userId: string
): Promise<UserSubscriptionInfoResponse> {
if (!userId) {
throw new Error('userId不能为空');
}
try {
return await get<UserSubscriptionInfoResponse>(`/api/subscription/user/info?user_id=${userId}`);
} catch (error) {
console.error('获取用户订阅信息失败:', error);
throw error;
}
}
/** 用户订阅信息响应体 */
export interface UserSubscriptionInfoResponse {
/** 状态码 */
code: number;
/** 消息 */
message: string;
/** 数据 */
data: {
/** 剩余点数 */
credits: number;
/** 订阅状态 */
subscription_status: string;
/** 订阅计划名 */
plan_name: string;
};
/** 是否成功 */
successful: boolean;
}
/**
* Checkout页面的工具函数