You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS 14中Stripe支付成功后无法删除Cookie问题

问题:Stripe支付成功重定向后无法删除模拟Cookie

在NextJS 14项目中,用户通过Stripe完成支付后会被重定向到src/app/client/payment/payment-success/page.tsx页面。该页面会检查envoi数据是否包含trackingNumber、qrCodeUrl且paid为真、simulationStatus为COMPLETED,若满足条件则调用deleteSimulationCookie函数删除模拟Cookie。此函数通过请求后端API路由src/app/api/v1/simulations/delete-cookies/route.ts来删除Cookie,在用户登出场景下可正常工作,但在Stripe支付成功重定向后却无法完成Cookie删除操作。


支付成功页面代码

// path: src/app/client/payment/payment-success/page.tsx
'use client';

import React, {useEffect, useState} from 'react';
import RequireAuth from "@/components/auth/RequireAuth";
import {getSimulationFromCookie} from "@/lib/simulationCookie";
import {toast} from "react-toastify";
import {getEnvoiById, updateEnvoiDatas} from '@/services/frontend-services/envoi/EnvoiService';
import {$Enums} from "@prisma/client";
import {checkAuthStatus} from "@/lib/auth";
import {useRouter} from "next/navigation";
import {deleteSimulationCookie} from '@/services/frontend-services/simulation/SimulationService';
import {DOMAIN} from "@/utils/constants";
import SimulationStatus = $Enums.SimulationStatus;

const PaymentSuccessPage = () => {
    const router = useRouter();
    const [isLoading, setIsLoading] = useState(false);
    const [didUpdate, setDidUpdate] = useState(false);
    const [isAuthenticated, setIsAuthenticated] = useState(false);
    const [userId, setUserId] = useState<string | null>(null);

    useEffect(() => {
        const checkAuth = async () => {
            setIsLoading(true);
            const authResult = await checkAuthStatus(false);
            setIsAuthenticated(authResult.isAuthenticated);
            if (authResult.isAuthenticated) {
                setUserId(authResult.userId || null);
            }
            setIsLoading(false);
        };
        checkAuth();
    }, [router]);

    useEffect(() => {
        const finalizePayment = async () => {
            const simulation = await getSimulationFromCookie();

            if (!simulation) {
                toast.error("Erreur lors de la suppression de la simulation.");
                return;
            }
            // get envoi by id
            const envoi = await getEnvoiById(Number(simulation.id));

            if (!envoi) {
                console.log("log ====> envoi not found from getEnvoiById in PaymentSuccessPage in path: src/app/client/payment/payment-success/page.tsx is : ", envoi);
                toast.error("Erreur lors de la suppression de l'envoi.");
                return;
            }
            console.log("log ====> envoi from getEnvoiById successful in PaymentSuccessPage with id:", envoi);

            if (envoi.trackingNumber &&
                envoi.qrCodeUrl &&
                envoi.paid &&
                (envoi.simulationStatus === SimulationStatus.COMPLETED)) {
                // Delete cookies and redirect
                console.log("log ====> deleting simulation cookie...");

                try {
                    await deleteSimulationCookie();
                    console.log("log ====> Cookie deleted, redirecting...");
                    router.push(`${DOMAIN}/client/profile`);
                    setDidUpdate(true);
                } catch (error) {
                    console.error("Failed to delete cookie:", error);
                    toast.error("Erreur lors de la suppression des cookies.");
                }

                return;
            }
            // 1. Update envoi data (generates tracking, QR code, etc.)
            const updatedEnvoi = await updateEnvoiDatas(Number(simulation.id));
            if (!updatedEnvoi) {
                toast.error("Erreur lors de la mise à jour de l'envoi.");
                return;
            }
            if (!envoi.arrivalAgency?.id || !envoi.userId) {
                toast.error("Erreur lors de la mise à jour de l'agence.");
                return;
            }

            // delete cookies of the simulation and redirect to the profile page
            toast.success("Envoi mis à jour avec succès.");

            router.push(`${DOMAIN}/client/profile`);
        };

        finalizePayment();
    }, [router]);

    return (
        <RequireAuth>
            <div className="flex flex-col items-center justify-center min-h-screen">
                <h1 className="text-3xl font-bold text-green-600">Paiement Réussi !</h1>
                <p className="mt-4">Merci pour votre paiement. Votre transaction a été complétée avec succès.</p>
            </div>
        </RequireAuth>
    );
};

export default PaymentSuccessPage;

核心判断逻辑

