分割支付POC:架构和设计决策

本页介绍分割支付概念验证背后的体系结构选择。 阅读本系列构建提示之前,请先阅读该指南,以便您了解每个组件的结构以及如何调整您项目中的模式。

核心原则

概念验证并非关于最优雅的分割支付实施。 此视频介绍​如何开始将Commerce逻辑移动到App Builder而无需一次重大重写

在整个中应用的规则是:

如果某些内容必须在Commerce请求周期中同步运行,或者必须调用没有干净外部表面的Commerce内部API,则它将保留在PHP中。 其他所有内容均移至App Builder。

Commerce (PHP)中的内容及其原因

​1. 存储信用申请: PlaceOrderPlugin

使用Magento\CustomerBalance\Api\BalanceManagementInterface::apply()将商店点数应用到购物车。 此方法仅适用于​ 活动 ​购物车。 下订单后,购物车即会停用。 App Builder会在下订单​后收到I/O事件,因此无法从App Builder申请商店积分。

课程:​必须在Commerce中运行任何必须更改购物车状态才能下达订单的内容。 没有解决方法。

​2. 同步阈值保护: CheckoutPlugin

在客户选择​ Place Order ​之前,$100订单阈值检查必须在付款步骤阻止客户。 必须在Commerce请求周期中同步响应。 App Builder是事件驱动型和非同步的,因此当下无法立即返回错误。

App Builder ​验证阈值(作为审核),但客户体验取决于先运行的Commerce检查。

​3. 自定义REST端点: webapi.xmlSplitPaymentManagement

以下端点必须:

  • 调用SplitInvoiceService(使用Commerce内部发票服务的发票)
  • 调用ShipOrder::execute()(Commerce的内部装运服务)
  • 使用Commerce的订单状态机更新订单状态和状态

/V1/split-payment/orders/:id/cash-received/V1/split-payment/orders/:id/cash-decline

该行为没有干净的公共REST层,因此Commerce会公开端点。 App Builder呼叫他们。

​4. 拆分报价和订单金额:观察者和插件

Commerce需要订单上的拆分数量(split_store_credit_amountsplit_cash_amountsplit_cash_status),这既适用于App Builder读取的REST响应,也适用于订单视图​Admin。 这些金额附有扩展属性,并且在sales_model_service_quote_submit_before上从报价复制到观察者的订单中。

App Builder的居住环境及原因

​1. 事件驱动的订单处理: payment-orchestrator

sales_order_place_before触发后,App Builder会接收该事件。 它重新验证阈值(作为审核),在订单上记录​ 待付现金 ​注释,并更新订单状态。 所有这些不需要新的PHP,只需REST返回Commerce。

​2. 现金承兑:payment-accept

当ERP(或仪表板中的操作员)确认已收到现金时,payment-accept致电POST /V1/split-payment/orders/:id/cash-received。 发票、发运和订单状态在Commerce中处理。 触发事件的是App Builder。

​3. 现金拒绝:payment-decline

payment-decline调用POST /V1/split-payment/orders/:id/cash-decline,Commerce将取消订单。 与现金承兑模式相同。

​4. 操作员仪表板: demo-dashboard

通过App Builder Web操作提供的自包含HTML功能板。 它会从Commerce REST获取等待现金的订单,并提供调用上述App Builder操作的​Accept / Decline​操作。 Commerce Admin​不是必需的。

阈值:故意强制两次

Customer at checkout
        |
        v
[Commerce: CheckoutPlugin]     <- Synchronous, blocks immediately, user sees error
        |
        |  (if somehow bypassed: direct API call, and so on)
        v
[Order placed] -> I/O Event -> [App Builder: payment-orchestrator]
                                        |
                                        v
                              [evaluateThreshold()]  <- Async audit, records failure comment

Commerce拥有面向用户的防护;App Builder拥有置入后审核。 这是有意为之。

商店点数:为什么它停留在PHP中

What you might think would work (it does not):
  Order placed -> I/O Event -> App Builder -> PUT /V1/carts/:id/store-credit
  (Fails: cart is inactive after place order)

What actually works:
  AroundPlaceOrder plugin
  -> BalanceManagementInterface::apply($cartId, $amount)  <- cart is still active
  -> place order
  -> order placed
  -> I/O event: App Builder (store credit is already applied)

Orchestrator中的store-credit.js文件记录了该信息。 它是一个无操作存根,其中包含注释,可解释为何不使用该存根。

扩展属性:粘合

拆分金额根据扩展属性在系统中移动:

Checkout JavaScript (Knockout)
    |  POST /V1/split-payment/set
    v
SplitPaymentSession (PHP session)
    |  AroundPlaceOrder reads the session
    v
CartInterface extension attributes
    |  `sales_model_service_quote_submit_before` observer
    v
OrderInterface extension attributes -> `sales_order` flat columns
    |  I/O event payload includes these fields
    v
App Builder `payment-orchestrator` reads the split amounts

数据模型

此模块添加的​sales_order个平面列

类型
用途
split_store_credit_amount
浮点数
已应用的商店积分
split_cash_amount
浮点数
应付现金金额
split_cash_status
varchar
pendingreceiveddeclined
split_sc_invoice_id
int
商店贷方发票的实体ID
split_cash_invoice_id
int
现金发票的实体ID

扩展属性 (在CartInterfaceOrderInterfaceOrderPaymentInterface上)

  • split_store_credit_amount (浮动)
  • split_cash_amount (浮动)
  • split_cash_status (字符串)

I/O事件有效负载字段

observer.sales_order_place_beforeio_events.xml中配置为在事件中包含以下内容:

entity_id, quote_id, increment_id, subtotal,
split_store_credit_amount, split_cash_amount, split_cash_status

App Builder使用entity_id作为订单ID,split_store_credit_amountsplit_cash_amount进行阈值验证。

概念验证涵盖的五个边缘案例

1. CapCustomerBalanceCollectPlugin

Commerce的本机​ Customer balance ​总计收集器可能会过度应用(它可以查看全部可用余额,而不是会话声明的分割量)。 此插件将数量限制为会话中声明的值。

2. FixSplitPaymentGrandTotalPlugin

应用商店积分后,报价单​ Grand Total ​可以丢弃为仅限现金的金额。 签出JavaScript必须在该更改的​ 之前计算拆分验证 ​的订单总数。 该插件在总计收集后运行并更正显示,而JavaScript不只信任grand_total并从小计区段中重建值。

3. FixInvoiceCustomerBalanceAfterTotalsPlugin

在收回发票合计时,可以应用两次商店贷项。 此插件更正了发票上的customer_balance_amount

4. SplitPaymentZeroTotalPlugin

在应用商店点数后,购物车​ Grand Total ​可以是$0(全商店点数订单)。 在这种情况下,Commerce的​ Zero subtotal checkout ​检查可以阻止COD。 当会话现金量大于0时,此插件允许COD。

​5. BalanceManagementInterface::apply()之前的引号回收

apply()根据当前​ Grand Total ​检查金额。 如果总计已经是仅现金部分,apply()可能会失败或达到上限。 PlaceOrderPlugin使用会话标志(beginBalanceApply / endBalanceApply)在应用余额时临时暂停总计修复。

recommendation-more-help
commerce-learn-help-home