feat(credits): exchange balance for points
Replace the legacy points-to-balance and external points purchase flows with direct integer balance-to-points exchange. Enforce exchange and points-transfer limits and capability checks across the UI and REST API.
This commit is contained in:
1 parent
f9bce15c9a
commit
1247f9bf4e
18 files changed
+234
-648
No files matched your search
@@ -56,6 +56,10 @@ if (!function_exists('aksrt_credits_label')) {
|
||||
'free_post_favorite'=> __('文章被收藏', 'aksrt'),
|
||||
'free_comment_new'=> __('发表评论', 'aksrt'),
|
||||
'free_comment_like'=> __('评论被赞', 'aksrt'),
|
||||
'points_exchange_in' => __('余额兑换积分', 'aksrt'),
|
||||
'transfer_in' => __('积分转入', 'aksrt'),
|
||||
'transfer_out' => __('积分转出', 'aksrt'),
|
||||
'transfer_refund' => __('积分转账退款', 'aksrt'),
|
||||
];
|
||||
if (isset($map[$reason])) {
|
||||
return $map[$reason];
|
||||
|
||||
@@ -24,8 +24,9 @@ $points_transfer_on = !empty($all_settings['points_transfer_s']);
|
||||
if ($points_transfer_on && class_exists('\AKSRT\Settings\Capabilities') && !\AKSRT\Settings\Capabilities::user_can('points_transfer', $points_uid)) {
|
||||
$points_transfer_on = false;
|
||||
}
|
||||
$points_buy_on = class_exists('\AKSRT\Credits\BalanceService') && \AKSRT\Credits\BalanceService::get_instance()->points_buy_enabled();
|
||||
$points_buy_view = $points_buy_on ? \AKSRT\Credits\BalanceService::get_instance()->get_points_buy_view((int) ($user_id ?? 0)) : [];
|
||||
$points_transfer_min = max(1, (int) ($all_settings['points_transfer_min'] ?? 1));
|
||||
$points_exchange = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_payment_recharge_settings()['points_exchange'] ?? [] : [];
|
||||
$points_exchange_on = !empty($points_exchange['enabled']) && class_exists('\AKSRT\Credits\Balance') && \AKSRT\Credits\Balance::enabled();
|
||||
$currency = (string) ($currency_name ?? __('积分', 'aksrt'));
|
||||
?>
|
||||
<div class="uc-asset-card uc-asset-card--points">
|
||||
@@ -46,10 +47,10 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
|
||||
</div>
|
||||
<div class="uc-asset-card__actions flex0">
|
||||
<?php if ($points_transfer_on) : ?>
|
||||
<button type="button" class="but c-green mr6 hollow" data-aksrt-transfer-open="points"><?php esc_html_e('转账', 'aksrt'); ?></button>
|
||||
<button type="button" class="but c-green mr6 hollow" data-aksrt-transfer-open="points" data-aksrt-transfer-min="<?php echo esc_attr((string) $points_transfer_min); ?>"><?php esc_html_e('转账', 'aksrt'); ?></button>
|
||||
<?php endif; ?>
|
||||
<?php if ($points_buy_on) : ?>
|
||||
<button type="button" class="but c-green" data-aksrt-points-buy-open><?php esc_html_e('购买积分', 'aksrt'); ?></button>
|
||||
<?php if ($points_exchange_on) : ?>
|
||||
<button type="button" class="but c-green" data-aksrt-points-exchange-open><?php esc_html_e('购买积分', 'aksrt'); ?></button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,54 +58,25 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
|
||||
<button type="button" class="uc-asset-card__link muted-2-color em09" data-aksrt-asset-tab="points_free"><?php esc_html_e('做任务赚积分', 'aksrt'); ?><i class="fa fa-angle-right em12 ml3" aria-hidden="true"></i></button>
|
||||
</div>
|
||||
|
||||
<?php if ($points_buy_on) :
|
||||
$pb_packages = is_array($points_buy_view['packages'] ?? null) ? $points_buy_view['packages'] : [];
|
||||
$pb_methods = is_array($points_buy_view['payment_methods'] ?? null) ? $points_buy_view['payment_methods'] : [];
|
||||
$pb_rate = (int) ($points_buy_view['rate'] ?? 100);
|
||||
$pb_custom = !empty($points_buy_view['custom_s']);
|
||||
$pb_limit = (int) ($points_buy_view['custom_limit'] ?? 99999);
|
||||
<?php if ($points_exchange_on) :
|
||||
$pb_rate = max(1, (int) ($points_exchange['rate'] ?? 100));
|
||||
$pb_min_points = max(1, (int) ($points_exchange['min_points'] ?? 100));
|
||||
$pb_daily_max_points = max(1, (int) ($points_exchange['daily_max_points'] ?? 100000));
|
||||
?>
|
||||
<div class="uc-modal" id="aksrt-points-buy-modal" hidden data-aksrt-points-buy-modal>
|
||||
<div class="uc-modal__overlay" data-aksrt-points-buy-close></div>
|
||||
<div class="uc-modal" id="aksrt-points-exchange-modal" hidden data-aksrt-points-exchange-modal>
|
||||
<div class="uc-modal__overlay" data-aksrt-points-exchange-close></div>
|
||||
<div class="uc-modal__dialog" role="dialog" aria-modal="true">
|
||||
<button type="button" class="uc-modal__close" data-aksrt-points-buy-close aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>"><i class="fas fa-times" aria-hidden="true"></i></button>
|
||||
<button type="button" class="uc-modal__close" data-aksrt-points-exchange-close aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>"><i class="fas fa-times" aria-hidden="true"></i></button>
|
||||
<h3 class="uc-modal__title"><i class="fas fa-coins mr6" aria-hidden="true"></i><?php esc_html_e('购买积分', 'aksrt'); ?></h3>
|
||||
<?php if (!empty($points_buy_view['desc'])) : ?>
|
||||
<div class="uc-modal__desc muted-box em09"><?php echo esc_html((string) $points_buy_view['desc']); ?></div>
|
||||
<?php if (!empty($points_exchange['desc'])) : ?>
|
||||
<div class="uc-modal__desc muted-box em09"><?php echo esc_html((string) $points_exchange['desc']); ?></div>
|
||||
<?php endif; ?>
|
||||
<form data-aksrt-points-buy-form>
|
||||
<?php if ($pb_packages) : ?>
|
||||
<div class="muted-color mb6"><?php esc_html_e('请选择积分套餐', 'aksrt'); ?></div>
|
||||
<div class="uc-charge-packages">
|
||||
<?php foreach ($pb_packages as $idx => $pkg) : ?>
|
||||
<label class="uc-charge-package<?php echo $idx === 0 ? ' is-active' : ''; ?>">
|
||||
<input type="radio" name="points_package" value="<?php echo esc_attr((string) $pkg['key']); ?>" <?php checked($idx === 0); ?>>
|
||||
<div class="uc-charge-package__credit"><?php echo esc_html((string) (int) $pkg['points']); ?> <span class="px12"><?php echo esc_html($currency); ?></span></div>
|
||||
<div class="uc-charge-package__pay c-red"><span class="px12">¥</span><?php echo esc_html((string) $pkg['pay_price']); ?></div>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if ($pb_custom) : ?>
|
||||
<div class="muted-color mt16 mb6"><?php echo $pb_packages ? esc_html__('自定义积分数量', 'aksrt') : esc_html__('请输入购买积分数量', 'aksrt'); ?></div>
|
||||
<input type="number" class="uc-card-pass__input" name="custom_points" min="<?php echo esc_attr((string) $pb_rate); ?>" max="<?php echo esc_attr((string) $pb_limit); ?>" step="1" data-aksrt-points-buy-custom>
|
||||
<p class="muted-2-color em09 mt6"><?php printf(esc_html__('1 元 = %d 积分', 'aksrt'), $pb_rate); ?></p>
|
||||
<?php if (!$pb_packages) : ?>
|
||||
<input type="hidden" name="points_package" value="custom">
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<?php if ($pb_methods) : ?>
|
||||
<div class="muted-color mt16 mb6"><?php esc_html_e('支付方式', 'aksrt'); ?></div>
|
||||
<div class="uc-charge-methods">
|
||||
<?php foreach ($pb_methods as $mi => $m) : ?>
|
||||
<label class="uc-charge-method">
|
||||
<input type="radio" name="points_pay_method" value="<?php echo esc_attr((string) ($m['id'] ?? '')); ?>" <?php checked($mi === 0); ?>>
|
||||
<span><?php echo esc_html((string) ($m['name'] ?? $m['id'] ?? '')); ?></span>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<p class="uc-modal__tip" data-aksrt-points-buy-tip></p>
|
||||
<form data-aksrt-points-exchange-form>
|
||||
<label class="muted-color em09"><?php esc_html_e('兑换余额', 'aksrt'); ?></label>
|
||||
<input type="number" class="uc-card-pass__input" name="amount" min="1" step="1" inputmode="numeric" data-aksrt-points-exchange-amount data-aksrt-points-exchange-rate="<?php echo esc_attr((string) $pb_rate); ?>">
|
||||
<p class="muted-2-color em09 mt6"><?php printf(esc_html__('1 余额 = %d 积分;单笔至少 %d 积分,每日最多 %d 积分。', 'aksrt'), $pb_rate, $pb_min_points, $pb_daily_max_points); ?></p>
|
||||
<p class="muted-2-color em09 mt6" data-aksrt-points-exchange-preview></p>
|
||||
<p class="uc-modal__tip" data-aksrt-points-exchange-tip></p>
|
||||
<button type="submit" class="but jb-blue btn-block mt10"><?php esc_html_e('立即购买', 'aksrt'); ?></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,6 @@ $transfer_on = !empty($recharge['transfer']['enabled']);
|
||||
if ($transfer_on && class_exists('\AKSRT\Settings\Capabilities') && !\AKSRT\Settings\Capabilities::user_can('balance_transfer', $uid)) {
|
||||
$transfer_on = false;
|
||||
}
|
||||
$exchange_on = !empty($recharge['exchange']['enabled']);
|
||||
$all_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
|
||||
$withdraw_on = !empty($all_settings['pay_balance_withdraw_s']);
|
||||
if ($withdraw_on && class_exists('\AKSRT\Settings\Capabilities') && !\AKSRT\Settings\Capabilities::user_can('balance_withdraw', $uid)) {
|
||||
@@ -89,18 +88,9 @@ $mark = (string) ($charge_view['currency_mark'] ?? '元');
|
||||
<button type="button" class="but b-blue-2" data-aksrt-charge-open><?php esc_html_e('充值', 'aksrt'); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($exchange_on) : ?>
|
||||
<button type="button" class="uc-asset-card__link muted-2-color em09" data-aksrt-exchange-open>
|
||||
<?php
|
||||
$rate = (int) ($recharge['exchange']['rate'] ?? 100);
|
||||
printf(esc_html__('积分兑换余额(%d 积分 = 1 元)', 'aksrt'), $rate);
|
||||
?>
|
||||
<i class="fa fa-angle-right em12 ml3" aria-hidden="true"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if ($withdraw_on) : ?>
|
||||
<div class="uc-asset-card__foot muted-2-color em09 mt6">
|
||||
<span><?php printf(esc_html__('处理中冻结 %s', 'aksrt'), esc_html(class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::format((float) $withdraw_pending_hold) : number_format_i18n((float) $withdraw_pending_hold, 2))); ?></span>
|
||||
<span><?php printf(esc_html__('冻结中 %s', 'aksrt'), esc_html(class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::format((float) $withdraw_pending_hold) : number_format_i18n((float) $withdraw_pending_hold, 2))); ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
@@ -181,60 +171,6 @@ $mark = (string) ($charge_view['currency_mark'] ?? '元');
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Transfer modal (balance) -->
|
||||
<div class="uc-modal" id="aksrt-transfer-modal" hidden data-aksrt-transfer-modal>
|
||||
<div class="uc-modal__overlay" data-aksrt-transfer-close></div>
|
||||
<div class="uc-modal__dialog" role="dialog" aria-modal="true">
|
||||
<button type="button" class="uc-modal__close" data-aksrt-transfer-close aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>"><i class="fas fa-times" aria-hidden="true"></i></button>
|
||||
<h3 class="uc-modal__title" data-aksrt-transfer-title><?php esc_html_e('余额转账', 'aksrt'); ?></h3>
|
||||
<form data-aksrt-transfer-form>
|
||||
<input type="hidden" name="type" value="balance" data-aksrt-transfer-type>
|
||||
<div class="mb10">
|
||||
<label class="muted-color em09"><?php esc_html_e('收款用户', 'aksrt'); ?></label>
|
||||
<input type="search" class="uc-card-pass__input" data-aksrt-transfer-search placeholder="<?php esc_attr_e('搜索用户名/昵称/邮箱', 'aksrt'); ?>" autocomplete="off">
|
||||
<div class="uc-transfer-results" data-aksrt-transfer-results></div>
|
||||
<input type="hidden" name="recipient" value="" data-aksrt-transfer-recipient>
|
||||
<div class="uc-transfer-selected muted-2-color em09 mt6" data-aksrt-transfer-selected></div>
|
||||
</div>
|
||||
<div class="mb10">
|
||||
<label class="muted-color em09"><?php esc_html_e('金额', 'aksrt'); ?></label>
|
||||
<input type="number" class="uc-card-pass__input" name="amount" min="0.01" step="0.01" data-aksrt-transfer-amount>
|
||||
</div>
|
||||
<?php if (!empty($recharge['transfer']['desc'])) : ?>
|
||||
<div class="muted-box em09 mb10"><?php echo esc_html((string) $recharge['transfer']['desc']); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($recharge['transfer']['service_charge'])) : ?>
|
||||
<div class="c-yellow em09 mb10"><?php printf(esc_html__('转账将扣除 %s%% 手续费', 'aksrt'), esc_html((string) (int) $recharge['transfer']['service_charge'])); ?></div>
|
||||
<?php endif; ?>
|
||||
<p class="uc-modal__tip" data-aksrt-transfer-tip></p>
|
||||
<button type="submit" class="but jb-blue btn-block" data-aksrt-transfer-submit><?php esc_html_e('确认转账', 'aksrt'); ?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Exchange modal -->
|
||||
<?php if ($exchange_on) : ?>
|
||||
<div class="uc-modal" id="aksrt-exchange-modal" hidden data-aksrt-exchange-modal>
|
||||
<div class="uc-modal__overlay" data-aksrt-exchange-close></div>
|
||||
<div class="uc-modal__dialog" role="dialog" aria-modal="true">
|
||||
<button type="button" class="uc-modal__close" data-aksrt-exchange-close aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>"><i class="fas fa-times" aria-hidden="true"></i></button>
|
||||
<h3 class="uc-modal__title"><?php esc_html_e('积分兑换余额', 'aksrt'); ?></h3>
|
||||
<?php if (!empty($recharge['exchange']['desc'])) : ?>
|
||||
<div class="muted-box em09 mb10"><?php echo esc_html((string) $recharge['exchange']['desc']); ?></div>
|
||||
<?php endif; ?>
|
||||
<form data-aksrt-exchange-form>
|
||||
<label class="muted-color em09"><?php esc_html_e('兑换积分数量', 'aksrt'); ?></label>
|
||||
<input type="number" class="uc-card-pass__input" name="points" min="<?php echo esc_attr((string) (int) ($recharge['exchange']['rate'] ?? 100)); ?>" step="1" data-aksrt-exchange-points>
|
||||
<p class="muted-2-color em09 mt6">
|
||||
<?php printf(esc_html__('比例:%d 积分 = 1 元(向下取整)', 'aksrt'), (int) ($recharge['exchange']['rate'] ?? 100)); ?>
|
||||
</p>
|
||||
<p class="uc-modal__tip" data-aksrt-exchange-tip></p>
|
||||
<button type="submit" class="but jb-blue btn-block" data-aksrt-exchange-submit><?php esc_html_e('确认兑换', 'aksrt'); ?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($withdraw_on) :
|
||||
$withdraw_modal_rendered = !empty($GLOBALS['aksrt_withdraw_modal_rendered']);
|
||||
if (!$withdraw_modal_rendered) {
|
||||
|
||||
@@ -42,6 +42,10 @@ if ($show_credits) {
|
||||
require $dir . '/credits-signin.php';
|
||||
}
|
||||
|
||||
if ($show_wallet || $show_credits) {
|
||||
require $dir . '/transfer-modal.php';
|
||||
}
|
||||
|
||||
// Tabbed ledgers (Zibll record tabs).
|
||||
if ($show_wallet || $show_credits) :
|
||||
$active_record = $show_wallet ? 'balance' : 'points';
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* Shared transfer modal for balance and points assets.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
$transfer_recharge = class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_payment_recharge_settings')
|
||||
? \AKSRT\Settings\Settings::get_payment_recharge_settings()
|
||||
: [];
|
||||
$transfer_all_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
|
||||
$points_transfer_min = max(1, (int) ($transfer_all_settings['points_transfer_min'] ?? 1));
|
||||
$points_transfer_fee = max(0, min(50, (int) ($transfer_all_settings['points_service_charge'] ?? 0)));
|
||||
?>
|
||||
<div class="uc-modal" id="aksrt-transfer-modal" hidden data-aksrt-transfer-modal>
|
||||
<div class="uc-modal__overlay" data-aksrt-transfer-close></div>
|
||||
<div class="uc-modal__dialog" role="dialog" aria-modal="true">
|
||||
<button type="button" class="uc-modal__close" data-aksrt-transfer-close aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>"><i class="fas fa-times" aria-hidden="true"></i></button>
|
||||
<h3 class="uc-modal__title" data-aksrt-transfer-title><?php esc_html_e('余额转账', 'aksrt'); ?></h3>
|
||||
<form data-aksrt-transfer-form>
|
||||
<input type="hidden" name="type" value="balance" data-aksrt-transfer-type>
|
||||
<div class="mb10">
|
||||
<label class="muted-color em09"><?php esc_html_e('收款用户', 'aksrt'); ?></label>
|
||||
<input type="search" class="uc-card-pass__input" data-aksrt-transfer-search placeholder="<?php esc_attr_e('搜索用户名/昵称/邮箱', 'aksrt'); ?>" autocomplete="off">
|
||||
<div class="uc-transfer-results" data-aksrt-transfer-results></div>
|
||||
<input type="hidden" name="recipient" value="" data-aksrt-transfer-recipient>
|
||||
<div class="uc-transfer-selected muted-2-color em09 mt6" data-aksrt-transfer-selected></div>
|
||||
</div>
|
||||
<div class="mb10">
|
||||
<label class="muted-color em09" data-aksrt-transfer-amount-label><?php esc_html_e('金额', 'aksrt'); ?></label>
|
||||
<input type="number" class="uc-card-pass__input" name="amount" min="0.01" step="0.01" data-aksrt-transfer-amount>
|
||||
</div>
|
||||
<div data-aksrt-transfer-balance-info>
|
||||
<?php if (!empty($transfer_recharge['transfer']['desc'])) : ?>
|
||||
<div class="muted-box em09 mb10"><?php echo esc_html((string) $transfer_recharge['transfer']['desc']); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($transfer_recharge['transfer']['service_charge'])) : ?>
|
||||
<div class="c-yellow em09 mb10"><?php printf(esc_html__('转账将扣除 %s%% 手续费', 'aksrt'), esc_html((string) (int) $transfer_recharge['transfer']['service_charge'])); ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="muted-box em09 mb10" hidden data-aksrt-transfer-points-info>
|
||||
<?php printf(esc_html__('积分转账仅支持正整数,单笔至少 %d 积分。', 'aksrt'), $points_transfer_min); ?>
|
||||
<?php if ($points_transfer_fee > 0) : ?>
|
||||
<?php printf(esc_html__(' 将扣除 %d%% 手续费。', 'aksrt'), $points_transfer_fee); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<p class="uc-modal__tip" data-aksrt-transfer-tip></p>
|
||||
<button type="submit" class="but jb-blue btn-block" data-aksrt-transfer-submit><?php esc_html_e('确认转账', 'aksrt'); ?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
Reference in new issue
Block a user