PAYATHON 2026

Spree 0.10.2 如何移除配送和支付结账步骤

支付老李

结论

需要调整 CheckoutsController,但不能把 object_params 留空。

移除 payment 状态后,Spree 0.10.2 原有的 clear_payments_if_in_payment_state 仍会在 state machine 中查找 "payment",因此会报错:

"payment" is an invalid name

可以覆盖该方法,让它不执行任何操作。与此同时,object_params 仍须返回订单参数,并保留账单地址、配送地址等嵌套属性。否则地址不会赋给订单,验证时所有地址字段都会报错。

如果结账流程完全不处理在线支付,也不应在进入 complete 前调用 process_payment。

修改 checkout state machine

可以把流程改为从 address 直接进入 complete:

Checkout.state_machines[:state] =
  StateMachine::Machine.new(Checkout, :initial => 'address') do

    after_transition :to => 'complete', :do => :complete_order

    event :next do
      transition :from => 'address', :to => 'complete'
    end
  end

原来的以下代码应当删除:

before_transition :to => 'complete', :do => :process_payment

process_payment 用于处理支付记录或调用支付网关。如果货到付款没有对应的支付记录,就不应在完成结账前调用它。

如果系统仍需记录“货到付款”这种支付方式,应自动创建相应的 COD payment,而不是调用在线支付处理逻辑。具体实现取决于项目使用的支付扩展。

覆盖控制器方法

在 Spree 0.10.2 使用的 Rails 2 风格扩展中,可以通过 class_eval 覆盖相关方法:

CheckoutsController.class_eval do
  private

  def clear_payments_if_in_payment_state
    # checkout flow 中已不存在 payment 状态,因此不再执行原来的清理逻辑
  end

  def object_params
    order_params = params[:order] || {}

    if order_params[:use_billing] == '1' &&
       order_params[:bill_address_attributes]
      order_params[:ship_address_attributes] =
        order_params[:bill_address_attributes].dup
    end

    [order_params]
  end
end

这里需要注意两点。

object_params 不能为空

如果写成:

def object_params
end

该方法会返回 nil。控制器更新订单时便无法取得:

params[:order][:bill_address_attributes]
params[:order][:ship_address_attributes]

地址内容因此没有赋给订单。执行订单验证时,地址对象仍为空,所有必填字段都会报错。

返回值通常需要是数组

Spree 0.10.2 使用的旧版 controller/resource controller 代码通常会按下面的方式调用参数:

@order.update_attributes(*object_params)

所以 object_params 应返回:

[order_params]

而不是:

order_params

如果当前项目修改过 CheckoutsController#update,需要查看它如何调用 object_params。只有在直接调用 update_attributes(object_params) 时,才应返回普通 Hash。

处理“账单地址同时作为配送地址”

即使采用货到付款,订单通常仍需要配送地址。如果页面有“配送地址与账单地址相同”选项,就要在更新订单前复制地址参数:

if order_params[:use_billing] == '1'
  order_params[:ship_address_attributes] =
    order_params[:bill_address_attributes].dup
end

如果页面只显示一个地址表单,也可以把该地址同时作为账单地址和配送地址提交:

address_params = order_params[:bill_address_attributes]

if address_params
  order_params[:ship_address_attributes] = address_params.dup
end

不要通过关闭 Address 模型的必填验证来处理这个问题。这里的验证错误通常并非规则有误,而是控制器没有把嵌套地址参数传给订单。

还需要检查的地方

将流程改为 address -> complete 后,还要确认:

  • complete_order 不会强制要求订单已有支付记录。
  • 订单完成验证不要求已经生成配送方式或 shipment。
  • 地址页面提供了系统实际需要的账单地址和配送地址。
  • 页面、路由和控制器不会继续跳转到 checkout_path(:payment)。
  • 如果后台订单需要显示货到付款信息,应通过 COD payment method 或订单属性记录,不能简单伪造“已支付”状态。

如果项目还扩展过 Spree 0.10.2 的 checkout controller,方法签名和 object_params 的返回结构可能会有所不同。判断标准是:覆盖后的参数方法必须将完整的 params[:order] 传给 update_attributes,同时不能再引用已经从 state machine 中删除的 payment 或 delivery 状态。

备注:内容仅供参考。