我一直试图让成功处理功能工作了几天,但没有进展。这个功能是假设作出一个把请求到后端,并设置订单。isPaid为真时,客户成功地用卡支付,然后将激活一个框在前端绿色显示客户,无论何时他们来到该页面的付款是成功的。我第一次使用贝宝作为工作相当不错的付款方式,但不得不改变到Paystack付款方式贝宝不工作在我的国家,但因为我做了改变,我开始遇到这个问题。也许有一个方法可以解决这个问题,希望有人能很快帮助我。下面是各自的代码供您参考。
后端
routes/orderRoutes.js
import express from 'express';
import expressAsyncHandler from 'express-async-handler';
import Order from '../models/orderModel.js';
import User from '../models/userModel.js';
import Product from '../models/productModel.js';
import { isAuth, isAdmin, mailgun, payOrderEmailTemplate } from '../utils.js';
import dotenv from 'dotenv';
dotenv.config();
const orderRouter = express.Router();
orderRouter.put(
'/:id/pay',
isAuth,
expressAsyncHandler(async (req, res) => {
const order = await Order.findById(req.params.id).populate(
'user',
'email name'
);
if (order) {
order.isPaid = true;
order.paidAt = Date.now();
order.paymentResult = {
id: req.body.id,
status: req.body.status,
update_time: req.body.update_time,
email_address: req.body.email_address,
};
const updatedOrder = await order.save();
res.send({ message: 'Order Paid', order: updatedOrder });
} else {
res.status(404).send({ message: 'Order Not Found' });
}
})
);
前端源代码/屏幕/订单屏幕. js
import axios from 'axios';
import React, { useContext, useEffect, useReducer } from 'react';
import Col from 'react-bootstrap/Col';
import Row from 'react-bootstrap/Row';
import Card from 'react-bootstrap/Card';
import { Helmet } from 'react-helmet-async';
import { Link, useNavigate, useParams } from 'react-router-dom';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import { Store } from '../Store';
import { getError, API_URL } from '../utils';
import ListGroup from 'react-bootstrap/ListGroup';
import { toast } from 'react-toastify';
import { Button } from 'react-bootstrap';
import { usePaystackPayment } from 'react-paystack';
axios.defaults.withCredentials = true;
function reducer(state, action) {
switch (action.type) {
case 'FETCH_REQUEST':
return { ...state, loading: true, error: '' };
case 'FETCH_SUCCESS':
return { ...state, loading: false, order: action.payload, error: '' };
case 'FETCH_FAIL':
return { ...state, loading: false, error: action.payload };
case 'PAY_REQUEST':
return { ...state, loadingPay: true };
case 'PAY_SUCCESS':
return { ...state, loadingPay: false, successPay: true };
case 'PAY_FAIL':
return { ...state, loadingPay: false, errorPay: action.payload };
case 'PAY_RESET':
return { ...state, loadingPay: false, successPay: false };
case 'DELIVER_REQUEST':
return { ...state, loadingDeliver: true };
case 'DELIVER_SUCCESS':
return { ...state, loadingDeliver: false, successDeliver: true };
case 'DELIVER_FAIL':
return { ...state, loadingDeliver: false };
case 'DELIVER_RESET':
return {
...state,
loadingDeliver: false,
successDeliver: false,
};
default:
return state;
}
}
export default function OrderScreen() {
const { state } = useContext(Store);
const { userInfo } = state;
const params = useParams();
const { id: orderId } = params;
const navigate = useNavigate();
const [
{
loading,
error,
order,
successPay,
loadingPay,
loadingDeliver,
successDeliver,
},
dispatch,
] = useReducer(reducer, {
loading: true,
order: {},
error: '',
successPay: false,
loadingPay: false,
});
function onSuccess() {
return async function () {
try {
dispatch({ type: 'PAY_REQUEST' });
const { data } = await axios.put(
`${API_URL}/api/order/${orderId}/pay`,
{
headers: { authorization: `Bearer ${userInfo.token}` },
}
);
dispatch({ type: 'PAY_SUCCESS', payload: data });
toast.success('Order is paid');
} catch (err) {
dispatch({ type: 'PAY_FAIL', payload: getError(err) });
toast.error(getError(err));
}
};
}
useEffect(() => {
const fetchOrder = async () => {
try {
dispatch({ type: 'FETCH_REQUEST' });
const { data } = await axios.get(`${API_URL}/api/orders/${orderId}`, {
headers: { authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: 'FETCH_SUCCESS', payload: data });
} catch (err) {
dispatch({ type: 'FETCH_FAIL', payload: getError(err) });
}
};
if (!userInfo) {
return navigate('/login');
}
if (
!order._id ||
successPay ||
successDeliver ||
(order._id && order._id !== orderId)
) {
fetchOrder();
if (successPay) {
dispatch({ type: 'PAY_RESET' });
}
if (successDeliver) {
dispatch({ type: 'DELIVER_RESET' });
}
}
}, [order, userInfo, orderId, navigate, successPay, successDeliver]);
async function deliverOrderHandler() {
try {
dispatch({ type: 'DELIVER_REQUEST' });
const { data } = await axios.put(
`${API_URL}/api/orders/${order._id}/deliver`,
{},
{
headers: { authorization: `Bearer ${userInfo.token}` },
}
);
dispatch({ type: 'DELIVER_SUCCESS', payload: data });
toast.success('Order is delivered');
} catch (err) {
toast.error(getError(err));
dispatch({ type: 'DELIVER_FAIL' });
}
}
const onClose = () => {
// implementation for whatever you want to do when the Paystack dialog closed.
console.log('closed');
};
const config = {
reference: new Date().getTime().toString(),
email: userInfo.email,
amount: order.totalPrice * 100, //Amount is in the country's lowest currency. E.g Kobo, so 20000 kobo = N200
publicKey: 'pk_test',
};
const PaystackHookExample = () => {
const initializePayment = usePaystackPayment(config);
return (
<div>
<Button
onClick={() => {
initializePayment(onClose, onSuccess);
}}
>
Make Payment
</Button>
</div>
);
};
return loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<div>
<Helmet>
<title>Order {orderId}</title>
</Helmet>
<h1 className="my-3">Order {orderId}</h1>
<Row>
<Col md={8}>
<Card className="mb-3">
<Card.Body>
<Card.Title>Shipping</Card.Title>
<Card.Text>
<strong>Name:</strong> {order.shippingAddress.fullName} <br />
<strong>Address: </strong> {order.shippingAddress.address},
{order.shippingAddress.city}, {order.shippingAddress.postalCode}
,{order.shippingAddress.country}
{order.shippingAddress.location &&
order.shippingAddress.location.lat && (
<a
target="_new"
href={`https://maps.google.com?q=${order.shippingAddress.location.lat},${order.shippingAddress.location.lng}`}
>
Show On Map
</a>
)}
</Card.Text>
{order.isDelivered ? (
<MessageBox variant="success">
Delivered at {order.deliveredAt}
</MessageBox>
) : (
<MessageBox variant="danger">Not Delivered</MessageBox>
)}
</Card.Body>
</Card>
<Card className="mb-3">
<Card.Body>
<Card.Title>Payment</Card.Title>
<Card.Text>
<strong>Method:</strong> {order.paymentMethod}
</Card.Text>
{order.isPaid ? (
<MessageBox variant="success">
Paid at {order.paidAt}
</MessageBox>
) : (
<MessageBox variant="danger">Not Paid</MessageBox>
)}
</Card.Body>
</Card>
<Card className="mb-3">
<Card.Body>
<Card.Title>Items</Card.Title>
<ListGroup variant="flush">
{order.orderItems?.map((item) => (
<ListGroup.Item key={item._id}>
<Row className="align-items-center">
<Col md={6}>
<img
src={item.image}
alt={item.name}
className="img-fluid rounded img-thumbnail"
></img>{' '}
<Link to={`/product/${item.slug}`}>{item.name}</Link>
</Col>
<Col md={3}>
<span>{item.quantity}</span>
</Col>
<Col md={3}>₦{item.price}</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
</Card.Body>
</Card>
</Col>
<Col md={4}>
<Card className="mb-3">
<Card.Body>
<Card.Title>Order Summary</Card.Title>
<ListGroup variant="flush">
<ListGroup.Item>
<Row>
<Col>Items</Col>
<Col>₦{order.itemsPrice.toFixed(2)}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Shipping</Col>
<Col>₦{order.shippingPrice.toFixed(2)}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Tax</Col>
<Col>₦{order.taxPrice.toFixed(2)}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>
<strong> Order Total</strong>
</Col>
<Col>
<strong>₦{order.totalPrice.toFixed(2)}</strong>
</Col>
</Row>
</ListGroup.Item>
{!order.isPaid && (
<ListGroup.Item>
<div>
<PaystackHookExample />
</div>
{loadingPay && <LoadingBox></LoadingBox>}
</ListGroup.Item>
)}
{userInfo.isAdmin && order.isPaid && !order.isDelivered && (
<ListGroup.Item>
{loadingDeliver && <LoadingBox></LoadingBox>}
<div className="d-grid">
<Button type="button" onClick={deliverOrderHandler}>
Deliver Order
</Button>
</div>
</ListGroup.Item>
)}
</ListGroup>
</Card.Body>
</Card>
</Col>
</Row>
</div>
);
}
下面的代码是什么工作时,我使用贝宝之前,我所作的变化如上所示。
import axios from 'axios';
import React, { useContext, useEffect, useReducer } from 'react';
import Col from 'react-bootstrap/Col';
import Row from 'react-bootstrap/Row';
import Card from 'react-bootstrap/Card';
import { PayPalButtons, usePayPalScriptReducer } from '@paypal/react-paypal-js';
import { Helmet } from 'react-helmet-async';
import { Link, useNavigate, useParams } from 'react-router-dom';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import { Store } from '../Store';
import { getError, API_URL } from '../utils';
import ListGroup from 'react-bootstrap/ListGroup';
import { toast } from 'react-toastify';
import { Button } from 'react-bootstrap';
axios.defaults.withCredentials = true;
function reducer(state, action) {
switch (action.type) {
case 'FETCH_REQUEST':
return { ...state, loading: true, error: '' };
case 'FETCH_SUCCESS':
return { ...state, loading: false, order: action.payload, error: '' };
case 'FETCH_FAIL':
return { ...state, loading: false, error: action.payload };
case 'PAY_REQUEST':
return { ...state, loadingPay: true };
case 'PAY_SUCCESS':
return { ...state, loadingPay: false, successPay: true };
case 'PAY_FAIL':
return { ...state, loadingPay: false, errorPay: action.payload };
case 'PAY_RESET':
return { ...state, loadingPay: false, successPay: false };
case 'DELIVER_REQUEST':
return { ...state, loadingDeliver: true };
case 'DELIVER_SUCCESS':
return { ...state, loadingDeliver: false, successDeliver: true };
case 'DELIVER_FAIL':
return { ...state, loadingDeliver: false };
case 'DELIVER_RESET':
return {
...state,
loadingDeliver: false,
successDeliver: false,
};
default:
return state;
}
}
export default function OrderScreen() {
const { state } = useContext(Store);
const { userInfo } = state;
const params = useParams();
const { id: orderId } = params;
const navigate = useNavigate();
const [
{
loading,
error,
order,
successPay,
loadingPay,
loadingDeliver,
successDeliver,
},
dispatch,
] = useReducer(reducer, {
loading: true,
order: {},
error: '',
successPay: false,
loadingPay: false,
});
const [{ isPending }, paypalDispatch] = usePayPalScriptReducer();
function createOrder(data, actions) {
return actions.order
.create({
purchase_units: [
{
amount: { value: order.totalPrice },
},
],
})
.then((orderID) => {
return orderID;
});
}
function onApprove(data, actions) {
return actions.order.capture().then(async function (details) {
try {
dispatch({ type: 'PAY_REQUEST' });
const { data } = await axios.put(
`${API_URL}/api/orders/${order._id}/pay`,
details,
{
headers: { authorization: `Bearer ${userInfo.token}` },
}
);
dispatch({ type: 'PAY_SUCCESS', payload: data });
toast.success('Order is paid');
} catch (err) {
dispatch({ type: 'PAY_FAIL', payload: getError(err) });
toast.error(getError(err));
}
});
}
function onError(err) {
toast.error(getError(err));
}
useEffect(() => {
const fetchOrder = async () => {
try {
dispatch({ type: 'FETCH_REQUEST' });
const { data } = await axios.get(`${API_URL}/api/orders/${orderId}`, {
headers: { authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: 'FETCH_SUCCESS', payload: data });
} catch (err) {
dispatch({ type: 'FETCH_FAIL', payload: getError(err) });
}
};
if (!userInfo) {
return navigate('/login');
}
if (
!order._id ||
successPay ||
successDeliver ||
(order._id && order._id !== orderId)
) {
fetchOrder();
if (successPay) {
dispatch({ type: 'PAY_RESET' });
}
if (successDeliver) {
dispatch({ type: 'DELIVER_RESET' });
}
} else {
const loadPayPalScript = async () => {
const { data: clientId } = await axios.get(
`${API_URL}/api/keys/paypal`,
{
headers: { authorization: `Bearer ${userInfo.token}` },
}
);
paypalDispatch({
type: 'resetOptions',
value: {
'client-id': clientId,
currency: 'NGN',
},
});
paypalDispatch({ type: 'setLoadingStatus', value: 'pending' });
};
loadPayPalScript();
}
}, [
order,
userInfo,
orderId,
navigate,
paypalDispatch,
successPay,
successDeliver,
]);
async function deliverOrderHandler() {
try {
dispatch({ type: 'DELIVER_REQUEST' });
const { data } = await axios.put(
`${API_URL}/api/orders/${order._id}/deliver`,
{},
{
headers: { authorization: `Bearer ${userInfo.token}` },
}
);
dispatch({ type: 'DELIVER_SUCCESS', payload: data });
toast.success('Order is delivered');
} catch (err) {
toast.error(getError(err));
dispatch({ type: 'DELIVER_FAIL' });
}
}
return loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<div>
<Helmet>
<title>Order {orderId}</title>
</Helmet>
<h1 className="my-3">Order {orderId}</h1>
<Row>
<Col md={8}>
<Card className="mb-3">
<Card.Body>
<Card.Title>Shipping</Card.Title>
<Card.Text>
<strong>Name:</strong> {order.shippingAddress.fullName} <br />
<strong>Address: </strong> {order.shippingAddress.address},
{order.shippingAddress.city}, {order.shippingAddress.postalCode}
,{order.shippingAddress.country}
{order.shippingAddress.location &&
order.shippingAddress.location.lat && (
<a
target="_new"
href={`https://maps.google.com?q=${order.shippingAddress.location.lat},${order.shippingAddress.location.lng}`}
>
Show On Map
</a>
)}
</Card.Text>
{order.isDelivered ? (
<MessageBox variant="success">
Delivered at {order.deliveredAt}
</MessageBox>
) : (
<MessageBox variant="danger">Not Delivered</MessageBox>
)}
</Card.Body>
</Card>
<Card className="mb-3">
<Card.Body>
<Card.Title>Payment</Card.Title>
<Card.Text>
<strong>Method:</strong> {order.paymentMethod}
</Card.Text>
{order.isPaid ? (
<MessageBox variant="success">
Paid at {order.paidAt}
</MessageBox>
) : (
<MessageBox variant="danger">Not Paid</MessageBox>
)}
</Card.Body>
</Card>
<Card className="mb-3">
<Card.Body>
<Card.Title>Items</Card.Title>
<ListGroup variant="flush">
{order.orderItems?.map((item) => (
<ListGroup.Item key={item._id}>
<Row className="align-items-center">
<Col md={6}>
<img
src={item.image}
alt={item.name}
className="img-fluid rounded img-thumbnail"
></img>{' '}
<Link to={`/product/${item.slug}`}>{item.name}</Link>
</Col>
<Col md={3}>
<span>{item.quantity}</span>
</Col>
<Col md={3}>₦{item.price}</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
</Card.Body>
</Card>
</Col>
<Col md={4}>
<Card className="mb-3">
<Card.Body>
<Card.Title>Order Summary</Card.Title>
<ListGroup variant="flush">
<ListGroup.Item>
<Row>
<Col>Items</Col>
<Col>₦{order.itemsPrice.toFixed(2)}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Shipping</Col>
<Col>₦{order.shippingPrice.toFixed(2)}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Tax</Col>
<Col>₦{order.taxPrice.toFixed(2)}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>
<strong> Order Total</strong>
</Col>
<Col>
<strong>₦{order.totalPrice.toFixed(2)}</strong>
</Col>
</Row>
</ListGroup.Item>
{!order.isPaid && (
<ListGroup.Item>
{isPending ? (
<LoadingBox />
) : (
<div>
<PayPalButtons
createOrder={createOrder}
onApprove={onApprove}
onError={onError}
></PayPalButtons>
</div>
)}
{loadingPay && <LoadingBox></LoadingBox>}
</ListGroup.Item>
)}
{userInfo.isAdmin && order.isPaid && !order.isDelivered && (
<ListGroup.Item>
{loadingDeliver && <LoadingBox></LoadingBox>}
<div className="d-grid">
<Button type="button" onClick={deliverOrderHandler}>
Deliver Order
</Button>
</div>
</ListGroup.Item>
)}
</ListGroup>
</Card.Body>
</Card>
</Col>
</Row>
</div>
);
}
1条答案
按热度按时间wkyowqbh1#
我只是用下面的代码替换了OrderScreen.js