if (envoi.trackingNumber &&
    envoi.qrCodeUrl &&
    envoi.paid &&
    (envoi.simulationStatus === SimulationStatus.COMPLETED)) {
    // Delete cookies and redirect
    console.log("log ====> deleting simulation cookie...");

    try {
        await deleteSimulationCookie();
        console.log("log ====> Cookie deleted, redirecting...");
        router.push(`${DOMAIN}/client/profile`);
        setDidUpdate(true);
    } catch (error) {
        console.error("Failed to delete cookie:", error);
        toast.error("Erreur lors de la suppression des cookies.");
    }

    return;
}

删除Cookie的服务函数

/**
 * Delete the simulation cookie
 * @returns {Promise<void | null>} A Promise that resolves when the status is updated
 */
// path: src/services/frontend-services/simulation/SimulationService.ts
export async function deleteSimulationCookie(): Promise<void | null> {
    try {
        await axios.get(`${API_DOMAIN}/simulations/delete-cookies`, { withCredentials: true });

    } catch (error) {
        throw error;
    }
}

后端API路由

// path: src/app/api/v1/simulations/delete-cookies/route.ts
import {NextResponse} from "next/server";
import {errorHandler} from "@/utils/handelErrors";
import {cookies} from "next/headers";
import {DOMAIN} from "@/utils/constants";

export async function GET() {
    console.log("log ====> GET request reached at: /api/v1/simulations/delete-cookies");
    try {
        const cookieName = process.env.SIMULATION_COOKIE_NAME;
        if (!cookieName) {
            return NextResponse.json(
                {message: "Server configuration error"},
                {status: 500}
            );
        }

        // Delete the simulation cookie with a correct path
        if (cookies().has(cookieName)) {
            cookies().delete(cookieName);
        }

        // Create response with CORS headers
        const response = NextResponse.json(
            {message: "Simulation cookies deleted successfully"},
            {status: 200}
        );

        response.headers.set('Access-Control-Allow-Origin', `${DOMAIN}`);
        response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
        response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
        response.headers.set('Access-Control-Allow-Credentials', 'true');

        return response;
    } catch (error) {
        return errorHandler("Internal server error", 500);
    }
}

可能的原因及解决方案

1. 重定向时机过早,API请求未完成

虽然代码中使用了await等待删除请求完成,但Next.js路由跳转可能会提前卸载当前页面,导致请求被中断。可以添加组件挂载状态,确保仅在组件挂载时执行后续操作:

useEffect(() => {
    let isMounted = true;
    const finalizePayment = async () => {
        // ... 现有逻辑
        try {
            await deleteSimulationCookie();
            if (isMounted) {
                console.log("log ====> Cookie deleted, redirecting...");
                router.push(`${DOMAIN}/client/profile`);
                setDidUpdate(true);
            }
        } catch (error) {
            if (isMounted) {
                console.error("Failed to delete cookie:", error);
                toast.error("Erreur lors de la suppression des cookies.");
            }
        }
    };
    finalizePayment();
    return () => { isMounted = false; }; // 组件卸载时标记为未挂载
}, [router]);

2. Cookie属性不匹配导致删除失败

Next.js的cookies().delete需要完全匹配Cookie的所有属性(路径、域名、SameSite等)才能成功删除。修改后端删除逻辑,明确指定Cookie的属性:

// 后端路由中修改删除逻辑
const cookieStore = cookies();
const existingCookie = cookieStore.get(cookieName);
if (existingCookie) {
    cookieStore.delete({
        name: cookieName,
        path: existingCookie.path || '/',
        domain: existingCookie.domain,
        secure: existingCookie.secure,
        sameSite: existingCookie.sameSite as 'strict' | 'lax' | 'none'
    });
}

3. 请求被浏览器拦截或CORS配置问题

检查API_DOMAIN是否与当前页面域名一致,确保axios请求的withCredentials: true正确传递Cookie。同时,后端CORS配置中可以尝试放宽Access-Control-Allow-Origin为当前域名(而非硬编码的DOMAIN),或者确认Stripe重定向后的页面域名在允许列表内。

4. 模拟Cookie未正确传递到API请求

在支付成功页面,先确认getSimulationFromCookie()能正确获取到Cookie值,再执行后续逻辑。可以在控制台打印simulation的值,验证Cookie是否存在。


内容的提问来源于stack exchange,提问作者Just Relax قناة هدو</think_never_used_51bce0c785ca2f68081bfa7d91973934>

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 07:32:32