react 和 paypal-rest-sdk:如何将数据从支付页面传回应用程序
Posted
技术标签:
【中文标题】react 和 paypal-rest-sdk:如何将数据从支付页面传回应用程序【英文标题】:react and paypal-rest-sdk: how to pass data from the payment page back to the application 【发布时间】:2020-07-16 06:08:44 【问题描述】:我有一个 React 单页应用程序,它允许用户通过 paypal-rest-sdk 购买保存的产品(文档 here)。我希望这个工作的方式是让登录的用户能够订购有库存的产品。用户输入他们想要多少产品,然后包含产品和数量的订单被创建并存储为“已创建”。然后用户被重定向到他们确认或取消付款的 Paypal 购买页面。确认之前创建的订单再次找到并设置为“已完成”,并创建了一个用于存储 Paypal 付款信息的交易。目前我可以在沙盒环境下成功支付、下单、交易。
我现在的问题是,我用于往返 Paypal 付款页面的重定向导致我的应用忘记了登录用户,因此在成功付款后我无法获取用户的“已创建”订单。我必须在我的 postOrder() 函数中使用 window.location ,否则我会收到交叉引用错误,正如用户对我的previous question 的回答中所解释的那样。我需要记住用户,否则任何标有“已创建”的订单都可能被抓取并设置为“已完成”,不用说这会在我的应用程序中造成大量问题。我应该如何解决这个问题,还是应该尝试另一种方法?
前端逻辑:
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 orderSchema = new Schema(
product:
_id:
type: String
,
name:
type: String
,
description:
type: String
,
price:
type: Number
,
amount:
type: Number
,
total:
type: Number
,
status: // Created or Completed
type: String
,
transaction:
type: mongoose.Schema.Types.ObjectId,
ref: "Transaction"
,
owner:
id: type: mongoose.Schema.Types.ObjectId, ref: "User",
username: String
,
createdAt:
type: Date,
default: Date.now()
);
module.exports = mongoose.model("Order", orderSchema);
交易模型:
const mongoose = require("mongoose"),
Schema = mongoose.Schema;
let transactionSchema = new Schema(
details: [],
createdAt:
type: Date,
default: Date.now()
);
module.exports = mongoose.model("Transaction", transactionSchema);
【问题讨论】:
【参考方案1】:为什么要重定向到 PayPal?
最好的解决方案是切换到这种前端模式,并且根本不重定向:https://developer.paypal.com/demo/checkout/#/pattern/server
那些 fetch "/demo/...." 需要替换为您服务器上的路由,以分别创建订单并返回 OrderID 和捕获 OrderID。
这里的好处是您的网站在后台保持打开状态,提供“上下文”结帐体验。
如果您想看看体验是什么样的,请尝试仅客户端 JS 的版本,它不依赖于那些 '/demo/...' 占位符的工作获取路由:https://developer.paypal.com/demo/checkout/#/pattern/client
【讨论】:
以上是关于react 和 paypal-rest-sdk:如何将数据从支付页面传回应用程序的主要内容,如果未能解决你的问题,请参考以下文章
paypal-rest-sdk 仍然是一种有效的方法还是我们应该切换到braintree?
Paypal rest sdk沙盒账户在0资金后仍然可以付款
如何用 redux 和 react-router 组织状态?
如何为 useReducer 键入 state 和 dispatch - typescript 和 react