如何用 Next.js 和 Stripe 构建电商网站 - 2026 最新实战指南
如何用 Next.js 和 Stripe 构建电商网站 - 2026 最新实战指南
我花了 3 个月时间,踩了 50+ 个坑,终于用 Next.js 15 和 Stripe 搭建了一个完整的电商网站。月收入从 0 到 $12,000。
这篇文章不讲理论,只讲实战。我会告诉你: • 为什么我放弃了 Shopify,选择自建站 • Stripe 支付集成的 3 个致命坑(官方文档不会告诉你) • 如何用 Next.js 15 的 Server Actions 优化性能 40% • 数据库设计的 5 个关键决策(影响后期扩展)
读完这篇文章,你能学会 80% 的核心技术。 想要完整源码和一键部署脚本?文末有惊喜。
让我们开始吧 👇
目录
- 为什么选择 Next.js + Stripe?
- 技术栈选型(我的 3 次失败经验)
- 项目架构设计(含流程图)
- 核心功能实现
- 我踩过的 10 个大坑
- 性能优化技巧
- 部署上线指南
- 总结与下一步
1. 为什么选择 Next.js + Stripe?
我的 3 次失败经验
第一次:用 Shopify
- 优点:快速上线(1天)
- 缺点:月费 $29,交易手续费 2%,定制困难
- 结果:3 个月后迁移,浪费 $500
第二次:用 WordPress + WooCommerce
- 优点:插件丰富
- 缺点:性能差(加载 5 秒),安全漏洞多
- 结果:被黑客攻击,数据泄露
第三次:用 Next.js + Stripe(成功)
- 优点:完全控制,性能极佳(加载 0.8 秒),无月费
- 缺点:开发周期长(2 周)
- 结果:月收入 $12,000,利润率 85%
技术栈对比表
| 方案 | 开发时间 | 月成本 | 性能 | 可定制性 | 推荐度 |
|---|---|---|---|---|---|
| Shopify | 1天 | $29+ | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ |
| WordPress | 3天 | $10+ | ⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
| Next.js | 2周 | $0 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
关键决策点
如果你符合以下条件,强烈推荐 Next.js: ✅ 有基础的 React 知识 ✅ 需要高度定制化 ✅ 追求极致性能 ✅ 想要完全控制数据 ✅ 长期运营(省月费)
如果你只是测试想法,用 Shopify。
2. 项目架构设计
整体架构
前端(Next.js 15)
↓
API 路由(Server Actions)
↓
数据库(PostgreSQL)
↓
支付网关(Stripe)
数据库设计(5 个关键决策)
决策 1:用户表设计
❌ 错误做法:
CREATE TABLE users (
id INT,
email VARCHAR(255),
password VARCHAR(255) -- 明文存储,危险!
);
✅ 正确做法:
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
password_hash TEXT NOT NULL, -- bcrypt 加密
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
为什么?
- UUID 比自增 ID 更安全(防止遍历攻击)
- 密码必须加密(bcrypt,成本因子 12)
- 时间戳用于审计
决策 2:订单表设计
这是我踩的最大的坑。最初我这样设计:
❌ 错误做法:
CREATE TABLE orders (
id INT,
user_id INT,
product_id INT, -- 只存 ID,产品信息变了怎么办?
price DECIMAL(10,2) -- 价格变了怎么办?
);
问题:
- 产品改名后,历史订单显示错误
- 价格调整后,财务对不上账
✅ 正确做法:
CREATE TABLE orders (
id UUID PRIMARY KEY,
user_id UUID,
-- 快照产品信息(订单时的状态)
product_snapshot JSONB NOT NULL,
price_at_purchase DECIMAL(10,2) NOT NULL,
stripe_payment_id VARCHAR(255),
status VARCHAR(50) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT NOW()
);
为什么?
- JSONB 存储产品快照(订单时的名称、描述、图片)
- price_at_purchase 记录购买时的价格
- stripe_payment_id 用于对账
这个坑让我损失了 $2,000(财务对不上账,税务问题)
3. Stripe 支付集成(3 个致命坑)
坑 1:Webhook 验证失败
我第一次集成 Stripe 时,支付成功了,但订单状态没更新。 查了 2 天日志,发现是 Webhook 签名验证失败。
❌ 错误代码:
// 直接读取 body,签名验证会失败!
export async function POST(req: Request) {
const body = await req.json(); // ❌ 错误!
const sig = req.headers.get('stripe-signature');
const event = stripe.webhooks.constructEvent(
body, // ❌ 这里应该是原始字符串,不是 JSON
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
}
✅ 正确代码:
export async function POST(req: Request) {
const body = await req.text(); // ✅ 读取原始字符串
const sig = req.headers.get('stripe-signature');
try {
const event = stripe.webhooks.constructEvent(
body,
sig!,
process.env.STRIPE_WEBHOOK_SECRET!
);
// 处理事件
if (event.type === 'payment_intent.succeeded') {
// 更新订单状态
}
} catch (err) {
console.error('Webhook 验证失败:', err);
return new Response('Webhook Error', { status: 400 });
}
}
关键点:
- 必须用
req.text()而不是req.json() - Stripe 需要原始字符串来验证签名
- 验证失败会导致订单状态不更新
这个坑让我损失了 3 笔订单($450)
坑 2:货币单位错误
Stripe 的金额单位是分,不是元!
❌ 错误代码:
const session = await stripe.checkout.sessions.create({
line_items: [{
price_data: {
currency: 'usd',
unit_amount: 9.99, // ❌ 错误!应该是 999
product_data: { name: '教程' }
},
quantity: 1
}]
});
结果:用户支付了 $0.0999(不到 1 美分)
✅ 正确代码:
const session = await stripe.checkout.sessions.create({
line_items: [{
price_data: {
currency: 'usd',
unit_amount: 999, // ✅ 9.99 美元 = 999 美分
product_data: { name: '教程' }
},
quantity: 1
}]
});
这个坑让我白送了 20 份教程($200)
坑 3:测试环境和生产环境混用
永远不要在生产环境使用测试密钥!
我曾经不小心在生产环境用了测试密钥,结果:
- 用户支付成功,但钱没到账
- 订单状态显示"已支付"
- 客服电话被打爆
解决方案:
// 使用环境变量区分
const stripeKey = process.env.NODE_ENV === 'production'
? process.env.STRIPE_LIVE_SECRET_KEY
: process.env.STRIPE_TEST_SECRET_KEY;
const stripe = new Stripe(stripeKey!);
4. 性能优化技巧(加载速度从 3.2s 到 0.8s)
优化 1:图片懒加载
❌ 优化前:
- 首页加载 20 张产品图(每张 500KB)
- 总大小:10MB
- 加载时间:3.2 秒
✅ 优化后:
import Image from 'next/image';
<Image
src="/product.jpg"
alt="产品"
width={300}
height={300}
loading="lazy" // 懒加载
placeholder="blur" // 模糊占位
/>
结果:
- 首屏只加载 3 张图
- 总大小:1.5MB
- 加载时间:0.8 秒
性能提升 75%
优化 2:使用 Server Components
Next.js 15 的 Server Components 可以大幅减少客户端 JavaScript。
❌ 优化前(Client Component):
'use client';
export default function ProductList() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('/api/products')
.then(res => res.json())
.then(setProducts);
}, []);
return <div>{/* 渲染产品 */}</div>;
}
✅ 优化后(Server Component):
export default async function ProductList() {
const products = await getProducts(); // 服务端获取
return <div>{/* 渲染产品 */}</div>;
}
JavaScript 体积减少 60%
优化 3:数据库查询优化
❌ 优化前(N+1 查询问题):
const products = await db.products.findMany();
for (const product of products) {
product.category = await db.categories.findUnique({
where: { id: product.categoryId }
});
}
✅ 优化后(使用 JOIN):
const products = await db.products.findMany({
include: {
category: true // 一次查询搞定
}
});
查询时间从 500ms 降到 50ms
5. 部署上线指南
步骤 1:准备环境变量
# .env.production
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxx
STRIPE_SECRET_KEY=sk_live_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
DATABASE_URL=postgresql://xxx
NEXT_PUBLIC_URL=https://yourdomain.com
步骤 2:配置 Vercel
- 连接 GitHub 仓库
- 添加环境变量
- 点击部署
步骤 3:配置 Stripe Webhook
- 登录 Stripe Dashboard
- 进入 Developers → Webhooks
- 添加端点:
https://yourdomain.com/api/webhooks/stripe - 选择事件:
payment_intent.succeeded - 复制 Webhook 密钥到环境变量
步骤 4:测试支付流程
使用 Stripe 测试卡号:
- 成功:4242 4242 4242 4242
- 失败:4000 0000 0000 0002
- 需要 3D 验证:4000 0025 0000 3155
6. 总结
通过这篇文章,你学会了: ✅ Next.js + Stripe 的完整架构 ✅ 数据库设计的 5 个关键决策 ✅ Stripe 支付集成的 3 个致命坑 ✅ 性能优化的 3 个技巧 ✅ 部署上线的完整流程
你现在可以自己搭建一个电商网站了!
但是,从 0 到 1 搭建还是需要时间的:
- 写代码:2 周
- 调试:1 周
- 部署:3 天
- 总计:约 1 个月
如果你想节省这 1 个月时间,直接获取:
- 价值上万的完整源码(15,000+ 行)
- 一键部署脚本
- 完整配置文件
- 详细技术文档
👇 继续往下看,解锁完整项目资源包