revert fa476af70e
revert refactor(pay): 充值商品以充值金额为唯一真值,移除销售金额与折扣 充值商品行此前是双值模型:price 充值金额决定到账、pay_price 销售金额决定支付, pay_price 为 0 时回落到 price,否则即「付更少拿更多」的折扣。该模型把三处耦合在 一起——后台 schema 暴露销售金额输入、线格式首段携带支付额、下单时计价与到账分离。 只改其中一处就会出现「后台不再可配但前端仍在展示折扣」或「展示已合并但下单仍按 旧价扣款」的静默不一致,故本次三层一并收口,充值金额即实际支付金额。 后台 schema 有两份且必须同步:SPA 侧 recharge.js 负责渲染表单,PHP 侧 SchemaPayment 负责默认值与清洗。只改一份会让界面不再可配而默认值仍在写 pay_price。 线格式三段 "支付|到账|标题" 保持不动,仅为兼容历史已存的字符串配置:支付段不再读取 任何配置来源,恒等于充值金额,故历史配置里的折扣价会被忽略。旧数据无需迁移,pay_price 键留在库中只是不再被读取,从重建后的后台保存一次即自然消失。 行为变化(需知悉): - 已配置折扣的站点,其用户将按充值金额全额支付,即对这些用户是涨价。这是「不做优惠 和促销」的必然结果。 - parse_packages 的校验口径随之变松:有效性只由到账段决定,故 "abc|10|x" 这类首段 非法、到账段合法的行会被解析为 10 元档,而旧实现两段都作金额校验、会丢弃。理由是 支付段已不表达任何含义,因一个无用字段是垃圾就丢弃一条本来有效的档位只会让运营困惑。 既有数据不受影响——旧格式化器写出的首段恒为数字,只有手改出的脏数据才可能命中。 该取舍已写入方法文档。 - recharge.js / SchemaPayment:删除 pay_price 字段与三档默认值中的该键,help 去掉 「同时支持设置折扣」 - PaymentRuntime::format_product_lines:不再读取 pay_price,支付段由充值金额充当 - BalanceServiceCharge::parse_packages:不再产出 pay_price 键,金额唯一来源为到账段 - BalanceServiceCharge::create_charge_order:支付额与到账额同源于 price - credits-wallet.php:移除销售金额行、「省X」折扣徽标及仅服务折扣的差额计算 - BalanceServiceRest:仅注释改写,说明积分路径的 pay_price 是售价而非折扣 - 新增 tests/widget-system/recharge-product-price-contract.test.cjs:5 条跨层契约 范围边界:积分商品 pay_points_product 的 pay_price 是「积分|支付|标签」里的实际售价, 本身不含折扣语义,保留不动。契约测试以正向断言固定该边界,避免日后被误当作同类问题 一并删掉。 验证:新增契约测试 5 项通过;node --test 全量 375 项 / 375 通过(原 370 项);PHPUnit 44 项 / 106 断言通过;改动的 PHP 文件 php -l 全部通过。变异验证:把 create_charge_order 改回按 pay_price 计价、把 pay_price 重新注入模板,对应断言分别转红,证明回归测试拦得 住回退。另以反射调用真实代码做端到端执行验证 11 项全过,覆盖结构化数组 -> 线格式 -> 商品数组全链路、数组内残存 pay_price 被忽略、历史折扣字符串(98|100|旧折扣档 取 100)、 到账段为 0 或非法时跳过等边界。 部署提示:assets/admin/build 为部署时构建、未入库,后台表单的改动需在目标机执行 cd assets/admin && npm ci && npm run build 后才会生效。前端 credits.js 走 filemtime 版本号,无需手工清缓存。
This commit is contained in:
@@ -142,11 +142,12 @@ class SchemaPayment {
|
||||
]],
|
||||
['key' => 'products', 'title' => '充值商品选项', 'fields' => [
|
||||
['key' => 'pay_balance_product', 'label' => '充值金额选项', 'type' => 'group', 'button_title' => '添加充值金额选项', 'default' => [
|
||||
['price' => 50, 'tag' => '', 'tag_class' => ''],
|
||||
['price' => 100, 'tag' => '', 'tag_class' => ''],
|
||||
['price' => 200, 'tag' => '推荐', 'tag_class' => 'jb-cyan'],
|
||||
], 'help' => '用户充值时候,可快速选择的充值金额;充值金额即实际支付金额', 'fields' => [
|
||||
['price' => 50, 'pay_price' => 0, 'tag' => '', 'tag_class' => ''],
|
||||
['price' => 100, 'pay_price' => 98, 'tag' => '', 'tag_class' => ''],
|
||||
['price' => 200, 'pay_price' => 188, 'tag' => '推荐', 'tag_class' => 'jb-cyan'],
|
||||
], 'help' => '用户充值时候,可快速选择的充值金额,同时支持设置折扣', 'fields' => [
|
||||
['key' => 'price', 'label' => '充值金额', 'type' => 'number', 'default' => 100, 'min' => 0, 'suffix' => '元'],
|
||||
['key' => 'pay_price', 'label' => '销售金额', 'type' => 'number', 'default' => 0, 'min' => 0, 'suffix' => '元', 'help' => '选填,设置不同的销售金额实现充值折扣的功能,设置为0则与充值金额一致'],
|
||||
['key' => 'tag', 'label' => '小标签', 'type' => 'input', 'default' => '', 'help' => '支持HTML,请注意控制长度'],
|
||||
['key' => 'tag_class', 'label' => '标签颜色类', 'type' => 'input', 'default' => '', 'help' => '主题标签颜色类,如 jb-cyan / jb-pink'],
|
||||
]],
|
||||
|
||||
@@ -186,18 +186,7 @@ trait BalanceServiceCharge {
|
||||
|
||||
|
||||
/**
|
||||
* 充值商品行解析("支付|到账|标题")。支付段是历史线格式的遗留位置,不再参与
|
||||
* 计算:充值金额(到账段)即实际支付金额,不设折扣。保留对三段的容忍是为了
|
||||
* 让历史已存的字符串配置仍能解析,其支付段会被忽略而非报错。
|
||||
*
|
||||
* 有效性只由到账段决定:到账段必须是正数金额,否则整行跳过。支付段的内容
|
||||
* 不再校验——它已不表达任何含义,因一个无用字段是垃圾就丢弃一条本来有效的
|
||||
* 充值档位只会让运营困惑。注意这与本方法旧行为有差异:旧实现两段都作金额
|
||||
* 校验,故 "abc|10|x" 这类首段非法的行此前会被丢弃,现在会被解析为 10 元档。
|
||||
* 该差异不影响既有数据——旧格式化器写出的首段恒为数字,手改出的垃圾行才可能
|
||||
* 命中,此时按到账段生效是更合理的降级。
|
||||
*
|
||||
* @return array<int,array{key:string,price:string,title:string,tag:string}>
|
||||
* @return array<int,array{key:string,price:string,pay_price:string,title:string,tag:string}>
|
||||
*/
|
||||
public function parse_packages(string $raw): array {
|
||||
$out = [];
|
||||
@@ -213,19 +202,21 @@ trait BalanceServiceCharge {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$pay = \AKSRT\Ledger\AssetAmount::balance((string) $parts[0]);
|
||||
$credit = \AKSRT\Ledger\AssetAmount::balance((string) $parts[1]);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
continue;
|
||||
}
|
||||
$title = $parts[2] ?? '';
|
||||
if ($credit->minor() <= 0) {
|
||||
if ($pay->minor() <= 0 || $credit->minor() <= 0) {
|
||||
continue;
|
||||
}
|
||||
$out[] = [
|
||||
'key' => (string) $i,
|
||||
'price' => $credit->decimal(),
|
||||
'title' => $title !== '' ? $title : sprintf(__('充值 %s 元', 'aksrt'), $credit->decimal()),
|
||||
'tag' => $title,
|
||||
'key' => (string) $i,
|
||||
'pay_price' => $pay->decimal(),
|
||||
'price' => $credit->decimal(),
|
||||
'title' => $title !== '' ? $title : sprintf(__('充值 %s 元', 'aksrt'), $credit->decimal()),
|
||||
'tag' => $title,
|
||||
];
|
||||
$i++;
|
||||
}
|
||||
@@ -320,8 +311,7 @@ trait BalanceServiceCharge {
|
||||
return new \WP_Error('aksrt_invalid_package', __('充值套餐无效', 'aksrt'), ['status' => 400]);
|
||||
}
|
||||
try {
|
||||
// 充值金额即实际支付金额:不做折扣,故支付额与到账额同源。
|
||||
$pay_amount = \AKSRT\Ledger\AssetAmount::balance((string) $found['price']);
|
||||
$pay_amount = \AKSRT\Ledger\AssetAmount::balance((string) $found['pay_price']);
|
||||
$credit_amount = \AKSRT\Ledger\AssetAmount::balance((string) $found['price']);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return new \WP_Error('aksrt_invalid_package', __('充值套餐金额无效', 'aksrt'), ['status' => 400]);
|
||||
|
||||
@@ -317,8 +317,7 @@ trait BalanceServiceRest {
|
||||
* Buy points with cash-wallet balance (积分购买弹窗 · 余额支付).
|
||||
*
|
||||
* Body: { product: 'custom'|<package_key>, custom?: <points int> }
|
||||
* 自定义按汇率折算支付金额;预设商品按配置的售价支付(该售价即 pay_price,
|
||||
* 是积分档位的唯一价格,不含折扣语义,与余额充值商品的双值模型不同)。
|
||||
* 自定义按汇率折算支付金额;商品按配置销售金额支付(参考主题同款语义)。
|
||||
*/
|
||||
public function rest_exchange_balance_for_points(\WP_REST_Request $request) {
|
||||
if (!$this->balance_feature_enabled()) {
|
||||
|
||||
@@ -607,10 +607,7 @@ class PaymentRuntime {
|
||||
|
||||
/**
|
||||
* Format recharge product rows (reference-theme group shape:
|
||||
* price 充值金额 / tag 小标签) into "支付|到账|标题" lines.
|
||||
*
|
||||
* 三段线格式保留「支付」前导段仅为兼容历史已存的字符串配置:该段不再读取
|
||||
* 任何配置来源,而是恒等于充值金额——充值金额即实际支付金额,不设折扣。
|
||||
* price 充值金额 / pay_price 销售金额 / tag 小标签) into "支付|到账|标题" lines.
|
||||
*
|
||||
* @param array<int,mixed> $products
|
||||
*/
|
||||
@@ -621,15 +618,17 @@ class PaymentRuntime {
|
||||
continue;
|
||||
}
|
||||
$price = max(0, (float) ($row['price'] ?? 0));
|
||||
$pay_price = max(0, (float) ($row['pay_price'] ?? 0));
|
||||
$pay = $pay_price > 0 ? $pay_price : $price;
|
||||
$title = trim((string) ($row['tag'] ?? ''));
|
||||
$lines[] = $price . '|' . $price . '|' . $title;
|
||||
$lines[] = $pay . '|' . $price . '|' . $title;
|
||||
}
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format points product rows (reference-theme group shape:
|
||||
* points 积分 / pay_price 售价 / tag 小标签) into "积分|支付|标签" lines.
|
||||
* points 积分 / pay_price 销售金额 / tag 小标签) into "积分|支付|标签" lines.
|
||||
*
|
||||
* @param array<int,mixed> $products
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user