feat(payment): 支付&付费菜单按参考主题重构并新增提现策略页

- 子菜单重组为知识付费/余额充值/VIP会员/创作分成/推广返佣/收款接口/提现策略
- 收款接口页 1:1 复刻 Zibll 双下拉结构,仅保留支付宝/微信/PayPal/Stripe/易支付
- 订单前缀/流水号前缀/提现单号前缀配置保留并移入收款接口页
- 知识付费/推广返佣按参考主题全字段复刻,余额充值并入充值卡配置
- 新增 tabbed/between_number/page_select 表单控件及后端 pages 搜索源
- 清理旧 SPA 组件、无消费者 REST 端点与废弃配置键(不写双路兼容)
- 更新存量契约测试至新 schema 文件
This commit is contained in:
2026-09-16 19:18:28 +08:00
parent fb5cdf247d
commit a48007a44d
36 changed files with 2042 additions and 2273 deletions
+3 -1
View File
@@ -47,7 +47,6 @@ final class CommissionLedger {
$map = ['vip' => 'membership', 'resource' => 'content'];
$order_type = $map[$type] ?? $type;
$settings = \AKSRT\Settings\Settings::get_affiliate_settings();
if (!in_array($order_type, (array) $settings['order_types'], true)) return 0;
$order_no = sanitize_text_field((string) ($order['order_number'] ?? ''));
$buyer = (int) ($order['user_id'] ?? 0);
try {
@@ -58,6 +57,9 @@ final class CommissionLedger {
if ($order_no === '' || $buyer <= 0 || $amount->minor() <= 0) return 0;
$promoter = ReferralAttribution::get_instance()->resolve($buyer);
if ($promoter <= 0) return 0;
// 返佣订单类型按推广者会员等级分档(参考主题:每档独立勾选)。
$promoter_level = function_exists('aksrt_get_user_vip_level') ? (int) aksrt_get_user_vip_level($promoter) : 0;
if (!in_array($order_type, \AKSRT\Settings\PaymentRuntime::affiliate_order_types_for_level($promoter_level), true)) return 0;
$ratio = \AKSRT\Settings\PaymentRuntime::affiliate_ratio_for_user($promoter);
$commission_minor = (intdiv($amount->minor(), 100) * $ratio)
+ intdiv((($amount->minor() % 100) * $ratio) + 50, 100);
+44 -3
View File
@@ -45,7 +45,7 @@ class Gateway {
}
/**
* 获取可用网关列表(已配置的)
* 获取可用网关列表(已配置的),按「收款接口显示顺序」配置排序。
*/
public function get_available(): array {
$available = [];
@@ -54,7 +54,48 @@ class Gateway {
$available[$id] = $gateway;
}
}
return $available;
return $this->apply_sdk_order($available);
}
/**
* 按 pay_sdk_order(收款接口显示顺序)重排可用网关。
* 易支付子通道跟随对应渠道槽位(epay_wxpay 跟随 wechat、epay_alipay 跟随 alipay);
* 未配置顺序时保持注册顺序,未纳入槽位的扩展网关保持在末尾。
*
* @param array<string,GatewayInterface> $available
* @return array<string,GatewayInterface>
*/
private function apply_sdk_order(array $available): array {
$settings = \AKSRT\Settings\Settings::get_all();
$configured = (array) ($settings['pay_sdk_order'] ?? []);
$order = array_values(array_filter(array_map('sanitize_key', array_keys($configured))));
if (!$order) {
return $available;
}
$slot_map = [
'wechat' => ['wechatpay', 'epay_wxpay'],
'alipay' => ['alipay', 'epay_alipay'],
'paypal' => ['paypal'],
'stripe' => ['stripe'],
'balance' => ['balance'],
'card_pass' => ['card_pass'],
];
$sorted = [];
foreach ($order as $slot) {
foreach (($slot_map[$slot] ?? []) as $id) {
if (isset($available[$id]) && !isset($sorted[$id])) {
$sorted[$id] = $available[$id];
}
}
}
foreach ($available as $id => $gateway) {
if (!isset($sorted[$id])) {
$sorted[$id] = $gateway;
}
}
return $sorted;
}
/**
@@ -311,7 +352,7 @@ class Gateway {
}
$gateway_id = sanitize_key((string) ($order['payment_method'] ?? ''));
if ($gateway_id === '' || in_array($gateway_id, ['balance', 'points', 'manual_review'], true)) {
if ($gateway_id === '' || in_array($gateway_id, ['balance', 'points'], true)) {
return null;
}
$gateway = $this->get_gateway($gateway_id);
+1 -1
View File
@@ -186,7 +186,7 @@ class UserOrders {
$image = $this->resolve_image($meta, $product_id);
$group = $this->status_group($status);
$payment_method = sanitize_key((string) ($order['payment_method'] ?? ''));
$can_pay = $group === OrderCenterQuery::STATUS_PENDING && $payment_method !== 'manual_review';
$can_pay = $group === OrderCenterQuery::STATUS_PENDING;
$can_cancel = $group === OrderCenterQuery::STATUS_PENDING;
$order_number = (string) ($order['order_number'] ?? '');
$updated_at = $this->resolve_updated_at($order);