MERN应用集成PayPal支付遇SameSite Cookie问题求助
MERN应用PayPal支付后续失败问题
问题描述
在MERN应用中集成PayPal支付按钮,使用标准沙箱账户,首次支付完全正常,但后续无法完成支付。Lighthouse检测到Cookie相关错误导致支付受阻。
Lighthouse错误提示
Indicate whether a cookie is intended to be set in a cross-site context by specifying its SameSite attribute
由于Cookie未设置SameSite属性或属性无效,默认采用SameSite=Lax,阻止了跨站上下文设置Cookie。此行为用于保护用户数据,防止意外泄露给第三方及跨站请求伪造。
解决方法:更新Cookie属性:
- 若需在跨站上下文设置Cookie,请指定SameSite=None和Secure属性(仅HTTPS传输的Cookie可使用Secure)。
- 若Cookie不应由跨站请求设置,请指定SameSite=Strict或SameSite=Lax。
相关代码
server.js
import express, { application } from 'express'; import dotenv from 'dotenv'; import colors from 'colors'; import { notFound, errorHandler } from './middleware/errorMiddleware.js'; import connectDB from './config/db.js'; import productRoutes from './routes/productRoutes.js'; import userRoutes from './routes/userRoutes.js'; import orderRoutes from './routes/orderRoutes.js'; import uploadRoutes from './routes/uploadRoutes.js'; dotenv.config(); connectDB(); const app = express(); app.use(express.json()); app.get('/', (req, res) => { res.send('API is running..'); }); app.use('/api/products/', productRoutes); app.use('/api/users/', userRoutes); app.use('/api/orders', orderRoutes); app.use('/api/upload', uploadRoutes); app.get('/api/config/paypal', (req, res) => res.send(process.env.PAYPAL_CLIENT_ID)); app.use(notFound); app.use(errorHandler); const PORT = process.env.PORT || 5000; app.listen(PORT, console.log(`Server running in ${process.env.NODE_ENV} on port ${PORT}`.red.bold));
orderScreen.js
import React, { useState, useEffect } from 'react'; import axios from 'axios'; //import { PayPalScriptProvider, PayPalButtons } from '@paypal/react-paypal-js'; import { PayPalButton } from 'react-paypal-button-v2'; import { useParams, Link, useLocation } from 'react-router-dom'; import { Button, Row, Col, ListGroup, Image, Card } from 'react-bootstrap'; import { useDispatch, useSelector } from 'react-redux'; import Loader from '../components/Loader'; import Message from '../components/Message'; import { getOrderDetails, payOrder, deliverOrder } from '../actions/orderActions.js'; import { ORDER_PAY_RESET, ORDER_DELIVER_RESET } from '../constants/orderConstants.js'; const OrderScreen = () => { const { id } = useParams(); const orderId = `${id}`; const dispatch = useDispatch(); const history = useLocation(); const [sdkReady, setSdkReady] = useState(false); const orderDetails = useSelector((state) => state.orderDetails); const { order, loading, error } = orderDetails; const orderPay = useSelector((state) => state.orderPay); const { loading: loadingPay, success: successPay } = orderPay; const orderDeliver = useSelector((state) => state.orderDeliver); const { loading: loadingDeliver, success: successDeliver } = orderDeliver; const userLogin = useSelector((state) => state.userLogin); const { userInfo } = userLogin; if (!loading) { // Calculate prices const addDecimals = (num) => { return (Math.round(num * 100) / 100).toFixed(2); }; order.itemsPrice = addDecimals(order.orderItems.reduce((acc, item) => acc + item.price * item.qty, 0)); } useEffect(() => { if (!userInfo) { history('/login'); } const addPayPalScript = async () => { const { data: clientId } = await axios.get('/api/config/paypal'); const script = document.createElement('script'); script.type = 'text/javascript'; script.src = `https://www.paypal.com/sdk/js?client-id=${clientId}`; script.async = true; script.onload = () => { setSdkReady(true); }; document.body.appendChild(script); }; if (!order || successPay || successDeliver || order._id !== orderId) { dispatch({ type: ORDER_PAY_RESET }); dispatch({ type: ORDER_DELIVER_RESET }); dispatch(getOrderDetails(orderId)); } else if (!order.isPaid) { if (!window.paypal) { addPayPalScript(); } else { setSdkReady(true); } } }, [dispatch, orderId, successPay, history, userInfo, successDeliver, order]); const successPaymentHandler = (paymentResult) => { console.log(paymentResult); dispatch(payOrder(orderId, paymentResult)); }; const deliverHandler = () => { dispatch(deliverOrder(order)); }; return loading ? ( <Loader /> ) : error ? ( <Message variant='danger'>{error}</Message> ) : ( <> <h1>Order {order._id}</h1> <Row> <Col md={8}> <ListGroup variant='flush'> <ListGroup.Item> <h2>Shipping</h2> <p> <strong>Name: </strong> {order.user.name} </p> <p> <strong>Email: </strong> <a href={`mailto:${order.user.email}`}>{order.user.email}</a> </p> <p> <strong>Address:</strong> {order.shippingAddress.address}, {order.shippingAddress.city} {order.shippingAddress.postalCode}, {order.shippingAddress.country} </p> {order.isDelivered ? <Message variant='success'>Delivered on {order.deliveredAt}</Message> : <Message variant='danger'>Not Delivered</Message>} </ListGroup.Item> <ListGroup.Item> <h2>Payment Method</h2> <p> <strong>Method: </strong> {order.paymentMethod} </p> {order.isPaid ? <Message variant='success'>Paid on {order.paidAt}</Message> : <Message variant='danger'>Not Paid</Message>} </ListGroup.Item> <ListGroup.Item> <h2>Order Items</h2> {order.orderItems.length === 0 ? ( <Message>Order is empty</Message> ) : ( <ListGroup variant='flush'> {order.orderItems.map((item, index) => ( <ListGroup.Item key={index}> <Row> <Col md={1}> <Image src={item.image} alt={item.name} fluid rounded /> </Col> <Col> <Link to={`/product/${item.product}`}>{item.name}</Link> </Col> <Col md={4}> {item.qty} x ${item.price} = ${item.qty * item.price} </Col> </Row> </ListGroup.Item> ))} </ListGroup> )} </ListGroup.Item> </ListGroup> </Col> <Col md={4}> <Card> <ListGroup variant='flush'> <ListGroup.Item> <h2>Order Summary</h2> </ListGroup.Item> <ListGroup.Item> <Row> <Col>Items</Col> <Col>${order.itemsPrice}</Col> </Row> </ListGroup.Item> <ListGroup.Item> <Row> <Col>Shipping</Col> <Col>${order.shippingPrice}</Col> </Row> </ListGroup.Item> <ListGroup.Item> <Row> <Col>Tax</Col> <Col>${order.taxPrice}</Col> </Row> </ListGroup.Item> <ListGroup.Item> <Row> <Col>Total</Col> <Col>${order.totalPrice}</Col> </Row> </ListGroup.Item> {!order.isPaid && ( <ListGroup.Item> {loadingPay && <Loader />} {!sdkReady ? <Loader /> : <PayPalButton amount={order.totalPrice} onSuccess={successPaymentHandler} />} </ListGroup.Item> )} {loadingDeliver && <Loader />} {userInfo && userInfo.isAdmin && order.isPaid && !order.isDelivered && ( <ListGroup.Item> <Button type='button' className='btn btn-block' onClick={deliverHandler}> Mark As Delivered </Button> </ListGroup.Item> )} </ListGroup> </Card> </Col> </Row> </> ); }; export default OrderScreen;
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="Find the best products for the cheapsest prices !" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <link rel="preconnect" href="https://fonts.gstatic.com" /> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Montserrat" rel="stylesheet" /> <script src="https://www.paypal.com/sdk/js?client-id=AZm6WLwtUgFKRRvQqV6hMDWIw_CmEDavupEI2NdSImpe3oKSYXWjqBN_dfIOgCfdQ7CCDcYFE3-fx5_X"></script> <title>Pro Shop</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> </body> </html>
index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import { Provider } from 'react-redux'; import store from './store.js'; import './bootstrap.min.css'; import './index.css'; import App from './App.js'; import reportWebVitals from './reportWebVitals'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider store={store}> <App /> </Provider> ); reportWebVitals();
已尝试的解决方法
- 切换多个浏览器(Brave、Chrome、Edge),问题依旧存在。
- 排查代码,多数反馈代码本身无问题,但明确问题根源是Cookie被阻止。
求助需求
希望得到针对性的解决方案,修复PayPal后续支付失败的问题。
内容的提问来源于stack exchange,提问作者Flora332
相关产品推荐
相关产品推荐

