我有一个正在运行的电子商务网站,其他支付平台的集成运行良好。然而,尝试集成 paystack 时遇到了一些错误,即使经过大量故障排除,我似乎也找不到问题出在哪里。
我需要 paystack 与其他网站一样功能,因为这个电子商务网站主要是针对尼日利亚的客户而设置的,我们需要能够使用他们的本地卡接受付款。付款的 onSuccess 组件在 paystack-public-key 设置下工作得很好,但它不会更新订单数据库,表明付款成功,并且还会返回带有标头的 404 错误
Request URL: http://localhost:3000/api/order/6626ea44b7683aa864d92837/pay
Request Method: PUT
Status Code: 404 Not Found
我在单页reactjs电子商务中使用了相同的付款方式,效果非常好,但我不知道为什么它在这个nextjs项目中没有做同样的事情。以下是为此设置的相关代码页。请查看并帮助谢谢。
/pages/order/[id].js
import styles from "@/styles/order.module.scss";
import Header from "@/components/header";
import Order from "@/models/Order";
import User from "@/models/user";
import { IoIosArrowForward } from "react-icons/io";
import db from "@/utils/db";
import { PayPalButtons, usePayPalScriptReducer } from "@paypal/react-paypal-js";
import { useReducer, useEffect } from "react";
import axios from "axios";
import { PaystackButton } from "react-paystack";
import { toast } from "react-toastify";
function reducer(state, action) {
switch (action.type) {
case "PAY_REQUEST":
return { ...state, loading: true };
case "PAY_SUCCESS":
return { ...state, loading: false, success: true };
case "PAY_FAIL":
return { ...state, loading: false, error: action.payload };
case "PAY_RESET":
return { ...state, loading: false, success: false, error: false };
}
}
export default function order({
orderData,
paypal_client_id,
country,
}) {
const [{ isPending }, paypalDispatch] = usePayPalScriptReducer();
const [dispatch] = useReducer(reducer, {
loading: true,
error: "",
success: "",
});
console.log({ orderData });
useEffect(() => {
if (!orderData._id) {
dispatch({
type: "PAY_RESET",
});
} else {
paypalDispatch({
type: "resetOptions",
value: {
"client-id": paypal_client_id,
currency: "USD",
},
});
paypalDispatch({
type: "setLoadingStatus",
value: "pending",
});
}
}, [order]);
function createOrderHanlder(data, actions) {
return actions.order
.create({
purchase_units: [
{
amount: {
value: orderData.total / 550,
},
},
],
})
.then((order_id) => {
return order_id;
});
}
function onApproveHandler(data, actions) {
return actions.order.capture().then(async function (details) {
try {
dispatch({ type: "PAY_REQUEST" });
const { data } = await axios.put(
`/api/order/${orderData._id}/pay`,
details
);
dispatch({ type: "PAY_SUCCESS", payload: data });
} catch (error) {
dispatch({ type: "PAY_ERROR", payload: error });
}
});
}
function onErroHandler(error) {
console.log(error);
}
const componentProps = {
reference: new Date().getTime().toString(),
email: orderData.user.email,
amount: orderData.total * 100, //Amount is in the country's lowest currency. E.g Kobo, so 20000 kobo = N200
currency: "NGN",
publicKey: process.env.PAYSTACK_PUBLIC_KEY,
text: "Proceed To Pay",
onSuccess: () => {
try {
const { data } = axios.put(`/api/order/${orderData._id}/pay`, {
orderData,
});
console.log(data, "paid successfully");
window.location.reload(false);
toast.success("Order is paid");
} catch (err) {
toast.error(getError(err));
}
},
onClose: () =>
toast.error("We are sorry you have to leave, come back soon!"),
};
return (
<>
<Header country={country} />
<div className={styles.order}>
<div className={styles.container}>
<div className={styles.order__infos}>
<div className={styles.order__header}>
<div className={styles.order__header_head}>
Home <IoIosArrowForward /> Orders <IoIosArrowForward /> ID{" "}
{orderData._id}
</div>
<div className={styles.order__header_status}>
Payment Status :{" "}
{orderData.isPaid ? (
<img src="/images/verified.png" alt="paid" />
) : (
<img src="/images/unverified.png" alt="not-paid" />
)}
</div>
<div className={styles.order__header_status}>
Order Status :
<span
className={
orderData.status == "Not Processed"
? styles.not_processed
: orderData.status == "Processing"
? styles.processing
: orderData.status == "Dispatched"
? styles.dispatched
: orderData.status == "Cancelled"
? styles.cancelled
: orderData.status == "Completed"
? styles.completed
: ""
}
>
{orderData.status}
</span>
</div>
</div>
<div className={styles.order__products}>
{orderData.products.map((product) => (
<div className={styles.product} key={product._id}>
<div className={styles.product__img}>
<img src={product.image} alt={product.name} />
</div>
<div className={styles.product__infos}>
<h1 className={styles.product__infos_name}>
{product.name.length > 30
? `${product.name.substring(0, 30)}...`
: product.name}
</h1>
<div className={styles.product__infos_style}>
<img src={product.color.image} alt="" /> / {product.size}
</div>
<div className={styles.product__infos_priceQty}>
{product.price}₦ x {product.qty}
</div>
<div className={styles.product__infos_total}>
{product.price * product.qty}₦
</div>
</div>
</div>
))}
<div className={styles.order__products_total}>
{orderData.couponApplied ? (
<>
<div className={styles.order__products_total_sub}>
<span>Subtotal</span>
<span>{orderData.totalBeforeDiscount}₦</span>
</div>
<div className={styles.order__products_total_sub}>
<span>
Coupon Applied <em>({orderData.couponApplied})</em>{" "}
</span>
<span>
-
{(
orderData.totalBeforeDiscount - orderData.total
).toFixed(2)}
₦
</span>
</div>
<div className={styles.order__products_total_sub}>
<span>Tax price</span>
<span>+{orderData.taxPrice}₦</span>
</div>
<div
className={`${styles.order__products_total_sub} ${styles.bordertop}`}
>
<span>TOTAL TO PAY</span>
<b>{orderData.total}₦</b>
</div>
</>
) : (
<>
<div className={styles.order__products_total_sub}>
<span>Tax price</span>
<span>+ {orderData.taxPrice}₦</span>
</div>
<div
className={`${styles.order__products_total_sub} ${styles.bordertop}`}
>
<span>TOTAL TO PAY</span>
<b>{orderData.total}₦</b>
</div>
</>
)}
</div>
</div>
</div>
<div className={styles.order__actions}>
<div className={styles.order__address}>
<h1>Customer's Info</h1>
<div className={styles.order__address_user}>
<div className={styles.order__address_user_infos}>
<img src={orderData.user.image} alt="" />
<div>
<span>{orderData.user.name}</span>
<span>{orderData.user.email}</span>
</div>
</div>
</div>
<div className={styles.order__address_shipping}>
<h2>Shipping Address</h2>
<span>
{orderData.shippingAddress.firstName}{" "}
{orderData.shippingAddress.lastName}
</span>
<span>{orderData.shippingAddress.address1}</span>
<span>{orderData.shippingAddress.address2}</span>
<span>
{orderData.shippingAddress.state},
{orderData.shippingAddress.city}
</span>
<span>{orderData.shippingAddress.zipCode}</span>
<span>{orderData.shippingAddress.country}</span>
</div>
<div className={styles.order__address_shipping}>
<h2>Billing Address</h2>
<span>
{orderData.shippingAddress.firstName}{" "}
{orderData.shippingAddress.lastName}
</span>
<span>{orderData.shippingAddress.address1}</span>
<span>{orderData.shippingAddress.address2}</span>
<span>
{orderData.shippingAddress.state},
{orderData.shippingAddress.city}
</span>
<span>{orderData.shippingAddress.zipCode}</span>
<span>{orderData.shippingAddress.country}</span>
</div>
</div>
{!orderData.isPaid && (
<div className={styles.order__payment}>
{orderData.paymentMethod == "paypal" && (
<div>
{isPending ? (
<span>loading...</span>
) : (
<PayPalButtons
createOrder={createOrderHanlder}
onApprove={onApproveHandler}
onError={onErroHandler}
></PayPalButtons>
)}
</div>
)}
{orderData.paymentMethod == "credit_card" && (
<PaystackButton
{...componentProps}
className="bg-[#441617] w-full text-center text-white h-16 rounded"
/>
)}
{orderData.paymentMethod == "cash" && (
<div className={styles.cash}>cash</div>
)}
</div>
)}
</div>
</div>
</div>
</>
);
}
export async function getServerSideProps(context) {
await db.connectDb();
const { query } = context;
const id = query.id;
const order = await Order.findById(id)
.populate({ path: "user", model: User })
.lean();
let paypal_client_id = process.env.PAYPAL_CLIENT_ID;
let stripe_public_key = process.env.STRIPE_PUBLIC_KEY;
await db.disconnectDb();
return {
props: {
orderData: JSON.parse(JSON.stringify(order)),
paypal_client_id,
country: {
name: "Nigeria",
flag: "https://cdn.ipregistry.co/flags/emojitwo/ng.svg",
code: "NG",
},
},
};
}
/pages/api/order/[id].js
import nc from "next-connect";
import auth from "@/middleware/auth";
import Order from "@/models/Order";
import db from "@/utils/db";
import User from "@/models/user";
const handler = nc().use(auth);
handler.put(async (req, res) => {
try {
await db.connectDb();
const orderId = req.query.id; // Ensure this is the correct way to extract the ID
console.log("Order ID:", orderId);
const order = await Order.findById(orderId).populate({
path: "user",
model: User,
});
if (order) {
order.isPaid = true;
order.paidAt = Date.now();
order.paymentResult = {
id: "12345",
status: "processed",
email: "[email protected]",
};
const newOrder = await order.save();
await db.disconnectDb();
console.log("Order updated:", newOrder);
res.json({ message: "Order is paid.", order: newOrder });
} else {
await db.disconnectDb();
console.log("Order not found");
res.status(404).json({ message: "Order is not found." });
}
} catch (error) {
console.error("Error processing payment:", error);
await db.disconnectDb();
return res.status(500).json({ message: "Internal server error." });
}
});
export default handler;
/models/Order.js
import mongoose from "mongoose";
const { ObjectId } = mongoose.Schema;
const orderSchema = new mongoose.Schema(
{
user: {
type: ObjectId,
ref: "User",
required: true,
},
products: [
{
product: {
type: ObjectId,
ref: "Product",
},
name: {
type: String,
},
image: {
type: String,
},
size: {
type: String,
},
qty: {
type: Number,
},
color: {
color: String,
image: String,
},
price: {
type: Number,
},
},
],
shippingAddress: {
firstName: {
type: String,
},
lastName: {
type: String,
},
phoneNumber: {
type: String,
},
address1: {
type: String,
},
address2: {
type: String,
},
city: {
type: String,
},
state: {
type: String,
},
zipCode: {
type: String,
},
country: {
type: String,
},
},
paymentMethod: {
type: String,
},
paymentResult: {
id: String,
status: String,
email: String,
},
total: {
type: Number,
required: true,
},
totalBeforeDiscount: {
type: Number,
},
couponApplied: {
type: String,
},
shippingPrice: {
type: Number,
required: true,
default: 0,
},
taxPrice: {
type: Number,
default: 0,
},
isPaid: {
type: Boolean,
required: true,
default: false,
},
status: {
type: String,
default: "Not Processed",
enum: [
"Not Processed",
"Processing",
"Dispatched",
"Cancelled",
"Completed",
],
},
paidAt: {
type: Date,
},
deliveredAt: {
type: Date,
},
},
{
timestamps: true,
}
);
const Order = mongoose.models.Order || mongoose.model("Order", orderSchema);
export default Order;
请求网址: http://localhost:3000/api/order/6626ea44b7683aa864d92837/支付请求 方法:PUT 状态代码:404 Not Found
404 表示类似于上述请求 URL 结构的端点不存在。
另外,我看到你正在这样做=>
const orderId = req.query.id;
但是我在请求 URL 中找不到查询。包含查询的请求 URL 的修改版本将是
http://localhost:3000/api/pay?orderId=6626ea44b7683aa864d92837
我不使用nextjs,而是检查你做了什么;如果您的路由器的文件名是 /pages/api/order/[id].js,您可能需要修改该文件中的 put 请求以包含“/pay”。可能是这样的
handler.put('/pay', async (req, res) => {