使用 Next.js 实现 Stripe 结账 - 完整指南
你好世界
我的名字是阿希克·查帕盖恩。
- 全栈 Web 开发人员
- 内容创作者
- 大学生
- 尼泊尔人
本文将带您了解在Next.js中使用Stripe Checkout的整个过程。
指数
介绍
让我们简要了解一下Stripe和Stripe Checkout。
Stripe: Stripe是一个面向企业的在线支付处理和信用卡处理平台。
Stripe Checkout: Stripe Checkout是一个预先构建的、托管的支付页面,针对转化率进行了优化。
演示
在线预览: https://stripe-checkout-next-js-demo.vercel.app/
Github: https://github.com/projectashik/stripe-checkout-next-js-demo
让我们一起建造
安装
正如标题所述,我们将使用Next.js来构建这个程序。
要使Next.js正常工作,我们需要安装Node.js并npm拥有它。
首先,如果您还没有安装Node.js ,请先安装它。
您可以按照以下指南安装Node.js。
可选:
如果您想使用yarn,请使用以下命令安装npm。
npm install --global yarn
现在,创建一个新的 Next.js 应用。
npx create-next-app stripe-checkout
yarn create next-app stripe-checkout
我使用了Tailwind CSS来设置组件样式。所以,我们也来安装Tailwind CSS吧。
yarn add --dev tailwindcss@latest postcss@latest autoprefixer@latest
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
然后运行以下命令创建 Tailwind CSS 配置文件和 PostCSS 配置文件。
npx tailwindcss init -p
现在用你喜欢的文本编辑器或 IDE(集成开发环境)打开项目。
对我来说,是Visual Studio Code。
# Run the following command to open the project in VS Code.
code .
现在,打开tailwind.config.js并更新purge选项并添加mode: 'jit'。
// tailwind.config.js
module.exports = {
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
mode: 'jit',
...
};
然后,打开styles/globals.css并删除所有内容,并添加以下内容。
@tailwind base;
@tailwind utilities;
@tailwind components;
*简便方法:*
如果您想使用简便方法,
只需运行以下命令即可。
npx create-next-app -e with-tailwindcss stripe-checkout
yarn create next-app -e with-tailwindcss stripe-checkout
现在,启动开发服务器并开始编写代码。
yarn dev
npm run dev
打扫
从 . 中移除main和footer标签pages/index.js。
如果你想编辑标签中的` titleand` ,那么你可以这样做。description<Head>
// pages/index.js
import Head from 'next/head';
import Image from 'next/image';
import styles from '../styles/Home.module.css';
export default function Home() {
return (
<div className={styles.container}>
<Head>
<title>Stripe Checkout with Next.js</title>
<meta name='description' content='Complete Step By Step Tutorial for integrating Stripe Checkout with Next.js' />
<link rel='icon' href='/favicon.ico' />
</Head>
</div>
);
}
.container并移除除in之外的所有样式styles/Home.module.css。
/* styles/Home.module.css */
.container {
min-height: 100vh;
padding: 0 0.5rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
}
制作用户界面
首先,我们将创建一个索引页面,其中包含一个用户可以购买的商品。
让我们给对象添加物品的详细信息。
// pages/index.js
const [item, setItem] = useState({
name: 'Apple AirPods',
description: 'Latest Apple AirPods.',
image:
'https://images.unsplash.com/photo-1572569511254-d8f925fe2cbb?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1400&q=80',
quantity: 0,
price: 999,
});
别忘了导入useState钩子。
import { useState } from 'react';
由于我们使用 Unsplash 作为图片来源,因此我们需要images.unsplash.com在images配置文件的相应部分进行配置next.config.js。
module.exports = {
reactStrictMode: true,
images: {
domains: ['images.unsplash.com'],
},
};
现在,让我们在用户界面中显示以下详细信息,并添加按钮来增加和减少用户想要购买的数量。
在,pages/index.js
...
<main>
<div className='shadow-lg border rounded p-2 '>
<Image src={item.image} width={300} height={150} alt={item.name} />
<h2 className='text-2xl'>$ {item.price}</h2>
<h3 className='text-xl'>{item.name}</h3>
<p className='text-gray-500'>{item.description}</p>
<p className='text-sm text-gray-600 mt-1'>Quantity:</p>
<div className='border rounded'>
<button
className='bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600'
>
-
</button>
<input
type='number'
className='p-2'
defaultValue={item.quantity}
/>
<button
className='bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600'
>
+
</button>
</div>
<p>Total: ${item.quantity * item.price}</p>
<button
disabled={item.quantity === 0}
className='bg-blue-500 hover:bg-blue-600 text-white block w-full py-2 rounded mt-2 disabled:cursor-not-allowed disabled:bg-blue-100'
>
Buy
</button>
</div>
</main>
...
现在你的页面应该看起来像这样。
现在,我们来制作增加和减少数量的按钮。
现在,我们将创建按钮点击时增加和减少物品数量的函数onQuantityPlus。onQuantityMinus
const changeQuantity = (value: number) => {
// Don't allow the quantity less than 0, if the quantity is greater than value entered by user then the user entered quantity is used, else 0
setItem({ ...item, quantity: Math.max(0, value) });
};
const onQuantityPlus = () => {
changeQuantity(item.quantity + 1);
};
const onQuantityMinus = () => {
changeQuantity(item.quantity - 1);
};
现在,将此函数添加到相应按钮的 onClick 事件中。
...
<button
onClick={onQuantityMinus}
className='bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600'
>
-
</button>
...
<button
onClick={onQuantityPlus}
className='bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600'
>
+
</button>
...
现在,让我们onChange在输入字段上添加事件来更新商品数量的值,也别忘了将其更改defaultValue为value。
...
<input
type='number'
className='p-2'
onChange={onInputChange}
value={item.quantity}
/>
...
现在,让我们创建onInputChange函数。
...
//
const onInputChange = (e) => {
changeQuantity(parseInt(e.target.value));
};
...
创建后端
首先,我们先来安装stripe库。
yarn add stripe
npm install stripe
现在,让我们添加所需内容environment variables。在根目录中
创建一个新文件,.env.local并添加以下数据。
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=YOUR_STRIPE_PUBLISHABLE_KEY
STRIPE_SECRET_KEY=YOUR_STRIPE_SECRET_KEY
您可以从以下位置获取这些凭据Dashboard -> Developers -> API Keys:
现在,我们需要构建一个 API 来获取将用户重定向到结账页面所需的会话 ID。
在 .目录下创建一个新文件api/create-stripe-session.js,并添加以下内容。
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
export default async (req, res) => {
const { item } = req.body;
};
在这里,我们将通过前端调用的 POST 方法获取商品详情。
创建 Stripe 所需的商品形状。
Stripe 需要接收特定类型的对象,这就是该对象。如果您愿意,请使用您的本地货币,而不是“usd”。
const transformedItem = {
price_data: {
currency: 'usd',
product_data: {
images: [item.image],
name: item.name,
},
unit_amount: item.price * 100,
},
description: item.description,
quantity: item.quantity,
};
*在后端创建 Stripe 会话:*
您需要创建一个 Stripe 会话对象,并在其中定义一些数据。
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [transformedItem],
mode: 'payment',
success_url: redirectURL + '?status=success',
cancel_url: redirectURL + '?status=cancel',
metadata: {
images: item.image,
},
});
res.json({ id: session.id });
-
payment_method_type在这里,我们添加了支付产品价格的支付方式。点击此处了解更多支付方式。 -
success_url在success_url中,您可以定义用户在付款成功后将要访问的页面。 -
cancel_url在cancel_url中,您可以定义用户点击返回按钮后将跳转到的页面。它可以是取消页面,也可以是结账页面。 -
metadata:在元数据中,我们将添加产品图片,如果您愿意,也可以添加其他选项。
如需了解其他选项,请点击此处。
现在,最终create-stripe-session.js文件应该看起来像这样。
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
async function CreateStripeSession(req, res) {
const { item } = req.body;
const redirectURL =
process.env.NODE_ENV === 'development'
? 'http://localhost:3000'
: 'https://stripe-checkout-next-js-demo.vercel.app';
const transformedItem = {
price_data: {
currency: 'usd',
product_data: {
images: [item.image],
name: item.name,
},
unit_amount: item.price * 100,
},
description: item.description,
quantity: item.quantity,
};
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [transformedItem],
mode: 'payment',
success_url: redirectURL + '?status=success',
cancel_url: redirectURL + '?status=cancel',
metadata: {
images: item.image,
},
});
res.json({ id: session.id });
}
export default CreateStripeSession;
现在,我们的后端已经准备就绪,现在我们需要向 API 发送 POST 请求来获取会话。
正在重定向到 Stripe 结账页面
为了重定向到 Stripe 结账页面,我们需要安装以下库。
yarn add @stripe/stripe-js axios
npm install @stripe/stripe-js axios
首先,我们先创建一个stripePromise变量。
const publishableKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
const stripePromise = loadStripe(publishableKey);
现在,我们将创建createCheckoutSession一个函数来获取结账的 Stripe 会话。
const createCheckOutSession = async () => {
const stripe = await stripePromise;
const checkoutSession = await axios.post('/api/create-stripe-session', {
item: item,
});
const result = await stripe.redirectToCheckout({
sessionId: checkoutSession.data.id,
});
if (result.error) {
alert(result.error.message);
}
};
别忘了导入loadStripe和axios。
import { loadStripe } from '@stripe/stripe-js';
import axios from 'axios';
现在,我们需要在用户点击Buy按钮时调用这个函数。onClick={createCheckoutSession}
<button
disabled={item.quantity === 0}
onClick={createCheckOutSession}
className='bg-blue-500 hover:bg-blue-600 text-white block w-full py-2 rounded mt-2 disabled:cursor-not-allowed disabled:bg-blue-100'
>
Buy
</button>
现在,我们来测试一下结账功能。
由于我们已经更新next.config.js并添加了该.env.local文件,因此请重启开发服务器。
yarn dev
npm run dev
现在,结果应该如下所示。
显示成功和取消消息
如果你们还记得的话,我们之前在and中使用过?status=查询参数。success_urlcancel_url
// pages/api/create-stripe-session.js
...
const session = await stripe.checkout.sessions.create({
...
success_url: redirectURL + '?status=success',
cancel_url: redirectURL + '?status=cancel',
...
});
...
因此,通过这个查询参数,我们就能知道付款是成功还是取消,并显示相应的消息。
首先,我们来获取状态。
// pages/index.js
import { useRouter } from 'next/router';
...
const router = useRouter();
const { status } = router.query;
现在,在主标签下方添加以下内容,以显示消息。
// pages/index.js
{status && status === 'success' && (
<div className='bg-green-100 text-green-700 p-2 rounded border mb-2 border-green-700'>
Payment Successful
</div>
)}
{status && status === 'cancel' && (
<div className='bg-red-100 text-red-700 p-2 rounded border mb-2 border-red-700'>
Payment Unsuccessful
</div>
)}
现在,结果应该是这样的。
我们的应用需要一些时间来分配 Stripe 会话。因此,在此期间,我们将显示其他内容,Processing...而不是Buy像演示中那样显示在按钮内部。
为此,
创建一个名为 loading 的新状态,并赋予其默认值false。
const [loading, setLoading] = useState(false);
然后,在创建 Stripe 会话时,将loading值更新为true。再次在创建 Stripe 会话时将loading值更新为 。false
const createCheckOutSession = async () => {
setLoading(true);
...
setLoading(false);
};
现在,更新Buy按钮。
<button
disabled={item.quantity === 0 || loading}
onClick={createCheckOutSession}
className='bg-blue-500 hover:bg-blue-600 text-white block w-full py-2 rounded mt-2 disabled:cursor-not-allowed disabled:bg-blue-100'
>
{loading ? 'Processing...' : 'Buy'}
</button>
这里,我们在加载时禁用按钮,并Processing...在加载时显示。
现在,结果应该是这样的:
全部完成。
文件
最终,您的pages/index.js文件应该如下所示:
import Head from 'next/head';
import Image from 'next/image';
import styles from '../styles/Home.module.css';
import { useState } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import axios from 'axios';
import { useRouter } from 'next/router';
export default function Home() {
const router = useRouter();
const { status } = router.query;
const [loading, setLoading] = useState(false);
const [item, setItem] = useState({
name: 'Apple AirPods',
description: 'Latest Apple AirPods.',
image:
'https://images.unsplash.com/photo-1572569511254-d8f925fe2cbb?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1400&q=80',
quantity: 0,
price: 999,
});
const changeQuantity = (value) => {
// Don't allow the quantity less than 0, if the quantity is greater than value entered by user then the user entered quantity is used, else 0
setItem({ ...item, quantity: Math.max(0, value) });
};
const onInputChange = (e) => {
changeQuantity(parseInt(e.target.value));
};
const onQuantityPlus = () => {
changeQuantity(item.quantity + 1);
};
const onQuantityMinus = () => {
changeQuantity(item.quantity - 1);
};
const publishableKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
const stripePromise = loadStripe(publishableKey);
const createCheckOutSession = async () => {
setLoading(true);
const stripe = await stripePromise;
const checkoutSession = await axios.post('/api/create-stripe-session', {
item: item,
});
const result = await stripe.redirectToCheckout({
sessionId: checkoutSession.data.id,
});
if (result.error) {
alert(result.error.message);
}
setLoading(false);
};
return (
<div className={styles.container}>
<Head>
<title>Stripe Checkout with Next.js</title>
<meta
name='description'
content='Complete Step By Step Tutorial for integrating Stripe Checkout with Next.js'
/>
<link rel='icon' href='/favicon.ico' />
</Head>
<main>
{status && status === 'success' && (
<div className='bg-green-100 text-green-700 p-2 rounded border mb-2 border-green-700'>
Payment Successful
</div>
)}
{status && status === 'cancel' && (
<div className='bg-red-100 text-red-700 p-2 rounded border mb-2 border-red-700'>
Payment Unsuccessful
</div>
)}
<div className='shadow-lg border rounded p-2 '>
<Image src={item.image} width={300} height={150} alt={item.name} />
<h2 className='text-2xl'>$ {item.price}</h2>
<h3 className='text-xl'>{item.name}</h3>
<p className='text-gray-500'>{item.description}</p>
<p className='text-sm text-gray-600 mt-1'>Quantity:</p>
<div className='border rounded'>
<button
onClick={onQuantityMinus}
className='bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600'
>
-
</button>
<input
type='number'
className='p-2'
onChange={onInputChange}
value={item.quantity}
/>
<button
onClick={onQuantityPlus}
className='bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600'
>
+
</button>
</div>
<p>Total: ${item.quantity * item.price}</p>
<button
disabled={item.quantity === 0 || loading}
onClick={createCheckOutSession}
className='bg-blue-500 hover:bg-blue-600 text-white block w-full py-2 rounded mt-2 disabled:cursor-not-allowed disabled:bg-blue-100'
>
{loading ? 'Processing...' : 'Buy'}
</button>
</div>
</main>
</div>
);
}
应该pages/api/create-stripe-sessoin.js看起来像这样。
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
async function CreateStripeSession(req, res) {
const { item } = req.body;
const redirectURL =
process.env.NODE_ENV === 'development'
? 'http://localhost:3000'
: 'https://stripe-checkout-next-js-demo.vercel.app';
const transformedItem = {
price_data: {
currency: 'usd',
product_data: {
images: [item.image],
name: item.name,
},
unit_amount: item.price * 100,
},
description: item.description,
quantity: item.quantity,
};
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [transformedItem],
mode: 'payment',
success_url: redirectURL + '?status=success',
cancel_url: redirectURL + '?status=cancel',
metadata: {
images: item.image,
},
});
res.json({ id: session.id });
}
export default CreateStripeSession;
结论
好了,文章到此结束。希望您喜欢这篇文章。请考虑给我一些反馈意见。
重要链接
鸣谢
- 消防船条纹课程
- @ Avneesh Agarwal Next.js 支付文章 - https://blog.avneesh.tech/payments-in-next
联系我:
文章来源:https://dev.to/byteslash/stripe-checkout-with-next-js-the-complete-guide-3i1





