feat: adjust affiliate commissions and withdrawal strategy
This commit is contained in:
@@ -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 } },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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' }] },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -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); });
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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'];
|
||||
}
|
||||
?>
|
||||
<div class="uc-modal" id="aksrt-withdraw-modal" hidden data-aksrt-withdraw-modal>
|
||||
<div class="uc-modal__overlay" data-aksrt-withdraw-close></div>
|
||||
@@ -21,8 +25,9 @@ $withdraw_fee_rate = (int) ($withdraw_settings['service_charge_rate'] ?? 5);
|
||||
<p class="muted-2-color em09 mt6"><?php printf(esc_html__('最低提现%s 元,今日可提现%d次,提现手续费率%d%%', 'aksrt'), esc_html((string) $withdraw_min), (int) ($withdraw_usage['remaining_count'] ?? 0), $withdraw_fee_rate); ?></p>
|
||||
<label class="muted-color em09 mt10"><?php esc_html_e('收款方式', 'aksrt'); ?></label>
|
||||
<select class="uc-card-pass__input" name="method" data-aksrt-withdraw-method>
|
||||
<option value="alipay"><?php esc_html_e('支付宝', 'aksrt'); ?></option>
|
||||
<option value="wechat"><?php esc_html_e('微信', 'aksrt'); ?></option>
|
||||
<?php foreach ($withdraw_channels as $channel) : ?>
|
||||
<option value="<?php echo esc_attr($channel); ?>"><?php echo esc_html($channel === 'wechat' ? __('微信收款码', 'aksrt') : __('支付宝收款码', 'aksrt')); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<label class="muted-color em09 mt10"><?php esc_html_e('收款账户信息', 'aksrt'); ?></label>
|
||||
<textarea class="uc-card-pass__input" name="account" rows="3" data-aksrt-withdraw-account placeholder="<?php esc_attr_e('姓名 + 账户 / 卡号', 'aksrt'); ?>"></textarea>
|
||||
|
||||
@@ -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']],
|
||||
]],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()]; }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -176,14 +176,15 @@ trait CreditsPaymentGroups {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{enabled:bool,ratio:int,window_days:int,require_paid_order:bool,note:string}
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
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(); }
|
||||
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user