Stripe支付成功后如何向数据库添加订单数据?
解决支付成功后添加订单到数据库的问题
核心思路
不能依赖前端跳转后的请求写入订单(存在篡改、跳过风险),必须通过Stripe Webhooks监听支付成功事件,在后端安全完成订单创建与数据库写入操作。
具体实现步骤
1. 配置Stripe Webhook端点
在Stripe后台的「开发者工具 -> Webhooks」中,添加你的后端Webhook地址(例如https://your-domain.com/payment/webhook),订阅checkout.session.completed事件(该事件会在用户支付成功后触发)。复制Stripe提供的签名密钥,存入NestJS环境变量(如STRIPE_WEBHOOK_SECRET)。
2. 在NestJS中添加Webhook控制器与处理逻辑
修改payment.controller.ts,新增Webhook接收接口:
import { Controller, Post, Body, Headers } from '@nestjs/common'; import { Stripe } from 'stripe'; import { PaymentService } from './payment.service'; @Controller('payment') export class PaymentController { private stripe: Stripe; constructor( private paymentService: PaymentService, ) { this.stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: '2024-06-20', // 使用当前Stripe最新API版本 }); } // 原有创建支付会话接口 @Post() async createPayment(@Body() data: CartItem[]) { return this.paymentService.processPayment(data); } // 新增Webhook处理接口 @Post('webhook') async handleWebhook( @Body() rawBody: Buffer, @Headers('stripe-signature') signature: string, ) { let event: Stripe.Event; try { // 验证请求签名,确保来自Stripe event = this.stripe.webhooks.constructEvent( rawBody, signature, process.env.STRIPE_WEBHOOK_SECRET, ); } catch (err) { console.log('Webhook签名验证失败', err.message); return { status: 'error', message: err.message }; } // 处理支付成功事件 if (event.type === 'checkout.session.completed') { const session = event.data.object as Stripe.Checkout.Session; await this.paymentService.createOrderFromSession(session); } return { status: 'success' }; } }
3. 优化支付会话创建,添加元数据(可选但推荐)
在创建Stripe会话时,通过metadata字段存储自定义数据(如用户ID、购物车标识),方便后续Webhook处理时关联订单:
修改payment.service.ts的createSession方法:
async createSession(processedCartItems: ProcessedCartItem[], userId?: string) { return this.stripe.checkout.sessions.create({ line_items: processedCartItems.map(item => ({ price_data: { currency: 'usd', unit_amount: item.price * 100, product_data: { name: item.name, images: ['https://material.angular.io/assets/img/examples/shiba1.jpg'] }, }, quantity: item.quantity, })), mode: 'payment', success_url: process.env.STRIPE_SUCCESS_URL, cancel_url: process.env.STRIPE_CANCEL_URL, // 添加元数据存储自定义信息 metadata: { userId: userId || 'guest', // 登录用户传ID,游客用默认值 // 可添加cartId等标识,方便关联前端购物车数据 }, }); }
4. 实现订单写入数据库的服务方法
在payment.service.ts中新增createOrderFromSession方法,解析Stripe会话数据并写入数据库:
async createOrderFromSession(session: Stripe.Checkout.Session) { // 1. 解析会话数据 const userId = session.metadata?.userId; const lineItems = await this.stripe.checkout.sessions.listLineItems(session.id); // 2. 构造订单数据结构 const orderData = { userId, stripeSessionId: session.id, totalAmount: session.amount_total / 100, // Stripe返回金额为分,转成元 status: 'paid', items: lineItems.data.map(item => ({ productName: item.description, quantity: item.quantity, price: item.amount_subtotal / 100, })), // 若需收货地址等信息,可从session.shipping等字段提取 }; // 3. 写入数据库(替换为你的订单Repository操作) // await this.orderRepository.save(orderData); console.log('订单已创建并写入数据库:', orderData); }
5. 前端注意事项
- 无需在success_url页面额外发送订单创建请求,Webhook已完成后端处理
- 若需在成功页展示订单信息,可通过前端API查询(例如用Stripe会话ID作为参数)
关键注意点
- 必须做签名验证:防止伪造请求,确保仅处理Stripe官方发送的事件
- 禁止依赖前端触发订单创建:用户可能直接访问成功页,Webhook是唯一可靠的支付成功通知渠道
- 保证幂等性:Stripe可能重试Webhook请求,需通过
stripeSessionId等唯一标识避免重复创建订单
内容的提问来源于stack exchange,提问作者Cookie
相关产品推荐
相关产品推荐

