客户通过Stripe支付成功后如何将预订支付状态修改为已支付
问题根源
你当前的代码仅完成了Stripe结账会话的创建,没有处理支付成功后的状态更新逻辑。注意:绝对不能通过success_url对应的页面动作来修改支付状态,原因有两个:
- 用户支付完成后可能因网络问题没跳转到成功页,状态会永远卡住
- 恶意用户可以直接访问成功页地址,无需支付就能把订单标记为已付款,存在严重安全漏洞
Stripe的支付结果是异步通知的,必须通过官方的Webhook机制接收支付成功事件,再更新本地订单状态,这是唯一可靠的实现方式。
实现步骤
1. 修改结账会话创建逻辑,绑定预订ID
创建Stripe Session时把对应预订ID存在元数据里,后续Webhook收到事件时就能匹配到对应的本地订单,同时修正金额计算的精度隐患:
class CheckoutController < ApplicationController def create product = Listing.find(params[:listing_id]) booking = Booking.find(params[:id]) duration = (booking.end_date - booking.start_date).to_i + 1 @session = Stripe::Checkout::Session.create({ payment_method_types: ['card'], line_items: [{ name: product.name, amount: (product.price * 100).to_i, currency: "gbp", quantity: duration }], mode: 'payment', # 绑定预订ID到会话元数据 metadata: { booking_id: booking.id }, success_url: "#{bookings_url}?session_id={CHECKOUT_SESSION_ID}", cancel_url: bookings_url }) respond_to do |format| format.js end end end
2. 添加Webhook路由
在路由文件中新增Stripe Webhook的接收端点:
# config/routes.rb post '/stripe/webhook', to: 'stripe_webhooks#create'
3. 编写Webhook处理逻辑
新建对应的控制器,注意要做Stripe请求签名校验,防止伪造请求篡改订单状态:
class StripeWebhooksController < ApplicationController # 跳过CSRF校验,Stripe发请求不会携带站点CSRF凭证 skip_before_action :verify_authenticity_token def create payload = request.body.read sig_header = request.env['HTTP_STRIPE_SIGNATURE'] endpoint_secret = ENV['STRIPE_WEBHOOK_SECRET'] event = nil begin event = Stripe::Webhook.construct_event(payload, sig_header, endpoint_secret) rescue JSON::ParserError return head 400 rescue Stripe::SignatureVerificationError return head 400 end # 仅处理支付完成的事件 if event.type == 'checkout.session.completed' session = event.data.object booking = Booking.find(session.metadata.booking_id) # 避免重复更新状态 booking.update!(paid: true) unless booking.paid end head 200 end end
4. 配置Stripe端的Webhook规则
- 本地开发时用Stripe CLI做端口转发,执行命令
stripe listen --forward-to localhost:3000/stripe/webhook,将CLI输出的Webhook签名密钥配置到本地环境变量STRIPE_WEBHOOK_SECRET即可调试 - 上线后在Stripe后台Webhook配置页,添加线上端点地址
https://你的站点域名/stripe/webhook,选择监听checkout.session.completed事件,将生成的签名密钥配置到生产环境变量即可
注意事项
- success_url仅用于前端向用户展示支付成功的提示,不要在该路由的逻辑里写任何订单状态更新的代码
- 必须开启Webhook签名校验,否则任何人都可以构造假请求将所有订单标记为已支付
- 金额字段必须转为整数再传给Stripe,浮点数精度问题会导致支付金额错误
内容的提问来源于stack exchange,提问作者Pauli
相关产品推荐
相关产品推荐

