реагировать и paypal-rest-sdk: как передать данные со страницы оплаты обратно в приложение - PullRequest
1 голос
/ 04 апреля 2020

У меня есть одностраничное приложение 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);

1 Ответ

0 голосов
/ 04 апреля 2020

Почему вы перенаправляете в PayPal и из PayPal?

Лучшее решение - это переключиться на этот внешний интерфейс, а не перенаправлять вообще: https://developer.paypal.com/demo/checkout/# / pattern / server

Эти выборки "/ demo / ...." необходимо заменить маршрутами на вашем сервере, чтобы создать заказ и вернуть OrderID, а также захватить OrderID соответственно.

Преимущество здесь ваш сайт остается открытым в фоновом режиме, что обеспечивает возможность проверки «в контексте».


Если вы хотите увидеть, как выглядит опыт, попробуйте на стороне клиента - JS - единственная версия, которая не зависит от рабочих маршрутов выборки для этих заполнителей '/ demo / ...': https://developer.paypal.com/demo/checkout/# / pattern / client

...