feat(user-center): add cash wallet balance separate from credits
Introduce AKSRT_Balance (user_meta aksrt_balance + balance_log) for Zibll-like pay_balance wallet. User center assets panel shows wallet and points; balance gateway/card-pass/refunds credit cash when enabled.
This commit is contained in:
@@ -6,6 +6,24 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dual assets: wallet + points cards stack with gap */
|
||||||
|
.uc-wallet-grid {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uc-balance-card--wallet .uc-balance-card__bg--wallet,
|
||||||
|
.uc-balance-card__bg--wallet {
|
||||||
|
background: linear-gradient(135deg, #0f766e 0%, #14b8a6 45%, #2dd4bf 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.uc-balance-card + .uc-balance-card {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uc-transactions--wallet {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.uc-content #panel-credits .uc-transactions {
|
.uc-content #panel-credits .uc-transactions {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
|||||||
@@ -224,6 +224,8 @@ require_once AKSRT_DIR . '/inc/classes/class-aksrt-db-migrator.php';
|
|||||||
|
|
||||||
// 加载积分服务
|
// 加载积分服务
|
||||||
require_once AKSRT_DIR . '/inc/classes/class-aksrt-credits.php';
|
require_once AKSRT_DIR . '/inc/classes/class-aksrt-credits.php';
|
||||||
|
// 站内现金钱包(与积分分离)
|
||||||
|
require_once AKSRT_DIR . '/inc/classes/class-aksrt-balance.php';
|
||||||
require_once AKSRT_DIR . '/inc/classes/class-aksrt-credits-earn.php';
|
require_once AKSRT_DIR . '/inc/classes/class-aksrt-credits-earn.php';
|
||||||
require_once AKSRT_DIR . '/inc/classes/class-aksrt-signin.php';
|
require_once AKSRT_DIR . '/inc/classes/class-aksrt-signin.php';
|
||||||
require_once AKSRT_DIR . '/inc/classes/class-aksrt-invite.php';
|
require_once AKSRT_DIR . '/inc/classes/class-aksrt-invite.php';
|
||||||
|
|||||||
@@ -514,16 +514,26 @@ class AKSRT_After_Sale {
|
|||||||
$payment_method = (string) ($order['payment_method'] ?? 'balance');
|
$payment_method = (string) ($order['payment_method'] ?? 'balance');
|
||||||
$order_number = (string) ($order['order_number'] ?? '');
|
$order_number = (string) ($order['order_number'] ?? '');
|
||||||
|
|
||||||
// Balance refund: credit back via AKSRT_Credits
|
// Balance refund: prefer cash wallet, fall back to points.
|
||||||
if ($payment_method === 'balance' && class_exists('AKSRT_Credits')) {
|
if ($payment_method === 'balance') {
|
||||||
|
if (class_exists('AKSRT_Balance') && AKSRT_Balance::enabled()) {
|
||||||
|
AKSRT_Balance::add(
|
||||||
|
$user_id,
|
||||||
|
(float) $refund_amount,
|
||||||
|
'order_refund',
|
||||||
|
['type' => 'order', 'order' => $order_number],
|
||||||
|
sprintf(__('售后退款:订单 %s', 'aksrt'), $order_number)
|
||||||
|
);
|
||||||
|
} elseif (class_exists('AKSRT_Credits')) {
|
||||||
AKSRT_Credits::add(
|
AKSRT_Credits::add(
|
||||||
$user_id,
|
$user_id,
|
||||||
(int) round($refund_amount),
|
(int) round($refund_amount),
|
||||||
sprintf(__('售后退款:订单 %s', 'aksrt'), $order_number),
|
'order_refund',
|
||||||
['order_number' => $order_number, 'type' => 'refund'],
|
['order_number' => $order_number, 'type' => 'refund'],
|
||||||
sprintf(__('售后退款金额 ¥%s', 'aksrt'), number_format($refund_amount, 2))
|
sprintf(__('售后退款金额 ¥%s', 'aksrt'), number_format($refund_amount, 2))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Deduct sales_volume for refunded items
|
// Deduct sales_volume for refunded items
|
||||||
$meta = json_decode((string) ($order['note'] ?? ''), true);
|
$meta = json_decode((string) ($order['note'] ?? ''), true);
|
||||||
|
|||||||
@@ -0,0 +1,277 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* AKSRT cash wallet (站内余额) — separate from points/credits.
|
||||||
|
*
|
||||||
|
* Storage:
|
||||||
|
* - Balance: user_meta `aksrt_balance` (float, 2 decimals)
|
||||||
|
* - Ledger: `{prefix}aksrt_balance_log`
|
||||||
|
*
|
||||||
|
* Mirrors Zibll zibpay_get_user_balance / zibpay_update_user_balance.
|
||||||
|
*
|
||||||
|
* @package AKSRT
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('ABSPATH') || exit;
|
||||||
|
|
||||||
|
class AKSRT_Balance {
|
||||||
|
|
||||||
|
public const META_KEY = 'aksrt_balance';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current cash balance (yuan).
|
||||||
|
*/
|
||||||
|
public static function balance(int $user_id): float {
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
$val = get_user_meta($user_id, self::META_KEY, true);
|
||||||
|
return is_numeric($val) ? round((float) $val, 2) : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_balance(int $user_id): float {
|
||||||
|
return self::balance($user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Credit cash wallet.
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $ref
|
||||||
|
* @return float|WP_Error New balance.
|
||||||
|
*/
|
||||||
|
public static function add(int $user_id, float $delta, string $reason, array $ref = [], string $remark = '') {
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
return new WP_Error('aksrt_balance_user', __('用户无效', 'aksrt'));
|
||||||
|
}
|
||||||
|
$delta = round((float) $delta, 2);
|
||||||
|
if ($delta <= 0) {
|
||||||
|
return new WP_Error('aksrt_balance_delta', __('增加余额必须为正数', 'aksrt'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$new = self::atomic_update($user_id, $delta, false);
|
||||||
|
if ($new === null) {
|
||||||
|
return new WP_Error('aksrt_balance_db', __('余额写入失败', 'aksrt'));
|
||||||
|
}
|
||||||
|
self::log($user_id, $delta, $new, $reason, $ref, $remark);
|
||||||
|
do_action('aksrt_balance_added', $user_id, $delta, $reason, $new);
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debit cash wallet.
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $ref
|
||||||
|
* @return float|WP_Error New balance.
|
||||||
|
*/
|
||||||
|
public static function deduct(int $user_id, float $cost, string $reason, array $ref = [], string $remark = '') {
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
return new WP_Error('aksrt_balance_user', __('用户无效', 'aksrt'));
|
||||||
|
}
|
||||||
|
$cost = round((float) $cost, 2);
|
||||||
|
if ($cost < 0) {
|
||||||
|
return new WP_Error('aksrt_balance_delta', __('扣减余额必须为非负数', 'aksrt'));
|
||||||
|
}
|
||||||
|
if ($cost === 0.0) {
|
||||||
|
return self::balance($user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$new = self::atomic_update($user_id, $cost, true);
|
||||||
|
if ($new === null) {
|
||||||
|
$current = self::balance($user_id);
|
||||||
|
return new WP_Error('aksrt_balance_insufficient', __('余额不足', 'aksrt'), [
|
||||||
|
'balance' => $current,
|
||||||
|
'cost' => $cost,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
self::log($user_id, -$cost, $new, $reason, $ref, $remark);
|
||||||
|
do_action('aksrt_balance_deducted', $user_id, $cost, $reason, $new);
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return float|null
|
||||||
|
*/
|
||||||
|
private static function atomic_update(int $user_id, float $abs_delta, bool $is_deduct) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$abs_delta = round(abs($abs_delta), 2);
|
||||||
|
$meta_key = self::META_KEY;
|
||||||
|
$table = $wpdb->usermeta;
|
||||||
|
|
||||||
|
// Ensure meta row exists.
|
||||||
|
$exists = $wpdb->get_var($wpdb->prepare(
|
||||||
|
"SELECT umeta_id FROM {$table} WHERE user_id = %d AND meta_key = %s LIMIT 1",
|
||||||
|
$user_id,
|
||||||
|
$meta_key
|
||||||
|
));
|
||||||
|
if (!$exists) {
|
||||||
|
add_user_meta($user_id, $meta_key, '0', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($is_deduct) {
|
||||||
|
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||||
|
$updated = $wpdb->query($wpdb->prepare(
|
||||||
|
"UPDATE {$table}
|
||||||
|
SET meta_value = CAST(ROUND(CAST(meta_value AS DECIMAL(18,2)) - %f, 2) AS CHAR)
|
||||||
|
WHERE user_id = %d AND meta_key = %s
|
||||||
|
AND CAST(meta_value AS DECIMAL(18,2)) >= %f",
|
||||||
|
$abs_delta,
|
||||||
|
$user_id,
|
||||||
|
$meta_key,
|
||||||
|
$abs_delta
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||||
|
$updated = $wpdb->query($wpdb->prepare(
|
||||||
|
"UPDATE {$table}
|
||||||
|
SET meta_value = CAST(ROUND(CAST(meta_value AS DECIMAL(18,2)) + %f, 2) AS CHAR)
|
||||||
|
WHERE user_id = %d AND meta_key = %s",
|
||||||
|
$abs_delta,
|
||||||
|
$user_id,
|
||||||
|
$meta_key
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($updated !== 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_cache_delete($user_id, 'user_meta');
|
||||||
|
return self::balance($user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string,mixed> $ref
|
||||||
|
*/
|
||||||
|
public static function log(int $user_id, float $delta, float $balance_after, string $reason, array $ref = [], string $remark = ''): void {
|
||||||
|
global $wpdb;
|
||||||
|
if (!class_exists('AKSRT_DB_Migrator')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$table = AKSRT_DB_Migrator::table('balance_log');
|
||||||
|
if ($table === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ip = '';
|
||||||
|
if (!empty($_SERVER['REMOTE_ADDR'])) {
|
||||||
|
$ip = sanitize_text_field(wp_unslash((string) $_SERVER['REMOTE_ADDR']));
|
||||||
|
}
|
||||||
|
$ua = '';
|
||||||
|
if (!empty($_SERVER['HTTP_USER_AGENT'])) {
|
||||||
|
$ua = substr(sanitize_text_field(wp_unslash((string) $_SERVER['HTTP_USER_AGENT'])), 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
|
||||||
|
$wpdb->insert(
|
||||||
|
$table,
|
||||||
|
[
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'delta' => round($delta, 2),
|
||||||
|
'balance_after' => round($balance_after, 2),
|
||||||
|
'reason' => sanitize_key(substr($reason, 0, 64)),
|
||||||
|
'remark' => $remark !== '' ? sanitize_text_field(substr($remark, 0, 255)) : null,
|
||||||
|
'ref_type' => isset($ref['type']) ? sanitize_key((string) $ref['type']) : null,
|
||||||
|
'ref_id' => isset($ref['id']) ? (int) $ref['id'] : (isset($ref['ref_id']) ? (int) $ref['ref_id'] : null),
|
||||||
|
'ip' => $ip !== '' ? $ip : null,
|
||||||
|
'ua' => $ua !== '' ? $ua : null,
|
||||||
|
'created_at' => current_time('mysql', true),
|
||||||
|
],
|
||||||
|
['%d', '%f', '%f', '%s', '%s', '%s', '%d', '%s', '%s', '%s']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string,mixed> $args
|
||||||
|
* @return array{items:array<int,array<string,mixed>>,total:int}
|
||||||
|
*/
|
||||||
|
public static function get_log(array $args = []): array {
|
||||||
|
global $wpdb;
|
||||||
|
if (!class_exists('AKSRT_DB_Migrator')) {
|
||||||
|
return ['items' => [], 'total' => 0];
|
||||||
|
}
|
||||||
|
$table = AKSRT_DB_Migrator::table('balance_log');
|
||||||
|
if ($table === '') {
|
||||||
|
return ['items' => [], 'total' => 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$user_id = max(0, (int) ($args['user_id'] ?? 0));
|
||||||
|
$page = max(1, (int) ($args['page'] ?? 1));
|
||||||
|
$per_page = max(1, min(50, (int) ($args['per_page'] ?? 10)));
|
||||||
|
$offset = ($page - 1) * $per_page;
|
||||||
|
|
||||||
|
$where = '1=1';
|
||||||
|
$params = [];
|
||||||
|
if ($user_id > 0) {
|
||||||
|
$where .= ' AND user_id = %d';
|
||||||
|
$params[] = $user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||||
|
$total_sql = "SELECT COUNT(*) FROM {$table} WHERE {$where}";
|
||||||
|
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||||
|
$total = (int) ($params ? $wpdb->get_var($wpdb->prepare($total_sql, $params)) : $wpdb->get_var($total_sql));
|
||||||
|
|
||||||
|
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||||
|
$list_sql = "SELECT * FROM {$table} WHERE {$where} ORDER BY id DESC LIMIT %d OFFSET %d";
|
||||||
|
$list_params = array_merge($params, [$per_page, $offset]);
|
||||||
|
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||||
|
$rows = $wpdb->get_results($wpdb->prepare($list_sql, $list_params), ARRAY_A);
|
||||||
|
if (!is_array($rows)) {
|
||||||
|
$rows = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$items[] = [
|
||||||
|
'id' => (int) ($row['id'] ?? 0),
|
||||||
|
'user_id' => (int) ($row['user_id'] ?? 0),
|
||||||
|
'delta' => round((float) ($row['delta'] ?? 0), 2),
|
||||||
|
'balance_after' => round((float) ($row['balance_after'] ?? 0), 2),
|
||||||
|
'reason' => (string) ($row['reason'] ?? ''),
|
||||||
|
'remark' => (string) ($row['remark'] ?? ''),
|
||||||
|
'created_at' => (string) ($row['created_at'] ?? ''),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['items' => $items, 'total' => $total];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether cash wallet feature is enabled (Zibll pay_balance_s).
|
||||||
|
*/
|
||||||
|
public static function enabled(): bool {
|
||||||
|
if (!class_exists('AKSRT_Settings')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (method_exists('AKSRT_Settings', 'get_payment_recharge_settings')) {
|
||||||
|
$cfg = AKSRT_Settings::get_payment_recharge_settings();
|
||||||
|
return !empty($cfg['enabled']);
|
||||||
|
}
|
||||||
|
return !empty(AKSRT_Settings::get_all()['pay_balance_s']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format yuan for display.
|
||||||
|
*/
|
||||||
|
public static function format(float $amount): string {
|
||||||
|
return number_format_i18n(round($amount, 2), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reason label for ledger UI.
|
||||||
|
*/
|
||||||
|
public static function reason_label(string $reason): string {
|
||||||
|
$map = [
|
||||||
|
'card_pass_balance' => __('卡密充值', 'aksrt'),
|
||||||
|
'order_payment' => __('订单支付', 'aksrt'),
|
||||||
|
'order_refund' => __('订单退款', 'aksrt'),
|
||||||
|
'recharge' => __('余额充值', 'aksrt'),
|
||||||
|
'transfer_in' => __('转入', 'aksrt'),
|
||||||
|
'transfer_out' => __('转出', 'aksrt'),
|
||||||
|
'admin' => __('管理员调整', 'aksrt'),
|
||||||
|
'exchange_from_credits' => __('积分兑换', 'aksrt'),
|
||||||
|
];
|
||||||
|
$key = sanitize_key($reason);
|
||||||
|
return $map[$key] ?? ($reason !== '' ? $reason : __('余额变动', 'aksrt'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -419,21 +419,40 @@ class AKSRT_Charge_Card {
|
|||||||
*/
|
*/
|
||||||
private function fulfill(int $user_id, string $type, array $meta, string $order_number, int $card_id) {
|
private function fulfill(int $user_id, string $type, array $meta, string $order_number, int $card_id) {
|
||||||
if ($type === self::TYPE_BALANCE) {
|
if ($type === self::TYPE_BALANCE) {
|
||||||
$amount = (int) round((float) ($meta['amount'] ?? 0));
|
$amount = round((float) ($meta['amount'] ?? 0), 2);
|
||||||
if ($amount <= 0 || !class_exists('AKSRT_Credits')) {
|
if ($amount <= 0) {
|
||||||
return new WP_Error('aksrt_card_amount', __('当前卡密可充值金额为0', 'aksrt'), ['status' => 400]);
|
return new WP_Error('aksrt_card_amount', __('当前卡密可充值金额为0', 'aksrt'), ['status' => 400]);
|
||||||
}
|
}
|
||||||
$new = AKSRT_Credits::add($user_id, $amount, 'card_pass_balance', [
|
// Cash wallet first (Zibll pay_balance); fall back to points if wallet class missing.
|
||||||
|
if (class_exists('AKSRT_Balance')) {
|
||||||
|
$new = AKSRT_Balance::add($user_id, $amount, 'card_pass_balance', [
|
||||||
|
'type' => 'charge_card',
|
||||||
|
'id' => $card_id,
|
||||||
|
'order' => $order_number,
|
||||||
|
], sprintf(__('卡密充值 %s 元', 'aksrt'), (string) $amount));
|
||||||
|
if (is_wp_error($new)) {
|
||||||
|
return new WP_Error('aksrt_balance_fail', $new->get_error_message() ?: __('余额入账失败', 'aksrt'), ['status' => 500]);
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
'message' => sprintf(__('卡密充值成功,充值金额:%s 元', 'aksrt'), (string) $amount),
|
||||||
|
'amount' => (float) $amount,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (!class_exists('AKSRT_Credits')) {
|
||||||
|
return new WP_Error('aksrt_card_amount', __('余额系统不可用', 'aksrt'), ['status' => 500]);
|
||||||
|
}
|
||||||
|
$pts = (int) round($amount);
|
||||||
|
$new = AKSRT_Credits::add($user_id, $pts, 'card_pass_balance', [
|
||||||
'ref_type' => 'charge_card',
|
'ref_type' => 'charge_card',
|
||||||
'ref_id' => $card_id,
|
'ref_id' => $card_id,
|
||||||
'order' => $order_number,
|
'order' => $order_number,
|
||||||
], sprintf(__('卡密充值 %d', 'aksrt'), $amount));
|
], sprintf(__('卡密充值 %d', 'aksrt'), $pts));
|
||||||
if ($new === null || $new === false) {
|
if ($new === null || $new === false || is_wp_error($new)) {
|
||||||
return new WP_Error('aksrt_credit_fail', __('余额入账失败', 'aksrt'), ['status' => 500]);
|
return new WP_Error('aksrt_credit_fail', __('余额入账失败', 'aksrt'), ['status' => 500]);
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
'message' => sprintf(__('卡密充值成功,充值金额:%s', 'aksrt'), (string) $amount),
|
'message' => sprintf(__('卡密充值成功,充值金额:%s', 'aksrt'), (string) $pts),
|
||||||
'amount' => (float) $amount,
|
'amount' => (float) $pts,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class AKSRT_DB_Migrator {
|
|||||||
use AKSRT_DB_Migrator_Part_3;
|
use AKSRT_DB_Migrator_Part_3;
|
||||||
|
|
||||||
/** Current schema version; changing it re-runs dbDelta. */
|
/** Current schema version; changing it re-runs dbDelta. */
|
||||||
const SCHEMA_VERSION = '1.14.0';
|
const SCHEMA_VERSION = '1.15.0';
|
||||||
|
|
||||||
const OPT_KEY = 'aksrt_db_schema_version';
|
const OPT_KEY = 'aksrt_db_schema_version';
|
||||||
|
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ class AKSRT_Payment_Gateway {
|
|||||||
/**
|
/**
|
||||||
* 站内余额支付网关(内置实现)
|
* 站内余额支付网关(内置实现)
|
||||||
*
|
*
|
||||||
* 使用积分/余额进行支付,无需第三方。
|
* 优先扣现金钱包(AKSRT_Balance / pay_balance);未启用时回退到积分。
|
||||||
*/
|
*/
|
||||||
class AKSRT_Balance_Gateway implements AKSRT_Payment_Gateway_Interface {
|
class AKSRT_Balance_Gateway implements AKSRT_Payment_Gateway_Interface {
|
||||||
|
|
||||||
@@ -351,7 +351,9 @@ class AKSRT_Balance_Gateway implements AKSRT_Payment_Gateway_Interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function is_configured(): bool {
|
public function is_configured(): bool {
|
||||||
if (!class_exists('AKSRT_Credits')) {
|
$wallet_on = class_exists('AKSRT_Balance') && AKSRT_Balance::enabled();
|
||||||
|
$credits_on = class_exists('AKSRT_Credits');
|
||||||
|
if (!$wallet_on && !$credits_on) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (class_exists('AKSRT_Settings_Payment_Runtime')) {
|
if (class_exists('AKSRT_Settings_Payment_Runtime')) {
|
||||||
@@ -361,17 +363,43 @@ class AKSRT_Balance_Gateway implements AKSRT_Payment_Gateway_Interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function create_payment(array $order): array {
|
public function create_payment(array $order): array {
|
||||||
|
$user_id = (int) ($order['user_id'] ?? 0);
|
||||||
|
$amount = round((float) ($order['amount'] ?? 0), 2);
|
||||||
|
$order_number = (string) ($order['order_number'] ?? '');
|
||||||
|
|
||||||
|
// Prefer cash wallet when pay_balance_s is on.
|
||||||
|
if (class_exists('AKSRT_Balance') && AKSRT_Balance::enabled()) {
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
return ['success' => false, 'message' => __('请先登录', 'aksrt')];
|
||||||
|
}
|
||||||
|
$balance = AKSRT_Balance::balance($user_id);
|
||||||
|
if ($balance < $amount) {
|
||||||
|
return ['success' => false, 'message' => __('余额不足', 'aksrt')];
|
||||||
|
}
|
||||||
|
$result = AKSRT_Balance::deduct(
|
||||||
|
$user_id,
|
||||||
|
$amount,
|
||||||
|
'order_payment',
|
||||||
|
['type' => 'order', 'id' => 0, 'order' => $order_number],
|
||||||
|
sprintf(__('订单 %s 支付', 'aksrt'), $order_number)
|
||||||
|
);
|
||||||
|
if (is_wp_error($result)) {
|
||||||
|
return ['success' => false, 'message' => $result->get_error_message() ?: __('扣款失败', 'aksrt')];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'transaction_id' => 'BAL_' . ($order_number !== '' ? $order_number : (string) time()),
|
||||||
|
'message' => __('余额支付成功', 'aksrt'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: legacy points-as-balance (1 元 = 100 积分).
|
||||||
if (!class_exists('AKSRT_Credits')) {
|
if (!class_exists('AKSRT_Credits')) {
|
||||||
return ['success' => false, 'message' => __('积分系统未启用', 'aksrt')];
|
return ['success' => false, 'message' => __('积分系统未启用', 'aksrt')];
|
||||||
}
|
}
|
||||||
|
|
||||||
$user_id = (int) ($order['user_id'] ?? 0);
|
$credits_cost = (int) ceil($amount * 100);
|
||||||
$amount = (float) ($order['amount'] ?? 0);
|
|
||||||
$credits_cost = (int) ceil($amount * 100); // 1 元 = 100 积分
|
|
||||||
$order_number = (string) ($order['order_number'] ?? '');
|
|
||||||
|
|
||||||
$balance = AKSRT_Credits::balance($user_id);
|
$balance = AKSRT_Credits::balance($user_id);
|
||||||
|
|
||||||
if ($balance < $credits_cost) {
|
if ($balance < $credits_cost) {
|
||||||
return ['success' => false, 'message' => __('余额不足', 'aksrt')];
|
return ['success' => false, 'message' => __('余额不足', 'aksrt')];
|
||||||
}
|
}
|
||||||
@@ -416,10 +444,6 @@ class AKSRT_Balance_Gateway implements AKSRT_Payment_Gateway_Interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function refund(string $order_number, float $amount): array {
|
public function refund(string $order_number, float $amount): array {
|
||||||
if (!class_exists('AKSRT_Credits')) {
|
|
||||||
return ['success' => false, 'message' => __('积分系统未启用', 'aksrt')];
|
|
||||||
}
|
|
||||||
|
|
||||||
$order_repo = AKSRT_Order_Repository::get_instance();
|
$order_repo = AKSRT_Order_Repository::get_instance();
|
||||||
$order = $order_repo->get($order_number);
|
$order = $order_repo->get($order_number);
|
||||||
|
|
||||||
@@ -428,8 +452,27 @@ class AKSRT_Balance_Gateway implements AKSRT_Payment_Gateway_Interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$user_id = (int) $order['user_id'];
|
$user_id = (int) $order['user_id'];
|
||||||
$credits_refund = (int) ceil($amount * 100);
|
$amount = round((float) $amount, 2);
|
||||||
|
|
||||||
|
if (class_exists('AKSRT_Balance') && AKSRT_Balance::enabled()) {
|
||||||
|
$result = AKSRT_Balance::add(
|
||||||
|
$user_id,
|
||||||
|
$amount,
|
||||||
|
'order_refund',
|
||||||
|
['type' => 'order', 'order' => $order_number],
|
||||||
|
sprintf(__('订单 %s 退款', 'aksrt'), $order_number)
|
||||||
|
);
|
||||||
|
if (is_wp_error($result)) {
|
||||||
|
return ['success' => false, 'message' => $result->get_error_message() ?: __('退款失败', 'aksrt')];
|
||||||
|
}
|
||||||
|
return ['success' => true, 'refund_id' => 'BAL_REF_' . $order_number, 'message' => __('退款成功', 'aksrt')];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!class_exists('AKSRT_Credits')) {
|
||||||
|
return ['success' => false, 'message' => __('积分系统未启用', 'aksrt')];
|
||||||
|
}
|
||||||
|
|
||||||
|
$credits_refund = (int) ceil($amount * 100);
|
||||||
$result = AKSRT_Credits::add(
|
$result = AKSRT_Credits::add(
|
||||||
$user_id,
|
$user_id,
|
||||||
$credits_refund,
|
$credits_refund,
|
||||||
|
|||||||
@@ -92,15 +92,35 @@ class AKSRT_User_Center {
|
|||||||
$this->panels = [];
|
$this->panels = [];
|
||||||
|
|
||||||
foreach ((array) ($settings['tabs'] ?? []) as $key) {
|
foreach ((array) ($settings['tabs'] ?? []) as $key) {
|
||||||
if ($key === 'credits' && (!class_exists('AKSRT_Settings') || !AKSRT_Settings::credits_enabled())) {
|
if ($key === 'credits') {
|
||||||
|
$credits_on = class_exists('AKSRT_Settings') && AKSRT_Settings::credits_enabled();
|
||||||
|
$balance_on = class_exists('AKSRT_Balance') && AKSRT_Balance::enabled();
|
||||||
|
if (!$credits_on && !$balance_on) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if ($balance_on) {
|
||||||
|
$definitions['credits']['label'] = __('我的资产', 'aksrt');
|
||||||
|
$definitions['credits']['icon'] = 'wallet';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($definitions[$key])) {
|
if (isset($definitions[$key])) {
|
||||||
$this->panels[$key] = $definitions[$key];
|
$this->panels[$key] = $definitions[$key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure assets panel when wallet and/or credits enabled (even if tabs list omitted credits).
|
||||||
|
$credits_on = class_exists('AKSRT_Settings') && AKSRT_Settings::credits_enabled();
|
||||||
|
$balance_on = class_exists('AKSRT_Balance') && AKSRT_Balance::enabled();
|
||||||
|
if (!isset($this->panels['credits']) && ($credits_on || $balance_on)) {
|
||||||
|
$panel = $definitions['credits'];
|
||||||
|
if ($balance_on) {
|
||||||
|
$panel['label'] = __('我的资产', 'aksrt');
|
||||||
|
$panel['icon'] = 'wallet';
|
||||||
|
}
|
||||||
|
$this->panels['credits'] = $panel;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($this->panels['settings'])) {
|
if (!isset($this->panels['settings'])) {
|
||||||
$this->panels['settings'] = $definitions['settings'];
|
$this->panels['settings'] = $definitions['settings'];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,25 @@ trait AKSRT_DB_Migrator_Part_2 {
|
|||||||
) {$charset_collate};";
|
) {$charset_collate};";
|
||||||
dbDelta($sql_credit_log);
|
dbDelta($sql_credit_log);
|
||||||
|
|
||||||
|
// ---------- 1b. 现金余额流水(站内钱包,与积分分离) ----------
|
||||||
|
$sql_balance_log = "CREATE TABLE {$prefix}aksrt_balance_log (
|
||||||
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
user_id BIGINT UNSIGNED NOT NULL,
|
||||||
|
delta DECIMAL(18,2) NOT NULL,
|
||||||
|
balance_after DECIMAL(18,2) NOT NULL,
|
||||||
|
reason VARCHAR(64) NOT NULL,
|
||||||
|
remark VARCHAR(255) DEFAULT NULL,
|
||||||
|
ref_type VARCHAR(32) DEFAULT NULL,
|
||||||
|
ref_id BIGINT UNSIGNED DEFAULT NULL,
|
||||||
|
ip VARCHAR(64) DEFAULT NULL,
|
||||||
|
ua VARCHAR(255) DEFAULT NULL,
|
||||||
|
created_at DATETIME NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
KEY idx_user_time (user_id, created_at),
|
||||||
|
KEY idx_reason (reason)
|
||||||
|
) {$charset_collate};";
|
||||||
|
dbDelta($sql_balance_log);
|
||||||
|
|
||||||
// ---------- 2. 签到 ----------
|
// ---------- 2. 签到 ----------
|
||||||
$sql_signin = "CREATE TABLE {$prefix}aksrt_signin_log (
|
$sql_signin = "CREATE TABLE {$prefix}aksrt_signin_log (
|
||||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ trait AKSRT_DB_Migrator_Part_3 {
|
|||||||
global $wpdb;
|
global $wpdb;
|
||||||
$allowed = [
|
$allowed = [
|
||||||
'credit_log' => 'aksrt_credit_log',
|
'credit_log' => 'aksrt_credit_log',
|
||||||
|
'balance_log' => 'aksrt_balance_log',
|
||||||
'signin_log' => 'aksrt_signin_log',
|
'signin_log' => 'aksrt_signin_log',
|
||||||
'invite' => 'aksrt_invite',
|
'invite' => 'aksrt_invite',
|
||||||
'dl_token' => 'aksrt_dl_token',
|
'dl_token' => 'aksrt_dl_token',
|
||||||
|
|||||||
@@ -432,6 +432,8 @@ if (!function_exists('aksrt_get_user_center_render_context')) {
|
|||||||
$affiliate_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_payment_affiliate_settings() : [];
|
$affiliate_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_payment_affiliate_settings() : [];
|
||||||
$show_credit_tabs = (bool) ($dashboard['credits_enabled'] ?? false);
|
$show_credit_tabs = (bool) ($dashboard['credits_enabled'] ?? false);
|
||||||
$currency_name = (string) ($dashboard['currency_name'] ?? __('积分', 'aksrt'));
|
$currency_name = (string) ($dashboard['currency_name'] ?? __('积分', 'aksrt'));
|
||||||
|
$balance_enabled = (bool) ($dashboard['balance_enabled'] ?? false);
|
||||||
|
$wallet_balance = (float) ($dashboard['wallet_balance'] ?? 0.0);
|
||||||
|
|
||||||
$render_context = [
|
$render_context = [
|
||||||
'user_center_settings' => $user_center_settings,
|
'user_center_settings' => $user_center_settings,
|
||||||
@@ -470,6 +472,8 @@ if (!function_exists('aksrt_get_user_center_render_context')) {
|
|||||||
'currency_name' => $currency_name,
|
'currency_name' => $currency_name,
|
||||||
'credit_balance' => (float) ($dashboard['credit_balance'] ?? 0.0),
|
'credit_balance' => (float) ($dashboard['credit_balance'] ?? 0.0),
|
||||||
'credits_enabled' => $show_credit_tabs,
|
'credits_enabled' => $show_credit_tabs,
|
||||||
|
'balance_enabled' => $balance_enabled,
|
||||||
|
'wallet_balance' => $wallet_balance,
|
||||||
'vip_settings' => $vip_settings,
|
'vip_settings' => $vip_settings,
|
||||||
'show_invite_section' => $show_credit_tabs
|
'show_invite_section' => $show_credit_tabs
|
||||||
&& class_exists('AKSRT_Settings')
|
&& class_exists('AKSRT_Settings')
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ function aksrt_get_user_dashboard_view(int $user_id, array $args = []): array {
|
|||||||
? AKSRT_Settings::get_currency_name()
|
? AKSRT_Settings::get_currency_name()
|
||||||
: __('积分', 'aksrt');
|
: __('积分', 'aksrt');
|
||||||
|
|
||||||
|
// Cash wallet (Zibll pay_balance) — separate from points.
|
||||||
|
$balance_enabled = $is_own_profile && class_exists('AKSRT_Balance') && AKSRT_Balance::enabled();
|
||||||
|
$wallet_balance = $balance_enabled ? AKSRT_Balance::balance($user_id) : 0.0;
|
||||||
|
|
||||||
$user_home_manager = $include_governance && class_exists('AKSRT_User_Home')
|
$user_home_manager = $include_governance && class_exists('AKSRT_User_Home')
|
||||||
? AKSRT_User_Home::get_instance()
|
? AKSRT_User_Home::get_instance()
|
||||||
: null;
|
: null;
|
||||||
@@ -103,6 +107,8 @@ function aksrt_get_user_dashboard_view(int $user_id, array $args = []): array {
|
|||||||
'credit_balance' => $credit_balance,
|
'credit_balance' => $credit_balance,
|
||||||
'currency_name' => $currency_name,
|
'currency_name' => $currency_name,
|
||||||
'credits_enabled' => $credits_enabled,
|
'credits_enabled' => $credits_enabled,
|
||||||
|
'balance_enabled' => $balance_enabled,
|
||||||
|
'wallet_balance' => $wallet_balance,
|
||||||
'is_own_profile' => $is_own_profile,
|
'is_own_profile' => $is_own_profile,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* User center credits: balance card + sign-in controls + card-pass redeem.
|
* User center credits: points card + sign-in controls + credits card-pass redeem.
|
||||||
*
|
*
|
||||||
* Expects extracted $context vars.
|
* Cash wallet UI lives in credits-wallet.php.
|
||||||
*
|
*
|
||||||
* @package AKSRT
|
* @package AKSRT
|
||||||
*/
|
*/
|
||||||
@@ -11,34 +11,26 @@ defined('ABSPATH') || exit;
|
|||||||
|
|
||||||
$balance = (float) ($credit_balance ?? 0.0);
|
$balance = (float) ($credit_balance ?? 0.0);
|
||||||
$credits_enabled = !empty($credits_enabled);
|
$credits_enabled = !empty($credits_enabled);
|
||||||
|
if (!$credits_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$card_pass_balance = ['enabled' => false, 'desc' => '', 'only_password' => true];
|
|
||||||
$card_pass_credits = ['enabled' => false, 'desc' => '', 'only_password' => true];
|
$card_pass_credits = ['enabled' => false, 'desc' => '', 'only_password' => true];
|
||||||
if (class_exists('AKSRT_Charge_Card')) {
|
if (class_exists('AKSRT_Charge_Card')) {
|
||||||
$opts = AKSRT_Charge_Card::get_instance()->get_public_options();
|
$opts = AKSRT_Charge_Card::get_instance()->get_public_options();
|
||||||
$card_pass_balance = is_array($opts['balance'] ?? null) ? $opts['balance'] : $card_pass_balance;
|
|
||||||
$card_pass_credits = is_array($opts['credits'] ?? null) ? $opts['credits'] : $card_pass_credits;
|
$card_pass_credits = is_array($opts['credits'] ?? null) ? $opts['credits'] : $card_pass_credits;
|
||||||
} elseif (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_payment_recharge_settings')) {
|
|
||||||
$recharge = AKSRT_Settings::get_payment_recharge_settings();
|
|
||||||
$pc = is_array($recharge['pass_charge'] ?? null) ? $recharge['pass_charge'] : [];
|
|
||||||
$card_pass_balance = [
|
|
||||||
'enabled' => !empty($pc['enabled']),
|
|
||||||
'desc' => (string) ($pc['desc'] ?? ''),
|
|
||||||
'only_password' => !isset($pc['only_password']) || !empty($pc['only_password']),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<div class="uc-balance-card">
|
<div class="uc-balance-card">
|
||||||
<div class="uc-balance-card__bg"></div>
|
<div class="uc-balance-card__bg"></div>
|
||||||
<div class="uc-balance-card__content">
|
<div class="uc-balance-card__content">
|
||||||
<div class="uc-balance-card__header">
|
<div class="uc-balance-card__header">
|
||||||
<span class="uc-balance-card__label"><?php esc_html_e('账户余额', 'aksrt'); ?></span>
|
<span class="uc-balance-card__label"><?php echo esc_html(sprintf(__('%s余额', 'aksrt'), $currency_name)); ?></span>
|
||||||
<span class="uc-balance-card__currency"><?php echo esc_html($currency_name); ?></span>
|
<span class="uc-balance-card__currency"><?php echo esc_html($currency_name); ?></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="uc-balance-card__amount">
|
<div class="uc-balance-card__amount">
|
||||||
<span class="uc-balance-card__value"><?php echo esc_html(aksrt_format_credits($balance)); ?></span>
|
<span class="uc-balance-card__value"><?php echo esc_html(aksrt_format_credits((int) $balance)); ?></span>
|
||||||
</div>
|
</div>
|
||||||
<?php if ($credits_enabled) : ?>
|
|
||||||
<div class="uc-balance-card__actions" data-aksrt-signin>
|
<div class="uc-balance-card__actions" data-aksrt-signin>
|
||||||
<span class="uc-balance-card__sign-status" data-signin-status></span>
|
<span class="uc-balance-card__sign-status" data-signin-status></span>
|
||||||
<button type="button" class="uc-balance-card__sign-btn" data-signin-btn disabled>
|
<button type="button" class="uc-balance-card__sign-btn" data-signin-btn disabled>
|
||||||
@@ -47,30 +39,9 @@ if (class_exists('AKSRT_Charge_Card')) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="uc-balance-card__tip" data-signin-tip></p>
|
<p class="uc-balance-card__tip" data-signin-tip></p>
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if (!empty($card_pass_balance['enabled'])) : ?>
|
|
||||||
<div class="uc-card-pass" data-aksrt-card-pass="balance">
|
|
||||||
<div class="uc-card-pass__title">
|
|
||||||
<i class="fas fa-ticket-alt" aria-hidden="true"></i>
|
|
||||||
<span><?php esc_html_e('卡密兑换余额', 'aksrt'); ?></span>
|
|
||||||
</div>
|
|
||||||
<?php if (!empty($card_pass_balance['desc'])) : ?>
|
|
||||||
<p class="uc-card-pass__desc"><?php echo esc_html((string) $card_pass_balance['desc']); ?></p>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (empty($card_pass_balance['only_password'])) : ?>
|
|
||||||
<input type="text" class="uc-card-pass__input" name="aksrt_card_no" data-aksrt-card-no autocomplete="off" placeholder="<?php esc_attr_e('请输入卡号', 'aksrt'); ?>">
|
|
||||||
<?php endif; ?>
|
|
||||||
<input type="text" class="uc-card-pass__input" name="aksrt_card_password" data-aksrt-card-password autocomplete="off" placeholder="<?php esc_attr_e('请输入卡密', 'aksrt'); ?>">
|
|
||||||
<button type="button" class="uc-card-pass__btn" data-aksrt-card-redeem data-type="balance">
|
|
||||||
<?php esc_html_e('兑换余额', 'aksrt'); ?>
|
|
||||||
</button>
|
|
||||||
<p class="uc-card-pass__tip" data-aksrt-card-tip></p>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php if (!empty($card_pass_credits['enabled'])) : ?>
|
<?php if (!empty($card_pass_credits['enabled'])) : ?>
|
||||||
<div class="uc-card-pass" data-aksrt-card-pass="credits">
|
<div class="uc-card-pass" data-aksrt-card-pass="credits">
|
||||||
<div class="uc-card-pass__title">
|
<div class="uc-card-pass__title">
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* User center: cash wallet (站内余额) card + ledger.
|
||||||
|
*
|
||||||
|
* Expects extracted $context vars: $user_id, $wallet_balance, $balance_enabled, $card pass opts.
|
||||||
|
*
|
||||||
|
* @package AKSRT
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('ABSPATH') || exit;
|
||||||
|
|
||||||
|
if (empty($balance_enabled)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$wallet = (float) ($wallet_balance ?? 0.0);
|
||||||
|
|
||||||
|
$card_pass_balance = ['enabled' => false, 'desc' => '', 'only_password' => true];
|
||||||
|
if (class_exists('AKSRT_Charge_Card')) {
|
||||||
|
$opts = AKSRT_Charge_Card::get_instance()->get_public_options();
|
||||||
|
$card_pass_balance = is_array($opts['balance'] ?? null) ? $opts['balance'] : $card_pass_balance;
|
||||||
|
} elseif (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_payment_recharge_settings')) {
|
||||||
|
$recharge = AKSRT_Settings::get_payment_recharge_settings();
|
||||||
|
$pc = is_array($recharge['pass_charge'] ?? null) ? $recharge['pass_charge'] : [];
|
||||||
|
$card_pass_balance = [
|
||||||
|
'enabled' => !empty($pc['enabled']),
|
||||||
|
'desc' => (string) ($pc['desc'] ?? ''),
|
||||||
|
'only_password' => !isset($pc['only_password']) || !empty($pc['only_password']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="uc-wallet-grid">
|
||||||
|
<div class="uc-balance-card uc-balance-card--wallet">
|
||||||
|
<div class="uc-balance-card__bg uc-balance-card__bg--wallet"></div>
|
||||||
|
<div class="uc-balance-card__content">
|
||||||
|
<div class="uc-balance-card__header">
|
||||||
|
<span class="uc-balance-card__label"><?php esc_html_e('钱包余额', 'aksrt'); ?></span>
|
||||||
|
<span class="uc-balance-card__currency"><?php esc_html_e('元', 'aksrt'); ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="uc-balance-card__amount">
|
||||||
|
<span class="uc-balance-card__value">
|
||||||
|
<?php
|
||||||
|
echo esc_html(
|
||||||
|
class_exists('AKSRT_Balance')
|
||||||
|
? AKSRT_Balance::format($wallet)
|
||||||
|
: number_format_i18n($wallet, 2)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p class="uc-balance-card__tip"><?php esc_html_e('可用于站内余额支付、卡密充值入账', 'aksrt'); ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!empty($card_pass_balance['enabled'])) : ?>
|
||||||
|
<div class="uc-card-pass" data-aksrt-card-pass="balance">
|
||||||
|
<div class="uc-card-pass__title">
|
||||||
|
<i class="fas fa-ticket-alt" aria-hidden="true"></i>
|
||||||
|
<span><?php esc_html_e('卡密兑换余额', 'aksrt'); ?></span>
|
||||||
|
</div>
|
||||||
|
<?php if (!empty($card_pass_balance['desc'])) : ?>
|
||||||
|
<p class="uc-card-pass__desc"><?php echo esc_html((string) $card_pass_balance['desc']); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (empty($card_pass_balance['only_password'])) : ?>
|
||||||
|
<input type="text" class="uc-card-pass__input" name="aksrt_card_no" data-aksrt-card-no autocomplete="off" placeholder="<?php esc_attr_e('请输入卡号', 'aksrt'); ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
<input type="text" class="uc-card-pass__input" name="aksrt_card_password" data-aksrt-card-password autocomplete="off" placeholder="<?php esc_attr_e('请输入卡密', 'aksrt'); ?>">
|
||||||
|
<button type="button" class="uc-card-pass__btn" data-aksrt-card-redeem data-type="balance">
|
||||||
|
<?php esc_html_e('兑换余额', 'aksrt'); ?>
|
||||||
|
</button>
|
||||||
|
<p class="uc-card-pass__tip" data-aksrt-card-tip></p>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="uc-transactions uc-transactions--wallet">
|
||||||
|
<div class="uc-transactions__header">
|
||||||
|
<h3 class="uc-transactions__title">
|
||||||
|
<i class="fas fa-list-ul" aria-hidden="true"></i>
|
||||||
|
<?php esc_html_e('余额明细', 'aksrt'); ?>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
$wallet_page = max(1, isset($_GET['wallet_page']) ? (int) $_GET['wallet_page'] : 1);
|
||||||
|
if (class_exists('AKSRT_Balance')) {
|
||||||
|
$wlog = AKSRT_Balance::get_log(['user_id' => (int) $user_id, 'page' => $wallet_page, 'per_page' => 10]);
|
||||||
|
} else {
|
||||||
|
$wlog = ['items' => [], 'total' => 0];
|
||||||
|
}
|
||||||
|
if (!empty($wlog['items'])) :
|
||||||
|
?>
|
||||||
|
<div class="uc-transactions__list">
|
||||||
|
<?php foreach ($wlog['items'] as $row) :
|
||||||
|
$delta = (float) $row['delta'];
|
||||||
|
$delta_class = $delta >= 0 ? 'is-positive' : 'is-negative';
|
||||||
|
$delta_txt = ($delta > 0 ? '+' : '') . (class_exists('AKSRT_Balance') ? AKSRT_Balance::format($delta) : number_format_i18n($delta, 2));
|
||||||
|
?>
|
||||||
|
<div class="uc-transactions__item">
|
||||||
|
<div class="uc-transactions__icon <?php echo esc_attr($delta_class); ?>">
|
||||||
|
<i class="fas fa-<?php echo $delta >= 0 ? 'arrow-up' : 'arrow-down'; ?>" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
<div class="uc-transactions__info">
|
||||||
|
<div class="uc-transactions__reason">
|
||||||
|
<?php
|
||||||
|
echo esc_html(
|
||||||
|
class_exists('AKSRT_Balance')
|
||||||
|
? AKSRT_Balance::reason_label((string) $row['reason'])
|
||||||
|
: (string) $row['reason']
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
<?php if (!empty($row['remark'])) : ?>
|
||||||
|
<span class="uc-transactions__remark">(<?php echo esc_html((string) $row['remark']); ?>)</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<div class="uc-transactions__time">
|
||||||
|
<?php echo esc_html(get_date_from_gmt((string) $row['created_at'], 'Y-m-d H:i')); ?>
|
||||||
|
</div>
|
||||||
|
</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(
|
||||||
|
/* translators: %s: wallet balance */
|
||||||
|
esc_html__('余额 %s', 'aksrt'),
|
||||||
|
esc_html(
|
||||||
|
class_exists('AKSRT_Balance')
|
||||||
|
? AKSRT_Balance::format((float) $row['balance_after'])
|
||||||
|
: number_format_i18n((float) $row['balance_after'], 2)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
$total_pages = (int) ceil((int) ($wlog['total'] ?? 0) / 10);
|
||||||
|
if ($total_pages > 1 && function_exists('aksrt_render_profile_pagination')) {
|
||||||
|
aksrt_render_profile_pagination($total_pages, $wallet_page, 'wallet_page');
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<?php else : ?>
|
||||||
|
<div class="uc-transactions__empty">
|
||||||
|
<div class="uc-transactions__empty-icon">
|
||||||
|
<i class="fas fa-wallet" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
<p><?php esc_html_e('还没有任何余额流水', 'aksrt'); ?></p>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* User center profile credits part (orchestrator).
|
* User center credits panel orchestrator.
|
||||||
|
*
|
||||||
|
* Zibll-like dual assets: cash wallet + points, when each is enabled.
|
||||||
*
|
*
|
||||||
* @package AKSRT
|
* @package AKSRT
|
||||||
*/
|
*/
|
||||||
@@ -11,7 +13,24 @@ $context = is_array($args['context'] ?? null) ? $args['context'] : [];
|
|||||||
extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
|
extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
|
||||||
|
|
||||||
$dir = __DIR__;
|
$dir = __DIR__;
|
||||||
|
|
||||||
|
$show_wallet = !empty($balance_enabled);
|
||||||
|
$show_credits = !empty($credits_enabled);
|
||||||
|
|
||||||
|
if ($show_wallet) {
|
||||||
|
require $dir . '/credits-wallet.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Points block (existing UI) — only when credits system on.
|
||||||
|
if ($show_credits) {
|
||||||
|
// Rename label in balance card is handled inside credits-balance (账户余额 → 积分).
|
||||||
require $dir . '/credits-balance.php';
|
require $dir . '/credits-balance.php';
|
||||||
require $dir . '/credits-vip.php';
|
require $dir . '/credits-vip.php';
|
||||||
require $dir . '/credits-signin.php';
|
require $dir . '/credits-signin.php';
|
||||||
require $dir . '/credits-transactions.php';
|
require $dir . '/credits-transactions.php';
|
||||||
|
} elseif ($show_wallet) {
|
||||||
|
// Wallet-only: still show VIP block if enabled.
|
||||||
|
if (!empty($show_vip)) {
|
||||||
|
require $dir . '/credits-vip.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
defined('ABSPATH') || exit;
|
defined('ABSPATH') || exit;
|
||||||
|
|
||||||
if (empty($orders_url) && empty($show_vip) && empty($show_credit_tabs)) {
|
if (empty($orders_url) && empty($show_vip) && empty($show_credit_tabs) && empty($balance_enabled)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|||||||
Reference in New Issue
Block a user