feat(payment): 通用卡密兑换弹窗并修正核销订单实付与订单名称

- /user/balance/ 余额卡片入口改为通用「卡密兑换」弹窗:请求不带类型,
  后端按卡密自动识别余额/会员/积分;卡号输入恒展示且选填
- 后台「余额充值 → 卡密充值」语义更新为「卡密兑换」,开关更名
  「启用卡密兑换功能」(设置键不变),双源 schema 同步并重建 SPA
- 删除死代码:credits-vip.php 死文件、credits-balance.php 内联积分
  兑换块、/pay/card-pass/options 路由与 rest_options、labels 数组、
  asset_points_card_description/password_only 死字段及内联块专属 CSS
- 卡密核销订单实付恒 0:create_redeem_order 源头写 0,UserOrders
  对 card_pass 订单视图层归零覆盖历史行(不迁移数据)
- 订单名称按 note.meta 生成:卡密兑换-余额充值X / 卡密兑换-积分充值X /
  购入[会员档位名]X天(永久)
- 新增契约 card-pass-universal-redeem.test.cjs,收紧 user-assets-layout
  契约(禁 .uc-card-pass 根类回潮)
This commit is contained in:
2026-09-17 19:04:47 +08:00
parent ba8d4b1b90
commit 3481620ed0
14 changed files with 213 additions and 415 deletions
+13 -38
View File
@@ -42,11 +42,6 @@ class ChargeCard {
}
public function register_public_routes(): void {
register_rest_route('aksrt/v1', '/pay/card-pass/options', [
'methods' => 'GET',
'callback' => [$this, 'rest_options'],
'permission_callback' => [$this, 'check_rest_nonce'],
]);
register_rest_route('aksrt/v1', '/pay/card-pass/redeem', [
'methods' => 'POST',
'callback' => [$this, 'rest_redeem'],
@@ -89,13 +84,6 @@ class ChargeCard {
return true;
}
public function rest_options(\WP_REST_Request $request) {
return new \WP_REST_Response([
'success' => true,
'data' => $this->get_public_options(),
], 200);
}
public function rest_redeem(\WP_REST_Request $request) {
$params = $request->get_json_params();
if (!is_array($params)) {
@@ -124,20 +112,14 @@ class ChargeCard {
}
/**
* 前台卡密兑换开关视图(按类型拆分)。
*
* balance.enabled 语义为「启用卡密兑换功能」总开关(后台 余额充值 → 卡密兑换),
* 与收款接口 card_pass 主开关共同决定前台兑换入口是否展示。
*
* @return array<string,mixed>
*/
public function get_public_options(): array {
$labels = [
'title' => __('卡密兑换', 'aksrt'),
'password' => __('卡密', 'aksrt'),
'card' => __('卡号', 'aksrt'),
'submit_balance' => __('兑换余额', 'aksrt'),
'submit_vip' => __('兑换会员', 'aksrt'),
'submit_credits' => __('兑换积分', 'aksrt'),
'placeholder_pw' => __('请输入卡密', 'aksrt'),
'placeholder_card' => __('请输入卡号', 'aksrt'),
];
// Master kill-switch on 收款接口 → 卡密充值.
$channel_on = !class_exists('\AKSRT\Settings\PaymentRuntime')
|| \AKSRT\Settings\PaymentRuntime::is_gateway_enabled('card_pass');
@@ -146,8 +128,7 @@ class ChargeCard {
return [
'balance' => $off,
'vip' => $off,
'credits' => $off,
'labels' => $labels,
'credits' => ['enabled' => false],
];
}
@@ -175,17 +156,15 @@ class ChargeCard {
}
}
$credits = ['enabled' => false, 'desc' => '', 'only_password' => true];
$credits = ['enabled' => false];
$all = class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_all')
? \AKSRT\Settings\Settings::get_all()
: (array) get_option('aksrt_settings', []);
if (!empty($all['asset_points_card_enabled'])) {
// Require credits system on when available.
$credits_on = !class_exists('\AKSRT\Settings\Settings') || !method_exists('\AKSRT\Settings\Settings', 'credits_enabled') || \AKSRT\Settings\Settings::credits_enabled();
// 积分卡仅作为后端类型闸门(assert_type_enabled);前台兑换统一走
// 通用「卡密兑换」弹窗,无独立入口与说明/单密码配置。
$credits = [
'enabled' => $credits_on,
'desc' => sanitize_textarea_field((string) ($all['asset_points_card_description'] ?? '')),
'only_password' => !isset($all['asset_points_card_password_only']) || !empty($all['asset_points_card_password_only']),
'enabled' => !class_exists('\AKSRT\Settings\Settings') || !method_exists('\AKSRT\Settings\Settings', 'credits_enabled') || \AKSRT\Settings\Settings::credits_enabled(),
];
}
@@ -193,7 +172,6 @@ class ChargeCard {
'balance' => $balance,
'vip' => $vip,
'credits' => $credits,
'labels' => $labels,
];
}
@@ -808,6 +786,8 @@ class ChargeCard {
return '';
}
// 卡密核销不发生真实支付(现金在售卡时已确认),订单金额恒为 0;
// 卡密面额/时长记录在 note.meta,订单名称由 UserOrders 按 note 生成。
$product_type = \AKSRT\Core\DomainModel::ORDER_PRODUCT_RECHARGE;
$product_id = $card_id;
$amount = '0.00';
@@ -815,14 +795,9 @@ class ChargeCard {
if ($type === self::TYPE_VIP) {
$product_type = \AKSRT\Core\DomainModel::ORDER_PRODUCT_VIP;
$amount = '0.00';
} elseif ($type === self::TYPE_BALANCE) {
$parsed_amount = \AKSRT\Ledger\AssetAmount::balance(is_string($meta['amount'] ?? null) ? $meta['amount'] : '');
$amount = $parsed_amount->decimal();
$currency = 'CNY';
} elseif ($type === self::TYPE_CREDITS) {
$product_type = \AKSRT\Core\DomainModel::ORDER_PRODUCT_POINTS;
$amount = (int) ($meta['points'] ?? 0);
$amount = '0';
$currency = 'points';
}