跨域请求在请求Paypal沙箱时被阻止,但其他本地主机端口运行正常 [英] Cross-Origin Request Blocked when requesting Paypal sandbox but a different localhost port works fine

查看:82
本文介绍了跨域请求在请求Paypal沙箱时被阻止,但其他本地主机端口运行正常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法理解为什么在尝试订购产品时尝试将我的应用程序中的用户发送到sandbox.paypal.com付款页面时收到跨域请求阻止错误.我的应用程序的前端(带有React)使用localhost端口3000,而后端使用localhost端口4000.执行CRUD操作时,两个端口之间的通信按预期进行.但是现在,我将Paypal引入了组合,在尝试订购产品时,该应用程序不会转到沙盒Paypal页面.这是控制台中的错误消息:

I'm having trouble understanding why I'm getting a Cross-Origin Request Blocked error when trying to send a user in my app to a sandbox.paypal.com payment page when ordering a product. The frontend of my app (with React) uses localhost port 3000 while the backend uses localhost port 4000. The communication between the two ports when performing CRUD operations works as intended. But now that I'm introducing paypal into the mix the app doesn't go to the sandbox paypal page when trying to order a product. This is the error message in the console:

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是否默认为"*"?我在Node.js中使用"paypal-rest-sdk"

I'm confused because communication already works between the two localhosts. Doesn't Access-Control-Allow-Origin default to "*"? I'm using "paypal-rest-sdk" in Node.js

前端逻辑:

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;

后端逻辑:

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(更改了stackoverflow的client_id和client_secret):

server.js (client_id and client_secret are changed for stackoverflow):

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);
});

推荐答案

您要让后端服务器处理付款,一旦成功,您的后端服务器就会执行 res.redirect(payment.links [i].href); 您的浏览器(默认将maxRedirects设置为5的浏览器)将遵循重定向,然后读取与您所在域不同的响应,PayPal拒绝您在跨域阅读办法.您被CORS阻止的原因.

You're asking your backend server to process the payment and upon success your backend server is doing res.redirect(payment.links[i].href); your browser (axios which has maxRedirects set to 5 by default) is going to follow the redirect and then read the response which is on a different domain than yours, which PayPal refuses you to read in a cross origin way. Reason why you get CORS being blocked.

您有两种解决此问题的方法:

You have two solutions to this problem:

  1. 代替执行 res.redirect(payment.links [i] .href); ,您应该回复链接并让浏览器重定向.
  1. Instead of doing a res.redirect(payment.links[i].href); you should reply the link and let the browser redirect.

例如:

// replace res.redirect(payment.links[i].href); by
res.json({forwardLink: payment.links[i].href});

然后在您的React应用程序中,您应该阅读响应并执行 window.location = response.forwardLink

Then in your React app, you should read the response and do a 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)
  })

成为

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)
  })

  1. 您还可以禁止Axios进行重定向(我认为 maxRedirects:0 会进行一些参数调整),在这种情况下,您的响应代码应为 302 (而不是200)),您可以阅读 headers.location 参数,然后使用该参数执行 window.location = headers.location
  1. You could also prohibit Axios from following redirect (some param tweak with maxRedirects: 0 I think), in which case your response code would be 302 (instead of 200) and you could read the headers.location param with which you can then do 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)
  })

这篇关于跨域请求在请求Paypal沙箱时被阻止,但其他本地主机端口运行正常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