У меня есть одностраничное приложение React, которое позволяет пользователям приобретать сохраненные продукты через PayPal-Rest-SDK (документы здесь ). Я хочу, чтобы это работало, чтобы вошедший в систему пользователь мог заказать продукт, если он есть в наличии. Пользователь вводит, сколько продуктов они хотят, и затем создается заказ, содержащий продукт и количество, и сохраняется как «Создано». Затем пользователь перенаправляется на страницу покупки Paypal, где он подтверждает или отменяет платеж. После подтверждения ранее созданный заказ обнаруживается снова и устанавливается на «Завершено» с транзакцией, созданной для хранения платежной информации Paypal. В настоящее время я могу сделать успешный платеж, заказ и транзакцию в среде «песочницы».
Моя проблема сейчас заключается в том, что перенаправление, которое я использую для перехода на страницу оплаты Paypal и обратно, приводит к тому, что мое приложение забывает о зарегистрированных пользователь не может получить заказ пользователя "Создан" после успешной оплаты. Я должен использовать window.location в своей функции postOrder (), иначе я получу ошибку перекрестной ссылки, как объяснено в ответе пользователя на мой предыдущий вопрос . Мне нужно, чтобы пользователя запомнили, иначе любой заказ с пометкой «Создан» можно было бы захватить и установить на «Завершено», разумеется, это вызовет огромные проблемы в моем приложении. Как мне go об этой проблеме или просто попробовать другой подход?
Frontend Logi c:
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;
Backend Logi c:
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);