Files
wordpress-theme-aksrt/inc/classes/admin/native/class-aksrt-admin-pay-menus.php
T
kate522 ebe3e188e1 feat(admin): add native 支付中心 menu with orders/withdraw/cards (Phase 2a)
Introduce AKSRT_Admin_Pay_Menus as a zibll-style top-level WP admin shell
for payment ops. Overview, orders, withdrawals, and charge cards are
usable PHP list pages reusing Order_Repository / Withdrawal / Charge_Card.
Shipping, after-sale, coupons, income, and rebate are stubbed with SPA
fallback until Phase 2b.
2026-07-15 01:48:53 +08:00

723 lines
30 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Native WP admin menus for payment / order ops (zibll zibpay-style).
*
* Config stays in the AKSRT SPA; this shell owns operational list pages.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
class AKSRT_Admin_Pay_Menus {
public const CAPABILITY = 'manage_options';
public const MENU_SLUG = 'aksrt_pay';
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function init(): void {
if (!is_admin()) {
return;
}
add_action('admin_menu', [$this, 'register_menus']);
add_action('admin_notices', [$this, 'pending_notices']);
add_action('admin_post_aksrt_pay_update_order_status', [$this, 'handle_update_order_status']);
add_action('admin_post_aksrt_pay_update_withdrawal', [$this, 'handle_update_withdrawal']);
add_action('admin_post_aksrt_pay_disable_charge_card', [$this, 'handle_disable_charge_card']);
}
/**
* @return array<string, array{slug:string,title:string,callback:callable}>
*/
private function pages(): array {
return [
'overview' => [
'slug' => self::MENU_SLUG,
'title' => __('概览', 'aksrt'),
'callback' => [$this, 'render_overview_page'],
],
'orders' => [
'slug' => 'aksrt_orders',
'title' => __('订单', 'aksrt'),
'callback' => [$this, 'render_orders_page'],
],
'shipping' => [
'slug' => 'aksrt_shipping',
'title' => __('发货及物流', 'aksrt'),
'callback' => [$this, 'render_stub_page'],
],
'after_sale' => [
'slug' => 'aksrt_after_sale',
'title' => __('售后管理', 'aksrt'),
'callback' => [$this, 'render_stub_page'],
],
'coupons' => [
'slug' => 'aksrt_coupons',
'title' => __('优惠码', 'aksrt'),
'callback' => [$this, 'render_stub_page'],
],
'charge_cards' => [
'slug' => 'aksrt_charge_cards',
'title' => __('卡密管理', 'aksrt'),
'callback' => [$this, 'render_charge_cards_page'],
],
'income' => [
'slug' => 'aksrt_income',
'title' => __('创作分成', 'aksrt'),
'callback' => [$this, 'render_stub_page'],
],
'rebate' => [
'slug' => 'aksrt_rebate',
'title' => __('推广返佣', 'aksrt'),
'callback' => [$this, 'render_stub_page'],
],
'withdraw' => [
'slug' => 'aksrt_withdraw',
'title' => __('提现审核', 'aksrt'),
'callback' => [$this, 'render_withdrawals_page'],
],
];
}
public function register_menus(): void {
if (!current_user_can(self::CAPABILITY)) {
return;
}
add_menu_page(
__('支付中心', 'aksrt'),
__('支付中心', 'aksrt'),
self::CAPABILITY,
self::MENU_SLUG,
[$this, 'render_overview_page'],
'dashicons-cart',
36
);
foreach ($this->pages() as $key => $page) {
$parent = self::MENU_SLUG;
// First item reuses top-level slug (WP pattern).
add_submenu_page(
$parent,
$page['title'],
$page['title'],
self::CAPABILITY,
$page['slug'],
$page['callback']
);
}
}
public function pending_notices(): void {
if (!current_user_can(self::CAPABILITY)) {
return;
}
$page = isset($_GET['page']) ? sanitize_key((string) wp_unslash($_GET['page'])) : '';
if ($page === 'aksrt_withdraw') {
return;
}
if (!class_exists('AKSRT_Withdrawal')) {
return;
}
$pending = (int) AKSRT_Withdrawal::get_instance()->get_count(['status' => AKSRT_Withdrawal::STATUS_PENDING]);
if ($pending <= 0) {
return;
}
$url = admin_url('admin.php?page=aksrt_withdraw&status=pending');
echo '<div class="notice notice-info is-dismissible">';
echo '<h3>' . esc_html__('提现申请待处理', 'aksrt') . '</h3>';
echo '<p>' . esc_html(sprintf(
/* translators: %d: pending withdrawal count */
__('您有 %d 个提现申请待处理', 'aksrt'),
$pending
)) . '</p>';
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a></p>';
echo '</div>';
}
/* ------------------------------------------------------------------
* Page shells
* ----------------------------------------------------------------*/
private function ensure_cap(): void {
if (!current_user_can(self::CAPABILITY)) {
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
}
}
private function render_header(string $title): void {
echo '<div class="wrap">';
echo '<h1>' . esc_html($title) . '</h1>';
$this->render_flash();
}
private function render_footer(): void {
echo '</div>';
}
private function render_flash(): void {
if (empty($_GET['aksrt_pay_msg'])) {
return;
}
$code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
$map = [
'order_updated' => __('订单状态已更新。', 'aksrt'),
'order_failed' => __('订单状态更新失败。', 'aksrt'),
'withdraw_updated' => __('提现状态已更新。', 'aksrt'),
'withdraw_failed' => __('提现状态更新失败。', 'aksrt'),
'card_disabled' => __('充值卡已禁用。', 'aksrt'),
'card_failed' => __('充值卡禁用失败。', 'aksrt'),
];
if (!isset($map[$code])) {
return;
}
$class = str_contains($code, 'failed') ? 'notice-error' : 'notice-success';
echo '<div class="notice ' . esc_attr($class) . ' is-dismissible"><p>' . esc_html($map[$code]) . '</p></div>';
}
private function page_url(string $slug, array $args = []): string {
return add_query_arg(array_merge(['page' => $slug], $args), admin_url('admin.php'));
}
public function render_overview_page(): void {
$this->ensure_cap();
$this->render_header(__('支付中心', 'aksrt'));
$order_stats = class_exists('AKSRT_Order_Repository')
? AKSRT_Order_Repository::get_instance()->get_stats()
: [];
$withdraw_stats = class_exists('AKSRT_Withdrawal')
? AKSRT_Withdrawal::get_instance()->get_stats()
: [];
$card_stats = class_exists('AKSRT_Charge_Card')
? AKSRT_Charge_Card::get_instance()->get_stats()
: [];
echo '<p class="description">' . esc_html__('运营数据入口(对齐 zibll 商城中心)。配置项仍在 AKSRT 主题设置 SPA。', 'aksrt') . '</p>';
echo '<div class="aksrt-pay-stats" style="display:flex;flex-wrap:wrap;gap:16px;margin:20px 0;">';
$this->stat_card(__('订单总数', 'aksrt'), (string) (int) ($order_stats['total'] ?? 0), $this->page_url('aksrt_orders'));
$this->stat_card(__('待支付', 'aksrt'), (string) (int) ($order_stats['pending'] ?? 0), $this->page_url('aksrt_orders', ['status' => 'pending']));
$this->stat_card(__('已支付', 'aksrt'), (string) (int) ($order_stats['paid'] ?? 0), $this->page_url('aksrt_orders', ['status' => 'paid']));
$this->stat_card(__('营收', 'aksrt'), number_format((float) ($order_stats['revenue'] ?? 0), 2), $this->page_url('aksrt_orders'));
$this->stat_card(__('待审提现', 'aksrt'), (string) (int) ($withdraw_stats['pending'] ?? 0), $this->page_url('aksrt_withdraw', ['status' => 'pending']));
$this->stat_card(__('可用卡密', 'aksrt'), (string) (int) ($card_stats['active'] ?? 0), $this->page_url('aksrt_charge_cards', ['status' => 'active']));
echo '</div>';
echo '<h2>' . esc_html__('快捷入口', 'aksrt') . '</h2><ul>';
foreach ($this->pages() as $page) {
if ($page['slug'] === self::MENU_SLUG) {
continue;
}
echo '<li><a href="' . esc_url($this->page_url($page['slug'])) . '">' . esc_html($page['title']) . '</a></li>';
}
echo '</ul>';
$spa = admin_url('admin.php?page=aksrt-settings#/payment/gateway');
echo '<p><a class="button" href="' . esc_url($spa) . '">' . esc_html__('打开支付配置(SPA)', 'aksrt') . '</a></p>';
$this->render_footer();
}
private function stat_card(string $label, string $value, string $url): void {
echo '<a href="' . esc_url($url) . '" style="display:block;min-width:140px;padding:16px;background:#fff;border:1px solid #c3c4c7;border-radius:4px;text-decoration:none;color:inherit;">';
echo '<div style="font-size:12px;color:#646970;">' . esc_html($label) . '</div>';
echo '<div style="font-size:22px;font-weight:600;margin-top:6px;">' . esc_html($value) . '</div>';
echo '</a>';
}
public function render_stub_page(): void {
$this->ensure_cap();
$page = isset($_GET['page']) ? sanitize_key((string) wp_unslash($_GET['page'])) : '';
$title = __('支付中心', 'aksrt');
foreach ($this->pages() as $item) {
if ($item['slug'] === $page) {
$title = $item['title'];
break;
}
}
$this->render_header($title);
echo '<div class="notice notice-info"><p>';
echo esc_html__('此运营页骨架已挂载到原生侧栏。完整列表与操作将在 Phase 2b 继续补齐;当前可暂时使用 SPA 对应页面。', 'aksrt');
echo '</p></div>';
$spa_map = [
'aksrt_shipping' => '#/shop/shipping',
'aksrt_after_sale' => '#/shop/after-sale',
'aksrt_coupons' => '#/shop/coupons',
'aksrt_income' => '#/payment/revenue',
'aksrt_rebate' => '#/payment/rebate',
];
if (isset($spa_map[$page])) {
$url = admin_url('admin.php?page=aksrt-settings' . $spa_map[$page]);
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('打开 SPA 运营页(过渡)', 'aksrt') . '</a></p>';
}
$this->render_footer();
}
/* ------------------------------------------------------------------
* Orders
* ----------------------------------------------------------------*/
public function render_orders_page(): void {
$this->ensure_cap();
$this->render_header(__('订单', 'aksrt'));
if (!class_exists('AKSRT_Order_Repository')) {
echo '<div class="notice notice-error"><p>' . esc_html__('订单服务不可用。', 'aksrt') . '</p></div>';
$this->render_footer();
return;
}
$status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
$search = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';
$page_num = max(1, (int) ($_GET['paged'] ?? 1));
$per_page = 20;
$repo = AKSRT_Order_Repository::get_instance();
$args = [
'limit' => $per_page,
'offset' => ($page_num - 1) * $per_page,
];
if ($status !== '' && $status !== 'all') {
$args['status'] = $status;
}
$items = $repo->get_orders_admin($args, $search);
$total = $repo->get_orders_count($args, $search);
$pages = max(1, (int) ceil($total / $per_page));
$labels = class_exists('AKSRT_Domain_Model')
? AKSRT_Domain_Model::order_status_labels()
: [];
echo '<form method="get" style="margin:12px 0;">';
echo '<input type="hidden" name="page" value="aksrt_orders" />';
echo '<select name="status">';
echo '<option value="">' . esc_html__('全部状态', 'aksrt') . '</option>';
foreach ($labels as $key => $label) {
printf(
'<option value="%s"%s>%s</option>',
esc_attr($key),
selected($status, $key, false),
esc_html($label)
);
}
echo '</select> ';
echo '<input type="search" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('订单号 / 类型', 'aksrt') . '" /> ';
submit_button(__('筛选', 'aksrt'), 'secondary', '', false);
echo '</form>';
echo '<table class="wp-list-table widefat fixed striped">';
echo '<thead><tr>';
echo '<th>' . esc_html__('订单号', 'aksrt') . '</th>';
echo '<th>' . esc_html__('用户', 'aksrt') . '</th>';
echo '<th>' . esc_html__('类型', 'aksrt') . '</th>';
echo '<th>' . esc_html__('金额', 'aksrt') . '</th>';
echo '<th>' . esc_html__('状态', 'aksrt') . '</th>';
echo '<th>' . esc_html__('时间', 'aksrt') . '</th>';
echo '<th>' . esc_html__('操作', 'aksrt') . '</th>';
echo '</tr></thead><tbody>';
if (!$items) {
echo '<tr><td colspan="7">' . esc_html__('暂无订单', 'aksrt') . '</td></tr>';
}
foreach ($items as $row) {
$order_number = (string) ($row['order_number'] ?? '');
$user_id = (int) ($row['user_id'] ?? 0);
$user = $user_id > 0 ? get_userdata($user_id) : null;
$status_key = (string) ($row['status'] ?? '');
$status_label = $labels[$status_key] ?? $status_key;
echo '<tr>';
echo '<td><code>' . esc_html($order_number) . '</code></td>';
echo '<td>' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . '</td>';
echo '<td>' . esc_html((string) ($row['product_type'] ?? '')) . '</td>';
echo '<td>' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . ' ' . esc_html((string) ($row['currency'] ?? '')) . '</td>';
echo '<td>' . esc_html($status_label) . '</td>';
echo '<td>' . esc_html((string) ($row['created_at'] ?? '')) . '</td>';
echo '<td>';
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" style="display:flex;gap:4px;align-items:center;">';
echo '<input type="hidden" name="action" value="aksrt_pay_update_order_status" />';
echo '<input type="hidden" name="order_number" value="' . esc_attr($order_number) . '" />';
wp_nonce_field('aksrt_pay_update_order_status_' . $order_number);
echo '<select name="new_status">';
foreach ($labels as $key => $label) {
printf(
'<option value="%s"%s>%s</option>',
esc_attr($key),
selected($status_key, $key, false),
esc_html($label)
);
}
echo '</select>';
submit_button(__('更新', 'aksrt'), 'small', '', false);
echo '</form>';
echo '</td>';
echo '</tr>';
}
echo '</tbody></table>';
if ($pages > 1) {
echo '<div class="tablenav"><div class="tablenav-pages">';
echo paginate_links([
'base' => add_query_arg('paged', '%#%'),
'format' => '',
'prev_text' => '&laquo;',
'next_text' => '&raquo;',
'total' => $pages,
'current' => $page_num,
]);
echo '</div></div>';
}
$this->render_footer();
}
public function handle_update_order_status(): void {
if (!current_user_can(self::CAPABILITY)) {
wp_die(esc_html__('权限不足', 'aksrt'));
}
$order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
$new_status = isset($_POST['new_status']) ? sanitize_key((string) wp_unslash($_POST['new_status'])) : '';
check_admin_referer('aksrt_pay_update_order_status_' . $order_number);
$ok = false;
if ($order_number !== '' && $new_status !== '' && class_exists('AKSRT_Order_Repository')) {
$ok = AKSRT_Order_Repository::get_instance()->update_status($order_number, $new_status, ['force' => true]);
}
wp_safe_redirect($this->page_url('aksrt_orders', [
'aksrt_pay_msg' => $ok ? 'order_updated' : 'order_failed',
]));
exit;
}
/* ------------------------------------------------------------------
* Withdrawals
* ----------------------------------------------------------------*/
public function render_withdrawals_page(): void {
$this->ensure_cap();
$this->render_header(__('提现审核', 'aksrt'));
if (!class_exists('AKSRT_Withdrawal')) {
echo '<div class="notice notice-error"><p>' . esc_html__('提现模块未初始化。', 'aksrt') . '</p></div>';
$this->render_footer();
return;
}
$status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
$page_num = max(1, (int) ($_GET['paged'] ?? 1));
$per_page = 20;
$svc = AKSRT_Withdrawal::get_instance();
$stats = $svc->get_stats();
echo '<p>' . esc_html(sprintf(
/* translators: 1: total 2: pending 3: completed 4: paid */
__('共 %1$d 条 · 待处理 %2$d · 已完成 %3$d · 已打款 %4$s', 'aksrt'),
(int) ($stats['total'] ?? 0),
(int) ($stats['pending'] ?? 0),
(int) ($stats['completed'] ?? 0),
number_format((float) ($stats['total_paid'] ?? 0), 2)
)) . '</p>';
echo '<form method="get" style="margin:12px 0;">';
echo '<input type="hidden" name="page" value="aksrt_withdraw" />';
echo '<select name="status">';
$status_options = [
'' => __('全部状态', 'aksrt'),
'pending' => __('待处理', 'aksrt'),
'approved' => __('已通过', 'aksrt'),
'processing' => __('处理中', 'aksrt'),
'completed' => __('已完成', 'aksrt'),
'rejected' => __('已拒绝', 'aksrt'),
];
foreach ($status_options as $key => $label) {
printf(
'<option value="%s"%s>%s</option>',
esc_attr($key),
selected($status, $key, false),
esc_html($label)
);
}
echo '</select> ';
submit_button(__('筛选', 'aksrt'), 'secondary', '', false);
echo '</form>';
$items = $svc->get_all([
'status' => $status,
'limit' => $per_page,
'offset' => ($page_num - 1) * $per_page,
]);
$total = $svc->get_count(['status' => $status]);
$pages = max(1, (int) ceil($total / $per_page));
echo '<table class="wp-list-table widefat fixed striped">';
echo '<thead><tr>';
echo '<th>ID</th>';
echo '<th>' . esc_html__('用户', 'aksrt') . '</th>';
echo '<th>' . esc_html__('金额', 'aksrt') . '</th>';
echo '<th>' . esc_html__('方式', 'aksrt') . '</th>';
echo '<th>' . esc_html__('账户', 'aksrt') . '</th>';
echo '<th>' . esc_html__('状态', 'aksrt') . '</th>';
echo '<th>' . esc_html__('申请时间', 'aksrt') . '</th>';
echo '<th>' . esc_html__('操作', 'aksrt') . '</th>';
echo '</tr></thead><tbody>';
if (!$items) {
echo '<tr><td colspan="8">' . esc_html__('暂无提现记录', 'aksrt') . '</td></tr>';
}
foreach ($items as $row) {
$id = (int) ($row['id'] ?? 0);
$user_id = (int) ($row['user_id'] ?? 0);
$user = $user_id > 0 ? get_userdata($user_id) : null;
$st = (string) ($row['status'] ?? '');
echo '<tr>';
echo '<td>' . esc_html((string) $id) . '</td>';
echo '<td>' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . '</td>';
echo '<td>' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . '</td>';
echo '<td>' . esc_html((string) ($row['method'] ?? '')) . '</td>';
echo '<td>' . esc_html((string) ($row['account_info'] ?? '')) . '</td>';
echo '<td>' . esc_html($status_options[$st] ?? $st) . '</td>';
echo '<td>' . esc_html((string) ($row['created_at'] ?? '')) . '</td>';
echo '<td>';
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" style="display:flex;gap:4px;flex-wrap:wrap;align-items:center;">';
echo '<input type="hidden" name="action" value="aksrt_pay_update_withdrawal" />';
echo '<input type="hidden" name="withdrawal_id" value="' . esc_attr((string) $id) . '" />';
wp_nonce_field('aksrt_pay_update_withdrawal_' . $id);
echo '<select name="new_status">';
foreach ($status_options as $key => $label) {
if ($key === '') {
continue;
}
printf(
'<option value="%s"%s>%s</option>',
esc_attr($key),
selected($st, $key, false),
esc_html($label)
);
}
echo '</select>';
echo '<input type="text" name="note" placeholder="' . esc_attr__('备注', 'aksrt') . '" value="" />';
submit_button(__('更新', 'aksrt'), 'small', '', false);
echo '</form>';
echo '</td>';
echo '</tr>';
}
echo '</tbody></table>';
if ($pages > 1) {
echo '<div class="tablenav"><div class="tablenav-pages">';
echo paginate_links([
'base' => add_query_arg('paged', '%#%'),
'format' => '',
'prev_text' => '&laquo;',
'next_text' => '&raquo;',
'total' => $pages,
'current' => $page_num,
]);
echo '</div></div>';
}
$this->render_footer();
}
public function handle_update_withdrawal(): void {
if (!current_user_can(self::CAPABILITY)) {
wp_die(esc_html__('权限不足', 'aksrt'));
}
$id = isset($_POST['withdrawal_id']) ? (int) $_POST['withdrawal_id'] : 0;
$new_status = isset($_POST['new_status']) ? sanitize_key((string) wp_unslash($_POST['new_status'])) : '';
$note = isset($_POST['note']) ? sanitize_textarea_field((string) wp_unslash($_POST['note'])) : '';
check_admin_referer('aksrt_pay_update_withdrawal_' . $id);
$ok = false;
if ($id > 0 && $new_status !== '' && class_exists('AKSRT_Withdrawal')) {
$ok = AKSRT_Withdrawal::get_instance()->update_status($id, $new_status, $note);
}
wp_safe_redirect($this->page_url('aksrt_withdraw', [
'aksrt_pay_msg' => $ok ? 'withdraw_updated' : 'withdraw_failed',
]));
exit;
}
/* ------------------------------------------------------------------
* Charge cards
* ----------------------------------------------------------------*/
public function render_charge_cards_page(): void {
$this->ensure_cap();
$this->render_header(__('卡密管理', 'aksrt'));
if (!class_exists('AKSRT_Charge_Card')) {
echo '<div class="notice notice-error"><p>' . esc_html__('卡密模块未初始化。', 'aksrt') . '</p></div>';
$this->render_footer();
return;
}
$status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
$type = isset($_GET['type']) ? sanitize_key((string) wp_unslash($_GET['type'])) : '';
$page_num = max(1, (int) ($_GET['paged'] ?? 1));
$per_page = 20;
$svc = AKSRT_Charge_Card::get_instance();
$stats = $svc->get_stats();
echo '<p>' . esc_html(sprintf(
/* translators: 1: total 2: active 3: used */
__('共 %1$d 张 · 可用 %2$d · 已用 %3$d', 'aksrt'),
(int) ($stats['total'] ?? 0),
(int) ($stats['active'] ?? 0),
(int) ($stats['used'] ?? 0)
)) . '</p>';
echo '<form method="get" style="margin:12px 0;">';
echo '<input type="hidden" name="page" value="aksrt_charge_cards" />';
echo '<select name="status">';
foreach ([
'' => __('全部状态', 'aksrt'),
'active' => __('可用', 'aksrt'),
'used' => __('已用', 'aksrt'),
'disabled' => __('已禁用', 'aksrt'),
] as $key => $label) {
printf(
'<option value="%s"%s>%s</option>',
esc_attr($key),
selected($status, $key, false),
esc_html($label)
);
}
echo '</select> ';
echo '<select name="type">';
foreach ([
'' => __('全部类型', 'aksrt'),
'balance' => __('余额', 'aksrt'),
'vip' => __('VIP', 'aksrt'),
'points' => __('积分', 'aksrt'),
] as $key => $label) {
printf(
'<option value="%s"%s>%s</option>',
esc_attr($key),
selected($type, $key, false),
esc_html($label)
);
}
echo '</select> ';
submit_button(__('筛选', 'aksrt'), 'secondary', '', false);
echo ' <a class="button" href="' . esc_url(admin_url('admin.php?page=aksrt-settings#/payment/charge-card')) . '">' . esc_html__('SPA 批量生成(过渡)', 'aksrt') . '</a>';
echo '</form>';
$result = $svc->get_list([
'status' => $status,
'type' => $type,
'limit' => $per_page,
'offset' => ($page_num - 1) * $per_page,
]);
$items = $result['items'] ?? [];
$total = (int) ($result['total'] ?? 0);
$pages = max(1, (int) ($result['max_pages'] ?? 1));
echo '<table class="wp-list-table widefat fixed striped">';
echo '<thead><tr>';
echo '<th>ID</th>';
echo '<th>' . esc_html__('类型', 'aksrt') . '</th>';
echo '<th>' . esc_html__('密码', 'aksrt') . '</th>';
echo '<th>' . esc_html__('面额', 'aksrt') . '</th>';
echo '<th>' . esc_html__('状态', 'aksrt') . '</th>';
echo '<th>' . esc_html__('使用者', 'aksrt') . '</th>';
echo '<th>' . esc_html__('创建时间', 'aksrt') . '</th>';
echo '<th>' . esc_html__('操作', 'aksrt') . '</th>';
echo '</tr></thead><tbody>';
if (!$items) {
echo '<tr><td colspan="8">' . esc_html__('暂无卡密', 'aksrt') . '</td></tr>';
}
foreach ($items as $row) {
$id = (int) ($row['id'] ?? 0);
$st = (string) ($row['status'] ?? '');
$user_id = (int) ($row['used_by'] ?? $row['user_id'] ?? 0);
$user = $user_id > 0 ? get_userdata($user_id) : null;
echo '<tr>';
echo '<td>' . esc_html((string) $id) . '</td>';
echo '<td>' . esc_html((string) ($row['type'] ?? '')) . '</td>';
echo '<td><code>' . esc_html((string) ($row['password'] ?? $row['code'] ?? '')) . '</code></td>';
echo '<td>' . esc_html((string) ($row['amount'] ?? '')) . '</td>';
echo '<td>' . esc_html($st) . '</td>';
echo '<td>' . ($user ? esc_html($user->display_name) : '—') . '</td>';
echo '<td>' . esc_html((string) ($row['created_at'] ?? '')) . '</td>';
echo '<td>';
if ($st === 'active') {
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '">';
echo '<input type="hidden" name="action" value="aksrt_pay_disable_charge_card" />';
echo '<input type="hidden" name="card_id" value="' . esc_attr((string) $id) . '" />';
wp_nonce_field('aksrt_pay_disable_charge_card_' . $id);
submit_button(__('禁用', 'aksrt'), 'small', '', false);
echo '</form>';
} else {
echo '—';
}
echo '</td>';
echo '</tr>';
}
echo '</tbody></table>';
if ($pages > 1) {
echo '<div class="tablenav"><div class="tablenav-pages">';
echo paginate_links([
'base' => add_query_arg('paged', '%#%'),
'format' => '',
'prev_text' => '&laquo;',
'next_text' => '&raquo;',
'total' => $pages,
'current' => $page_num,
]);
echo '</div></div>';
}
$this->render_footer();
}
public function handle_disable_charge_card(): void {
if (!current_user_can(self::CAPABILITY)) {
wp_die(esc_html__('权限不足', 'aksrt'));
}
$id = isset($_POST['card_id']) ? (int) $_POST['card_id'] : 0;
check_admin_referer('aksrt_pay_disable_charge_card_' . $id);
$ok = false;
if ($id > 0 && class_exists('AKSRT_Charge_Card')) {
$ok = AKSRT_Charge_Card::get_instance()->disable($id);
}
wp_safe_redirect($this->page_url('aksrt_charge_cards', [
'aksrt_pay_msg' => $ok ? 'card_disabled' : 'card_failed',
]));
exit;
}
}