Stripe webhook接收顺序异常引发NoMethodError错误如何解决
Stripe Webhook 交付顺序混乱问题的规避方案
Stripe 官方明确说明不保证 Webhook 事件的交付顺序,网络链路抖动、多节点转发都可能导致生成时间更早的事件后到达服务端,以下是可落地的规避方案:
方案1:统一事件处理器,用Upsert逻辑替代拆分的创建/更新逻辑
放弃按事件类型拆分创建、更新处理器,所有payment_intent相关事件共用同一个处理器,直接基于事件全量数据做存在则更新、不存在则创建的操作,从根本上避免找不到记录的问题。
代码示例:
# 统一处理器,替换原有CreateChargeRecord、UpdateChargeRecord class HandlePaymentIntentEvent def call(event) charge_object = event.data.object # 先判断当前事件的生成时间是否晚于本地已有记录的最新更新时间,避免旧事件覆盖新状态 event_created_time = Time.at(event.created) charge = Charge.find_or_initialize_by(stripe_id: charge_object.id) # 只有当本地记录是新建、或者当前事件更新时间更晚时才执行写入 if charge.new_record? || event_created_time > charge.updated_at charge.attributes = { user: User.find_by(stripe_id: charge_object.customer), receiver_id: User.find_by(merchant_id: charge_object.transfer_data&.destination)&.id, amount: charge_object.amount / 100, currency: 'eur', application_fee_amount: charge_object.application_fee_amount / 100, status: charge_object.status } charge.save! end end end # 配置修改为所有相关事件走同一个处理器 StripeEvent.configure do |events| events.subscribe 'payment_intent.created', HandlePaymentIntentEvent.new events.subscribe 'payment_intent.processing', HandlePaymentIntentEvent.new events.subscribe 'payment_intent.succeeded', HandlePaymentIntentEvent.new end
方案2:更新操作增加重试机制
如果不想调整现有代码结构,可以在更新逻辑中增加找不到记录时的延迟重试逻辑,重试几次后仍失败再抛出异常:
class UpdateChargeRecord def call(event) charge_object = event.data.object charge = Charge.find_by(stripe_id: charge_object.id) if charge.nil? # 把当前事件扔到延迟队列,3秒后重试,最多重试3次 PaymentIntentUpdateJob.set(wait: 3.seconds).perform_later(event.id, retry_count: 1) return end charge.update!(status: charge_object.status) end end
方案3:增加事件顺序校验字段
在Charge表增加latest_event_created_at字段,每次处理事件前先比较事件本身的created时间戳和本地存储的最新事件时间,只有当新事件的时间更晚时才执行写入,避免更早的事件后到达覆盖最新状态。
注意:所有方案都需要提前做好字段的非空校验,避免关联数据不存在导致的额外异常。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

