У меня возникают проблемы с пониманием, почему при попытке отправить пользователя в моем приложении на страницу оплаты sandbox.paypal.com при заказе продукта возникает ошибка «Блокировано перекрестное происхождение запроса». Интерфейс моего приложения (с React) использует локальный порт 3000, в то время как внутренний интерфейс использует локальный порт 4000. Связь между двумя портами при выполнении операций CRUD работает, как предполагалось. Но теперь, когда я ввожу PayPal в набор, приложение не go на странице PayPal песочницы при попытке заказать продукт. Это сообщение об ошибке в консоли:
Cross-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 ‘Access-Control-Allow-Origin’ missing).
Я запутался, потому что связь между двумя локальными хостами уже работает. Разве Access-Control-Allow-Origin по умолчанию не равен "*"? Я использую "paypal-rest-sdk" в Node.js
Frontend logi c:
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { useID, useAdmin } from "../../context/auth";
import { Button, Accordion, Card, ListGroup, Form } from "react-bootstrap";
import axios from "axios";
function ProductDetails(props) {
const [isError, setIsError] = useState(false);
const [id, setID] = useState("");
const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [price, setPrice] = useState(0);
const [stock, setStock] = useState(0);
const [messages, setMessages] = useState([]);
const { IDTokens } = useID();
const { adminTokens } = useAdmin();
const Message = props => (
<Card>
<Card.Body>
<Card.Title>
{props.message.owner.username === "(User removed)" ? (
<span>{props.message.owner.username}</span>
) : (
<Link to={`/users/${props.message.owner.id}`}>{props.message.owner.username}</Link>
)}
</Card.Title>
<Card.Text>
{props.message.content}
</Card.Text>
{IDTokens === props.message.owner.id || adminTokens ? (
<span>
<Link to={`/products/list/${id}/messages/${props.message._id}/edit/`} style={{ marginRight: 10, marginLeft: 10 }}>
Edit
</Link>
<Link to={`/products/list/${id}/messages/${props.message._id}/delete/`}>Delete</Link>
</span>
) : (
<span></span>
)}
</Card.Body>
</Card>
)
useEffect(() => {
axios.get(`http://localhost:4000/products/${props.match.params.id}`)
.then(res => {
setID(res.data.product._id);
setName(res.data.product.name);
setDescription(res.data.product.description);
setPrice(res.data.product.price);
setStock(res.data.product.stock);
setMessages(res.data.messages);
}).catch(function(err) {
setIsError(true);
})
}, [props, IDTokens]);
function messageList() {
return messages.map(function(currentMessage, i){
return <Message message={currentMessage} key={i} />;
})
}
function postOrder() {
if(stock > 0) {
let productInfo = {
name,
description,
price
};
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);
});
}
}
return (
<div className="text-center">
<h2>Products Details</h2>
<Accordion>
<Card>
<Card.Header>
<Accordion.Toggle as={Button} variant="link" eventKey="0">
Product Info
</Accordion.Toggle>
</Card.Header>
<Accordion.Collapse eventKey="0">
<Card.Body>
<ListGroup>
<ListGroup.Item>Name: {name}</ListGroup.Item>
<ListGroup.Item>Description: {description}</ListGroup.Item>
<ListGroup.Item>Price: ${price.toFixed(2)}</ListGroup.Item>
<ListGroup.Item>Stock: {stock}</ListGroup.Item>
</ListGroup>
{stock > 0 ? (
<Form>
<Button onClick={postOrder} variant="success">Order Now</Button>
{ isError &&<p>Something went wrong with making the order!</p> }
</Form>
) : (
"Cannot order, currently out of stock"
)}
</Card.Body>
</Accordion.Collapse>
</Card>
</Accordion>
<Link to={`/products/list/${id}/messages/new`}>Questions or Comments Regarding this Product? Leave a Message.</Link>
<h3>Messages: </h3>
{messages.length > 0 ? (
messageList()
) : (
<p>(No messages)</p>
)}
{ isError &&<p>Something went wrong with getting the product!</p> }
</div>
)
}
export default ProductDetails;
Backend logi c:
const express = require("express"),
router = express.Router(),
paypal = require("paypal-rest-sdk"),
Order = require("../database/models/order");
router.post("/pay", function(req, res) {
console.log("req.body: ", req.body);
const create_payment_json = {
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": "http://localhost:3000/orders/success",
"cancel_url": "http://localhost:3000/orders/cancel"
},
"transactions": [{
"item_list": {
"items": [{
"name": req.body.name,
"sku": "001",
"price": req.body.price,
"currency": "USD",
"quantity": 1
}]
},
"amount": {
"currency": "USD",
"total": req.body.price
},
"description": req.body.description
}]
};
paypal.payment.create(create_payment_json, function (err, payment) {
if(err) {
console.log(err.message);
} else {
for(let i = 0; i < payment.links.length; i++){
if(payment.links[i].rel === 'approval_url'){
res.redirect(payment.links[i].href);
}
}
}
});
});
router.get('/success', (req, res) => {
console.log("req.query: ", req.query)
const payerId = req.query.PayerID;
const paymentId = req.query.paymentId;
const execute_payment_json = {
"payer_id": payerId,
"transactions": [{
"amount": {
"currency": "USD",
"total": req.query.total
}
}]
};
paypal.payment.execute(paymentId, execute_payment_json, function (error, payment) {
if(err) {
console.log(err.message);
} else {
let order = new Order(JSON.stringify(payment));
order.save().then(order => {
res.status(200).json(`Order added successfully! Created order details: ${order}`);
}).catch(err => {
console.log("Order create error: ", err.message);
});
}
});
});
server. js (client_id и client_secret изменены для стекового потока):
const express = require("express"),
app = express(),
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
session = require("express-session"),
passport = require("passport"),
localStrategy = require("passport-local"),
paypal = require("paypal-rest-sdk"),
cors = require("cors"),
PORT = 4000,
// Require models
User = require("./database/models/user"),
// Require routes
productRoutes = require("./routes/products"),
messageRoutes = require("./routes/messages"),
orderRoutes = require("./routes/orders"),
userRoutes = require("./routes/users");
app.use(bodyParser.json());
app.use(cors());
// Paypal config
paypal.configure({
"mode": "sandbox", //sandbox or live
"client_id": "...",
"client_secret": "..."
});
// Mongoose config
mongoose.set('useUnifiedTopology', true);
mongoose.set('useFindAndModify', false);
mongoose.connect("mongodb://localhost/barnwood", {
useNewUrlParser: true,
useCreateIndex: true
});
// Sessions
app.use(
session({
secret: "Birdhouses are cool.", // Secret can be any string
resave: false,
saveUninitialized: false
})
);
app.use(passport.initialize());
app.use(passport.session());
passport.use(new localStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
// Routes config
app.use("/products", productRoutes);
app.use("/messages", messageRoutes);
app.use("/orders", orderRoutes);
app.use("/users", userRoutes);
// Start server
app.listen(PORT, function() {
console.log("Server is running on Port: " + PORT);
});