Solidus 调换 delivery 与 address 并移除 payment 步骤
结论
不建议把 :delivery 直接移到 :address 前面,也不要修改 Solidus 核心文件 checkout.rb。
Solidus 在计算可用配送方式时,通常需要收货地址、库存地点、配送区域以及商品的 shipping category。如果进入 delivery 时订单还没有地址,系统就无法生成有效的 shipment 或 shipping rate,很容易出现 unshippable items。
另外,cash on delivery 本质上是支付方式,in store pick up 才是 shipping option,两者不应放在同一组选项里。
推荐的 checkout flow
让地址步骤继续排在配送步骤之前:
Spree::Order.class_eval do
checkout_flow do
go_to_state :address
go_to_state :delivery
go_to_state :payment
go_to_state :confirm
go_to_state :complete
end
end
如果网站完全不需要支付步骤,可以移除 :payment:
Spree::Order.class_eval do
checkout_flow do
go_to_state :address
go_to_state :delivery
go_to_state :confirm
go_to_state :complete
end
end
移除后,用户也就无法在 checkout 中选择货到付款。要支持 COD,通常应保留 :payment,并将货到付款注册为 Spree::PaymentMethod。
配置店内自提
店内自提应配置为独立的 shipping method,同时确认以下设置:
- 商品使用了正确的 shipping category。
- 门店对应的 stock location 有库存。
- shipping method 关联了正确的 shipping category。
- shipping method 对订单地址所属的 zone 可用。
- 自提运费计算器返回
0,或者返回业务规定的费用。
遇到 unshippable items 时,除了检查地址是否为空,还要检查 stock location、库存、zone 和 shipping category。仅创建一个名为 “Store Pickup” 的配送方式,并不会让它自动适用于所有订单。
自动使用门店地址
用户选择自提配送方式后,可以在自定义 checkout controller、service object 或订单更新逻辑中,将 ship_address 替换为门店地址。不要直接修改 Solidus gem 内的 checkout.rb。
下面是一个简化示例:
class ApplyStorePickupAddress
STORE_ADDRESS = {
firstname: "Store",
lastname: "Pickup",
address1: "123 Store Street",
city: "Shanghai",
zipcode: "200000",
phone: "021-00000000"
}.freeze
def self.call(order)
address = order.ship_address || Spree::Address.new
address.assign_attributes(STORE_ADDRESS)
# country_id 和 state_id 应使用当前数据库中的实际记录。
address.country_id = Spree::Country.find_by(iso: "CN")&.id
order.ship_address = address
order.save!
end
end
配送方式提交成功后,可以根据所选 shipping rate 对应的 shipping method 判断是否为自提,再调用该服务:
if selected_shipping_method.store_pickup?
ApplyStorePickupAddress.call(@order)
end
最好为 shipping method 增加明确的标识,例如 store_pickup? 或自定义 preference,不要依赖名称字符串:
shipping_method.name == "Store Pickup"
管理员可能会修改名称,名称也可能需要本地化,依赖字符串容易导致判断逻辑失效。
更新地址后,可能还要重新生成 shipments 和 shipping rates。不同 Solidus 版本使用的方法可能不同,应调用当前版本提供的订单更新器或 shipment 重建接口,不要直接套用其他版本的内部方法。
更合理的交互设计
如果用户必须先选择配送或到店自提,可以在标准 checkout 前增加一个 fulfillment step:
- 用户选择送货或自提。
- 选择自提后立即设置门店地址。
- 选择送货后进入
address。 - 地址确定后再进入
delivery,生成配送费率。 - 最后进入
payment和confirm。
这种设计比直接交换 delivery 和 address 更稳妥,因为系统在计算 shipping rate 前始终可以取得有效地址。
如果支持多门店自提,还要先让用户选择具体门店,不能用同一个固定地址覆盖所有自提订单。
关于修改 checkout.rb
没有必要直接修改 Solidus 核心文件,也不应该这样做。升级 gem 时,这些修改会被覆盖,还可能破坏订单状态机。通常可以通过以下方式扩展:
- Order decorator:调整 checkout 状态。
- Checkout controller decorator:处理用户提交和页面跳转。
- Service object:设置门店地址、重建 shipment。
- 自定义 payment method:实现货到付款。
- 自定义 shipping method 或扩展字段:标识店内自提。
查阅资料时,应优先使用与项目 Solidus 版本一致的官方文档和源码,重点查看 Checkout、Orders、Shipments、Stock Locations、Zones、Shipping Methods 和 Payment Methods。不同版本的状态机和订单更新接口可能不同,尤其不要直接调用其他版本示例中的私有方法。
备注:内容仅供参考。
