And*_*rex 5 paypal node.js cors express reactjs
我无法理解为什么在订购产品时尝试将应用程序中的用户发送到 sandbox.paypal.com 付款页面时收到“跨源请求被阻止”错误。我的应用程序(使用 React)的前端使用 localhost 端口 3000,而后端使用 localhost 端口 4000。执行 CRUD 操作时两个端口之间的通信按预期工作。但现在我将贝宝引入其中,当尝试订购产品时,应用程序不会进入沙箱贝宝页面。这是控制台中的错误消息:
\n\nCross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-0Y510528E2479935T. (Reason: CORS header \xe2\x80\x98Access-Control-Allow-Origin\xe2\x80\x99 missing).\n
Run Code Online (Sandbox Code Playgroud)\n\n我很困惑,因为两个本地主机之间的通信已经可以正常工作。Access-Control-Allow-Origin 不是默认为“*”吗?我在 Node.js 中使用“paypal-rest-sdk”
\n\n前端逻辑:
\n\nimport React, { useState, useEffect } from "react";\nimport { Link } from "react-router-dom";\nimport { useID, useAdmin } from "../../context/auth";\nimport { Button, Accordion, Card, ListGroup, Form } from "react-bootstrap";\nimport axios from "axios";\n\nfunction ProductDetails(props) {\n const [isError, setIsError] = useState(false);\n const [id, setID] = useState("");\n const [name, setName] = useState("");\n const [description, setDescription] = useState("");\n const [price, setPrice] = useState(0);\n const [stock, setStock] = useState(0);\n const [messages, setMessages] = useState([]);\n const { IDTokens } = useID();\n const { adminTokens } = useAdmin();\n\n const Message = props => (\n <Card>\n <Card.Body>\n <Card.Title>\n {props.message.owner.username === "(User removed)" ? (\n <span>{props.message.owner.username}</span>\n ) : (\n <Link to={`/users/${props.message.owner.id}`}>{props.message.owner.username}</Link> \n )}\n </Card.Title>\n <Card.Text>\n {props.message.content}\n </Card.Text>\n {IDTokens === props.message.owner.id || adminTokens ? (\n <span>\n <Link to={`/products/list/${id}/messages/${props.message._id}/edit/`} style={{ marginRight: 10, marginLeft: 10 }}>\n Edit\n </Link>\n <Link to={`/products/list/${id}/messages/${props.message._id}/delete/`}>Delete</Link>\n </span>\n ) : (\n <span></span>\n )}\n </Card.Body>\n </Card>\n )\n\n useEffect(() => {\n axios.get(`http://localhost:4000/products/${props.match.params.id}`)\n .then(res => {\n setID(res.data.product._id);\n setName(res.data.product.name);\n setDescription(res.data.product.description);\n setPrice(res.data.product.price);\n setStock(res.data.product.stock);\n setMessages(res.data.messages);\n }).catch(function(err) {\n setIsError(true);\n })\n }, [props, IDTokens]);\n\n function messageList() {\n return messages.map(function(currentMessage, i){\n return <Message message={currentMessage} key={i} />;\n })\n }\n\n function postOrder() {\n if(stock > 0) {\n let productInfo = {\n name,\n description,\n price\n };\n\n axios.post("http://localhost:4000/orders/pay",\n productInfo\n ).then(res => {\n if(res.status === 200) {\n console.log(res.data);\n } else {\n setIsError(true);\n }\n }).catch(err => {\n setIsError(true);\n });\n }\n }\n\n return (\n <div className="text-center">\n <h2>Products Details</h2>\n <Accordion>\n <Card>\n <Card.Header>\n <Accordion.Toggle as={Button} variant="link" eventKey="0">\n Product Info\n </Accordion.Toggle>\n </Card.Header>\n <Accordion.Collapse eventKey="0">\n <Card.Body>\n <ListGroup>\n <ListGroup.Item>Name: {name}</ListGroup.Item>\n <ListGroup.Item>Description: {description}</ListGroup.Item>\n <ListGroup.Item>Price: ${price.toFixed(2)}</ListGroup.Item>\n <ListGroup.Item>Stock: {stock}</ListGroup.Item>\n </ListGroup>\n {stock > 0 ? (\n <Form>\n <Button onClick={postOrder} variant="success">Order Now</Button>\n { isError &&<p>Something went wrong with making the order!</p> }\n </Form>\n ) : (\n "Cannot order, currently out of stock"\n )}\n </Card.Body>\n </Accordion.Collapse>\n </Card>\n </Accordion>\n <Link to={`/products/list/${id}/messages/new`}>Questions or Comments Regarding this Product? Leave a Message.</Link>\n <h3>Messages: </h3>\n {messages.length > 0 ? (\n messageList()\n ) : (\n <p>(No messages)</p>\n )}\n { isError &&<p>Something went wrong with getting the product!</p> }\n </div>\n )\n}\n\nexport default ProductDetails;\n
Run Code Online (Sandbox Code Playgroud)\n\n后端逻辑:
\n\nconst express = require("express"),\nrouter = express.Router(),\npaypal = require("paypal-rest-sdk"),\nOrder = require("../database/models/order");\n\nrouter.post("/pay", function(req, res) {\n console.log("req.body: ", req.body);\n const create_payment_json = {\n "intent": "sale",\n "payer": {\n "payment_method": "paypal"\n },\n "redirect_urls": {\n "return_url": "http://localhost:3000/orders/success",\n "cancel_url": "http://localhost:3000/orders/cancel"\n },\n "transactions": [{\n "item_list": {\n "items": [{\n "name": req.body.name,\n "sku": "001",\n "price": req.body.price,\n "currency": "USD",\n "quantity": 1\n }]\n },\n "amount": {\n "currency": "USD",\n "total": req.body.price\n },\n "description": req.body.description\n }]\n };\n\n paypal.payment.create(create_payment_json, function (err, payment) {\n if(err) {\n console.log(err.message);\n } else {\n for(let i = 0; i < payment.links.length; i++){\n if(payment.links[i].rel === \'approval_url\'){\n res.redirect(payment.links[i].href);\n }\n }\n }\n });\n});\n\nrouter.get(\'/success\', (req, res) => {\n console.log("req.query: ", req.query)\n const payerId = req.query.PayerID;\n const paymentId = req.query.paymentId;\n\n const execute_payment_json = {\n "payer_id": payerId,\n "transactions": [{\n "amount": {\n "currency": "USD",\n "total": req.query.total\n }\n }]\n };\n\n paypal.payment.execute(paymentId, execute_payment_json, function (error, payment) {\n if(err) {\n console.log(err.message);\n } else {\n let order = new Order(JSON.stringify(payment));\n order.save().then(order => {\n res.status(200).json(`Order added successfully! Created order details: ${order}`);\n }).catch(err => {\n console.log("Order create error: ", err.message);\n });\n }\n });\n});\n
Run Code Online (Sandbox Code Playgroud)\n\nserver.js(client_id 和 client_secret 已针对 stackoverflow 进行更改):
\n\nconst express = require("express"),\napp = express(),\nbodyParser = require("body-parser"),\nmongoose = require("mongoose"),\nsession = require("express-session"),\npassport = require("passport"),\nlocalStrategy = require("passport-local"),\npaypal = require("paypal-rest-sdk"),\ncors = require("cors"),\nPORT = 4000,\n\n// Require models\nUser = require("./database/models/user"),\n\n// Require routes\nproductRoutes = require("./routes/products"),\nmessageRoutes = require("./routes/messages"),\norderRoutes = require("./routes/orders"),\nuserRoutes = require("./routes/users");\n\napp.use(bodyParser.json());\napp.use(cors());\n\n// Paypal config\npaypal.configure({\n "mode": "sandbox", //sandbox or live\n "client_id": "...",\n "client_secret": "..."\n});\n\n// Mongoose config\nmongoose.set(\'useUnifiedTopology\', true);\nmongoose.set(\'useFindAndModify\', false);\nmongoose.connect("mongodb://localhost/barnwood", { \n useNewUrlParser: true,\n useCreateIndex: true\n});\n\n// Sessions\napp.use(\n session({\n secret: "Birdhouses are cool.", // Secret can be any string\n resave: false,\n saveUninitialized: false\n })\n);\napp.use(passport.initialize());\napp.use(passport.session());\npassport.use(new localStrategy(User.authenticate()));\npassport.serializeUser(User.serializeUser());\npassport.deserializeUser(User.deserializeUser());\n\n// Routes config\napp.use("/products", productRoutes);\napp.use("/messages", messageRoutes);\napp.use("/orders", orderRoutes);\napp.use("/users", userRoutes);\n\n// Start server\napp.listen(PORT, function() {\n console.log("Server is running on Port: " + PORT);\n});\n
Run Code Online (Sandbox Code Playgroud)\n
您要求后端服务器处理付款,成功后,您的后端服务器将执行res.redirect(payment.links[i].href);
您的浏览器(axios 默认情况下 maxRedirects 设置为 5)将遵循重定向,然后读取位于不同域的响应您的,PayPal 拒绝您以跨源方式阅读。CORS 被阻止的原因。
对于这个问题你有两种解决方案:
res.redirect(payment.links[i].href);
您应该回复链接并让浏览器重定向,而不是这样做。例如:
// replace res.redirect(payment.links[i].href); by
res.json({forwardLink: payment.links[i].href});
Run Code Online (Sandbox Code Playgroud)
然后在你的 React 应用程序中,你应该阅读响应并执行window.location = response.forwardLink
axios
.post('http://localhost:4000/orders/pay', productInfo)
.then((res) => {
if (res.status === 200) {
console.log(res.data)
} else {
setIsError(true)
}
})
.catch((err) => {
setIsError(true)
})
Run Code Online (Sandbox Code Playgroud)
变成
axios
.post('http://localhost:4000/orders/pay', productInfo)
.then((res) => {
if (res.status === 200) {
console.log(res.data)
window.location = res.data.forwardLink
} else {
setIsError(true)
}
})
.catch((err) => {
setIsError(true)
})
Run Code Online (Sandbox Code Playgroud)
maxRedirects: 0
我认为进行了一些参数调整),在这种情况下,您的响应代码将是302
(而不是 200),您可以读取参数headers.location
,然后您可以执行以下操作window.location = headers.location
你的代码会变成这样:
axios({
maxRedirects: 0,
method: 'post',
url: 'http://localhost:4000/orders/pay',
data: productInfo,
})
.then((res) => {
if (res.status === 302) {
console.log(res.headers)
window.location = res.headers.location
} else {
setIsError(true)
}
})
.catch((err) => {
setIsError(true)
})
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
4542 次 |
最近记录: |