forked from 77media/video-flow
adds alipay
This commit is contained in:
parent
3022a497d5
commit
f8b2ec2d4b
@ -25,8 +25,10 @@ export default function RootLayout({
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const [showCallbackModal, setShowCallbackModal] = useState(false)
|
||||
const [paymentType, setPaymentType] = useState<'subscription' | 'token'>('subscription')
|
||||
const openCallback = async function (ev: MessageEvent<any>) {
|
||||
if (ev.data.type === 'waiting-payment') {
|
||||
setPaymentType(ev.data.paymentType || 'subscription')
|
||||
setShowCallbackModal(true)
|
||||
}
|
||||
}
|
||||
@ -84,7 +86,7 @@ export default function RootLayout({
|
||||
{/* <ScreenAdapter /> */}
|
||||
<div id="app" className='h-full w-full'>
|
||||
{children}
|
||||
{showCallbackModal && <CallbackModal onClose={() => setShowCallbackModal(false)} />}
|
||||
{showCallbackModal && <CallbackModal paymentType={paymentType} onClose={() => setShowCallbackModal(false)} />}
|
||||
</div>
|
||||
</Providers>
|
||||
</CallbackModalContext.Provider>
|
||||
|
||||
@ -28,8 +28,8 @@ export default function PricingPage() {
|
||||
/**价格方案 */
|
||||
function HomeModule5() {
|
||||
const [billingType, setBillingType] = useState<"month" | "year">("month");
|
||||
|
||||
const [plans, setPlans] = useState<SubscriptionPlan[]>([]);
|
||||
const [loadingPlan, setLoadingPlan] = useState<string | null>(null); // 跟踪哪个计划正在加载
|
||||
|
||||
// 从后端获取订阅计划数据
|
||||
useEffect(() => {
|
||||
@ -71,6 +71,7 @@ function HomeModule5() {
|
||||
}, [plans, billingType]);
|
||||
|
||||
const handleSubscribe = async (planName: string) => {
|
||||
setLoadingPlan(planName); // 设置加载状态
|
||||
|
||||
try {
|
||||
const { createCheckoutSession, redirectToCheckout } = await import(
|
||||
@ -94,10 +95,14 @@ 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",
|
||||
paymentType: "subscription"
|
||||
}, "*");
|
||||
// 2. 直接跳转到Stripe托管页面(就这么简单!)
|
||||
window.location.href = result.data.checkout_url;
|
||||
} catch (error) {
|
||||
setLoadingPlan(null); // 出错时清除加载状态
|
||||
throw new Error("create checkout session failed, please try again later");
|
||||
}
|
||||
};
|
||||
|
||||
@ -21,7 +21,13 @@ enum PaymentStatus {
|
||||
* 支付回调模态框组件
|
||||
* 处理Stripe Checkout支付完成后的状态展示和用户操作
|
||||
*/
|
||||
export default function CallbackModal({ onClose }: { onClose: () => void }) {
|
||||
export default function CallbackModal({
|
||||
paymentType = 'subscription',
|
||||
onClose
|
||||
}: {
|
||||
paymentType?: 'subscription' | 'token';
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const [paymentStatus, setPaymentStatus] = useState<PaymentStatus>(PaymentStatus.LOADING)
|
||||
@ -119,6 +125,12 @@ export default function CallbackModal({ onClose }: { onClose: () => void }) {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{paymentInfo?.tokenPurchase && (
|
||||
<>
|
||||
<p>Token Purchase Successful!</p>
|
||||
<p>Your credits will be updated shortly.</p>
|
||||
</>
|
||||
)}
|
||||
{paymentInfo?.paymentTime && (
|
||||
<p>Payment Time: {new Date(paymentInfo.paymentTime).toLocaleString()}</p>
|
||||
)}
|
||||
@ -154,14 +166,52 @@ export default function CallbackModal({ onClose }: { onClose: () => void }) {
|
||||
setPaymentStatus(PaymentStatus.FAILED)
|
||||
return
|
||||
}
|
||||
|
||||
const userId = JSON.parse(localStorage.getItem('currentUser') || '{}').id
|
||||
const res = await getCheckoutSessionStatus(ev.data.sessionId, userId)
|
||||
if (res.successful && res.data) {
|
||||
if(res.data.payment_status === 'success'){
|
||||
setPaymentStatus(PaymentStatus.SUCCESS)
|
||||
}else{
|
||||
setPaymentStatus(PaymentStatus.FAILED)
|
||||
|
||||
try {
|
||||
if (paymentType === 'subscription') {
|
||||
// 订阅支付状态查询
|
||||
const res = await getCheckoutSessionStatus(ev.data.sessionId, userId)
|
||||
if (res.successful && res.data) {
|
||||
if (res.data.payment_status === 'success') {
|
||||
setPaymentStatus(PaymentStatus.SUCCESS)
|
||||
setPaymentInfo({
|
||||
orderId: res.data.biz_order_no,
|
||||
sessionId: ev.data.sessionId,
|
||||
paymentTime: res.data.pay_time,
|
||||
subscription: res.data.subscription
|
||||
})
|
||||
} else if (res.data.payment_status === 'fail') {
|
||||
setPaymentStatus(PaymentStatus.FAILED)
|
||||
}
|
||||
} else {
|
||||
setPaymentStatus(PaymentStatus.FAILED)
|
||||
}
|
||||
} else if (paymentType === 'token') {
|
||||
// Token购买状态查询
|
||||
const { getTokenPurchaseStatus } = await import('@/lib/stripe')
|
||||
const res = await getTokenPurchaseStatus(ev.data.sessionId)
|
||||
if (res.successful && res.data) {
|
||||
if (res.data.payment_status === 'success') {
|
||||
setPaymentStatus(PaymentStatus.SUCCESS)
|
||||
setPaymentInfo({
|
||||
orderId: ev.data.sessionId,
|
||||
sessionId: ev.data.sessionId,
|
||||
tokenPurchase: { success: true }
|
||||
})
|
||||
// Token购买成功后,延迟刷新页面以更新积分显示
|
||||
setTimeout(() => window.location.reload(), 2000)
|
||||
} else if (res.data.payment_status === 'fail') {
|
||||
setPaymentStatus(PaymentStatus.FAILED)
|
||||
}
|
||||
} else {
|
||||
setPaymentStatus(PaymentStatus.FAILED)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('支付状态查询失败:', error)
|
||||
setPaymentStatus(PaymentStatus.FAILED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,8 +100,13 @@ export function TopBar({ collapsed, isDesktop=true }: { collapsed: boolean, isDe
|
||||
});
|
||||
|
||||
if (response.successful && response.data?.checkout_url) {
|
||||
// 跳转到Stripe支付页面
|
||||
window.location.href = response.data.checkout_url;
|
||||
// 通知当前窗口等待支付,标识为Token购买
|
||||
window.postMessage({
|
||||
type: "waiting-payment",
|
||||
paymentType: "token"
|
||||
}, "*");
|
||||
// 在新标签页中打开Stripe支付页面
|
||||
window.open(response.data.checkout_url, '_blank');
|
||||
} else {
|
||||
console.error("创建Token购买失败:", response.message);
|
||||
}
|
||||
|
||||
@ -77,6 +77,12 @@ export interface BuyTokensData {
|
||||
|
||||
export type BuyTokensResponse = ApiResponse<BuyTokensData>;
|
||||
|
||||
export interface TokenPurchaseStatusData {
|
||||
payment_status: "success" | "fail" | "pending";
|
||||
}
|
||||
|
||||
export type TokenPurchaseStatusResponse = ApiResponse<TokenPurchaseStatusData>;
|
||||
|
||||
/**
|
||||
* 获取订阅计划列表
|
||||
* 从后端API获取所有活跃的订阅计划,后端已经过滤了活跃计划
|
||||
@ -219,6 +225,21 @@ export async function buyTokens(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询Token购买状态
|
||||
*
|
||||
* 用于轮询检查Token购买的支付状态
|
||||
*/
|
||||
export async function getTokenPurchaseStatus(
|
||||
sessionId: string
|
||||
): Promise<TokenPurchaseStatusResponse> {
|
||||
try {
|
||||
return await get<TokenPurchaseStatusResponse>(`/api/payment/token-purchase-status/${sessionId}`);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单的跳转到Customer Portal页面的工具函数
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user