Node.js react和paypal rest sdk:如何将数据从支付页面传递回应用程序
我有一个React单页应用程序,允许用户通过paypal rest sdk(文档)购买保存的产品。我希望这样做的方式是让登录用户能够订购有库存的产品。用户输入他们想要的产品数量,然后创建包含产品和数量的订单,并将其存储为“created”。然后,用户将被重定向到Paypal购买页面,在那里他们确认或取消付款。在确认之前创建的订单后,将再次找到并设置为“已完成”,并创建一个用于存储贝宝支付信息的交易。目前,我可以在沙箱环境中成功完成付款、订单和交易 我现在的问题是,我用来进出Paypal支付页面的重定向导致我的应用程序忘记了登录的用户,因此我无法在成功支付后获得用户的“创建”订单。我必须在postOrder()函数中使用window.location,否则会出现交叉引用错误,正如用户在回答我的问题时所解释的那样。我需要记住用户,否则任何标记为“已创建”的订单都可能被抓取并设置为“已完成”,不用说,这将在我的应用程序中造成大量问题。我应该如何解决这个问题,还是应该尝试另一种方法 前端逻辑:Node.js react和paypal rest sdk:如何将数据从支付页面传递回应用程序,node.js,reactjs,paypal,axios,Node.js,Reactjs,Paypal,Axios,我有一个React单页应用程序,允许用户通过paypal rest sdk(文档)购买保存的产品。我希望这样做的方式是让登录用户能够订购有库存的产品。用户输入他们想要的产品数量,然后创建包含产品和数量的订单,并将其存储为“created”。然后,用户将被重定向到Paypal购买页面,在那里他们确认或取消付款。在确认之前创建的订单后,将再次找到并设置为“已完成”,并创建一个用于存储贝宝支付信息的交易。目前,我可以在沙箱环境中成功完成付款、订单和交易 我现在的问题是,我用来进出Paypal支付页面的
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { useID, useUserName, useAdmin } from "../../context/auth";
import { Button, Accordion, Card, ListGroup, Form, Col } 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 [amount, setAmount] = useState(0);
const [messages, setMessages] = useState([]);
const { IDTokens } = useID();
const { userNameTokens } = useUserName();
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(amount <= stock) {
let productInfo = {
id,
name,
description,
price,
amount
};
let orderInfo = {
owner: {
id: IDTokens,
username: userNameTokens
},
status: "Created"
};
axios.post("http://localhost:4000/orders/pay",
{productInfo, orderInfo}
).then((res) => {
if(res.status === 200) {
window.location = res.data.forwardLink;
} 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 && IDTokens ? (
<Form>
<h2>Order This Product</h2>
<Form.Row>
<Form.Group as={Col} sm={{ span: 6, offset: 3 }}>
<Form.Label htmlFor="formAmount">Amount</Form.Label>
<Form.Control
controlid="formAmount"
type="number"
min="1"
max="5"
step="1"
onChange={e => {
setAmount(e.target.value);
}}
placeholder="Enter amount to order (max 5)"
/>
</Form.Group>
</Form.Row>
<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 or user is not currently logged in"
)}
</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"),
Transaction = require("../database/models/transaction");
// Order pay logic route
router.post("/pay", function(req, res) {
const productInfo = req.body.productInfo;
const create_payment_json = {
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": "http://localhost:4000/orders/success",
"cancel_url": "http://localhost:4000/orders/cancel"
},
"transactions": [{
"item_list": {
"items": [{
"name": productInfo.name,
"sku": "001",
"price": productInfo.price,
"currency": "USD",
"quantity": productInfo.amount
}]
},
"amount": {
"currency": "USD",
"total": productInfo.price * productInfo.amount
},
"description": productInfo.description
}]
};
paypal.payment.create(create_payment_json, function(err, payment) {
if(err) {
console.log(err.message);
} else {
let order = new Order(req.body.orderInfo);
order.product = {
_id: productInfo.id,
name: productInfo.name,
description: productInfo.description,
price: productInfo.price,
amount: productInfo.amount
}
order.total = productInfo.price * productInfo.amount;
order.save().then(order => {
console.log(`Order saved successfully! Created order details: ${order}`);
}).catch(err => {
console.log("Order create error: ", err.message);
});
for(let i = 0; i < payment.links.length; i++) {
if(payment.links[i].rel === "approval_url") {
res.status(200).json({forwardLink: payment.links[i].href});
} else {
console.log("approval_url not found");
}
}
}
});
});
router.get("/success", function(req, res) {
const payerId = req.query.PayerID;
const paymentId = req.query.paymentId;
Order.findOneAndUpdate({ "owner.id": req.user, status: "Created" }).then((order) => {
order.status = "Completed";
const execute_payment_json = {
"payer_id": payerId,
"transactions": [{
"amount": {
"currency": "USD",
"total": order.total
}
}]
};
paypal.payment.execute(paymentId, execute_payment_json, function(err, payment) {
if(err) {
console.log("paypal.payment.execute error: ", err.response);
} else {
let transaction = new Transaction();
transaction.details = JSON.stringify(payment);
order.transaction = transaction;
order.save().then(order => {
transaction.save().then(transaction => {
res.status(200).json(`Payment accepted! Order details: ${order}`);
}).catch(err => {
console.log("Transaction create error: ", err.message);
});
}).catch(err => {
console.log("Order complete error: ", err.message);
});
}
});
}).catch(err => {
console.log("Payment error: ", err.message);
});
});
module.exports = router;
交易模型:
const mongoose = require("mongoose"),
Schema = mongoose.Schema;
let transactionSchema = new Schema({
details: [],
createdAt: {
type: Date,
default: Date.now()
}
});
module.exports = mongoose.model("Transaction", transactionSchema);
你为什么要重定向到贝宝和从贝宝 最好的解决方案是切换到此前端模式,而不是重定向: 这些fetch“/demo/…”需要替换为服务器上的路由,以分别创建订单、返回订单ID和捕获订单ID 这里的好处是您的站点在后台保持开放,提供“上下文”结帐体验
如果您想了解体验,请尝试客户端JS only版本,该版本不依赖于到那些“/demo/…”占位符的工作获取路由:
const mongoose = require("mongoose"),
Schema = mongoose.Schema;
let transactionSchema = new Schema({
details: [],
createdAt: {
type: Date,
default: Date.now()
}
});
module.exports = mongoose.model("Transaction", transactionSchema);