current_payment_price 全链路(超详细版)

1. 业务定义(一句话)

⚡⚡⚡ 买家选择支付方式后,系统按「固定费 + 比例费」计算手续费,以 total_price - payment_price 为基数,并通过 lockMaxOrderPrice 封顶修正后写入 current_payment_price

2. 后台配置到前台计算的关系

2.1 商家后台可控项(配置映射表)

后台配置项典型存储/来源前台读取点对计算的直接影响
手续费开关o_payment.formulagetPaymentFee0=关闭→手续费为0
固定费o_payment.formula_param.pricegetPaymentFee固定附加金额
比例费o_payment.formula_param.percentagegetPaymentFee按基数百分比收取
展示条件o_payment.display_paramgetPaymentFee10+种条件过滤,决定是否展示
支付方式IDo_payment.id买家选择匹配具体费率和规则
会员封顶策略cart.minmaxoffer_max_order_pricelockMaxOrderPrice影响最终手续费

2.2 全链路时序图(配置 → 计算 → 落库)

flowchart TD
A[商家配置支付方式] --> B[PaymentService.getPaymentMethodsWithCart]
B --> C{遍历每种支付方式}
C --> D[display_param 条件过滤]
D -->|不通过| E[该支付方式返回 false,不展示]
D -->|通过| F[getPaymentFee 计算手续费]
F --> G[基数 = total_price - payment_price]
G --> H[fee = fixed + round(基数 × percentage ÷ 100, 2)]
H --> I[lockMaxOrderPrice 封顶修正]
I --> J[返回支付列表含手续费]
J --> K[买家选择 payment_id]
K --> L[savePaymentMethod 写 current_payment_price]
L --> M[renew 重算 total_price]

3. 核心入口与数据结构

3.1 核心方法链路

PaymentService.php:442-513
  └─ getPaymentFee($cart, $payment_param, $payment_formula, $display_param)
        ├─ 步骤1: 基数 = total_price - payment_price(避免循环依赖)
        ├─ 步骤2: display_param 10+种条件过滤
        ├─ 步骤3: fee = fixed + round(基数 × percentage / 100, 2)
        └─ 步骤4: lockMaxOrderPrice 封顶修正

3.2 display_param 展示条件完整清单

条件字段逻辑不通过时
morethan_nonetotal_price > morethan_none → 隐藏金额过高时不展示
lessthan_nonetotal_price <= lessthan_none → 隐藏金额过低时不展示
country_whitelistcountry_code 不在列表 → 隐藏特定国家外不可用
country_blacklistcountry_code 在列表 → 隐藏特定国家内不可用
is_bill_address账单地址缺失 → 隐藏需提供账单地址
product_type_whitelist任何商品 type 不在列表 → 隐藏白名单商品类型外不可用
product_type_blacklist任何商品 type 在列表 → 隐藏黑名单商品类型外不可用
customerDisplay()顾客订单数/消费额/标签不符 → 隐藏新客/老客/特定标签限制
domain_list当前域名不在列表 → 隐藏特定域名限定
shipping_zone_plan_whitelist物流方案名称不在列表 → 隐藏特定物流方式限定

4. 完整计算逻辑

4.1 基数计算(防止循环依赖)

$total_price = $cart['total_price'] - $cart['payment_price'];
// 即:当前应付金额 - 已有的手续费
// 这样 first iteration 时 payment_price=0,基数就是总价本身
// 后续迭代时,基数 = 总价 - 已计算的手续费,避免手续费的"手续费"滚大

4.2 手续费公式

if (empty($payment_formula)) {
    return $this->lockMaxOrderPrice($cart, $total_price, 0);
}
 
$payment_fee = (floatval($payment_param->price) ?? 0)
             + round(($total_price * floatval($payment_param->percentage) ?? 0) / 100, 2);
 
return $this->lockMaxOrderPrice($cart, $total_price, $payment_fee);

公式:

fee = fixed_price + round((基数 × percentage) / 100, 2)

比例分母是 100(不是 1000),即 percentage=3 表示 3%。

4.3 lockMaxOrderPrice 封顶修正

// 读取会员封顶策略
$max = $cart['minmaxoffer_max_order_price'] ?? 0;
 
if (!empty($cart['minmaxoffer_diff_price']) && $max <= 0) {
    // 差值模式:修正订单小计
    $order->current_subtotal_price += currencyExchange($cart['minmaxoffer_diff_price'], $cart['currency']);
}
 
if ($max > 0 && 会员封顶条件满足) {
    // 封顶模式:若 total_price + payment_fee > max,则减少 payment_fee 补差
    if (($total_price + $paymentFee) > $max) {
        $paymentFee += ($max - ($total_price + $paymentFee));
    }
}
 
return round($paymentFee, 2);

封顶修正语义:保证 total_price + payment_fee ≤ max_order_price,通过减少手续费实现(而非减少商品金额)。

4.4 输出

// getPaymentFee 返回 float(手续费金额)或 false(不展示)
return $payment_fee;  // 或 false
 
// 在 getPaymentMethodsWithCart 中写入返回数组:
'price'          => $payment_fee,
'original_price' => $payment_fee,

5. 字段输出结构

字段类型含义示例
cart.payment.pricefloat该支付方式手续费(正数)2.50
cart.payment.original_pricefloat同上2.50
cart.payment_methods[]array所有可用支付方式列表含各通道名称/ID/手续费
o_order.current_payment_pricefloat已选支付方式的手续费2.50

6. 边界场景说明

6.1 首次加载时 payment_price 为 0

基数 = total_price - payment_price = total_price - 0 = total_price,全额作为手续费计算基数。

6.2 比例 percentage = 0

只收固定费,无比例附加。

6.3 fixed_price = 0

只收比例费(无固定附加)。

6.4 display_param 条件全部不满足

getPaymentFee 返回 false,该支付方式不进入可用列表。

6.5 lockMaxOrderPrice 截断后 payment_fee 为负

理论上 $paymentFee += ($max - total),若 max 远小于 total,paymentFee 可能为负。但实际场景中 max 通常 ≥ total - payment_fee,所以通常不会为负。

6.6 支付方式 A 切换到 B

重新调用 getPaymentFee 走同样的计算链,savePaymentMethod 写入新的 current_payment_price,触发 renew 更新总价。

7. 与总价 total_price 的关系

total_price 的组成(9字段):
  subtotal_price        = Σ(final_line_price)              商品行小计
  discount_price        = 整单折扣(负数)
  refund_price          = 退款口径独立
  current_total_price   = subtotal + shipping              仅展示
  total_price           = subtotal - promotion - coupon + shipping + tax + tip + payment + insurance + offer
                        └─ payment_price 在此处被加(正数)

⚡⚡⚡ payment_price正数,在 total_price 公式中做加法。

⚠️ 注意total_price 在这里被 getPaymentFee 引用做基数,然后又由 total_price 包含 payment_price——但通过 基数 = total_price - payment_price 的设计巧妙绕开了循环依赖。

8. 关键代码索引

逻辑文件:行号
入口:getPaymentFeePaymentService.php:442-513
基数计算PaymentService.php:446
display_param 10+种过滤PaymentService.php:452-508
手续费公式PaymentService.php:509-510
lockMaxOrderPricePaymentService.php:1663-1717
封顶修正逻辑PaymentService.php:1693-1717
支付方式列表返回PaymentService.php:601-619
保存已选支付方式OrderService::savePaymentMethod
总价重算OrderService::renew