最近我一直在尝试在我的网站上实施Stripe Payment系统,但仍然不起作用。
我有一个包含订单的数据库,并且我在本地存储上设置了付款意图 ID,但如果这是我第一次尝试付款,条带会在我的本地存储上创建大量
js.stripe.com
并不断更改其上的付款意图 id,它在两个付款意图 id 之间不断变化。我按照我的逻辑看到控制台输出最后一个创建顺序和第一个更新顺序,因此它在这两个顺序之间不断交替。
也许这是我逻辑的一部分失败了,但我就是找不到它。
const [paymentIntent, setPaymentIntent] = useState<string | null>(null);
//Get payment Intent
useEffect(() => {
const cart_payment_intent: string | null = localStorage.getItem(INTENT_KEY);
if (cart_payment_intent && cart_payment_intent !== undefined) {
const payment_intent: string = JSON.parse(cart_payment_intent);
setPaymentIntent(payment_intent);
}
}, []);
//set payment intent on Local Storage
function setPaymentIntentLocalStorage(value: string | null) {
localStorage.setItem(INTENT_KEY, JSON.stringify(value));
setPaymentIntent(value);
}
"use client";
import axios from "axios";
import toast from "react-hot-toast";
import CheckoutForm from "./CheckoutForm";
import { LocalStorageContext } from "@/utils/providers/LocalStorageProvider";
import { useContext, useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { StripeElementsOptions, loadStripe } from "@stripe/stripe-js";
import { Elements } from "@stripe/react-stripe-js";
import { v4 } from "uuid";
import CheckoutError from "./components/CheckoutError";
import PaymentSuccess from "./components/CheckoutPaymentSuccess";
import CheckoutLoading from "./components/CheckoutLoading";
import CheckoutNoItem from "./components/CheckoutNoItems";
const stripePromise = loadStripe(
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY as string
);
export default function CheckoutMenu() {
const router = useRouter();
const { cartItems, paymentIntent, setPaymentIntentLocalStorage, tema } =
useContext(LocalStorageContext);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const [clientSecret, setClientSecret] = useState("");
const [paymentSucess, setPaymentSucess] = useState(false);
useEffect(() => {
async function handleIntent() {
if (cartItems) {
setLoading(true);
setError(false);
try {
const response = await axios.post("/api/create-payment-intent", {
items: cartItems,
payment_intent_id: paymentIntent,
});
setLoading(false);
setClientSecret(response.data?.paymentIntent.client_secret || "");
setPaymentIntentLocalStorage(response.data?.paymentIntent.id);
} catch (err) {
setError(true);
toast.error("Verifique se está logado e tente novamente", {
id: "Requsision Error",
});
return router.push("/login");
}
}
}
if (cartItems && cartItems.length > 0) {
handleIntent();
}
}, [cartItems, paymentIntent, router, setPaymentIntentLocalStorage]);
我在 Next.js 上的完整 API 路线:
import Stripe from "stripe";
import prisma from "@/libs/prismaDb";
import productVariables from "@/utils/ProductsDB/ProductVariables";
import getCurrentUser from "@/utils/interfaces/getCurrentUser";
import { NextResponse } from "next/server";
import { LocalStorageItem } from "@/utils/providers/LocalStorageProvider";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string, {
apiVersion: "2023-10-16",
});
function findProduct(productItem: LocalStorageItem) {
const product = productVariables.find(
(e) => e.productId === productItem.productId
);
if (!product) {
throw new Error(
`Preço para o produto com o seguinte id:${productItem.productId} não encontrado `
);
}
return product;
}
function calculateTotalPrice(items: LocalStorageItem[]) {
return items.reduce((total, item) => {
const product = findProduct(item);
return total + product.price * item.quantity;
}, 0);
}
export async function POST(req: Request) {
const currentUser = await getCurrentUser();
if (!currentUser) {
return NextResponse.json(
{ error: "Sem autorização, realize o login e tente novamente" },
{ status: 401 }
);
}
const body = await req.json();
interface item {
productId: string;
quantity: number;
color: string;
price: number;
}
const {
items,
payment_intent_id,
}: { items: item[]; payment_intent_id: string } = body;
items.forEach((item) => {
const product = findProduct(item);
item.price = product.price;
});
const total = Math.round(calculateTotalPrice(items) * 100);
if (total === 0) {
return NextResponse.json(
{ error: "O carrinho está vazio" },
{ status: 400 }
);
}
const orderData = {
user: { connect: { id: currentUser.id } },
amount: total,
currency: "brl",
payment_intent_id: payment_intent_id,
products: {
create: items.map((item: item) => {
return {
productId: item.productId,
quantity: item.quantity,
color: item.color,
price: item.price,
};
}),
},
};
if (payment_intent_id) {
const current_intent = await stripe.paymentIntents.retrieve(
payment_intent_id
);
if (!current_intent) {
console.error("Error in retrieving payment intent");
return NextResponse.json(
{ error: "Error in retrieving payment intent" },
{ status: 400 }
);
}
if (current_intent) {
const existingOrder = await prisma.order.findFirst({
where: { payment_intent_id: current_intent.id },
});
if (!existingOrder) {
orderData.payment_intent_id = current_intent.id;
const createdOrder = await prisma.order.create({ data: orderData });
return NextResponse.json({
paymentIntent: current_intent,
});
}
const updated_intent = await stripe.paymentIntents.update(
payment_intent_id,
{ amount: total }
);
await prisma.order.update({
where: { payment_intent_id: updated_intent.id },
data: {
amount: total,
products: {
deleteMany: {},
create: items.map((item: item) => {
return {
productId: item.productId,
quantity: item.quantity,
color: item.color,
price: item.price,
};
}),
},
},
});
return NextResponse.json({ paymentIntent: updated_intent });
}
} else if (!payment_intent_id) {
try {
const existingOrder = await prisma.order.findFirst({
where: { userId: currentUser.id, status: "Pendente" },
});
if (existingOrder) {
// Case: User has an existing order with status "Pendente"
const current_intent = await stripe.paymentIntents.retrieve(
existingOrder.payment_intent_id
);
if (!current_intent) {
console.error("Error in retrieving payment intent");
return NextResponse.json(
{ error: "Error in retrieving payment intent" },
{ status: 400 }
);
}
const updated_intent = await stripe.paymentIntents.update(
current_intent.id,
{ amount: total }
);
await prisma.order.update({
where: { payment_intent_id: updated_intent.id },
data: {
amount: total,
products: {
deleteMany: {},
create: items.map((item: item) => {
return {
productId: item.productId,
quantity: item.quantity,
color: item.color,
price: item.price,
};
}),
},
},
});
return NextResponse.json({ paymentIntent: updated_intent });
} else {
// Case: User doesn't have an existing order with status "Pendente"
const paymentIntent = await stripe.paymentIntents.create({
amount: total,
currency: "brl",
automatic_payment_methods: { enabled: true },
});
orderData.payment_intent_id = paymentIntent.id;
const newOrder = await prisma.order.create({ data: orderData });
return NextResponse.json({ paymentIntent: paymentIntent });
}
} catch (err) {
console.error(err);
return NextResponse.json({ error: "Erro ao criar Intent de pagamento" });
}
}
}
我尝试更改所有逻辑,为我想到的每个选项创建一条路径,并查看 Stripe 文档以发现这是一个正常问题。它主要发生在第一次进入结账页面时,当你这样做时,它会创建很多
js.stripe.com
和一些警报,因为我处于测试模式,例如:
您可以通过 HTTP 测试 Stripe.js 集成。但是,实时 Stripe.js 集成必须使用 HTTPS。
[Stripe.js] 如果您正在测试 Apple Pay 或 Google Pay,则必须通过 HTTPS 提供此页面,因为它无法通过 HTTP 运行。请阅读 https://stripe.com/docs/stripe-js/elements/ payment-request-button#html-js-precessions 了解更多详情。
[Stripe.js] 您尚未注册或验证域名,因此付款元素中未启用以下付款方式:
- 苹果支付
请按照https://stripe.com/docs/ payments/ payment-methods/pmd-registration注册并验证域名。
js?key=AIzaSyCab6eIMNih34mQb3XI_QWXagmF2_rvQAg&libraries=places&callback=initGoogleMapsService:264 Google Maps JavaScript API已直接加载,无需loading=async。这可能会导致性能不佳。有关最佳实践加载模式,请参阅 https://goo.gle/js-api-loading
所以,对于所有有同样问题的人来说,经过几天的搜索,实际上有两个非常简单的想法:
1 - 我的页面上依赖项的错误使用,我添加了很多依赖项,因为担心破坏 ES-Lint,所以要停止垃圾邮件实际上只是删除依赖项:
useEffect(() => {
async function handleIntent() {
if (cartItems) {
setLoading(true);
setError(false);
try {
const response = await axios.post("/api/create-payment-intent", {
items: cartItems,
payment_intent_id: paymentIntent,
});
setLoading(false);
setClientSecret(response.data?.paymentIntent.client_secret || "");
setPaymentIntentLocalStorage(response.data?.paymentIntent.id);
} catch (err) {
setError(true);
toast.error("Verifique se está logado e tente novamente", {
id: "Requsision Error",
});
return router.push("/login");
}
}
}
if (cartItems && cartItems.length > 0) {
handleIntent();
}
}, []);
2 - 第二个也是最重要的是 Next.js App Routing 上已经启用的限制模式,默认情况下,它会渲染所有组件两次,以便以更明确的方式显示错误,因此导致了双重渲染是这个。要禁用它,您只需将
reactStrictMode: false
添加到 next.config.js
谢谢大家!