diff --git a/assets/admin/src/schema/sections/payment/affiliate.js b/assets/admin/src/schema/sections/payment/affiliate.js
index 0bd2368b..ef605a00 100644
--- a/assets/admin/src/schema/sections/payment/affiliate.js
+++ b/assets/admin/src/schema/sections/payment/affiliate.js
@@ -2,22 +2,16 @@ export const affiliateSchema = {
key: 'payment.affiliate',
title: '推广返佣',
icon: 'fas fa-share-alt',
- description: '配置推广归因、佣金比例、结算和提现规则。',
+ description: '配置推广归因、分级佣金比例和结算规则。',
defaults: {
affiliate_enabled: false,
- affiliate_ratio: 5,
+ affiliate_ratio_user: 5,
+ affiliate_ratio_vip_1: 5,
+ affiliate_ratio_vip_2: 5,
affiliate_order_types: ['membership', 'content'],
affiliate_touch_window_days: 30,
affiliate_settlement_days: 7,
- affiliate_allow_self_purchase: false,
- affiliate_show_stats: true,
affiliate_promotion_text: '',
- affiliate_payout_channels: ['alipay', 'wechat'],
- affiliate_min_withdrawal: 50,
- affiliate_withdrawal_fee: 5,
- affiliate_daily_withdrawal_count: 3,
- affiliate_daily_withdrawal_limit: 500,
- affiliate_withdrawal_reset_time: '00:00',
},
groups: [
{
@@ -26,25 +20,12 @@ export const affiliateSchema = {
icon: 'fas fa-project-diagram',
fields: [
{ key: 'affiliate_enabled', label: '启用推广返佣', type: 'switch', default: false },
- { key: 'affiliate_ratio', label: '全站佣金比例(%)', type: 'number', default: 5, min: 0, max: 100, dependsOn: { key: 'affiliate_enabled', value: true } },
- { key: 'affiliate_order_types', label: '适用订单类型', type: 'checkbox', default: ['membership', 'content'], sanitize_type: 'array', options: [{ label: '会员订单', value: 'membership' }, { label: '付费内容', value: 'content' }], dependsOn: { key: 'affiliate_enabled', value: true } },
+ { key: 'affiliate_ratio_user', label: '普通会员佣金比例(%)', type: 'number', default: 5, min: 0, max: 100, dependsOn: { key: 'affiliate_enabled', value: true } },
+ { key: 'affiliate_ratio_vip_1', label: '一级会员佣金比例(%)', type: 'number', default: 5, min: 0, max: 100, dependsOn: { key: 'affiliate_enabled', value: true } },
+ { key: 'affiliate_ratio_vip_2', label: '二级会员佣金比例(%)', type: 'number', default: 5, min: 0, max: 100, dependsOn: { key: 'affiliate_enabled', value: true } },
+ { key: 'affiliate_order_types', label: '适用订单类型', type: 'checkbox', default: ['membership', 'content'], sanitize_type: 'array', options: [{ label: '会员订单', value: 'membership' }, { label: '付费内容', value: 'content' }, { label: '购买积分', value: 'credits' }], dependsOn: { key: 'affiliate_enabled', value: true } },
{ key: 'affiliate_touch_window_days', label: '首次触达窗口(天)', type: 'number', default: 30, min: 1, max: 365, dependsOn: { key: 'affiliate_enabled', value: true } },
{ key: 'affiliate_settlement_days', label: '结算观察期(天)', type: 'number', default: 7, min: 0, max: 90, dependsOn: { key: 'affiliate_enabled', value: true } },
- { key: 'affiliate_allow_self_purchase', label: '允许自购返佣', type: 'switch', default: false, dependsOn: { key: 'affiliate_enabled', value: true } },
- { key: 'affiliate_show_stats', label: '展示推广统计', type: 'switch', default: true, dependsOn: { key: 'affiliate_enabled', value: true } },
- ],
- },
- {
- key: 'withdrawal',
- title: '提现规则',
- icon: 'fas fa-wallet',
- fields: [
- { key: 'affiliate_min_withdrawal', label: '最低提现金额', type: 'number', default: 50, min: 0, max: 99999, dependsOn: { key: 'affiliate_enabled', value: true } },
- { key: 'affiliate_withdrawal_fee', label: '提现手续费(%)', type: 'number', default: 5, min: 0, max: 100, dependsOn: { key: 'affiliate_enabled', value: true } },
- { key: 'affiliate_daily_withdrawal_count', label: '每日可提现次数', type: 'number', default: 3, min: 0, max: 99999, dependsOn: { key: 'affiliate_enabled', value: true } },
- { key: 'affiliate_daily_withdrawal_limit', label: '每日提现上限', type: 'number', default: 500, min: 0, max: 999999, dependsOn: { key: 'affiliate_enabled', value: true } },
- { key: 'affiliate_withdrawal_reset_time', label: '重置时间', type: 'input', default: '00:00', help: '按站点 WordPress 时区重置,格式 HH:MM。', dependsOn: { key: 'affiliate_enabled', value: true } },
- { key: 'affiliate_payout_channels', label: '可用收款渠道', type: 'checkbox', default: ['alipay', 'wechat'], sanitize_type: 'array', options: [{ label: '支付宝收款码', value: 'alipay' }, { label: '微信收款码', value: 'wechat' }], dependsOn: { key: 'affiliate_enabled', value: true } },
],
},
{
diff --git a/assets/admin/src/schema/sections/payment/withdrawal.js b/assets/admin/src/schema/sections/payment/withdrawal.js
index bffdb44e..34908ba2 100644
--- a/assets/admin/src/schema/sections/payment/withdrawal.js
+++ b/assets/admin/src/schema/sections/payment/withdrawal.js
@@ -1,15 +1,15 @@
export const withdrawalSchema = {
key: 'payment.withdrawal',
- title: '提现管理',
+ title: '提现策略',
icon: 'fas fa-hand-holding-usd',
description: '配置提现门槛、手续费比例与每日限制。',
defaults: {
- pay_balance_withdraw_s: false,
- pay_rebate_withdraw_lowest_money: 50,
- withdraw_service_charge: 5,
- withdraw_daily_count: 3,
- withdraw_daily_limit: 50,
- withdraw_reset_time: '00:00',
+ affiliate_min_withdrawal: 50,
+ affiliate_withdrawal_fee: 5,
+ affiliate_daily_withdrawal_count: 3,
+ affiliate_daily_withdrawal_limit: 500,
+ affiliate_withdrawal_reset_time: '00:00',
+ affiliate_payout_channels: ['alipay', 'wechat'],
},
groups: [
{
@@ -17,12 +17,12 @@ export const withdrawalSchema = {
title: '提现设置',
icon: 'fas fa-sliders-h',
fields: [
- { key: 'pay_balance_withdraw_s', label: '启用余额提现', type: 'switch', default: false },
- { key: 'pay_rebate_withdraw_lowest_money', label: '最低提现金额', type: 'number', default: 50, min: 0, max: 99999, dependsOn: { key: 'pay_balance_withdraw_s', value: true } },
- { key: 'withdraw_service_charge', label: '提现手续费(%)', type: 'number', default: 5, min: 0, max: 50, dependsOn: { key: 'pay_balance_withdraw_s', value: true } },
- { key: 'withdraw_daily_count', label: '每日可提现次数', type: 'number', default: 3, min: 0, max: 99999, dependsOn: { key: 'pay_balance_withdraw_s', value: true } },
- { key: 'withdraw_daily_limit', label: '每日提现上限', type: 'number', default: 50, min: 0, max: 999999, dependsOn: { key: 'pay_balance_withdraw_s', value: true } },
- { key: 'withdraw_reset_time', label: '重置时间', type: 'input', default: '00:00', help: '按站点 WordPress 时区重置,格式 HH:MM。', dependsOn: { key: 'pay_balance_withdraw_s', value: true } },
+ { key: 'affiliate_min_withdrawal', label: '最低提现金额', type: 'number', default: 50, min: 0, max: 99999 },
+ { key: 'affiliate_withdrawal_fee', label: '提现手续费(%)', type: 'number', default: 5, min: 0, max: 100 },
+ { key: 'affiliate_daily_withdrawal_count', label: '每日可提现次数', type: 'number', default: 3, min: 0, max: 99999 },
+ { key: 'affiliate_daily_withdrawal_limit', label: '每日提现上限', type: 'number', default: 500, min: 0, max: 999999 },
+ { key: 'affiliate_withdrawal_reset_time', label: '重置时间', type: 'input', default: '00:00', help: '按站点 WordPress 时区重置,格式 HH:MM。' },
+ { key: 'affiliate_payout_channels', label: '可用收款渠道', type: 'checkbox', default: ['alipay', 'wechat'], sanitize_type: 'array', options: [{ label: '支付宝收款码', value: 'alipay' }, { label: '微信收款码', value: 'wechat' }] },
],
},
],
diff --git a/assets/js/credits.js b/assets/js/credits.js
index ef6148c9..3c17bb64 100644
--- a/assets/js/credits.js
+++ b/assets/js/credits.js
@@ -209,7 +209,7 @@
input.style.width = Math.max(28, Math.min(96, linkLength || 28)) + 'ch';
}
if (copy) copy.disabled = !j.url;
- var ratio = root.querySelector('[data-affiliate-ratio]'); if (ratio) ratio.textContent = String(j.ratio || 0) + '%';
+ var ratio = root.querySelector('[data-affiliate-ratio]'); if (ratio) ratio.textContent = String(j.current_ratio || 0) + '%';
var promoters = root.querySelector('[data-affiliate-promoters]'); if (promoters) promoters.textContent = String(stats.promoters || 0);
var payers = root.querySelector('[data-affiliate-payers]'); if (payers) payers.textContent = String(stats.effective_payers || 0);
}).catch(function () { setFeedback('推广数据加载失败。', true); });
diff --git a/inc/helpers/aksrt-pay.php b/inc/helpers/aksrt-pay.php
index 7e677217..2fa192c7 100644
--- a/inc/helpers/aksrt-pay.php
+++ b/inc/helpers/aksrt-pay.php
@@ -606,19 +606,7 @@ if (!function_exists('aksrt_get_post_rebate_discount')) {
return 0.0;
}
// Need a positive ratio for referrer (VIP tier of referrer preferred).
- $ratio = (int) ($aff['ratio'] ?? 0);
- $user_ratio = is_array($aff['user_ratio'] ?? null) ? $aff['user_ratio'] : [];
- $ref_vip = 0;
- if (function_exists('aksrt_get_user_vip_level')) {
- $ref_vip = (int) aksrt_get_user_vip_level($referrer_id);
- }
- if ($ref_vip >= 2 && !empty($user_ratio['vip2'])) {
- $ratio = (int) $user_ratio['vip2'];
- } elseif ($ref_vip >= 1 && !empty($user_ratio['vip1'])) {
- $ratio = (int) $user_ratio['vip1'];
- } elseif (isset($user_ratio['user'])) {
- $ratio = (int) $user_ratio['user'];
- }
+ $ratio = (int) round(\AKSRT\Settings\Settings::affiliate_ratio_for_user($referrer_id));
if ($ratio <= 0) {
return 0.0;
}
diff --git a/inc/helpers/user-center-renderers/profile-revenue.php b/inc/helpers/user-center-renderers/profile-revenue.php
index d4b3c5df..44714a0b 100644
--- a/inc/helpers/user-center-renderers/profile-revenue.php
+++ b/inc/helpers/user-center-renderers/profile-revenue.php
@@ -26,7 +26,7 @@ $withdraw_on = !empty($affiliate['enabled'])
if ($withdraw_on && class_exists('\AKSRT\Settings\Capabilities') && !\AKSRT\Settings\Capabilities::user_can('balance_withdraw', $user_id)) {
$withdraw_on = false;
}
-$withdraw_min = max(1, (float) ($affiliate['min_withdrawal'] ?? $withdrawal['min_amount'] ?? 50));
+$withdraw_min = max(1, (float) ($withdrawal['min_amount'] ?? 50));
$wallet_fmt = class_exists('\AKSRT\Credits\Balance')
? \AKSRT\Credits\Balance::format((float) \AKSRT\Credits\Balance::balance($user_id))
: '0.00';
@@ -38,7 +38,9 @@ $available_commission = class_exists('\AKSRT\Credits\Balance')
? (float) \AKSRT\Credits\Balance::balance($user_id)
: (float) ($commission_stats['settled'] ?? 0);
$vip_ratios = [];
-$commission_ratio = (float) ($affiliate['ratio'] ?? 0);
+$commission_ratio = class_exists('\AKSRT\Settings\Settings')
+ ? (float) \AKSRT\Settings\Settings::affiliate_ratio_for_user($user_id)
+ : 0.0;
$details_text = trim((string) ($affiliate['promotion_text'] ?? ''));
if ($details_text === '') {
$details_text = __('好友通过推广链接完成符合条件的订单后,系统会按当前佣金比例记录返佣,结算后进入可提现余额。', 'aksrt');
diff --git a/inc/helpers/user-center-renderers/withdraw-modal.php b/inc/helpers/user-center-renderers/withdraw-modal.php
index d1418c9e..87192468 100644
--- a/inc/helpers/user-center-renderers/withdraw-modal.php
+++ b/inc/helpers/user-center-renderers/withdraw-modal.php
@@ -9,6 +9,10 @@ $GLOBALS['aksrt_withdraw_modal_rendered'] = true;
$withdraw_min = max(0, (float) ($withdraw_min ?? 50));
$withdraw_usage = is_array($withdraw_usage ?? null) ? $withdraw_usage : ['remaining_count' => 0, 'remaining_amount' => 0];
$withdraw_fee_rate = (int) ($withdraw_settings['service_charge_rate'] ?? 5);
+$withdraw_channels = array_values(array_intersect(['alipay', 'wechat'], (array) ($withdraw_settings['payout_channels'] ?? ['alipay', 'wechat'])));
+if (!$withdraw_channels) {
+ $withdraw_channels = ['alipay', 'wechat'];
+}
?>
@@ -21,8 +25,9 @@ $withdraw_fee_rate = (int) ($withdraw_settings['service_charge_rate'] ?? 5);
diff --git a/src/Admin/Schema/SchemaPayment.php b/src/Admin/Schema/SchemaPayment.php
index d422bf4c..5a88999d 100644
--- a/src/Admin/Schema/SchemaPayment.php
+++ b/src/Admin/Schema/SchemaPayment.php
@@ -143,23 +143,15 @@ class SchemaPayment {
'groups' => [
['key' => 'policy', 'title' => '返佣策略', 'fields' => [
['key' => 'affiliate_enabled', 'label' => '启用推广返佣', 'type' => 'switch', 'default' => false],
- ['key' => 'affiliate_ratio', 'label' => '全站佣金比例(%)', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 100],
- ['key' => 'affiliate_order_types', 'label' => '适用订单类型', 'type' => 'checkbox', 'options' => [['value' => 'membership', 'label' => '会员订单'], ['value' => 'content', 'label' => '付费内容']], 'default' => ['membership', 'content']],
+ ['key' => 'affiliate_ratio_user', 'label' => '普通会员佣金比例(%)', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 100],
+ ['key' => 'affiliate_ratio_vip_1', 'label' => '一级会员佣金比例(%)', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 100],
+ ['key' => 'affiliate_ratio_vip_2', 'label' => '二级会员佣金比例(%)', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 100],
+ ['key' => 'affiliate_order_types', 'label' => '适用订单类型', 'type' => 'checkbox', 'options' => [['value' => 'membership', 'label' => '会员订单'], ['value' => 'content', 'label' => '付费内容'], ['value' => 'credits', 'label' => '购买积分']], 'default' => ['membership', 'content']],
['key' => 'affiliate_touch_window_days', 'label' => '首次触达窗口(天)', 'type' => 'number', 'default' => 30, 'min' => 1, 'max' => 365],
['key' => 'affiliate_settlement_days', 'label' => '结算观察期(天)', 'type' => 'number', 'default' => 7, 'min' => 0, 'max' => 90],
- ['key' => 'affiliate_allow_self_purchase', 'label' => '允许自购返佣', 'type' => 'switch', 'default' => false],
- ['key' => 'affiliate_show_stats', 'label' => '展示推广统计', 'type' => 'switch', 'default' => true],
- ]],
- ['key' => 'withdrawal', 'title' => '提现规则', 'fields' => [
- ['key' => 'affiliate_min_withdrawal', 'label' => '最低提现金额', 'type' => 'number', 'default' => 50, 'min' => 0, 'max' => 99999],
- ['key' => 'affiliate_withdrawal_fee', 'label' => '提现手续费(%)', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 100],
- ['key' => 'affiliate_daily_withdrawal_count', 'label' => '每日可提现次数', 'type' => 'number', 'default' => 3, 'min' => 0, 'max' => 99999],
- ['key' => 'affiliate_daily_withdrawal_limit', 'label' => '每日提现上限', 'type' => 'number', 'default' => 500, 'min' => 0, 'max' => 999999],
- ['key' => 'affiliate_withdrawal_reset_time', 'label' => '重置时间', 'type' => 'input', 'default' => '00:00'],
]],
['key' => 'text', 'title' => '文案设置', 'fields' => [
['key' => 'affiliate_promotion_text', 'label' => '推广说明', 'type' => 'textarea', 'default' => ''],
- ['key' => 'affiliate_payout_channels', 'label' => '可用收款渠道', 'type' => 'checkbox', 'options' => [['value' => 'alipay', 'label' => '支付宝收款码'], ['value' => 'wechat', 'label' => '微信收款码']], 'default' => ['alipay', 'wechat']],
]],
],
],
@@ -317,7 +309,7 @@ class SchemaPayment {
],
'withdrawal' => [
'key' => 'withdrawal',
- 'title' => '提现管理',
+ 'title' => '提现策略',
'version' => '1.0.0',
'option_key' => 'aksrt_settings',
'groups' => [
@@ -327,6 +319,7 @@ class SchemaPayment {
['key' => 'affiliate_daily_withdrawal_count', 'label' => '每日可提现次数', 'type' => 'number', 'default' => 3, 'min' => 0, 'max' => 99999],
['key' => 'affiliate_daily_withdrawal_limit', 'label' => '每日提现上限', 'type' => 'number', 'default' => 500, 'min' => 0, 'max' => 999999],
['key' => 'affiliate_withdrawal_reset_time', 'label' => '重置时间', 'type' => 'input', 'default' => '00:00', 'help' => '按站点 WordPress 时区重置,格式 HH:MM。'],
+ ['key' => 'affiliate_payout_channels', 'label' => '可用收款渠道', 'type' => 'checkbox', 'options' => [['value' => 'alipay', 'label' => '支付宝收款码'], ['value' => 'wechat', 'label' => '微信收款码']], 'default' => ['alipay', 'wechat']],
]],
],
],
diff --git a/src/Content/PayHelpers.php b/src/Content/PayHelpers.php
index c4c5346d..d80af59d 100644
--- a/src/Content/PayHelpers.php
+++ b/src/Content/PayHelpers.php
@@ -383,20 +383,7 @@ trait PayHelpers {
return $empty;
}
- $ratio = (float) ($aff['ratio'] ?? 0);
- $user_ratio = is_array($aff['user_ratio'] ?? null) ? $aff['user_ratio'] : [];
- $ref_vip = 0;
- if (function_exists('aksrt_get_user_vip_level')) {
- $ref_vip = (int) aksrt_get_user_vip_level($referrer_id);
- }
- if ($ref_vip >= 2 && isset($user_ratio['vip2']) && (float) $user_ratio['vip2'] > 0) {
- $ratio = (float) $user_ratio['vip2'];
- } elseif ($ref_vip >= 1 && isset($user_ratio['vip1']) && (float) $user_ratio['vip1'] > 0) {
- $ratio = (float) $user_ratio['vip1'];
- } elseif (isset($user_ratio['user'])) {
- $ratio = (float) $user_ratio['user'];
- }
- $ratio = max(0.0, min(100.0, $ratio));
+ $ratio = (float) \AKSRT\Settings\Settings::affiliate_ratio_for_user($referrer_id);
if ($ratio <= 0) {
return $empty;
}
diff --git a/src/Credits/BalanceServiceRest.php b/src/Credits/BalanceServiceRest.php
index 7fa708b1..4c066c9e 100644
--- a/src/Credits/BalanceServiceRest.php
+++ b/src/Credits/BalanceServiceRest.php
@@ -338,6 +338,7 @@ trait BalanceServiceRest {
if (is_wp_error($refund)) return new \WP_Error('aksrt_exchange_recovery_failed', __('积分入账失败,余额退回失败,请联系管理员', 'aksrt'), ['status' => 500]);
return $add;
}
+ do_action('aksrt_points_exchange_completed', $user_id, (float) $amount, $points, (string) $exchange_ref);
return rest_ensure_response(['success' => true, 'data' => [
'message' => sprintf(__('兑换成功,获得 %d 积分', 'aksrt'), $points),
'wallet_balance' => Balance::balance($user_id),
@@ -387,6 +388,7 @@ trait BalanceServiceRest {
}
$amount = (int) $raw_amount;
$method = sanitize_key((string) ($params['method'] ?? 'alipay'));
+ $method = $method !== '' ? $method : 'alipay';
$account = sanitize_textarea_field((string) ($params['account_info'] ?? $params['account'] ?? ''));
$withdraw_settings = class_exists('\\AKSRT\\Settings\\Settings')
? \AKSRT\Settings\Settings::get_withdrawal_settings()
@@ -398,7 +400,10 @@ trait BalanceServiceRest {
if ($account === '') {
return new \WP_Error('aksrt_withdraw_account', __('请填写收款账号信息', 'aksrt'), ['status' => 400]);
}
- $result = \AKSRT\Payment\Withdrawal::get_instance()->create_balance_request($user_id, $amount, $method !== '' ? $method : 'alipay', $account, $withdraw_settings);
+ if (!in_array($method, (array) ($withdraw_settings['payout_channels'] ?? []), true)) {
+ return new \WP_Error('aksrt_withdraw_channel', __('当前收款渠道不可用', 'aksrt'), ['status' => 400]);
+ }
+ $result = \AKSRT\Payment\Withdrawal::get_instance()->create_balance_request($user_id, $amount, $method, $account, $withdraw_settings);
if (is_wp_error($result)) {
return $result;
}
diff --git a/src/Payment/CommissionLedger.php b/src/Payment/CommissionLedger.php
index 99adcffa..dfccfc49 100644
--- a/src/Payment/CommissionLedger.php
+++ b/src/Payment/CommissionLedger.php
@@ -12,6 +12,7 @@ final class CommissionLedger {
public function init(): void {
add_action('aksrt_order_paid', [$this, 'on_order_paid'], 20, 2);
add_action('aksrt_order_status_changed', [$this, 'on_order_status_changed'], 20, 3);
+ add_action('aksrt_points_exchange_completed', [$this, 'on_points_exchange_completed'], 20, 4);
add_action('aksrt_affiliate_settle_due', [$this, 'settle_due']);
add_action('rest_api_init', [$this, 'register_routes']);
if (!wp_next_scheduled('aksrt_affiliate_settle_due')) {
@@ -23,6 +24,16 @@ final class CommissionLedger {
public function on_order_status_changed(string $order_number, string $status, array $order = []): void {
if (in_array($status, ['refunding', 'refunded'], true)) { $this->reverse($order_number, (float) ($order['amount'] ?? 0), 'refund'); }
}
+ public function on_points_exchange_completed(int $user_id, float $amount, int $points, string $exchange_ref): void {
+ $this->record_paid_order([
+ 'order_number' => 'points-exchange-' . sanitize_key($exchange_ref),
+ 'product_type' => 'credits',
+ 'user_id' => $user_id,
+ 'amount' => $amount,
+ 'exchange_ref' => $exchange_ref,
+ 'points' => $points,
+ ]);
+ }
public function record_paid_order(array $order): int {
if (!\AKSRT\Settings\Settings::affiliate_enabled()) return 0;
$type = sanitize_key((string) ($order['product_type'] ?? ''));
@@ -35,14 +46,16 @@ final class CommissionLedger {
$amount = round((float) ($order['amount'] ?? 0), 2);
if ($order_no === '' || $buyer <= 0 || $amount <= 0) return 0;
$promoter = ReferralAttribution::get_instance()->resolve($buyer);
- if ($promoter <= 0 || $promoter === $buyer) return 0;
- if (!$settings['allow_self_purchase'] && $promoter === $buyer) return 0;
- $ratio = (float) $settings['ratio'];
+ if ($promoter <= 0) return 0;
+ $ratio = (float) \AKSRT\Settings\PaymentRuntime::affiliate_ratio_for_user($promoter);
$commission = round($amount * $ratio / 100, 2);
if ($commission <= 0) return 0;
global $wpdb;
$table = $this->table('commission_ledger');
- $key = hash('sha256', 'affiliate:' . $order_no . ':' . $order_type);
+ $identity = $order_type === 'credits' && !empty($order['exchange_ref'])
+ ? 'credits:' . sanitize_key((string) $order['exchange_ref'])
+ : $order_no . ':' . $order_type;
+ $key = hash('sha256', 'affiliate:' . $identity);
$now = current_time('mysql', true);
$available = gmdate('Y-m-d H:i:s', time() + ((int) $settings['settlement_days'] * DAY_IN_SECONDS));
$ok = $wpdb->insert($table, ['promoter_id'=>$promoter,'source_user_id'=>$buyer,'order_number'=>$order_no,'order_type'=>$order_type,'eligible_base'=>$amount,'ratio'=>$ratio,'original_amount'=>$commission,'adjustment_total'=>0,'status'=>'pending','idempotency_key'=>$key,'available_at'=>$available,'created_at'=>$now], ['%d','%d','%s','%s','%f','%f','%f','%f','%s','%s','%s','%s']);
@@ -123,9 +136,9 @@ final class CommissionLedger {
$user_id=get_current_user_id(); $amount=(int)round((float)$request->get_param('amount')); $method=sanitize_key((string)$request->get_param('method')); $account=sanitize_text_field((string)$request->get_param('account')); $settings=\AKSRT\Settings\Settings::get_withdrawal_settings(); $affiliate=\AKSRT\Settings\Settings::get_affiliate_settings();
if (empty($affiliate['enabled'])) return new \WP_Error('affiliate_disabled',__('推广返佣未启用。','aksrt'),['status'=>403]);
if (\AKSRT\Payment\Withdrawal::get_instance()->has_open_withdrawal($user_id)) return new \WP_Error('affiliate_withdraw_pending',__('已有待处理提现申请。','aksrt'),['status'=>409]);
- if ($amount < (int)ceil((float)$affiliate['min_withdrawal'])) return new \WP_Error('affiliate_withdraw_min',__('未达到最低提现金额。','aksrt'),['status'=>400]);
+ if ($amount < (int)ceil((float)$settings['min_amount'])) return new \WP_Error('affiliate_withdraw_min',__('未达到最低提现金额。','aksrt'),['status'=>400]);
if ((float)get_user_meta($user_id,self::DEBT_META,true)>0) return new \WP_Error('affiliate_withdraw_debt',__('存在待偿还的佣金欠款,暂不可提现。','aksrt'),['status'=>409]);
- if (!in_array($method,(array)$affiliate['payout_channels'],true) || $account==='') return new \WP_Error('affiliate_withdraw_account',__('请提供有效的收款渠道和收款码。','aksrt'),['status'=>400]);
+ if (!in_array($method,(array)$settings['payout_channels'],true) || $account==='') return new \WP_Error('affiliate_withdraw_account',__('请提供有效的收款渠道和收款码。','aksrt'),['status'=>400]);
$result=\AKSRT\Payment\Withdrawal::get_instance()->create_balance_request($user_id,$amount,$method,$account,$settings); if(is_wp_error($result)) return $result; return new \WP_REST_Response(['success'=>true,'data'=>$result],201);
}
public function admin_withdrawals(array $args): array { $service=\AKSRT\Payment\Withdrawal::get_instance(); return ['items'=>$service->get_all(['status'=>sanitize_key((string)($args['status']??'')),'limit'=>max(1,min(100,(int)($args['per_page']??20))),'offset'=>max(0,((int)($args['page']??1)-1)*max(1,(int)($args['per_page']??20)))]),'stats'=>$service->get_stats()]; }
diff --git a/src/Payment/ReferralAttribution.php b/src/Payment/ReferralAttribution.php
index 2f4fdbec..82bcc2c1 100644
--- a/src/Payment/ReferralAttribution.php
+++ b/src/Payment/ReferralAttribution.php
@@ -197,7 +197,8 @@ final class ReferralAttribution {
return new \WP_REST_Response([
'code' => $link['code'],
'url' => $link['url'],
- 'ratio' => (float) ($settings['ratio'] ?? 5),
+ 'current_ratio' => (float) \AKSRT\Settings\Settings::affiliate_ratio_for_user($uid),
+ 'ratios' => (array) ($settings['ratios'] ?? []),
'order_types' => (array) ($settings['order_types'] ?? ['membership', 'content']),
'rules' => ['window_days' => (int) ($settings['window_days'] ?? 30), 'settlement_days' => (int) ($settings['settlement_days'] ?? 7)],
'stats' => $stats,
diff --git a/src/Settings/CreditsPaymentGroups.php b/src/Settings/CreditsPaymentGroups.php
index 676145bd..94492b1e 100644
--- a/src/Settings/CreditsPaymentGroups.php
+++ b/src/Settings/CreditsPaymentGroups.php
@@ -176,14 +176,15 @@ trait CreditsPaymentGroups {
}
/**
- * @return array{enabled:bool,ratio:int,window_days:int,require_paid_order:bool,note:string}
+ * @return array
*/
public static function get_payment_affiliate_settings(): array {
return PaymentRuntime::get_payment_affiliate_settings();
}
public static function affiliate_enabled(): bool { return PaymentRuntime::affiliate_enabled(); }
- public static function affiliate_ratio(): float { return PaymentRuntime::affiliate_ratio(); }
+ public static function affiliate_ratio_for_user(int $user_id = 0): float { return PaymentRuntime::affiliate_ratio_for_user($user_id > 0 ? $user_id : get_current_user_id()); }
+ public static function affiliate_ratio_for_level(int $level): float { return PaymentRuntime::affiliate_ratio_for_level($level); }
public static function get_affiliate_settings(): array { return PaymentRuntime::get_affiliate_settings(); }
public static function get_settlement_settings(): array { return PaymentRuntime::get_settlement_settings(); }
diff --git a/src/Settings/PaymentRuntime.php b/src/Settings/PaymentRuntime.php
index ab394260..a15541cc 100644
--- a/src/Settings/PaymentRuntime.php
+++ b/src/Settings/PaymentRuntime.php
@@ -282,30 +282,34 @@ class PaymentRuntime {
*/
public static function get_payment_affiliate_settings(): array {
$settings = Settings::get_all();
- $ratio = max(0, min(100, (float) ($settings['affiliate_ratio'] ?? 5)));
- $fee = max(0, min(100, (float) ($settings['affiliate_withdrawal_fee'] ?? 5)));
+ $clamp_ratio = static fn ($value): float => max(0, min(100, (float) $value));
$types = (array) ($settings['affiliate_order_types'] ?? ['membership', 'content']);
- $types = array_values(array_intersect(['membership', 'content'], array_map('sanitize_key', $types)));
+ $types = array_values(array_intersect(['membership', 'content', 'credits'], array_map('sanitize_key', $types)));
if (!$types) { $types = ['membership', 'content']; }
- $channels = array_values(array_intersect(['alipay', 'wechat'], array_map('sanitize_key', (array) ($settings['affiliate_payout_channels'] ?? ['alipay', 'wechat']))));
- if (!$channels) { $channels = ['alipay', 'wechat']; }
return [
'enabled' => !empty($settings['affiliate_enabled']),
- 'ratio' => $ratio,
+ 'ratios' => [
+ 'user' => $clamp_ratio($settings['affiliate_ratio_user'] ?? 5),
+ 'vip_1' => $clamp_ratio($settings['affiliate_ratio_vip_1'] ?? 5),
+ 'vip_2' => $clamp_ratio($settings['affiliate_ratio_vip_2'] ?? 5),
+ ],
'order_types' => $types,
'window_days' => max(1, min(365, (int) ($settings['affiliate_touch_window_days'] ?? 30))),
'settlement_days' => max(0, min(90, (int) ($settings['affiliate_settlement_days'] ?? 7))),
- 'min_withdrawal' => max(0, min(999999, (float) ($settings['affiliate_min_withdrawal'] ?? 50))),
- 'withdrawal_fee' => $fee,
- 'payout_channels' => $channels,
- 'allow_self_purchase' => !empty($settings['affiliate_allow_self_purchase']),
- 'show_stats' => !isset($settings['affiliate_show_stats']) || !empty($settings['affiliate_show_stats']),
'promotion_text' => sanitize_textarea_field((string) ($settings['affiliate_promotion_text'] ?? '')),
];
}
public static function affiliate_enabled(): bool { return !empty(self::get_payment_affiliate_settings()['enabled']); }
- public static function affiliate_ratio(): float { return (float) self::get_payment_affiliate_settings()['ratio']; }
+ public static function affiliate_ratio_for_user(int $user_id): float {
+ $level = function_exists('aksrt_get_user_vip_level') ? (int) aksrt_get_user_vip_level($user_id) : 0;
+ return self::affiliate_ratio_for_level($level);
+ }
+ public static function affiliate_ratio_for_level(int $level): float {
+ $settings = self::get_payment_affiliate_settings();
+ $key = $level >= 2 ? 'vip_2' : ($level >= 1 ? 'vip_1' : 'user');
+ return (float) ($settings['ratios'][$key] ?? 0);
+ }
public static function get_affiliate_settings(): array { return self::get_payment_affiliate_settings(); }
public static function get_settlement_settings(): array {
$s = self::get_payment_affiliate_settings();
@@ -436,18 +440,20 @@ class PaymentRuntime {
];
}
- /** @return array{min_amount:float,service_charge_rate:int,daily_count:int,daily_limit:float,reset_time:string} */
+ /** @return array{min_amount:float,service_charge_rate:int,daily_count:int,daily_limit:float,reset_time:string,payout_channels:string[]} */
public static function get_withdrawal_settings(): array {
$s = Settings::get_all();
- $affiliate = self::get_payment_affiliate_settings();
+ $channels = array_values(array_intersect(['alipay', 'wechat'], array_map('sanitize_key', (array) ($s['affiliate_payout_channels'] ?? ['alipay', 'wechat']))));
+ if (!$channels) { $channels = ['alipay', 'wechat']; }
return [
- 'min_amount' => (float) $affiliate['min_withdrawal'],
- 'service_charge_rate' => (int) round((float) $affiliate['withdrawal_fee']),
+ 'min_amount' => max(0, min(999999, (float) ($s['affiliate_min_withdrawal'] ?? 50))),
+ 'service_charge_rate' => (int) round(max(0, min(100, (float) ($s['affiliate_withdrawal_fee'] ?? 5)))),
'daily_count' => max(0, min(99999, (int) ($s['affiliate_daily_withdrawal_count'] ?? 3))),
'daily_limit' => max(0, min(999999, (float) ($s['affiliate_daily_withdrawal_limit'] ?? 500))),
'reset_time' => preg_match('/^(?:[01]\\d|2[0-3]):[0-5]\\d$/', (string) ($s['affiliate_withdrawal_reset_time'] ?? '00:00'))
? (string) $s['affiliate_withdrawal_reset_time']
: '00:00',
+ 'payout_channels' => $channels,
];
}