Intershop 7.10 如何实现零金额购物车免支付结账
结论
Intershop 7.10 没有通用 preference,无法让零金额 basket 在完全不设置 payment method 的情况下直接创建订单。
即使 Total Amount 为 0,标准订单创建流程仍会校验 basket 的支付信息,并查找适用的 payment method。如果找不到,就会抛出:
No payment method is applicable for basket
更稳妥的做法是配置或开发一个零金额订单专用的 payment method,例如 Free Order 或 Zero Payment。这个方式不处理真实支付,只用于满足结账流程和订单模型对支付方式的要求。
为什么金额为 0 仍然需要 payment method
在 Intershop 7.10 的标准流程中,“是否需要扣款”和”订单是否需要支付方式”是两个不同的判断。
金额为 0 只表示:
payment amount = 0
订单创建时仍可能执行以下检查:
- basket 是否有适用的 payment method;
- 当前 payment method 是否适用于该 basket;
- 是否可以创建 payment instrument 或 payment transaction;
- basket 的支付分配是否完整;
- 支付数据能否复制到订单。
所以,不能因为 Total Amount == 0 就认定 payment method 可以为空。报错来自 PaymentServiceBORepositoryImpl,说明流程失败在支付方式适用性检查阶段,而不是实际扣款阶段。
推荐方案:提供零金额支付方式
可以为零金额订单提供一个不接入外部支付处理的 payment method,并限制它只能用于待支付金额为零的 basket。
这个支付方式通常需要满足以下条件:
- 只在待支付金额为
0时适用; - 不显示信用卡、银行账户等输入字段;
- 不调用 PSP;
- 不执行 authorization、capture 或 refund;
- 在订单中保留明确的支付类型,例如
ZERO_PAYMENT; - 不能用于非零金额 basket。
storefront 可以自动选择这个方式,并跳过常规的 payment method 选择页面。这里的”跳过”是指客户无须手工选择,basket 中仍然需要设置 payment method。
零金额判断
判断依据应该是最终待支付金额,而不是商品小计。折扣、运费、税费、礼品卡和舍入都可能改变最终金额。
示意代码如下:
Money payableAmount = basket.getTotal();
boolean isZeroAmount =
payableAmount != null
&& payableAmount.getValue().signum() == 0;
实际项目中,需要将其替换为当前 7.10 定制层使用的 basket API。不要通过字符串或 double 比较金额,例如:
// 不推荐
basket.getTotal().getValue().doubleValue() == 0.0;
如果业务允许出现负金额 basket,还需要单独处理,不能将所有”小于等于零”的情况都当作免费订单。
结账流程中的处理方式
在进入 payment selection 前检查最终金额。如果金额为零,就自动写入专用 payment method,然后进入下一步:
if (isZeroAmount(basket)) {
assignZeroPaymentMethod(basket);
validateBasket(basket);
continueCheckout();
} else {
showPaymentMethodSelection();
}
assignZeroPaymentMethod 的具体实现取决于项目采用的支付框架和 cartridge。重点是通过标准 payment service 将支付方式或 payment instrument 关联到 basket,而不是只写入一个自定义属性:
private void assignZeroPaymentMethod(Basket basket) {
PaymentMethod zeroPayment = findPaymentMethod("ZERO_PAYMENT");
if (zeroPayment == null || !zeroPayment.isApplicable(basket)) {
throw new IllegalStateException(
"ZERO_PAYMENT is not configured for the current application"
);
}
setBasketPaymentMethod(basket, zeroPayment);
}
上面的类名和方法仅用于说明处理逻辑,并不是可以在 Intershop 7.10 中原样编译的公共 API。实际实现需要根据项目使用的 BasketBO、payment instrument 和 payment service 接口进行适配。
支付方式的适用性规则
零金额支付方式至少要检查以下条件:
public boolean isApplicable(Basket basket) {
Money amount = basket.getTotal();
return amount != null
&& amount.getValue().signum() == 0;
}
同时还要确认它已在以下范围内启用:
- 当前 channel;
- 当前 application;
- 当前 currency;
- 当前 customer type;
- 当前 shipping destination;
- 当前 basket 类型。
如果 payment method 已经创建,却没有分配给对应的 channel 或 application,创建订单时仍可能出现相同的错误。
是否可以彻底不设置 payment method
技术上可以,但需要定制订单创建和支付校验流程,无法通过 preference 调整实现。
至少需要同步修改:
- basket 支付完整性校验;
- applicable payment method 查询;
- basket 到 order 的支付数据复制;
- order creation validation;
- 后续订单处理、导出和状态流转;
- 取消、退货、退款以及客服后台对支付信息的处理。
只绕过抛出异常的那一处检查通常解决不了问题。后续代码、订单导出接口或 OMS 集成仍可能默认订单带有支付方式。除非整个订单链路都明确支持”无支付信息订单”,否则不建议将 payment method 设为 null。
Storefront 展示建议
对客户来说,零金额订单不需要展示可操作的支付步骤。可以采用以下流程:
- 重新计算并验证 basket。
- 确认最终待支付金额为
0。 - 后台自动设置
ZERO_PAYMENT。 - 隐藏或跳过支付方式选择页面。
- 在订单确认页显示”无需支付”或”免费订单”。
这样既符合客户预期,也能保留 Intershop 标准订单模型要求的支付信息。
注意事项
- 必须等促销、税费、运费和舍入全部计算完成后,再判断金额是否为零。
- 不要用普通支付方式处理
0金额,部分 PSP 会拒绝零金额 authorization。 ZERO_PAYMENT不能用于任何正金额订单。- basket 重新计算后,如果金额从
0变为正数,需要清除该支付方式并要求客户重新选择。 - 如果订单通过礼品卡或余额抵扣到零,需要区分”免费订单”和”已由储值工具全额支付”。两者的财务记录通常不同。
- 上线前需要覆盖匿名客户、注册客户、不同币种、促销、免运费、税费调整、订单取消和退货等场景。
因此,在 Intershop 7.10 中,推荐的处理方式不是让 payment method 缺失,而是为零金额 basket 自动设置一个不会触发真实支付处理的专用 payment method。
备注:内容仅供参考。
