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

如何在Express中重定向路由并传递订阅价格至支付页面?

问题与解决方案

需求与现状

我有一个订阅类型选择页面,用户选择对应订阅类型后点击「Buy Subscription」按钮,需要获取该订阅类型的价格,并重定向至/payments页面展示该价格。

目前已通过fetch API发送POST请求到Express后端,后端能根据订阅类型(1/6/12)返回对应价格并在控制台打印,但希望不通过URL参数,而是以请求体或其他方式传递价格并完成重定向,询问是否可行。

现有代码

前端代码

// Send a POST request to verify the subscription ID and get its price
const purchaseSubscription = async (subscription) => {
    const response = await fetch(`http://localhost:3000/subscription/purchaseSub/${subscription}`, {
        method: "POST"
    })
    .then((response) => {
        if (response.ok) {
            // Price received back from the backend
            console.log(response.json());
        }
     })
    .catch((err) => {
        console.log(err);
    });
};

// Get the value of each subscription type using their dataset omitted...

后端Express路由代码

// Verification of 1, 6 and 12 in getSubscriptionPrice() omitted...
subscriptionRouter.post("/purchaseSub/:subType", (req, res) => {
    const subscriptionType = req.params.subType;
    if (subscriptionType == 1 || subscriptionType == 6 || subscriptionType == 12) {
        const price = getSubscriptionPrice(subscriptionType);
        res.json({price: price});
    } else {
        res.status(400).send();
    }
});

可行方案

当然可行,以下是几种无需URL参数传递价格的实现方式:

1. 前端存储价格后跳转(最直接)

拿到后端返回的价格后,通过前端存储(比如sessionStorage/localStorage)暂存,再跳转到/payments页面,页面加载时读取存储的价格展示:

修改前端代码:

const purchaseSubscription = async (subscription) => {
    try {
        const response = await fetch(`http://localhost:3000/subscription/purchaseSub/${subscription}`, {
            method: "POST"
        });
        if (response.ok) {
            const data = await response.json();
            // 用sessionStorage暂存价格,关闭浏览器后自动清除
            sessionStorage.setItem('subscriptionPrice', data.price);
            // 跳转到支付页面
            window.location.href = '/payments';
        }
    } catch (err) {
        console.log(err);
    }
};

然后在/payments页面的前端代码中读取:

// 页面加载时获取价格
const price = sessionStorage.getItem('subscriptionPrice');
if (price) {
    // 渲染价格到页面
    document.getElementById('price-display').textContent = `$${price}`;
    // 可选:用完后清除存储,避免重复使用
    sessionStorage.removeItem('subscriptionPrice');
}

2. 后端设置Cookie,前端跳转后读取

后端在返回价格时,将价格存入允许前端读取的Cookie,前端跳转后从Cookie中获取:

修改后端路由代码:

subscriptionRouter.post("/purchaseSub/:subType", (req, res) => {
    const subscriptionType = req.params.subType;
    if (subscriptionType == 1 || subscriptionType == 6 || subscriptionType == 12) {
        const price = getSubscriptionPrice(subscriptionType);
        // 设置Cookie,maxAge可按需调整,这里设为1小时
        res.cookie('subscriptionPrice', price, { maxAge: 3600000, httpOnly: false });
        // 返回成功响应,让前端跳转
        res.json({success: true});
    } else {
        res.status(400).send();
    }
});

前端代码修改同方案1,读取方式改为从Cookie获取:

// 在/payments页面读取Cookie
const getCookie = (name) => {
    const value = `; ${document.cookie}`;
    const parts = value.split(`; ${name}=`);
    if (parts.length === 2) return parts.pop().split(';').shift();
};

const price = getCookie('subscriptionPrice');
if (price) {
    document.getElementById('price-display').textContent = `$${price}`;
    // 可选:清除Cookie
    document.cookie = "subscriptionPrice=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
}

3. 后端生成临时会话,前端跳转后验证(更安全)

如果担心价格被篡改,可以在后端生成临时会话ID,关联价格存储在服务器端(比如Redis、内存缓存),前端跳转时携带会话ID(通过Cookie),支付页面请求后端获取对应价格:

后端修改(示例用内存缓存,生产建议用Redis)

// 内存缓存,生产环境替换为Redis等持久化存储
const sessionCache = new Map();

subscriptionRouter.post("/purchaseSub/:subType", (req, res) => {
    const subscriptionType = req.params.subType;
    if (subscriptionType == 1 || subscriptionType == 6 || subscriptionType == 12) {
        const price = getSubscriptionPrice(subscriptionType);
        // 生成随机会话ID
        const sessionId = crypto.randomUUID();
        // 存储会话ID和价格,设置过期时间(比如15分钟)
        sessionCache.set(sessionId, { price, expires: Date.now() + 15*60*1000 });
        // 设置HttpOnly Cookie存储会话ID,防止XSS
        res.cookie('paymentSessionId', sessionId, { maxAge: 15*60*1000, httpOnly: true });
        res.json({success: true});
    } else {
        res.status(400).send();
    }
});

// 支付页面获取价格的接口
subscriptionRouter.get("/payment/price", (req, res) => {
    const sessionId = req.cookies.paymentSessionId;
    if (!sessionId) return res.status(400).send('Invalid session');
    
    const sessionData = sessionCache.get(sessionId);
    if (!sessionData || sessionData.expires < Date.now()) {
        sessionCache.delete(sessionId);
        return res.status(400).send('Session expired');
    }
    
    res.json({price: sessionData.price});
});

前端修改

// 订阅页面跳转逻辑
const purchaseSubscription = async (subscription) => {
    try {
        const response = await fetch(`http://localhost:3000/subscription/purchaseSub/${subscription}`, {
            method: "POST"
        });
        if (response.ok) {
            window.location.href = '/payments';
        }
    } catch (err) {
        console.log(err);
    }
};

// 支付页面获取价格
const loadPaymentPrice = async () => {
    try {
        const response = await fetch(`http://localhost:3000/subscription/payment/price`, {
            method: "GET",
            credentials: 'include' // 携带Cookie
        });
        if (response.ok) {
            const data = await response.json();
            document.getElementById('price-display').textContent = `$${data.price}`;
        }
    } catch (err) {
        console.log(err);
    }
};

// 页面加载时调用
window.addEventListener('load', loadPaymentPrice);

内容的提问来源于stack exchange,提问作者sunflower

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 19:14:52