积分与余额账务系统硬切重构

This commit is contained in:
2026-08-24 15:59:11 +08:00
parent ca12226b39
commit 5cae5b5c93
159 changed files with 7275 additions and 4718 deletions
+10 -11
View File
@@ -48,27 +48,26 @@ if (!function_exists('aksrt_burn_order_coupon')) {
$coupon = is_array($meta['coupon'] ?? null) ? $meta['coupon'] : [];
$coupon_code = sanitize_text_field((string) ($coupon['code'] ?? $meta['coupon_code'] ?? ''));
$coupon_discount = (float) ($meta['coupon_discount'] ?? $meta['discount'] ?? ($coupon['discount'] ?? 0));
$coupon_discount = (string) ($meta['coupon_discount'] ?? $meta['discount'] ?? ($coupon['discount'] ?? '0.00'));
if ($coupon_code === '') {
return false;
}
try {
$coupon_discount = \AKSRT\Ledger\AssetAmount::balance($coupon_discount)->decimal();
} catch (\InvalidArgumentException $e) {
return false;
}
$coupon_api = \AKSRT\Payment\Coupon::get_instance();
$coupon_api->use_coupon($coupon_code);
// Prefer snapshot id: post-increment validate may fail on usage_limit edge.
$coupon_id = (int) ($coupon['id'] ?? 0);
if ($coupon_id <= 0) {
$validated = $coupon_api->validate($coupon_code, max(0.01, $coupon_discount), [
'user_id' => max(0, $buyer_id),
]);
$coupon_id = (int) ($validated['coupon']['id'] ?? 0);
return false;
}
if ($coupon_id > 0) {
$coupon_api->log_usage($coupon_id, max(0, $buyer_id), $order_number, max(0.0, $coupon_discount));
if (!$coupon_api->log_usage($coupon_id, max(0, $buyer_id), $order_number, $coupon_discount)) {
return aksrt_coupon_usage_exists($order_number);
}
return true;
return $coupon_api->use_coupon($coupon_code);
}
}
+25 -63
View File
@@ -1,76 +1,38 @@
<?php
/**
* AKSRT Credits Helper
*
* 模板层使用的顶层辅助函数:
* aksrt_user_credits($user_id = 0) 读取余额
* aksrt_format_credits(int $n) 格式化展示
* aksrt_credits_label(string $reason) 流水原因 → 友好文案
* Unified asset presentation helpers.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_user_credits')) {
/**
* 取用户当前积分余额。
*
* @param int $user_id 0 表示当前登录用户
* @return int
*/
function aksrt_user_credits(int $user_id = 0): int {
if ($user_id <= 0) $user_id = get_current_user_id();
if ($user_id <= 0) return 0;
if (!class_exists('\AKSRT\Credits\Credits')) return 0;
return \AKSRT\Credits\Credits::balance($user_id);
}
if (!function_exists('aksrt_user_points')) {
function aksrt_user_points(int $user_id = 0): int {
$user_id = $user_id > 0 ? $user_id : get_current_user_id();
return $user_id > 0
? \AKSRT\Ledger\LedgerService::get_instance()->balance($user_id, \AKSRT\Ledger\AssetType::POINTS)
: 0;
}
}
if (!function_exists('aksrt_format_credits')) {
function aksrt_format_credits(int $n): string {
return number_format_i18n($n);
}
if (!function_exists('aksrt_user_balance_decimal')) {
function aksrt_user_balance_decimal(int $user_id = 0): string {
$user_id = $user_id > 0 ? $user_id : get_current_user_id();
return $user_id > 0
? \AKSRT\Ledger\LedgerService::get_instance()->balance_decimal($user_id)
: '0.00';
}
}
if (!function_exists('aksrt_credits_label')) {
/**
* 把 reason 字段翻译成展示文案。
*/
function aksrt_credits_label(string $reason): string {
$map = [
'register' => __('注册赠送', 'aksrt'),
'signin' => __('每日签到', 'aksrt'),
'comment' => __('发表评论', 'aksrt'),
'referral' => __('推广注册奖励', 'aksrt'),
'referral_new_user' => __('推广注册新用户奖励', 'aksrt'),
'download' => __('下载消费', 'aksrt'),
'admin' => __('管理员调整', 'aksrt'),
'refund' => __('退款', 'aksrt'),
'free_sign_up' => __('注册奖励', 'aksrt'),
'free_sign_in' => __('每日签到', 'aksrt'),
'free_login' => __('每日登录', 'aksrt'),
'free_followed'=> __('被关注', 'aksrt'),
'free_post_new'=> __('发布文章', 'aksrt'),
'free_post_like'=> __('文章被赞', 'aksrt'),
'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];
}
if (str_starts_with($reason, 'free_') && class_exists('\AKSRT\Credits\PointsFree')) {
$key = substr($reason, 5);
$cat = \AKSRT\Credits\PointsFree::catalog();
if (isset($cat[$key][0])) {
return (string) $cat[$key][0];
}
}
return $reason;
}
if (!function_exists('aksrt_format_points')) {
function aksrt_format_points(int $points): string {
return number_format_i18n($points);
}
}
if (!function_exists('aksrt_asset_reason_label')) {
function aksrt_asset_reason_label(string $reason): string {
return \AKSRT\Ledger\LedgerPresentation::reason_label($reason);
}
}
+3 -3
View File
@@ -89,10 +89,10 @@ if (!function_exists('aksrt_resolve_single_post_display_settings')) :
'rewards_enabled' => false,
'rewards_text' => '',
'rewards_btn_text' => '赞赏',
'rewards_amounts' => [5, 10, 20, 50, 100],
'rewards_amounts' => ['5.00', '10.00', '20.00', '50.00', '100.00'],
'rewards_custom' => true,
'rewards_min' => 1,
'rewards_max' => 1000,
'rewards_min' => '1.00',
'rewards_max' => '1000.00',
'maxheight_enabled' => false,
'maxheight' => 1000,
'copyright_enabled' => true,
+2 -5
View File
@@ -77,14 +77,11 @@ if (!function_exists('aksrt_header_user_wallet_mini')) :
if ($user_id <= 0) {
return '';
}
$credits = function_exists('aksrt_user_credits') ? (int) aksrt_user_credits($user_id) : 0;
$credits = aksrt_user_points($user_id);
$currency = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_currency_name()
: __('积分', 'aksrt');
$wallet = 0.0;
if (class_exists('\AKSRT\Credits\Balance')) {
$wallet = (float) \AKSRT\Credits\Balance::balance($user_id);
}
$wallet = aksrt_user_balance_decimal($user_id);
$center = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url('credits') : '#';
$html = '<a class="aksrt-header-wallet flex ac jsb" href="' . esc_url($center) . '">';
+131 -57
View File
@@ -35,6 +35,30 @@ if (!defined('AKSRT_PAY_TYPE_VIP')) {
define('AKSRT_PAY_TYPE_VIP', \AKSRT\Core\DomainModel::PAY_TYPE_VIP);
}
if (!function_exists('aksrt_asset_price_minor')) {
function aksrt_asset_price_minor($value, string $pay_modo): int {
if ($pay_modo === 'points') {
return is_int($value) ? max(0, $value) : 0;
}
if (!is_string($value)) {
return 0;
}
try {
return \AKSRT\Ledger\AssetAmount::balance_minor($value);
} catch (\InvalidArgumentException $e) {
return 0;
}
}
}
if (!function_exists('aksrt_asset_price_value')) {
function aksrt_asset_price_value(int $minor, string $pay_modo) {
return $pay_modo === 'points'
? max(0, $minor)
: \AKSRT\Ledger\AssetAmount::decimal_from_minor(max(0, $minor));
}
}
/**
* 检查用户是否为 VIP 豁免用户(免扣积分下载)。
*
@@ -195,18 +219,18 @@ if (!function_exists('aksrt_get_download_box_meta')) {
if (!in_array($pay_limit, [0, 1, 2], true)) {
$pay_limit = 0;
}
$over_price = max(0.0, (float) ($pay_meta['download_limit_over_price'] ?? 0));
$over_price = $pay_meta['download_limit_over_price'] ?? ($pay_modo === 'points' ? 0 : '0.00');
return [
'version' => (string) ($pay_meta['version'] ?? ''),
'size' => (string) ($pay_meta['size'] ?? ''),
'price' => (float) ($pay_meta['price'] ?? 0),
'price' => (string) ($pay_meta['price'] ?? '0.00'),
'points_price' => (int) ($pay_meta['points_price'] ?? 0),
'pay_modo' => $pay_modo,
'pay_limit' => $pay_limit,
'original_price' => (float) ($pay_meta['original_price'] ?? 0),
'vip1_price' => (float) ($pay_meta['vip1_price'] ?? 0),
'vip2_price' => (float) ($pay_meta['vip2_price'] ?? 0),
'original_price' => (string) ($pay_meta['original_price'] ?? '0.00'),
'vip1_price' => (string) ($pay_meta['vip1_price'] ?? '0.00'),
'vip2_price' => (string) ($pay_meta['vip2_price'] ?? '0.00'),
'vip1_points' => (int) ($pay_meta['vip1_points'] ?? 0),
'vip2_points' => (int) ($pay_meta['vip2_points'] ?? 0),
'pay_type' => (string) ($pay_meta['pay_type'] ?? AKSRT_PAY_TYPE_NO),
@@ -349,8 +373,8 @@ if (!function_exists('aksrt_content_order_cookie_token_key')) {
$post_id = (int) ($order['product_id'] ?? $meta['post_id'] ?? 0);
$num = (string) ($order['order_number'] ?? '');
$amount = function_exists('aksrt_format_price')
? aksrt_format_price((float) ($order['amount'] ?? 0))
: (string) round((float) ($order['amount'] ?? 0), 2);
? aksrt_format_price((string) ($order['amount'] ?? '0.00'))
: (string) ($order['amount'] ?? '0.00');
return $id . '_' . $post_id . '_' . trim($num) . '_' . $amount . '_' . trim($ip);
}
}
@@ -484,7 +508,7 @@ if (!function_exists('aksrt_guest_has_paid_access')) {
*
* @param int|WP_Post|null $post
* @param int $user_id
* @return array{amount:float,base:float,vip_amount:float,modo:string,currency:string,is_free:bool,pay_limit:int,vip_level:int,original:float,vip1:float,vip2:float}
* @return array{amount:int|string,base:int|string,vip_amount:int|string|null,modo:string,currency:string,is_free:bool,pay_limit:int,vip_level:int,original:int|string,vip1:int|string,vip2:int|string}
*/
if (!function_exists('aksrt_get_user_pay_price')) {
function aksrt_get_user_pay_price($post = null, int $user_id = 0): array {
@@ -512,49 +536,74 @@ if (!function_exists('aksrt_get_user_pay_price')) {
}
if ($pay_modo === 'points') {
$base = (float) ($meta['points_price'] ?? 0);
$vip1 = (float) ($meta['vip1_points'] ?? 0);
$vip2 = (float) ($meta['vip2_points'] ?? 0);
$base_minor = max(0, (int) ($meta['points_price'] ?? 0));
$vip1_minor = max(0, (int) ($meta['vip1_points'] ?? 0));
$vip2_minor = max(0, (int) ($meta['vip2_points'] ?? 0));
$currency_label = $currency;
} else {
$base = (float) ($meta['price'] ?? 0);
$vip1 = (float) ($meta['vip1_price'] ?? 0);
$vip2 = (float) ($meta['vip2_price'] ?? 0);
$parse_cash = static function ($value): int {
if (!is_string($value)) {
return 0;
}
try {
return \AKSRT\Ledger\AssetAmount::balance_minor($value);
} catch (\InvalidArgumentException $e) {
return 0;
}
};
$base_minor = $parse_cash($meta['price'] ?? '0');
$vip1_minor = $parse_cash($meta['vip1_price'] ?? '0');
$vip2_minor = $parse_cash($meta['vip2_price'] ?? '0');
$currency_label = $cash_symbol;
}
// Positive VIP tier prices only (0 defaults mean "not configured").
$vip_amount = -1.0;
if ($vip_level >= 2 && $vip2 > 0) {
$vip_amount = $vip2;
} elseif ($vip_level >= 1 && $vip1 > 0) {
$vip_amount = $vip1;
$vip_minor = null;
if ($vip_level >= 2 && $vip2_minor > 0) {
$vip_minor = $vip2_minor;
} elseif ($vip_level >= 1 && $vip1_minor > 0) {
$vip_minor = $vip1_minor;
}
$amount = $base;
if ($vip_amount >= 0 && ($base <= 0 || $vip_amount < $base)) {
$amount = $vip_amount;
$amount_minor = $base_minor;
if ($vip_minor !== null && ($base_minor <= 0 || $vip_minor < $base_minor)) {
$amount_minor = $vip_minor;
}
// Role/VIP free-download exemption (global setting).
if ($amount > 0 && $uid > 0 && function_exists('aksrt_is_download_credit_exempt_user') && aksrt_is_download_credit_exempt_user($uid)) {
$amount = 0.0;
if ($amount_minor > 0 && $uid > 0 && function_exists('aksrt_is_download_credit_exempt_user') && aksrt_is_download_credit_exempt_user($uid)) {
$amount_minor = 0;
}
$original = (float) ($meta['original_price'] ?? 0);
if ($pay_modo === 'points') {
$original = max(0, (int) ($meta['original_price'] ?? 0));
$amount = $amount_minor;
$base = $base_minor;
$vip1 = $vip1_minor;
$vip2 = $vip2_minor;
$vip_amount = $vip_minor;
} else {
$original_minor = $parse_cash($meta['original_price'] ?? '0');
$amount = \AKSRT\Ledger\AssetAmount::decimal_from_minor($amount_minor);
$base = \AKSRT\Ledger\AssetAmount::decimal_from_minor($base_minor);
$vip1 = \AKSRT\Ledger\AssetAmount::decimal_from_minor($vip1_minor);
$vip2 = \AKSRT\Ledger\AssetAmount::decimal_from_minor($vip2_minor);
$vip_amount = $vip_minor === null ? null : \AKSRT\Ledger\AssetAmount::decimal_from_minor($vip_minor);
$original = \AKSRT\Ledger\AssetAmount::decimal_from_minor($original_minor);
}
return [
'amount' => max(0.0, (float) $amount),
'base' => max(0.0, (float) $base),
'vip_amount' => $vip_amount >= 0 ? max(0.0, (float) $vip_amount) : -1.0,
'amount' => $amount,
'base' => $base,
'vip_amount' => $vip_amount,
'modo' => $pay_modo,
'currency' => $currency_label,
'is_free' => $amount <= 0,
'is_free' => $amount_minor <= 0,
'pay_limit' => $pay_limit,
'vip_level' => $vip_level,
'original' => $original,
'vip1' => max(0.0, (float) $vip1),
'vip2' => max(0.0, (float) $vip2),
'vip1' => $vip1,
'vip2' => $vip2,
];
}
}
@@ -585,32 +634,33 @@ if (!function_exists('aksrt_is_content_coupon_allowed')) {
* Cash only; mirrors aksrtpay_get_post_rebate_discount.
*/
if (!function_exists('aksrt_get_post_rebate_discount')) {
function aksrt_get_post_rebate_discount($post = null, int $user_id = 0): float {
function aksrt_get_post_rebate_discount($post = null, int $user_id = 0): string {
$meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post) : [];
$fixed = max(0.0, (float) ($meta['pay_rebate_discount'] ?? 0));
if ($fixed <= 0) {
return 0.0;
$fixed = is_string($meta['pay_rebate_discount'] ?? null) ? (string) $meta['pay_rebate_discount'] : '0.00';
$fixed_minor = aksrt_asset_price_minor($fixed, '0');
if ($fixed_minor <= 0) {
return '0.00';
}
if (!class_exists('\AKSRT\Settings\Settings') || !method_exists('\AKSRT\Settings\Settings', 'get_payment_affiliate_settings')) {
return 0.0;
return '0.00';
}
$aff = \AKSRT\Settings\Settings::get_payment_affiliate_settings();
if (empty($aff['enabled'])) {
return 0.0;
return '0.00';
}
$uid = $user_id > 0 ? $user_id : (function_exists('get_current_user_id') ? (int) get_current_user_id() : 0);
$referrer_id = class_exists('\AKSRT\Payment\ReferralAttribution')
? \AKSRT\Payment\ReferralAttribution::get_instance()->resolve($uid)
: 0;
if ($referrer_id <= 0) {
return 0.0;
return '0.00';
}
// Need a positive ratio for referrer (VIP tier of referrer preferred).
$ratio = (int) round(\AKSRT\Settings\Settings::affiliate_ratio_for_user($referrer_id));
$ratio = (int) \AKSRT\Settings\Settings::affiliate_ratio_for_user($referrer_id);
if ($ratio <= 0) {
return 0.0;
return '0.00';
}
return round($fixed, 2);
return \AKSRT\Ledger\AssetAmount::decimal_from_minor($fixed_minor);
}
}
@@ -702,10 +752,10 @@ if (!function_exists('aksrt_is_payshow_purchase_enabled')) {
$pay_modo = ($meta['pay_modo'] ?? '0') === 'points' ? 'points' : '0';
$price = $pay_modo === 'points'
? (float) ($meta['points_price'] ?? 0)
: (float) ($meta['price'] ?? 0);
? (int) ($meta['points_price'] ?? 0)
: (string) ($meta['price'] ?? '0.00');
return $price > 0;
return aksrt_asset_price_minor($price, $pay_modo) > 0;
}
}
@@ -751,7 +801,9 @@ if (!function_exists('aksrt_has_paid_access')) {
// 兜底:仅在 service 不可用时使用,避免离线环境直接拒绝。
$meta = aksrt_get_pay_meta($p);
if ($meta['pay_type'] === AKSRT_PAY_TYPE_NO) return true;
if ((float) $meta['price'] <= 0 && $meta['pay_type'] !== AKSRT_PAY_TYPE_VIP) return true;
$pay_modo = ($meta['pay_modo'] ?? '0') === 'points' ? 'points' : '0';
$price = $pay_modo === 'points' ? (int) ($meta['points_price'] ?? 0) : (string) ($meta['price'] ?? '0.00');
if (aksrt_asset_price_minor($price, $pay_modo) <= 0 && $meta['pay_type'] !== AKSRT_PAY_TYPE_VIP) return true;
$uid = $user_id > 0 ? $user_id : get_current_user_id();
@@ -843,7 +895,10 @@ if (!function_exists('aksrt_get_download_cost')) {
}
$meta = aksrt_get_pay_meta($post);
return (int) ceil((float) $meta['price']);
if (($meta['pay_modo'] ?? '0') !== 'points') {
return 0;
}
return max(0, (int) ($meta['points_price'] ?? 0));
}
}
@@ -918,18 +973,19 @@ if (!function_exists('aksrt_get_pay_price_label')) {
'text' => __('会员专属', 'aksrt'),
'free' => false,
'amount' => 0,
'base' => is_array($priced) ? (float) ($priced['base'] ?? 0) : 0,
'base' => is_array($priced) ? ($priced['base'] ?? ($pay_modo === 'points' ? 0 : '0.00')) : ($pay_modo === 'points' ? 0 : '0.00'),
'modo' => $pay_modo,
'pay_limit' => $pay_limit,
];
}
$price = is_array($priced)
? (float) ($priced['amount'] ?? 0)
: ($pay_modo === 'points' ? (float) ($meta['points_price'] ?? 0) : (float) ($meta['price'] ?? 0));
$base = is_array($priced) ? (float) ($priced['base'] ?? $price) : $price;
? ($priced['amount'] ?? ($pay_modo === 'points' ? 0 : '0.00'))
: ($pay_modo === 'points' ? (int) ($meta['points_price'] ?? 0) : (string) ($meta['price'] ?? '0.00'));
$base = is_array($priced) ? ($priced['base'] ?? $price) : $price;
$price_minor = aksrt_asset_price_minor($price, $pay_modo);
if ($price <= 0) {
if ($price_minor <= 0) {
return [
'text' => __('免费', 'aksrt'),
'free' => true,
@@ -963,9 +1019,21 @@ if (!function_exists('aksrt_get_pay_price_label')) {
}
if (!function_exists('aksrt_format_price')) {
function aksrt_format_price(float $price): string {
if (fmod($price, 1.0) === 0.0) return (string) (int) $price;
return rtrim(rtrim(number_format($price, 2, '.', ''), '0'), '.');
function aksrt_format_price($price): string {
if (is_int($price)) {
return (string) max(0, $price);
}
if (!is_string($price)) {
return '0';
}
try {
$decimal = \AKSRT\Ledger\AssetAmount::balance($price)->decimal();
} catch (\InvalidArgumentException $e) {
return '0';
}
return str_ends_with($decimal, '.00')
? substr($decimal, 0, -3)
: rtrim($decimal, '0');
}
}
@@ -1132,7 +1200,10 @@ if (!function_exists('aksrt_get_paywall_html')) {
$pay_type = $meta['pay_type'];
$priced = function_exists('aksrt_get_user_pay_price') ? aksrt_get_user_pay_price($post) : null;
$pay_modo = is_array($priced) ? ((($priced['modo'] ?? '0') === 'points') ? 'points' : '0') : ((($meta['pay_modo'] ?? '0') === 'points') ? 'points' : '0');
$price = is_array($priced) ? (float) ($priced['amount'] ?? 0) : (float) ($meta['price'] ?? 0);
$price = is_array($priced)
? ($priced['amount'] ?? ($pay_modo === 'points' ? 0 : '0.00'))
: ($pay_modo === 'points' ? (int) ($meta['points_price'] ?? 0) : (string) ($meta['price'] ?? '0.00'));
$price_minor = aksrt_asset_price_minor($price, $pay_modo);
$vip_level = is_array($priced) ? (int) ($priced['vip_level'] ?? 0) : 0;
$currency = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt');
$cash_symbol = '¥';
@@ -1156,8 +1227,11 @@ if (!function_exists('aksrt_get_paywall_html')) {
}
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink($post));
$is_logged_in = is_user_logged_in();
$original_price = (float) ($meta['original_price'] ?? 0);
$has_original = $original_price > 0 && $original_price > $price;
$original_price = $pay_modo === 'points'
? max(0, (int) ($meta['original_price'] ?? 0))
: (string) ($meta['original_price'] ?? '0.00');
$original_minor = aksrt_asset_price_minor($original_price, $pay_modo);
$has_original = $original_minor > 0 && $original_minor > $price_minor;
$pay_limit = (int) ($meta['pay_limit'] ?? 0);
$member_blocked = $pay_limit > 0 && $vip_level < $pay_limit;
$vip_url = function_exists('aksrt_get_user_center_tab_url')
+21 -27
View File
@@ -67,13 +67,13 @@ if (!function_exists('aksrt_get_resource_meta')) {
return [
'version' => (string) ($pay_meta['version'] ?? ''),
'size' => (string) ($pay_meta['size'] ?? ''),
'price' => (float) ($pay_meta['price'] ?? 0),
'price' => (string) ($pay_meta['price'] ?? '0.00'),
'points_price' => (int) ($pay_meta['points_price'] ?? 0),
'pay_modo' => (($pay_meta['pay_modo'] ?? '0') === 'points') ? 'points' : '0',
'pay_limit' => in_array((int) ($pay_meta['pay_limit'] ?? 0), [0, 1, 2], true) ? (int) $pay_meta['pay_limit'] : 0,
'original_price' => (float) ($pay_meta['original_price'] ?? 0),
'vip1_price' => (float) ($pay_meta['vip1_price'] ?? 0),
'vip2_price' => (float) ($pay_meta['vip2_price'] ?? 0),
'original_price' => (string) ($pay_meta['original_price'] ?? '0.00'),
'vip1_price' => (string) ($pay_meta['vip1_price'] ?? '0.00'),
'vip2_price' => (string) ($pay_meta['vip2_price'] ?? '0.00'),
'vip1_points' => (int) ($pay_meta['vip1_points'] ?? 0),
'vip2_points' => (int) ($pay_meta['vip2_points'] ?? 0),
'pay_type' => (string) ($pay_meta['pay_type'] ?? \AKSRT\Core\DomainModel::PAY_TYPE_DOWNLOAD),
@@ -99,13 +99,13 @@ if (!function_exists('aksrt_default_resource_meta')) {
return [
'version' => (string) ($meta['version'] ?? ''),
'size' => (string) ($meta['size'] ?? ''),
'price' => (float) ($meta['price'] ?? 0),
'price' => (string) ($meta['price'] ?? '0.00'),
'points_price' => (int) ($meta['points_price'] ?? 0),
'pay_modo' => (($meta['pay_modo'] ?? '0') === 'points') ? 'points' : '0',
'pay_limit' => in_array((int) ($meta['pay_limit'] ?? 0), [0, 1, 2], true) ? (int) $meta['pay_limit'] : 0,
'original_price' => (float) ($meta['original_price'] ?? 0),
'vip1_price' => (float) ($meta['vip1_price'] ?? 0),
'vip2_price' => (float) ($meta['vip2_price'] ?? 0),
'original_price' => (string) ($meta['original_price'] ?? '0.00'),
'vip1_price' => (string) ($meta['vip1_price'] ?? '0.00'),
'vip2_price' => (string) ($meta['vip2_price'] ?? '0.00'),
'vip1_points' => (int) ($meta['vip1_points'] ?? 0),
'vip2_points' => (int) ($meta['vip2_points'] ?? 0),
'pay_type' => \AKSRT\Core\DomainModel::PAY_TYPE_DOWNLOAD,
@@ -128,19 +128,18 @@ if (!function_exists('aksrt_resource_price_label')) {
/**
* 把价格数字渲染成展示徽章文案。
*
* @param float $price
* @param int $price
* @return array { text:string, free:bool }
*/
function aksrt_resource_price_label(float $price): array {
function aksrt_resource_price_label(int $price): array {
if ($price <= 0) {
return [
'text' => __('免费', 'aksrt'),
'free' => true,
];
}
$n = (fmod($price, 1.0) === 0.0) ? (string) (int) $price : rtrim(rtrim(number_format($price, 2, '.', ''), '0'), '.');
return [
'text' => sprintf(__('%s 积分', 'aksrt'), $n),
'text' => sprintf(__('%s 积分', 'aksrt'), (string) $price),
'free' => false,
];
}
@@ -150,15 +149,11 @@ if (!function_exists('aksrt_resource_price_amount')) {
/**
* Format the raw resource price number for compact UI labels.
*
* @param float $price
* @param int $price
* @return string
*/
function aksrt_resource_price_amount(float $price): string {
if (fmod($price, 1.0) === 0.0) {
return (string) (int) $price;
}
return rtrim(rtrim(number_format($price, 2, '.', ''), '0'), '.');
function aksrt_resource_price_amount(int $price): string {
return (string) max(0, $price);
}
}
@@ -201,14 +196,15 @@ if (!function_exists('aksrt_resource_card_badge_text')) {
$priced = function_exists('aksrt_get_user_pay_price')
? aksrt_get_user_pay_price($p)
: [
'amount' => $pay_modo === 'points' ? (float) ($meta['points_price'] ?? 0) : (float) ($meta['price'] ?? 0),
'amount' => $pay_modo === 'points' ? (int) ($meta['points_price'] ?? 0) : (string) ($meta['price'] ?? '0.00'),
'modo' => $pay_modo,
];
$price = (float) ($priced['amount'] ?? 0);
$price = $priced['amount'] ?? ($pay_modo === 'points' ? 0 : '0.00');
$pay_modo = (($priced['modo'] ?? $pay_modo) === 'points') ? 'points' : '0';
$price_minor = function_exists('aksrt_asset_price_minor') ? aksrt_asset_price_minor($price, $pay_modo) : 0;
// Free paid-type (price 0).
if ($price <= 0) {
if ($price_minor <= 0) {
if ($pay_type === 'download') {
return __('免费资源', 'aksrt');
}
@@ -218,7 +214,7 @@ if (!function_exists('aksrt_resource_card_badge_text')) {
if ($pay_modo === 'points') {
$currency = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt');
$amount = function_exists('aksrt_resource_price_amount')
? aksrt_resource_price_amount($price)
? aksrt_resource_price_amount($price_minor)
: (string) (int) $price;
return sprintf('%s %s%s', $type_name, $amount, $currency);
}
@@ -461,13 +457,11 @@ if (!function_exists('aksrt_resource_download_cost')) {
}
if (is_array($priced)) {
$price = (float) ($priced['amount'] ?? 0);
return $price > 0 ? (int) ceil($price) : 0;
return is_int($priced['amount'] ?? null) ? max(0, (int) $priced['amount']) : 0;
}
$meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post) : [];
$price = (float) ($meta['points_price'] ?? $meta['price'] ?? 0);
return $price > 0 ? (int) ceil($price) : 0;
return max(0, (int) ($meta['points_price'] ?? 0));
}
}
+54 -19
View File
@@ -44,6 +44,17 @@ if (!function_exists('aksrt_render_rewards_modal_for_user')) {
$done[$key] = true;
$settings = \AKSRT\Settings\Settings::get_post_page_settings();
$amounts = (array) ($settings['rewards_amounts'] ?? [5, 10, 20, 50, 100]);
$render_amounts = [];
foreach ($amounts as $amount) {
try {
$parsed = \AKSRT\Ledger\AssetAmount::balance(is_string($amount) ? $amount : '');
if ($parsed->minor() > 0) {
$render_amounts[] = $parsed->decimal();
}
} catch (\InvalidArgumentException $e) {
continue;
}
}
$recipient = get_userdata($user_id);
?>
<div class="reward-modal" id="reward-modal-<?php echo (int) $user_id; ?>" data-user-id="<?php echo (int) $user_id; ?>" data-post-id="<?php echo (int) $post_id; ?>" aria-hidden="true">
@@ -54,8 +65,8 @@ if (!function_exists('aksrt_render_rewards_modal_for_user')) {
<h3 id="reward-title-<?php echo (int) $user_id; ?>"><?php echo esc_html((string) ($settings['rewards_text'] ?? __('赞赏', 'aksrt'))); ?></h3>
<p class="muted-2-color em09"><?php printf(esc_html__('赞赏将直接进入 %s 的站内余额', 'aksrt'), esc_html($recipient ? $recipient->display_name : __('作者', 'aksrt'))); ?></p>
<div class="reward-amounts" data-aksrt-reward-amounts>
<?php foreach ($amounts as $index => $amount) : ?>
<button type="button" class="reward-amount<?php echo $index === 0 ? ' is-active' : ''; ?>" data-reward-amount="<?php echo esc_attr(number_format((float) $amount, 2, '.', '')); ?>">¥<?php echo esc_html(number_format_i18n((float) $amount, 2)); ?></button>
<?php foreach ($render_amounts as $index => $amount) : ?>
<button type="button" class="reward-amount<?php echo $index === 0 ? ' is-active' : ''; ?>" data-reward-amount="<?php echo esc_attr($amount); ?>">¥<?php echo esc_html($amount); ?></button>
<?php endforeach; ?>
</div>
<?php if (!empty($settings['rewards_custom'])) : ?>
@@ -75,7 +86,8 @@ add_action('wp_ajax_aksrt_submit_tip', function (): void {
if (!is_user_logged_in()) {
wp_send_json_error(['message' => __('请先登录后赞赏', 'aksrt')], 401);
}
if (!aksrt_rewards_enabled() || !class_exists('\AKSRT\Credits\Balance') || !\AKSRT\Credits\Balance::enabled()) {
$recharge = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_payment_recharge_settings() : [];
if (!aksrt_rewards_enabled() || empty($recharge['enabled'])) {
wp_send_json_error(['message' => __('赞赏功能当前不可用', 'aksrt')], 400);
}
@@ -94,40 +106,63 @@ add_action('wp_ajax_aksrt_submit_tip', function (): void {
}
$settings = \AKSRT\Settings\Settings::get_post_page_settings();
$amount = isset($_POST['amount']) ? round((float) wp_unslash($_POST['amount']), 2) : 0.0;
$minimum = (float) ($settings['rewards_min'] ?? 1);
$maximum = (float) ($settings['rewards_max'] ?? 1000);
if (!is_finite($amount) || $amount < $minimum || $amount > $maximum) {
wp_send_json_error(['message' => sprintf(__('赞赏金额需在 ¥%1$s 至 ¥%2$s 之间', 'aksrt'), number_format_i18n($minimum, 2), number_format_i18n($maximum, 2))], 400);
$raw_amount = isset($_POST['amount']) ? (string) wp_unslash($_POST['amount']) : '';
try {
$amount = \AKSRT\Ledger\AssetAmount::balance($raw_amount);
$minimum = \AKSRT\Ledger\AssetAmount::balance(is_string($settings['rewards_min'] ?? null) ? $settings['rewards_min'] : '');
$maximum = \AKSRT\Ledger\AssetAmount::balance(is_string($settings['rewards_max'] ?? null) ? $settings['rewards_max'] : '');
} catch (\InvalidArgumentException $e) {
wp_send_json_error(['message' => __('赞赏金额格式无效', 'aksrt')], 400);
}
if ($amount->minor() < $minimum->minor() || $amount->minor() > $maximum->minor()) {
wp_send_json_error(['message' => sprintf(__('赞赏金额需在 ¥%1$s 至 ¥%2$s 之间', 'aksrt'), $minimum->formatted(), $maximum->formatted())], 400);
}
if (empty($settings['rewards_custom'])) {
$allowed_amounts = array_map(static fn($value): float => round((float) $value, 2), (array) ($settings['rewards_amounts'] ?? []));
if (!in_array($amount, $allowed_amounts, true)) {
$allowed_amounts = [];
foreach ((array) ($settings['rewards_amounts'] ?? []) as $allowed) {
try {
$allowed_amounts[] = \AKSRT\Ledger\AssetAmount::balance(is_string($allowed) ? $allowed : '')->minor();
} catch (\InvalidArgumentException $e) {
continue;
}
}
if (!in_array($amount->minor(), $allowed_amounts, true)) {
wp_send_json_error(['message' => __('请选择预设的赞赏金额', 'aksrt')], 400);
}
}
$request_key = sanitize_text_field(wp_unslash((string) ($_SERVER['HTTP_IDEMPOTENCY_KEY'] ?? '')));
if (!wp_is_uuid($request_key)) {
wp_send_json_error(['message' => __('缺少或无效的 Idempotency-Key 请求头', 'aksrt')], 400);
}
$operation_key = \AKSRT\Ledger\OperationKey::deterministic('client-tip', [$sender_id, strtolower($request_key)]);
$rate_key = 'aksrt_tip_rate_' . $sender_id;
if (get_transient($rate_key)) {
$recent_operation = (string) get_transient($rate_key);
if ($recent_operation !== '' && !hash_equals($recent_operation, $operation_key)) {
wp_send_json_error(['message' => __('操作太频繁,请稍后再试', 'aksrt')], 429);
}
set_transient($rate_key, 1, 3);
set_transient($rate_key, $operation_key, 3);
$result = \AKSRT\Credits\Balance::transfer(
$result = \AKSRT\Ledger\LedgerService::get_instance()->transfer(
$sender_id,
$recipient_id,
$amount,
'tip_sent',
'tip_received',
\AKSRT\Ledger\AssetType::BALANCE,
$amount->minor(),
0,
$operation_key,
'tip',
['type' => 'tip', 'id' => $post_id],
$post_id > 0 ? sprintf(__('赞赏文章 #%d', 'aksrt'), $post_id) : __('用户赞赏', 'aksrt')
$post_id > 0 ? sprintf(__('赞赏文章 #%d', 'aksrt'), $post_id) : __('用户赞赏', 'aksrt'),
__('收到用户赞赏', 'aksrt')
);
if (is_wp_error($result)) {
wp_send_json_error(['message' => $result->get_error_message()], 400);
wp_send_json_error(['message' => $result->get_error_message()], (int) ($result->get_error_data()['status'] ?? 400));
}
wp_send_json_success([
'message' => __('赞赏成功,金额已进入对方余额', 'aksrt'),
'balance' => \AKSRT\Credits\Balance::format((float) $result['sender_balance']),
'transaction_id' => $result->transaction_id,
'replayed' => $result->replayed,
'balance' => \AKSRT\Ledger\AssetAmount::from_minor(\AKSRT\Ledger\AssetType::BALANCE, $result->balance($sender_id, \AKSRT\Ledger\AssetType::BALANCE))->decimal(),
]);
});
-51
View File
@@ -48,54 +48,3 @@ function aksrt_get_orders_page_view(int $user_id, int $paged = 1, int $per_page
return $empty;
}
function aksrt_get_revenue_page_view(int $user_id): array {
$balance = 0.0;
$total_earned = 0.0;
$total_withdrawn = 0.0;
$records = [];
if (class_exists('\AKSRT\Credits\Balance')) {
$balance = (float) \AKSRT\Credits\Balance::balance($user_id);
}
if (class_exists('\AKSRT\Payment\Revenue')) {
$revenues = \AKSRT\Payment\Revenue::get_instance()->get_user_revenues($user_id, ['limit' => 200]);
foreach ($revenues as $rev) {
$row = is_array($rev) ? $rev : (array) $rev;
$total_earned += (float) ($row['amount'] ?? 0);
$records[] = [
'type' => 'earning',
'title' => (string) ($row['description'] ?? $row['type'] ?? __('创作分成', 'aksrt')),
'amount' => (float) ($row['amount'] ?? 0),
'created_at' => (string) ($row['created_at'] ?? ''),
];
}
}
if (class_exists('\AKSRT\Payment\Withdrawal')) {
$withdrawals = \AKSRT\Payment\Withdrawal::get_instance()->get_user_withdrawals($user_id, ['limit' => 200]);
foreach ($withdrawals as $w) {
$wrow = is_array($w) ? $w : (array) $w;
$total_withdrawn += (float) ($wrow['amount'] ?? 0);
$records[] = [
'type' => 'withdrawal',
'title' => __('提现', 'aksrt'),
'amount' => (float) ($wrow['amount'] ?? 0),
'created_at' => (string) ($wrow['created_at'] ?? ''),
'status' => (string) ($wrow['status'] ?? ''),
];
}
}
usort($records, static function ($a, $b) {
return strcmp($b['created_at'] ?? '', $a['created_at'] ?? '');
});
return [
'balance' => $balance,
'total_earned' => $total_earned,
'total_withdrawn' => $total_withdrawn,
'records' => $records,
];
}
+5 -5
View File
@@ -70,16 +70,16 @@ function aksrt_get_user_dashboard_view(int $user_id, array $args = []): array {
$background_url = function_exists('aksrt_get_user_cover_url') ? aksrt_get_user_cover_url($user_id) : '';
$credits_enabled = class_exists('\AKSRT\Settings\Settings') && \AKSRT\Settings\Settings::credits_enabled();
$credit_balance = $credits_enabled && $is_own_profile && function_exists('aksrt_user_credits')
? (float) aksrt_user_credits($user_id)
$credit_balance = $credits_enabled && $is_own_profile
? (float) aksrt_user_points($user_id)
: 0.0;
$currency_name = $credits_enabled && class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_currency_name()
: __('积分', 'aksrt');
// Cash wallet (legacy pay_balance) — separate from points.
$balance_enabled = $is_own_profile && class_exists('\AKSRT\Credits\Balance') && \AKSRT\Credits\Balance::enabled();
$wallet_balance = $balance_enabled ? \AKSRT\Credits\Balance::balance($user_id) : 0.0;
$recharge = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_payment_recharge_settings() : [];
$balance_enabled = $is_own_profile && !empty($recharge['enabled']);
$wallet_balance = $balance_enabled ? aksrt_user_balance_decimal($user_id) : '0.00';
$user_home_manager = $include_governance && class_exists('\AKSRT\User\Home')
? \AKSRT\User\Home::get_instance()
+1 -1
View File
@@ -307,7 +307,7 @@ if (!function_exists('aksrt_check_free_download_quota')) {
];
// Optional overbuy price for free-quota exhausted (legacy download_limit_over_price).
// Callers may pass post context via filter.
$over = apply_filters('aksrt_download_limit_over_price', 0.0, $user_id);
$over = apply_filters('aksrt_download_limit_over_price', 0, $user_id);
if ($over > 0) {
$data['download_limit_over_price'] = $over;
$data['allow_limit_over'] = true;
@@ -7,7 +7,7 @@
defined('ABSPATH') || exit;
$balance = (float) ($credit_balance ?? 0.0);
$balance = max(0, (int) ($credit_balance ?? 0));
$credits_enabled = !empty($credits_enabled);
if (!$credits_enabled) {
return;
@@ -26,7 +26,8 @@ if ($points_transfer_on && class_exists('\AKSRT\Settings\Capabilities') && !\AKS
}
$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();
$recharge = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_payment_recharge_settings() : [];
$points_exchange_on = !empty($points_exchange['enabled']) && !empty($recharge['enabled']);
$currency = (string) ($currency_name ?? __('积分', 'aksrt'));
?>
<div class="uc-asset-card uc-asset-card--points">
@@ -45,7 +46,7 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
</div>
<div class="uc-asset-card__body flex jsb ac">
<div class="uc-asset-card__amount c-green">
<span class="uc-asset-card__value font-bold"><?php echo esc_html(function_exists('aksrt_format_credits') ? aksrt_format_credits((int) $balance) : (string) (int) $balance); ?></span>
<span class="uc-asset-card__value font-bold"><?php echo esc_html(aksrt_format_points($balance)); ?></span>
</div>
<div class="uc-asset-card__actions flex0">
<?php if ($points_transfer_on) : ?>
@@ -74,7 +75,7 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
<?php endif; ?>
<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); ?>">
<input type="number" class="uc-card-pass__input" name="amount" min="0.01" step="0.01" inputmode="decimal" 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>
<button type="submit" class="but jb-blue btn-block mt10"><?php esc_html_e('立即购买', 'aksrt'); ?></button>
@@ -18,16 +18,12 @@ defined('ABSPATH') || exit;
</div>
<?php
$credits_page = max(1, isset($_GET['credits_page']) ? (int) $_GET['credits_page'] : 1);
if (class_exists('\AKSRT\Credits\Credits')) {
$log = \AKSRT\Credits\Credits::get_log(['user_id' => $user_id, 'page' => $credits_page, 'per_page' => 10]);
} else {
$log = ['items' => [], 'total' => 0];
}
$log = \AKSRT\Ledger\LedgerQuery::get_instance()->history(['user_id' => $user_id, 'asset' => 'points', 'page' => $credits_page, 'per_page' => 10]);
if (!empty($log['items'])) :
?>
<div class="uc-transactions__list">
<?php foreach ($log['items'] as $row) :
$delta = (int) $row['delta'];
$delta = (int) $row['delta_minor'];
$delta_class = $delta >= 0 ? 'is-positive' : 'is-negative';
?>
<div class="uc-transactions__item">
@@ -36,7 +32,7 @@ defined('ABSPATH') || exit;
</div>
<div class="uc-transactions__info">
<div class="uc-transactions__reason">
<?php echo esc_html(aksrt_credits_label((string) $row['reason'])); ?>
<?php echo esc_html(aksrt_asset_reason_label((string) $row['reason'])); ?>
<?php if (!empty($row['remark'])) : ?>
<span class="uc-transactions__remark">(<?php echo esc_html((string) $row['remark']); ?>)</span>
<?php endif; ?>
@@ -44,6 +40,7 @@ defined('ABSPATH') || exit;
<div class="uc-transactions__time">
<?php echo esc_html(get_date_from_gmt((string) $row['created_at'], 'Y-m-d H:i')); ?>
</div>
<?php if (!empty($row['counterparty_user_id'])) : ?><div class="muted-2-color em09"><?php printf(esc_html__('对方用户 #%d', 'aksrt'), (int) $row['counterparty_user_id']); ?></div><?php endif; ?>
</div>
<div class="uc-transactions__amount">
<span class="uc-transactions__delta <?php echo esc_attr($delta_class); ?>">
@@ -52,9 +49,11 @@ defined('ABSPATH') || exit;
<span class="uc-transactions__balance">
<?php
/* translators: %s: balance */
printf(esc_html__('余额 %s', 'aksrt'), esc_html((string) (int) $row['balance_after']));
printf(esc_html__('余额 %s', 'aksrt'), esc_html((string) (int) $row['balance_after_minor']));
?>
</span>
<?php if ((int) ($row['fee_minor'] ?? 0) > 0) : ?><span class="muted-2-color em09"><?php printf(esc_html__('手续费 %d', 'aksrt'), (int) $row['fee_minor']); ?></span><?php endif; ?>
<span class="muted-2-color em09"><?php printf(esc_html__('交易 #%d', 'aksrt'), (int) $row['id']); ?></span>
</div>
</div>
<?php endforeach; ?>
@@ -13,10 +13,15 @@ if (empty($balance_enabled)) {
return;
}
$wallet = (float) ($wallet_balance ?? 0.0);
$wallet_fmt = class_exists('\AKSRT\Credits\Balance')
? \AKSRT\Credits\Balance::format($wallet)
: number_format_i18n($wallet, 2);
$normalize_balance = static function ($value): \AKSRT\Ledger\AssetAmount {
try {
return \AKSRT\Ledger\AssetAmount::balance(is_string($value) ? $value : '');
} catch (\InvalidArgumentException $e) {
return \AKSRT\Ledger\AssetAmount::balance('0');
}
};
$wallet = $normalize_balance($wallet_balance ?? '0');
$wallet_fmt = $wallet->formatted();
$recharge = class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_payment_recharge_settings')
? \AKSRT\Settings\Settings::get_payment_recharge_settings()
@@ -32,9 +37,9 @@ if ($withdraw_on && class_exists('\AKSRT\Settings\Capabilities') && !\AKSRT\Sett
$withdraw_on = false;
}
$withdraw_settings = class_exists('\\AKSRT\\Settings\\Settings') ? \AKSRT\Settings\Settings::get_withdrawal_settings() : [];
$withdraw_min = max(0, (float) ($withdraw_settings['min_amount'] ?? 50));
$withdraw_usage = ['remaining_count' => 0, 'remaining_amount' => 0];
$withdraw_pending_hold = 0.0;
$withdraw_min = $normalize_balance($withdraw_settings['min_amount'] ?? '50')->decimal();
$withdraw_usage = ['remaining_count' => 0, 'remaining_amount' => '0.00'];
$withdraw_pending_hold = '0.00';
$withdraw_history = [];
if ($withdraw_on && class_exists('\AKSRT\Payment\Withdrawal')) {
$wd_svc = \AKSRT\Payment\Withdrawal::get_instance();
@@ -42,7 +47,7 @@ if ($withdraw_on && class_exists('\AKSRT\Payment\Withdrawal')) {
$withdraw_history = $wd_svc->get_user_withdrawals((int) ($user_id ?? 0), ['limit' => 30]);
$withdraw_usage = $wd_svc->daily_usage((int) ($user_id ?? 0), $withdraw_settings);
}
$withdraw_processing = $withdraw_pending_hold > 0;
$withdraw_processing = $normalize_balance($withdraw_pending_hold)->minor() > 0;
$card_pass_balance = ['enabled' => false, 'desc' => '', 'only_password' => true];
if (class_exists('\AKSRT\Payment\ChargeCard')) {
$opts = \AKSRT\Payment\ChargeCard::get_instance()->get_public_options();
@@ -90,7 +95,7 @@ $mark = (string) ($charge_view['currency_mark'] ?? '元');
</div>
<?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($normalize_balance($withdraw_pending_hold)->formatted())); ?></span>
</div>
<?php endif; ?>
</div>
@@ -128,13 +133,17 @@ $mark = (string) ($charge_view['currency_mark'] ?? '元');
<?php if ($packages) : ?>
<div class="muted-color mb6"><?php esc_html_e('请选择充值金额', 'aksrt'); ?></div>
<div class="uc-charge-packages">
<?php foreach ($packages as $idx => $pkg) : ?>
<?php foreach ($packages as $idx => $pkg) :
$package_credit = $normalize_balance($pkg['price'] ?? '');
$package_pay = $normalize_balance($pkg['pay_price'] ?? '');
$package_saving_minor = max(0, $package_credit->minor() - $package_pay->minor());
?>
<label class="uc-charge-package<?php echo $idx === 0 ? ' is-active' : ''; ?>">
<input type="radio" name="balance_package" value="<?php echo esc_attr((string) $pkg['key']); ?>" <?php checked($idx === 0); ?>>
<div class="uc-charge-package__credit"><span class="px12">¥</span><?php echo esc_html((string) $pkg['price']); ?></div>
<div class="uc-charge-package__pay c-red"><span class="px12">¥</span><?php echo esc_html((string) $pkg['pay_price']); ?>
<?php if ((float) $pkg['pay_price'] < (float) $pkg['price']) : ?>
<span class="ml6 c-yellow smail"><?php printf(esc_html__('省%s', 'aksrt'), esc_html((string) ((float) $pkg['price'] - (float) $pkg['pay_price']))); ?></span>
<?php if ($package_saving_minor > 0) : ?>
<span class="ml6 c-yellow smail"><?php printf(esc_html__('省%s', 'aksrt'), esc_html(\AKSRT\Ledger\AssetAmount::decimal_from_minor($package_saving_minor))); ?></span>
<?php endif; ?>
</div>
<?php if (!empty($pkg['tag'])) : ?>
@@ -195,19 +204,19 @@ endif; ?>
<?php if (!empty($withdraw_history)) : ?>
<?php foreach ($withdraw_history as $wd) :
$st = sanitize_key((string) ($wd['status'] ?? ''));
$gross = (float) ($wd['gross_amount'] ?? $wd['amount'] ?? 0);
$net = (float) ($wd['amount'] ?? 0);
$gross = $normalize_balance($wd['gross_amount'] ?? $wd['amount'] ?? '0');
$net = $normalize_balance($wd['amount'] ?? '0');
$st_class = $st === 'rejected' ? 'c-red' : ($st === 'completed' ? 'c-green' : 'c-blue');
?>
<div class="uc-withdraw-history__item border-bottom padding-h10">
<div class="flex jsb ac">
<span class="badg badg-sm <?php echo esc_attr($st_class); ?>"><?php echo esc_html($status_labels[$st] ?? $st); ?></span>
<b class="em12"><?php echo esc_html(class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::format($gross) : number_format_i18n($gross, 2)); ?></b>
<b class="em12"><?php echo esc_html($gross->formatted()); ?></b>
</div>
<div class="muted-2-color em09 mt6">
<?php echo esc_html((string) ($wd['created_at'] ?? '')); ?>
<?php if (abs($gross - $net) > 0.001) : ?>
· <?php printf(esc_html__('到账 %s', 'aksrt'), esc_html(class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::format($net) : number_format_i18n($net, 2))); ?>
<?php if ($gross->minor() !== $net->minor()) : ?>
· <?php printf(esc_html__('到账 %s', 'aksrt'), esc_html($net->formatted())); ?>
<?php endif; ?>
</div>
<?php if (!empty($wd['note'])) : ?>
@@ -233,16 +242,14 @@ endif; ?>
</div>
<?php
$wallet_page = max(1, isset($_GET['wallet_page']) ? (int) $_GET['wallet_page'] : 1);
$wlog = class_exists('\AKSRT\Credits\Balance')
? \AKSRT\Credits\Balance::get_log(['user_id' => (int) $user_id, 'page' => $wallet_page, 'per_page' => 10])
: ['items' => [], 'total' => 0];
$wlog = \AKSRT\Ledger\LedgerQuery::get_instance()->history(['user_id' => (int) $user_id, 'asset' => 'balance', 'page' => $wallet_page, 'per_page' => 10]);
if (!empty($wlog['items'])) :
?>
<div class="uc-transactions__list">
<?php foreach ($wlog['items'] as $row) :
$delta = (float) $row['delta'];
$delta = (int) $row['delta_minor'];
$delta_class = $delta >= 0 ? 'is-positive' : 'is-negative';
$delta_txt = ($delta > 0 ? '+' : '') . (class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::format($delta) : number_format_i18n($delta, 2));
$delta_txt = ($delta > 0 ? '+' : '-') . \AKSRT\Ledger\AssetAmount::from_minor(\AKSRT\Ledger\AssetType::BALANCE, abs($delta))->formatted();
?>
<div class="uc-transactions__item">
<div class="uc-transactions__icon <?php echo esc_attr($delta_class); ?>">
@@ -250,7 +257,7 @@ endif; ?>
</div>
<div class="uc-transactions__info">
<div class="uc-transactions__reason">
<?php echo esc_html(class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::reason_label((string) $row['reason']) : (string) $row['reason']); ?>
<?php echo esc_html(aksrt_asset_reason_label((string) $row['reason'])); ?>
<?php if (!empty($row['remark'])) : ?>
<span class="uc-transactions__remark">(<?php echo esc_html((string) $row['remark']); ?>)</span>
<?php endif; ?>
@@ -263,7 +270,7 @@ endif; ?>
<?php
printf(
esc_html__('余额 %s', 'aksrt'),
esc_html(class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::format((float) $row['balance_after']) : number_format_i18n((float) $row['balance_after'], 2))
esc_html(\AKSRT\Ledger\AssetAmount::from_minor(\AKSRT\Ledger\AssetType::BALANCE, (int) $row['balance_after_minor'])->formatted())
);
?>
</span>
@@ -39,7 +39,14 @@ $page_url = static function (int $target_page) use ($base_url): string {
);
};
$format = static function ($amount, string $unit): string {
return number_format_i18n((float) $amount, $unit === 'points' ? 0 : 2);
if ($unit === 'points') {
return aksrt_format_points(max(0, (int) $amount));
}
try {
return \AKSRT\Ledger\AssetAmount::balance(is_string($amount) ? $amount : '')->formatted();
} catch (\InvalidArgumentException $e) {
return '0.00';
}
};
$note = trim((string) ($settings['note'] ?? ''));
if ($note === '') {
@@ -77,17 +77,15 @@ if ($show_wallet || $show_credits) :
// Render only the ledger portion by reusing balance log markup.
$user_id = (int) ($user_id ?? 0);
$wallet_page = max(1, isset($_GET['wallet_page']) ? (int) $_GET['wallet_page'] : 1);
$wlog = class_exists('\AKSRT\Credits\Balance')
? \AKSRT\Credits\Balance::get_log(['user_id' => $user_id, 'page' => $wallet_page, 'per_page' => 10])
: ['items' => [], 'total' => 0];
$wlog = \AKSRT\Ledger\LedgerQuery::get_instance()->history(['user_id' => $user_id, 'asset' => 'balance', 'page' => $wallet_page, 'per_page' => 10]);
?>
<div class="uc-transactions uc-transactions--wallet">
<?php if (!empty($wlog['items'])) : ?>
<div class="uc-transactions__list">
<?php foreach ($wlog['items'] as $row) :
$delta = (float) $row['delta'];
$delta = (int) $row['delta_minor'];
$delta_class = $delta >= 0 ? 'is-positive' : 'is-negative';
$delta_txt = ($delta > 0 ? '+' : '') . (class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::format($delta) : number_format_i18n($delta, 2));
$delta_txt = ($delta > 0 ? '+' : '-') . \AKSRT\Ledger\AssetAmount::from_minor(\AKSRT\Ledger\AssetType::BALANCE, abs($delta))->formatted();
?>
<div class="uc-transactions__item">
<div class="uc-transactions__icon <?php echo esc_attr($delta_class); ?>">
@@ -95,7 +93,7 @@ if ($show_wallet || $show_credits) :
</div>
<div class="uc-transactions__info">
<div class="uc-transactions__reason">
<?php echo esc_html(class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::reason_label((string) $row['reason']) : (string) $row['reason']); ?>
<?php echo esc_html(aksrt_asset_reason_label((string) $row['reason'])); ?>
<?php if (!empty($row['remark'])) : ?>
<span class="uc-transactions__remark">(<?php echo esc_html((string) $row['remark']); ?>)</span>
<?php endif; ?>
@@ -104,7 +102,9 @@ if ($show_wallet || $show_credits) :
</div>
<div class="uc-transactions__amount">
<span class="uc-transactions__delta <?php echo esc_attr($delta_class); ?>"><?php echo esc_html($delta_txt); ?></span>
<span class="uc-transactions__balance"><?php printf(esc_html__('余额 %s', 'aksrt'), esc_html(class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::format((float) ($row['balance_after'] ?? 0)) : number_format_i18n((float) ($row['balance_after'] ?? 0), 2))); ?></span>
<span class="uc-transactions__balance"><?php printf(esc_html__('余额 %s', 'aksrt'), esc_html(\AKSRT\Ledger\AssetAmount::from_minor(\AKSRT\Ledger\AssetType::BALANCE, (int) ($row['balance_after_minor'] ?? 0))->formatted())); ?></span>
<?php if ((int) ($row['fee_minor'] ?? 0) > 0) : ?><span class="muted-2-color em09"><?php printf(esc_html__('手续费 %s', 'aksrt'), esc_html(\AKSRT\Ledger\AssetAmount::from_minor(\AKSRT\Ledger\AssetType::BALANCE, (int) $row['fee_minor'])->formatted())); ?></span><?php endif; ?>
<span class="muted-2-color em09"><?php printf(esc_html__('交易 #%d', 'aksrt'), (int) $row['id']); ?></span>
<?php if (!empty($row['ref_id'])) : ?>
<span class="uc-transactions__ref muted-2-color em09"><?php printf(esc_html__('单号 %s', 'aksrt'), esc_html((string) $row['ref_id'])); ?></span>
<?php endif; ?>
@@ -111,9 +111,16 @@ $tabs = [
<?php foreach ($orders as $order) :
$number = (string) ($order['order_number'] ?? '');
$actions = (array) ($order['allowed_actions'] ?? []);
$amount = !empty($order['is_points'])
? esc_html((string) ($order['currency_symbol'] ?? __('积分', 'aksrt')) . ' ' . (string) (int) round((float) ($order['amount'] ?? 0)))
: '¥' . esc_html(number_format((float) ($order['amount'] ?? 0), 2));
if (!empty($order['is_points'])) {
$amount = esc_html((string) ($order['currency_symbol'] ?? __('积分', 'aksrt')) . ' ' . (string) max(0, (int) ($order['amount'] ?? 0)));
} else {
try {
$order_amount = \AKSRT\Ledger\AssetAmount::balance(is_string($order['amount'] ?? null) ? $order['amount'] : '')->formatted();
} catch (\InvalidArgumentException $e) {
$order_amount = '0.00';
}
$amount = '¥' . esc_html($order_amount);
}
?>
<article class="aksrt-orders-page__card" data-aksrt-order-card="<?php echo esc_attr($number); ?>" data-order-status="<?php echo esc_attr((string) ($order['status_group'] ?? '')); ?>">
<div class="aksrt-orders-page__card-rail"><span><?php echo esc_html((string) ($order['product_type_label'] ?? '')); ?></span><time><?php echo esc_html((string) ($order['created_at'] ?? '')); ?></time><span class="aksrt-orders-page__card-status is-<?php echo esc_attr((string) ($order['status_group'] ?? '')); ?>"><?php echo esc_html((string) ($order['status_label'] ?? '')); ?></span></div>
@@ -21,22 +21,34 @@ $affiliate_on = !empty($affiliate['enabled']);
$withdrawal = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_withdrawal_settings()
: [];
$withdraw_on = $affiliate_on
&& class_exists('\AKSRT\Credits\Balance')
&& \AKSRT\Credits\Balance::enabled();
$recharge = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_payment_recharge_settings() : [];
$withdraw_on = $affiliate_on && !empty($recharge['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) ($withdrawal['min_amount'] ?? 50));
$normalize_balance = static function ($value): \AKSRT\Ledger\AssetAmount {
try {
return \AKSRT\Ledger\AssetAmount::balance(is_string($value) ? $value : '');
} catch (\InvalidArgumentException $e) {
return \AKSRT\Ledger\AssetAmount::balance('0');
}
};
$signed_balance_minor = static function ($value) use ($normalize_balance): int {
$value = is_string($value) ? trim($value) : '';
$negative = str_starts_with($value, '-');
$amount = $normalize_balance($negative ? substr($value, 1) : $value)->minor();
return $negative ? -$amount : $amount;
};
$withdraw_min = $normalize_balance($withdrawal['min_amount'] ?? '50')->decimal();
$commission_stats = class_exists('\AKSRT\Payment\CommissionLedger')
? \AKSRT\Payment\CommissionLedger::get_instance()->overview($user_id)
: ['total' => 0, 'pending' => 0, 'settled' => 0, 'wallet' => 0];
$pending_commission = (float) ($commission_stats['pending'] ?? 0);
$pending_commission = $normalize_balance($commission_stats['pending'] ?? '0')->decimal();
$vip_ratios = [];
$commission_ratio = class_exists('\AKSRT\Settings\Settings')
? (float) \AKSRT\Settings\Settings::affiliate_ratio_for_user($user_id)
: 0.0;
? (int) \AKSRT\Settings\Settings::affiliate_ratio_for_user($user_id)
: 0;
$details_text = trim((string) ($affiliate['promotion_text'] ?? ''));
if ($details_text === '') {
$details_text = __('好友通过推广链接完成符合条件的订单后,系统会按当前佣金比例记录返佣,结算后进入可提现余额。', 'aksrt');
@@ -56,8 +68,8 @@ $base_url = function_exists('aksrt_get_user_center_tab_url')
$reward_url = static function (string $tab, array $extra = []) use ($base_url): string {
return add_query_arg(array_merge(['reward_tab' => $tab], $extra), $base_url);
};
$format_amount = static function ($amount): string {
return number_format_i18n((float) $amount, 2);
$format_amount = static function ($amount) use ($normalize_balance): string {
return $normalize_balance($amount)->formatted();
};
$commission_page = max(1, (int) ($revenue_request['commission_page'] ?? ($_GET['commission_page'] ?? 1)));
@@ -151,7 +163,11 @@ $status_labels = [
$source_user = !empty($item['source_user_id']) ? get_userdata((int) $item['source_user_id']) : null;
$source_name = $source_user instanceof WP_User ? $source_user->display_name : __('推广用户', 'aksrt');
?>
<article class="uc-commission-row"><span class="uc-commission-row__icon"><i class="fa fas fa-file-alt" aria-hidden="true"></i></span><div class="uc-commission-row__main"><strong><?php echo esc_html($source_name); ?></strong><span><?php printf(esc_html__('订单 %s', 'aksrt'), esc_html((string) ($item['order_number'] ?? '-'))); ?></span><time><?php echo esc_html((string) ($item['created_at'] ?? '')); ?></time></div><div class="uc-commission-row__value"><strong><?php echo esc_html($format_amount((float) ($item['original_amount'] ?? 0) + (float) ($item['adjustment_total'] ?? 0))); ?></strong><span class="is-<?php echo esc_attr($status); ?>"><?php echo esc_html($status_labels[$status] ?? $status); ?></span></div></article>
<?php
$commission_minor = $signed_balance_minor($item['original_amount'] ?? '0') + $signed_balance_minor($item['adjustment_total'] ?? '0');
$commission_amount = \AKSRT\Ledger\AssetAmount::decimal_from_minor(max(0, $commission_minor));
?>
<article class="uc-commission-row"><span class="uc-commission-row__icon"><i class="fa fas fa-file-alt" aria-hidden="true"></i></span><div class="uc-commission-row__main"><strong><?php echo esc_html($source_name); ?></strong><span><?php printf(esc_html__('订单 %s', 'aksrt'), esc_html((string) ($item['order_number'] ?? '-'))); ?></span><time><?php echo esc_html((string) ($item['created_at'] ?? '')); ?></time></div><div class="uc-commission-row__value"><strong><?php echo esc_html($format_amount($commission_amount)); ?></strong><span class="is-<?php echo esc_attr($status); ?>"><?php echo esc_html($status_labels[$status] ?? $status); ?></span></div></article>
<?php endforeach; ?>
</div>
<?php if ($commission_total_pages > 1) : ?><nav class="uc-reward-pagination" aria-label="<?php esc_attr_e('佣金明细分页', 'aksrt'); ?>"><?php for ($i = 1; $i <= $commission_total_pages; $i++) : ?><a class="<?php echo $i === $commission_page ? 'is-active' : ''; ?>" href="<?php echo esc_url($reward_url('orders', ['commission_page' => $i])); ?>"><?php echo esc_html((string) $i); ?></a><?php endfor; ?></nav><?php endif; ?>
@@ -14,15 +14,19 @@ if (!$show_wallet && !$show_credits) {
}
$assets_url = aksrt_get_user_center_tab_url('credits');
$wallet = (float) ($wallet_balance ?? 0);
$points = (float) ($credit_balance ?? 0);
try {
$wallet = \AKSRT\Ledger\AssetAmount::balance(is_string($wallet_balance ?? null) ? $wallet_balance : '')->formatted();
} catch (\InvalidArgumentException $e) {
$wallet = '0.00';
}
$points = max(0, (int) ($credit_balance ?? 0));
?>
<div class="uc-sidebar__pair uc-sidebar__pair--assets">
<?php if ($show_wallet) : ?>
<a class="uc-summary-card uc-summary-card--wallet" href="<?php echo esc_url($assets_url); ?>" data-tab="credits">
<span class="uc-summary-card__head"><?php esc_html_e('账户余额', 'aksrt'); ?><i class="fa fas fa-angle-right" aria-hidden="true"></i></span>
<span class="uc-summary-card__body">
<strong><?php echo esc_html(class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::format($wallet) : number_format_i18n($wallet, 2)); ?></strong>
<strong><?php echo esc_html($wallet); ?></strong>
<i class="fa fas fa-money-bill" aria-hidden="true"></i>
</span>
</a>
@@ -31,7 +35,7 @@ $points = (float) ($credit_balance ?? 0);
<a class="uc-summary-card uc-summary-card--points" href="<?php echo esc_url($assets_url); ?>" data-tab="credits">
<span class="uc-summary-card__head"><?php echo esc_html((string) ($currency_name ?? __('积分', 'aksrt'))); ?><i class="fa fas fa-angle-right" aria-hidden="true"></i></span>
<span class="uc-summary-card__body">
<strong><?php echo esc_html(function_exists('aksrt_format_credits') ? aksrt_format_credits((int) $points) : number_format_i18n((int) $points)); ?></strong>
<strong><?php echo esc_html(aksrt_format_points($points)); ?></strong>
<i class="fa fas fa-money-bill" aria-hidden="true"></i>
</span>
</a>
@@ -6,8 +6,12 @@ if (!empty($GLOBALS['aksrt_withdraw_modal_rendered'])) {
return;
}
$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];
try {
$withdraw_min = \AKSRT\Ledger\AssetAmount::balance(is_string($withdraw_min ?? null) ? $withdraw_min : '')->decimal();
} catch (\InvalidArgumentException $e) {
$withdraw_min = '0.00';
}
$withdraw_usage = is_array($withdraw_usage ?? null) ? $withdraw_usage : ['remaining_count' => 0, 'remaining_amount' => '0.00'];
$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) {
@@ -21,8 +25,8 @@ if (!$withdraw_channels) {
<h3 class="uc-modal__title"><?php esc_html_e('余额提现', 'aksrt'); ?></h3>
<form data-aksrt-withdraw-form>
<label class="muted-color em09"><?php esc_html_e('提现金额', 'aksrt'); ?></label>
<input type="number" class="uc-card-pass__input" name="amount" min="<?php echo esc_attr((string) $withdraw_min); ?>" max="<?php echo esc_attr((string) ($withdraw_usage['remaining_amount'] ?? 0)); ?>" step="1" inputmode="numeric" data-aksrt-withdraw-amount data-withdraw-min="<?php echo esc_attr((string) $withdraw_min); ?>" data-withdraw-remaining-amount="<?php echo esc_attr((string) ($withdraw_usage['remaining_amount'] ?? 0)); ?>" data-withdraw-remaining-count="<?php echo esc_attr((string) ($withdraw_usage['remaining_count'] ?? 0)); ?>">
<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>
<input type="number" class="uc-card-pass__input" name="amount" min="<?php echo esc_attr($withdraw_min); ?>" max="<?php echo esc_attr((string) ($withdraw_usage['remaining_amount'] ?? '0.00')); ?>" step="0.01" inputmode="decimal" data-aksrt-withdraw-amount data-withdraw-min="<?php echo esc_attr($withdraw_min); ?>" data-withdraw-remaining-amount="<?php echo esc_attr((string) ($withdraw_usage['remaining_amount'] ?? '0.00')); ?>" data-withdraw-remaining-count="<?php echo esc_attr((string) ($withdraw_usage['remaining_count'] ?? 0)); ?>">
<p class="muted-2-color em09 mt6"><?php printf(esc_html__('最低提现%s 元,今日可提现%d次,提现手续费率%d%%', 'aksrt'), esc_html($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>
<?php foreach ($withdraw_channels as $channel) : ?>
+36 -21
View File
@@ -53,8 +53,10 @@ function aksrt_notification_type_label(string $type): string {
'comment_reply' => __('评论回复', 'aksrt'),
'comment_approved' => __('评论审核', 'aksrt'),
'download_ready' => __('下载就绪', 'aksrt'),
'credits_added' => __('积分增加', 'aksrt'),
'credits_deducted' => __('积分扣减', 'aksrt'),
'asset_points_increase' => __('积分增加', 'aksrt'),
'asset_points_decrease' => __('积分扣减', 'aksrt'),
'asset_balance_increase' => __('余额增加', 'aksrt'),
'asset_balance_decrease' => __('余额扣减', 'aksrt'),
'pending_comments' => __('待审核评论', 'aksrt'),
'notice' => __('通知', 'aksrt'),
];
@@ -549,25 +551,38 @@ add_action('transition_comment_status', function ($new_status, $old_status, $com
update_comment_meta($comment->comment_ID, 'aksrt_reply_notified', 1);
}, 20, 3);
add_action('aksrt_credits_added', function ($user_id, $delta, $reason, $new_balance) {
if ($user_id <= 0 || $delta <= 0) return;
aksrt_add_user_notification((int) $user_id, [
'type' => 'credits_added',
'title' => __('积分增加', 'aksrt'),
'desc' => sprintf(__('积分变动:+%1$d,当前余额 %2$d。', 'aksrt'), (int) $delta, (int) $new_balance),
'url' => aksrt_get_user_center_url('credits'),
]);
}, 10, 4);
add_action('aksrt_credits_deducted', function ($user_id, $cost, $reason, $new_balance) {
if ($user_id <= 0 || $cost <= 0) return;
aksrt_add_user_notification((int) $user_id, [
'type' => 'credits_deducted',
'title' => __('积分扣减', 'aksrt'),
'desc' => sprintf(__('积分变动:-%1$d,当前余额 %2$d。', 'aksrt'), (int) $cost, (int) $new_balance),
'url' => aksrt_get_user_center_url('credits'),
]);
}, 10, 4);
add_action('aksrt_asset_transaction_committed', function ($result, $command): void {
if (!$result instanceof \AKSRT\Ledger\LedgerResult || !$command instanceof \AKSRT\Ledger\LedgerCommand) {
return;
}
$changes = [];
foreach ($result->entries as $entry) {
$user_id = (int) ($entry['user_id'] ?? 0);
$asset = (string) ($entry['asset'] ?? '');
if ($user_id <= 0 || !in_array($asset, ['points', 'balance'], true)) continue;
$key = $user_id . ':' . $asset;
$changes[$key] ??= ['user_id' => $user_id, 'asset' => $asset, 'delta' => 0, 'balance' => 0];
$changes[$key]['delta'] += (int) ($entry['delta_minor'] ?? 0);
$changes[$key]['balance'] = (int) ($entry['balance_after_minor'] ?? 0);
}
foreach ($changes as $change) {
if ((int) $change['delta'] === 0) continue;
$is_points = $change['asset'] === 'points';
$positive = $change['delta'] > 0;
$amount = $is_points
? (string) abs((int) $change['delta'])
: \AKSRT\Ledger\AssetAmount::from_minor(\AKSRT\Ledger\AssetType::BALANCE, abs((int) $change['delta']))->decimal();
$balance = $is_points
? (string) $change['balance']
: \AKSRT\Ledger\AssetAmount::from_minor(\AKSRT\Ledger\AssetType::BALANCE, (int) $change['balance'])->decimal();
aksrt_add_user_notification((int) $change['user_id'], [
'type' => 'asset_' . $change['asset'] . '_' . ($positive ? 'increase' : 'decrease'),
'title' => $is_points ? __('积分变动', 'aksrt') : __('余额变动', 'aksrt'),
'desc' => sprintf(__('%1$s:%2$s%3$s,当前余额 %4$s。', 'aksrt'), \AKSRT\Ledger\LedgerPresentation::reason_label($command->reason), $positive ? '+' : '-', $amount, $balance),
'url' => aksrt_get_user_center_url('credits'),
]);
}
}, 10, 2);