403Webshell
Server IP : 43.141.49.92  /  Your IP : 113.219.202.141
Web Server : Apache
System : Linux VM-8-5-opencloudos 6.6.34-9.oc9.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jun 19 19:35:45 CST 2024 x86_64
User : www ( 1000)
PHP Version : 8.1.27
Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /www/wwwroot/www.ucppt.com/wp-content/plugins/wpcom-member-pro/includes/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /www/wwwroot/www.ucppt.com/wp-content/plugins/wpcom-member-pro/includes/cards.php
<?php
namespace WPCOM\Member;
use WPCOM\Themer\Plugin\Utils;

defined('ABSPATH') || exit;

class Cards{
    protected static $products_table = 'wpcom_card_products';
    protected static $items_table = 'wpcom_card_items';
    private static $slug = 'wpcom-cards';
    private static $base_url;

    function __construct() {
        self::$base_url = admin_url('admin.php?page=' . self::$slug);

        $this->register_actions();
        $this->register_filters();

        add_shortcode('wpcom-cards', [$this, 'shortcode']);
    }

    // 注册 action 钩子
    private function register_actions() {
        add_action('admin_menu', [$this, 'admin_menu']);
        add_action('wpcom_themer_maybe_updated', function(){ self::init_database(); });

        add_action('wp_ajax_wpcom_get_card_product_data', [$this, 'get_card_product_data']);
        add_action('wp_ajax_nopriv_wpcom_get_card_product_data', [$this, 'get_card_product_data']);

        add_action('wpcom_card_payment_complete', [$this, 'payment_complete'], 10, 2);
        add_action('wpcom_order_item_detail_card', [$this, 'show_in_order'], 10, 2);
    }

    // 注册 filter 钩子
    private function register_filters() {
        add_filter('wpcom_member_card_product_metas', [$this, 'card_product_metas']);
        add_filter('wpcom_member_card_item_metas', [$this, 'card_item_metas'], 10, 2);
        add_filter('wpcom_init_card_product_options', [$this, 'card_product_options'], 10, 2);
        add_filter('wpcom_init_card_item_options', [$this, 'card_item_options'], 10, 2);
        add_filter('parent_file', [$this, 'highlight_parent_menu']);
        add_filter('submenu_file', [$this, 'highlight_submenu']);
        add_filter('wpcom_card_stock_check', [$this, 'stock_check'], 10, 2);
    }

    // 后台菜单高亮处理
    public function highlight_parent_menu($parent_file) {
        global $plugin_page;
        if ($plugin_page === 'wpcom-cards-items') {
            global $_wp_real_parent_file, $_wp_menu_nopriv;
            $_wp_real_parent_file[$plugin_page] = 'wpcom-member-pro';
            $_wp_menu_nopriv[$plugin_page] = 'wpcom-member-pro';
            return 'wpcom-member-pro';
        }
        return $parent_file;
    }

    public function highlight_submenu($submenu_file) {
        global $pagenow;
        $screen = get_current_screen();
        if ($pagenow == 'admin.php' && $screen->base == 'admin_page_wpcom-cards-items') {
            return 'wpcom-cards';
        }
        return $submenu_file;
    }

    function admin_menu(){
        add_submenu_page('wpcom-member-pro', '卡密管理', '卡密管理', 'manage_options', self::$slug, [$this, 'cards'], 10);
        add_submenu_page(self::$slug, '管理卡密', '管理卡密', 'manage_options', 'wpcom-cards-items', [$this, 'card_items'], 20);
    }

    function cards(){
        $action = isset($_GET['action']) ? $_GET['action'] : '';
        if ($action === 'add' || $action === 'edit') {
            // 加载添加卡密表单页面
            $this->edit_card_product_page();
            return;
        }

        if (!class_exists('Card_Products')) {
            require_once WPCOM_MP_DIR . '/includes/card-products.php';
        }
        $list_table = new Card_Products();
        $list_table->prepare_items();
        echo '<div class="wrap">';
        echo '<h1 class="wp-heading-inline">卡密管理</h1>';
        echo ' <a href="' . self::$base_url . '&action=add" class="page-title-action">添加卡密</a>';
        echo '<form method="get">';
        echo '<input type="hidden" name="page" value="wpcom-cards" />';
        $list_table->views();
        $list_table->search_box('搜索卡密', 'card_product');
        $list_table->display();
        echo '</form>';
        echo '</div>';
    }

    function card_items() {
        $product_id = isset($_GET['product_id']) ? intval($_GET['product_id']) : 0;
        $action = isset($_GET['action']) ? $_GET['action'] : '';
        $item_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
        if (!$product_id) {
            echo '<div class="notice notice-error"><p>参数错误,未指定产品ID。</p></div>';
            return;
        }
        if ($action === 'add' || $action === 'edit') {
            $this->edit_card_item_page($product_id, $item_id);
            return;
        }

        if ($action === 'import') {
            $this->import_card_item_page($product_id);
            return;
        }

        if (isset($_GET['status']) && $_GET['status'] === 'success') {
            $count = isset($_GET['count']) ? intval($_GET['count']) : 0;
            $repeat = isset($_GET['repeat']) ? intval($_GET['repeat']) : 0;
            $msg = ["导入完成"];
            if ($count) $msg[] = "添加成功 {$count} 条";
            if ($repeat) $msg[] = "跳过 {$repeat} 条重复";
            echo '<div class="notice notice-success"><p>' . implode(',', $msg) . '。</p></div>';
        }

        // 这里可以加载 Card_Items 类,显示卡密列表
        if (!class_exists('Card_Items')) {
            require_once WPCOM_MP_DIR . '/includes/card-items.php';
        }
        $list_table = new Card_Items($product_id);
        $list_table->prepare_items();
        echo '<div class="wrap">';
        echo '<h1 class="wp-heading-inline">管理卡密</h1>';
        echo ' <a href="' . self::$base_url . '" class="page-title-action"><span class="dashicons dashicons-arrow-left-alt" style="line-height: inherit;width:1em;height:1em;font-size:inherit;"></span> 返回</a>';
        echo ' <a href="' . admin_url('admin.php?page=wpcom-cards-items&product_id=' . $product_id . '&action=add') . '" class="page-title-action">添加卡密</a>';
        echo ' <a href="' . admin_url('admin.php?page=wpcom-cards-items&product_id=' . $product_id . '&action=import') . '" class="page-title-action">导入卡密</a>';
        echo '<form method="get">';
        echo '<input type="hidden" name="page" value="wpcom-cards-items" />';
        echo '<input type="hidden" name="product_id" value="' . $product_id . '" />';
        $list_table->views();
        $list_table->search_box('搜索卡密', 'card_item');
        $list_table->display();
        echo '</form>';
        echo '</div>';
    }

    function edit_card_product_page() {
        $product_id = isset($_GET['id']) ? sanitize_text_field($_GET['id']) : 0;
        require_once WPCOM_ADMIN_PATH . 'includes/class-utils.php';
        Utils::panel_script();

        $error = $this->save_card();
        ?>
        <div class="wrap">
            <h1 class="wp-heading-inline">
                <?php echo $product_id ? '编辑' : '添加';?>卡密
                <a class="page-title-action" href="<?php echo self::$base_url; ?>"><span class="dashicons dashicons-arrow-left-alt" style="line-height: inherit;width:1em;height:1em;font-size:inherit;"></span> 返回</a>
            </h1>
            <?php if ($error === false || (isset($_GET['status']) && $_GET['status'] === 'success')) { ?>
                <div id="message" class="notice notice-success">
                    <p><strong>卡密信息已更新。</strong></p>
                    <p>
                        <a href="<?php echo self::$base_url; ?>" style="text-decoration: none;">
                            <span class="dashicons dashicons-arrow-left-alt" style="line-height: inherit;width:1em;height:1em;font-size:inherit;"></span> 返回“卡密管理”页面
                        </a>
                    </p>
                </div>
            <?php } else if ($error) { ?>
                <div id="message" class="notice notice-error">
                    <p><strong>卡密信息更新失败。</strong></p>
                    <p><?php echo $error; ?></p>
                </div>
            <?php } ?>

            <form name="edit-card" id="edittag" method="post" action="" class="card-edit validate">
                <input type="hidden" name="action" value="<?php echo  $product_id ? 'edit' : 'new';?>" />
                <input type="hidden" name="product_id" value="<?php echo $product_id; ?>" />
                <?php wp_nonce_field('card-edit', 'card-edit-nonce'); ?>
                <table class="form-table" role="presentation">
                    <tr id="wpcom-plugin-panel" class="wpcom-term-wrap">
                        <td colspan="2">
                            <term-panel :ready="ready" />
                        </td>
                    </tr>
                </table>
                <input type="submit" class="button button-primary" value="提交" />
            </form>
            <div style="display: none;"><?php wp_editor('EDITOR', 'WPCOM-EDITOR', Utils::editor_settings(array('textarea_name' => 'EDITOR-NAME'))); ?></div>
            <script>
                if (typeof _plugins_options === 'undefined') _plugins_options = [];
                _plugins_options.push(<?php echo $this->get_metas(); ?>);
                if (window.location.search.indexOf('status=success') > -1) {
                    var url = new URL(window.location.href);
                    url.searchParams.delete('status');
                    window.history.replaceState({}, document.title, url.toString());
                }
            </script>
        </div>
    <?php }

    function edit_card_item_page($product_id, $item_id) {
        $item_id = isset($_GET['id']) ? sanitize_text_field($_GET['id']) : 0;
        require_once WPCOM_ADMIN_PATH . 'includes/class-utils.php';
        Utils::panel_script();
        $base_url = admin_url('admin.php?page=wpcom-cards-items&product_id=' . $product_id);
        $error = $this->save_card_item($product_id, $item_id);

        $is_edit = $item_id ? true : false;
        ?>
        <div class="wrap">
            <h1 class="wp-heading-inline">
                <?php echo $item_id ? '编辑' : '添加';?>卡密
                <a class="page-title-action" href="<?php echo $base_url; ?>"><span class="dashicons dashicons-arrow-left-alt" style="line-height: inherit;width:1em;height:1em;font-size:inherit;"></span> 返回</a>
            </h1>
            <?php if ($error === false || (isset($_GET['status']) && $_GET['status'] === 'success')) { ?>
                <div id="message" class="notice notice-success">
                    <p><strong>卡密已更新。</strong></p>
                    <p>
                        <a href="<?php echo $base_url; ?>" style="text-decoration: none;">
                            <span class="dashicons dashicons-arrow-left-alt" style="line-height: inherit;width:1em;height:1em;font-size:inherit;"></span> 返回“管理卡密”页面
                        </a>
                    </p>
                </div>
            <?php } else if ($error) { ?>
                <div id="message" class="notice notice-error">
                    <p><strong>卡密更新失败。</strong></p>
                    <p><?php echo $error; ?></p>
                </div>
            <?php } ?>

            <form name="edit-card" id="edittag" method="post" action="" class="card-edit validate">
                <input type="hidden" name="action" value="<?php echo  $item_id ? 'edit' : 'new';?>" />
                <input type="hidden" name="product_id" value="<?php echo $product_id; ?>" />
                <input type="hidden" name="item_id" value="<?php echo $item_id; ?>" />
                <?php wp_nonce_field('card_item-edit', 'card_item-edit-nonce'); ?>
                <table class="form-table" role="presentation">
                    <tr id="wpcom-plugin-panel" class="wpcom-term-wrap">
                        <td colspan="2">
                            <term-panel :ready="ready" />
                        </td>
                    </tr>
                </table>
                <input type="submit" class="button button-primary" value="提交" />
            </form>
            <div style="display: none;"><?php wp_editor('EDITOR', 'WPCOM-EDITOR', Utils::editor_settings(array('textarea_name' => 'EDITOR-NAME'))); ?></div>
            <script>
                if (typeof _plugins_options === 'undefined') _plugins_options = [];
                _plugins_options.push(<?php echo $this->get_metas('card_item', $is_edit); ?>);
                if (window.location.search.indexOf('status=success') > -1) {
                    var url = new URL(window.location.href);
                    url.searchParams.delete('status');
                    window.history.replaceState({}, document.title, url.toString());
                }
            </script>
        </div>
    <?php }

    function import_card_item_page($product_id) {
        $error = '';
        $success = '';
        $expire_at = isset($_POST['expire_at']) ? sanitize_text_field($_POST['expire_at']) : '';
        if (current_user_can('manage_options') && isset($_FILES['import_file']) && isset($_POST['card_item-import-nonce']) && wp_verify_nonce($_POST['card_item-import-nonce'], 'card_item-import')) {
            $file = $_FILES['import_file'];
            if ($file['error'] === UPLOAD_ERR_OK) {
                $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
                $content = file_get_contents($file['tmp_name']);
                if ($ext === 'csv') {
                    $codes = array_map('trim', explode("\n", $content));
                } else {
                    $codes = preg_split('/\r\n|\r|\n/', $content);
                }
                $count = 0;
                $repeat = 0;
                if (count($codes) > 0) {
                    global $wpdb;
                    $table = $wpdb->prefix . self::$items_table;
                    foreach ($codes as $code) {
                        $code = trim($code);
                        if ($code === '') continue;
                        if ($this->is_code_exists($code, $product_id)) {
                            $repeat++;
                            continue;
                        }
                        require_once WPCOM_ADMIN_PATH . 'includes/class-utils.php';
                        $wpdb->insert($table, [
                            'product_id' => $product_id,
                            'code' => $code,
                            'status' => 0,
                            'expire_at' => Utils::input_time($expire_at),
                            'created_at' => current_time('mysql')
                        ]);
                        $count++;
                    }

                    // 更新库存
                    self::update_stock($product_id);

                    // 导入成功后跳转到列表页并提示
                    $url = admin_url('admin.php?page=wpcom-cards-items&product_id=' . $product_id . '&status=success&count=' . $count . '&repeat=' . $repeat);
                    echo '<script>window.location.href="' . $url . '";</script>';
                    exit;
                } else {
                    $error = '导入失败,未识别到有效卡密内容。';
                }
            } else {
                $error = '文件上传失败,请重试。';
            }
        }
        $return_url = admin_url('admin.php?page=wpcom-cards-items&product_id=' . $product_id);
        ?>
        <div class="wrap">
            <h1 class="wp-heading-inline">导入卡密
                <a class="page-title-action" href="<?php echo esc_attr($return_url); ?>"><span class="dashicons dashicons-arrow-left-alt" style="line-height: inherit;width:1em;height:1em;font-size:inherit;"></span> 返回</a>
            </h1>
            <?php if ($error): ?>
                <div class="notice notice-error"><p><?php echo esc_html($error); ?></p></div>
            <?php elseif ($success): ?>
                <div class="notice notice-success"><p><?php echo esc_html($success); ?></p></div>
            <?php endif; ?>
            <form method="post" enctype="multipart/form-data">
                <?php wp_nonce_field('card_item-import', 'card_item-import-nonce'); ?>
                <table class="form-table" role="presentation">
                    <tr>
                        <th scope="row"><label for="import_file">选择文件</label></th>
                        <td>
                            <input type="file" name="import_file" id="import_file" accept=".txt,.csv" required>
                            <p class="description">支持 txt 或 csv 格式文件,每行一个卡密。<br>温馨提示:为确保正常导入建议控制单次导入数量,避免服务器处理超时。</p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="expire_at">过期时间</label></th>
                        <td>
                            <input type="datetime-local" name="expire_at" id="expire_at" value="<?php echo esc_attr($expire_at); ?>">
                            <p class="description">可选,为本次导入的所有卡密设置统一过期时间,不填则不过期。</p>
                        </td>
                    </tr>
                </table>
                <p class="submit"><input type="submit" class="button-primary" value="导入"></p>
            </form>
        </div>
        <?php
    }

    function get_metas($type = 'card_product', $is_edit = false){
        require_once WPCOM_ADMIN_PATH . 'includes/class-utils.php';
        $res = [ 'type' => 'taxonomy', 'tax' => $type, 'options' => [] ];
        $the_id = isset($_GET['id']) ? sanitize_text_field($_GET['id']) : 0;

        $plugin = $GLOBALS['wpcom_mp']->info;
        $res['filters'] = apply_filters('wpcom_member_' . $type . '_metas', [], $is_edit);
        $res['ver'] = $plugin['ver'];
        $res['plugin-id'] = $plugin['plugin_id'];
        $res['plugin-slug'] = $plugin['slug'];
        $res['framework_ver'] = Utils::framework_version($plugin['slug']);
        $res = apply_filters('wpcom_init_' . $type . '_options', $res, $the_id);
        return wp_json_encode($res);
    }

    function card_product_options($res, $id){
        $res = $res ?: [];
        $res['options'] = $res['options'] ?: [];

        if ($id && $product = $this->get_card_product($id)) {
            $res['options'] = [
                'title' => $product->title,
                'price' => $product->price,
                'pay_type' => $product->price_type,
                'status' => $product->status,
                'expire_at' => $product->expire_at,
                'description' => $product->description
            ];
        }

        if(isset($_POST) && isset($_POST['wpcom_price'])){
            $data = map_deep(stripslashes_deep($_POST), 'sanitize_text_field');
            if(isset($data['wpcom_title'])){
                $res['options']['title'] = $data['wpcom_title'];
            }
            if(isset($data['wpcom_price'])){
                $res['options']['price'] = $data['wpcom_price'];
            }
            if(isset($data['wpcom_pay_type'])){
                $res['options']['pay_type'] = (int) $data['wpcom_pay_type'];
            }
            if(isset($data['wpcom_status'])){
                $res['options']['status'] = $data['wpcom_status'];
            }
            if(isset($data['wpcom_expire_at'])){
                require_once WPCOM_ADMIN_PATH . 'includes/class-utils.php';
                $res['options']['expire_at'] = Utils::input_time($data['wpcom_expire_at']);
            }
            if(isset($data['wpcom_description'])){
                $res['options']['description'] = sanitize_textarea_field($_POST['wpcom_description']);
            }
        }

        if(isset($res['options']['price']) && $res['options']['price'] !== '' && $res['options']['pay_type'] == 1){
            $res['options']['price'] = intval($res['options']['price']);
        }

        return $res;
    }

    function card_item_options($res, $id){
        $res = $res ?: [];
        $res['options'] = $res['options'] ?: [];

        if ($id && $item = $this->get_card_item($id)) {
            $res['options'] = [
                'code' => $item->code,
                'status' => $item->status,
                'order_id' => $item->order_id,
                'expire_at' => $item->expire_at
            ];
        }

        if(isset($_POST) && isset($_POST['wpcom_code'])){
            $data = map_deep(stripslashes_deep($_POST), 'sanitize_text_field');
            if(isset($data['wpcom_code'])){
                $res['options']['code'] = $data['wpcom_code'];
            }
            if(isset($data['wpcom_status'])){
                $res['options']['status'] = $data['wpcom_status'];
            }
            if(isset($data['wpcom_order_id'])){
                $res['options']['order_id'] = $data['wpcom_order_id'];
            }
            if(isset($data['wpcom_expire_at'])){
                require_once WPCOM_ADMIN_PATH . 'includes/class-utils.php';
                $res['options']['expire_at'] = Utils::input_time($data['wpcom_expire_at']);
            }
        }

        return $res;
    }

    function card_product_metas($metas){
        $options = $GLOBALS['wpmx_options'];
        $is_points = isset($options['points_on']) && $options['points_on'] == '1';
        $metas['card_product'] = [
            [
                'n' => 'title',
                'l' => '卡密名称',
                'd' => '卡密产品的标题'
            ],
        ];
        if($is_points){
            $metas['card_product'] = array_merge($metas['card_product'], [
                [
                    'n' => 'pay_type',
                    'l' => '积分购买',
                    't' => 't',
                    's' => 0,
                    'd' => '开启后将使用积分购买'
                ],
                [
                    'n' => 'price',
                    'l' => '价格',
                    'd' => '卡密单价,单位:元,如果使用积分购买则为单位为:积分;如果是积分购买,则价格需为整数'
                ]
            ]);
        }else{
            $metas['card_product'] = array_merge($metas['card_product'], [
                [
                    'n' => 'price',
                    'l' => '价格',
                    'd' => '卡密单价,单位:元'
                ]
            ]);
        }

        $metas['card_product'] = array_merge($metas['card_product'], [
            [
                'n' => 'status',
                'l' => '状态',
                't' => 'r',
                's' => 0,
                'o' => [
                    0 => '在售',
                    1 => '下架'
                ],
                'd' => '卡密产品的销售状态'
            ],
            [
                'n' => 'expire_at',
                'l' => '过期时间',
                't' => 'dp',
                'time' => 1,
                'd' => '可选,卡密的统一过期时间,不设置则不过期'
            ],
            [
                'n' => 'description',
                'l' => '使用说明',
                't' => 'ta',
                'rows' => 3,
                'd' => '卡密使用说明,支持多行,购买后在订单详情显示'
            ]
        ]);
        return $metas;
    }

    function card_item_metas($metas, $is_edit = false){
        $metas['card_item'] = [
            [
                'n' => 'expire_at',
                'l' => '过期时间',
                't' => 'dp',
                'time' => 1,
                'd' => '可选,卡密的过期时间'
            ]
        ];

        if($is_edit){
            $metas['card_item'] = array_merge([
                [
                    'n' => 'code',
                    'l' => '卡密',
                    'd' => '卡密内容'
                ],
                [
                    'n' => 'status',
                    'l' => '状态',
                    't' => 'r',
                    's' => 0,
                    'o' => [
                        0 => '未售出',
                        1 => '已售出',
                        2 => '作废'
                    ],
                    'd' => '卡密的状态'
                ],
                [
                    'n' => 'order_id',
                    'l' => '关联订单',
                    'f' => 'status:1',
                    'd' => '如果卡密已售出,则可以填写关联的<b>订单ID</b>或者<b>订单号</b>'
                ]
            ], $metas['card_item']);
        }else{
            $metas['card_item'] = array_merge([
                [
                    'n' => 'code',
                    'l' => '卡密',
                    't' => 'ta',
                    'rows' => 8,
                    'd' => '每行一个卡密,可批量添加'
                ]
            ], $metas['card_item']);
        }
        return $metas;
    }

    function save_card(){
        global $wpdb;
        if (current_user_can('manage_options') && isset($_POST['product_id']) && $_POST['action'] && isset($_POST['wpcom_title'])) {
            $data = map_deep(stripslashes_deep($_POST), 'sanitize_text_field');

            // nonce
            if (!wp_verify_nonce($data['card-edit-nonce'], 'card-edit')) {
                return '表单校验失败,请刷新页面重试';
            }

            // 数据检查
            if (empty($data['wpcom_title'])) {
                return '标题不能为空';
            }
            if (!isset($data['wpcom_price']) || $data['wpcom_price'] === '') {
                return '价格不能为空';
            }
            if (!is_numeric($data['wpcom_price']) || floatval($data['wpcom_price']) < 0) {
                return '价格填写不正确';
            }

            // 判断是否使用积分
            $price_type = 0;
            $options = $GLOBALS['wpmx_options'];
            $is_points = isset($options['points_on']) && $options['points_on'] == '1';
            if ($is_points && isset($data['wpcom_pay_type']) && $data['wpcom_pay_type'] == '1') {
                $price_type = 1;
                // 积分取整
                $data['wpcom_price'] = intval($data['wpcom_price']);
            }

            $table = $wpdb->prefix . self::$products_table;

            $product_id = intval($data['product_id']);
            require_once WPCOM_ADMIN_PATH . 'includes/class-utils.php';
            $_data = [
                'title' => $data['wpcom_title'],
                'price' => $data['wpcom_price'],
                'price_type' => $price_type,
                'status' => $data['wpcom_status'],
                'expire_at' => Utils::input_time($data['wpcom_expire_at']),
                'description' => isset($_POST['wpcom_description']) ? sanitize_textarea_field($_POST['wpcom_description']) : '',
            ];

            if($product_id && $this->get_card_product($product_id)){
                $result = $wpdb->update($table, $_data, ['ID' => $product_id]);
                if($result === false) return '更新失败';
            }else if($product_id === 0){
                $result = $wpdb->insert($table, $_data);
                $product_id = $wpdb->insert_id;

                if(!$product_id) return '添加失败';
            }

            if($data['action'] === 'new'){
                $url = self::$base_url . '&action=edit&id='.$product_id.'&status=success';
                echo '<script>window.location.href="' . $url . '";</script>';
                exit;
            }
            return false;
        }
    }

    function save_card_item($product_id = 0, $item_id = 0){
        global $wpdb;
        $table = $wpdb->prefix . self::$items_table;
        if (current_user_can('manage_options') && isset($_POST['card_item-edit-nonce'])) {
            $data = map_deep(stripslashes_deep($_POST), 'sanitize_text_field');
            $is_edit = $item_id ? true : false;
            // nonce 校验
            if (!wp_verify_nonce($data['card_item-edit-nonce'], 'card_item-edit')) {
                return '表单校验失败,请刷新页面重试';
            }
            if (empty($data['wpcom_code'])) {
                return '卡密不能为空';
            }
            $status = isset($data['wpcom_status']) ? intval($data['wpcom_status']) : 0;
            $order_id_input = isset($data['wpcom_order_id']) ? trim($data['wpcom_order_id']) : '';
            require_once WPCOM_ADMIN_PATH . 'includes/class-utils.php';
            $expire_at = !empty($data['wpcom_expire_at']) ? Utils::input_time($data['wpcom_expire_at']) : null;

            // 订单号兼容处理,仅在已售出时处理
            $order_id = null;
            if ($status == 1 && $order_id_input !== '') {
                if (is_numeric($order_id_input) && strlen($order_id_input) < 16) {
                    // 直接当作订单ID
                    $order_id = intval($order_id_input);
                    // 验证订单是否存在
                    $order = Order::get_order($order_id);
                    if (!$order) {
                        return '关联订单不存在,请检查订单ID是否正确';
                    }
                } else {
                    // 订单号,查找订单ID
                    $order = Order::get_order_by_number($order_id_input);
                    if ($order && isset($order->ID)) {
                        $order_id = $order->ID;
                    } else {
                        return '关联订单不存在,请检查订单号是否正确';
                    }
                }
            }

            if ($is_edit) {
                // 查询卡密明细
                $item = $this->get_card_item($item_id);
                if(!$item) return '卡密不存在或已被删除';

                $code = trim($data['wpcom_code']);
                if ($this->is_code_exists($code, $product_id, $item_id)) {
                    return '卡密内容已存在,请勿重复添加';
                }

                // 售出时间处理
                $sold_at = $item && $status == 1 && $item->status != $status ? current_time('mysql') : ($item ? $item->sold_at : null);

                $wpdb->update($table, [
                    'code' => $code,
                    'status' => $status,
                    'order_id' => $order_id,
                    'expire_at' => $expire_at,
                    'sold_at' => $sold_at
                ], ['ID' => $item_id]);

                // 更新库存
                self::update_stock($product_id);

                $url = admin_url('admin.php?page=wpcom-cards-items&product_id=' . $product_id . '&action=edit&id=' . $item_id . '&status=success');
                echo '<script>window.location.href="' . $url . '";</script>';
                exit;
            } else {
                // 批量添加
                $lines = preg_split('/\r\n|\r|\n/', sanitize_textarea_field($_POST['wpcom_code']));
                $count = 0;
                $repeat = 0;
                foreach ($lines as $code) {
                    $code = trim($code);
                    if ($code === '') continue;
                    if ($this->is_code_exists($code, $product_id)) {
                        $repeat++;
                        continue;
                    }
                    $wpdb->insert($table, [
                        'product_id' => $product_id,
                        'code' => $code,
                        'status' => $status,
                        'order_id' => $order_id,
                        'expire_at' => $expire_at,
                        'created_at' => current_time('mysql')
                    ]);
                    $count++;
                }

                // 更新库存
                self::update_stock($product_id);

                // 跳转到列表页并提示
                $url = admin_url('admin.php?page=wpcom-cards-items&product_id=' . $product_id . '&status=success&count=' . $count . '&repeat=' . $repeat);
                echo '<script>window.location.href="' . $url . '";</script>';
                exit;
            }
        }
        return '';
    }

    function get_card_product($id){
        global $wpdb;
        $table = $wpdb->prefix . self::$products_table;
        if(!$id) return null;
        $product = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE ID = %d", $id));
        if ($product && isset($product->expire_at) && $product->expire_at && strtotime($product->expire_at) < time()) {
            // 视为下架
            $product->status = 1;
            $wpdb->update(
                $table,
                ['status' => 1],
                ['ID' => $id]
            );
        }
        return $product;
    }

    function get_card_product_data(){
        $res = ['result' => 0];
        if (WPCOM_MP_can_setup_order()) {
            $product_id = isset($_POST['product_id']) ? sanitize_text_field($_POST['product_id']) : '';
            $post_id = isset($_POST['post_id']) ? sanitize_text_field($_POST['post_id']) : '';
            $email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
            $has_email = $email && get_current_user_id() == 0;

            if($has_email && !is_email($email)){
                $res = ['result' => -11, 'msg' => '请输入正确的邮箱地址'];
                wp_send_json($res);
            }

            if($product_id) {
                $product = $this->get_card_product($product_id);
                if($product && isset($product->status) && $product->status == 1){
                    $res = ['result' => -11, 'msg' => '卡密已下架,无法购买'];
                    wp_send_json($res);
                }
            }

            if(isset($product) && $product->ID){
                $points = $product->price_type == 1;
                $items = [
                    [
                        'title' => $product->title,
                        'price' => number_format($product->price, $points ? 0 : 2, '.', ''),
                        'url' => get_permalink($post_id),
                        'number' => 1,
                        'set_number' => 1,
                        'type' => 'card',
                        'type_id' => $product_id,
                        'data' => [
                            'post_id' => $post_id
                        ]
                    ]
                ];

                if(!$this->stock_check(true, $items[0])){
                    $res = ['result' => -10, 'msg' => '库存不足,无法购买'];
                }else{
                    if($has_email) $items[0]['data']['email'] = $email;
                    $res['total'] = number_format($product->price, $points ? 0 : 2, '.', '');
                    $res['items'] = $items;
                    if($points) $res['points'] = $points ? 1 : 0;
                    $res = Order::setup_order_data($res);
                }
            }else{
                $res['result'] = -2;
                $res['msg'] = '产品信息获取失败';
            }
        }else{ // 未登录
            $res['result'] = -1;
        }
        wp_send_json($res);
    }

    function stock_check($check, $item){
        global $wpdb;
        $product_id = $item['type_id'];
        $number = 1;
        if(isset($item['update_number']) && $item['update_number']){
            $number = $item['update_number'];
        }else if(isset($item['number']) && $item['number']){
            $number = $item['number'];
        }else if(isset($item['data']) && $item['data']){
            $data = json_decode($item['data'], true);
            $number = $data['quantity'] ?? $number;
        }

        // 更新已过期卡密状态为作废
        $items_table = $wpdb->prefix . self::$items_table;
        $now = current_time('mysql');
        $wpdb->query($wpdb->prepare(
            "UPDATE $items_table SET status = 2 WHERE product_id = %d AND status = 0 AND expire_at IS NOT NULL AND expire_at < %s",
            $product_id, $now
        ));

        // 检查库存
        $product = $this->get_card_product($product_id);
        if($product && $product->stock < $number){
            return false;
        }

        return $check;
    }

    function payment_complete($item, $order){
        global $wpdb;
        $product_id = $item->type_id;
        $number = 1;
        if (isset($item->data)) {
            // data 可能是 JSON 字符串,需要先解码
            $data = is_array($item->data) ? $item->data : json_decode($item->data, true);
            if (is_array($data) && isset($data['quantity'])) {
                $number = intval($data['quantity']);
            }
        }
        $items_table = $wpdb->prefix . self::$items_table;

        // 1. 选取未售出卡密
        $now = current_time('mysql');
        $cards = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM $items_table WHERE product_id = %d AND status = 0 AND (expire_at IS NULL OR expire_at >= %s) LIMIT %d",
            $product_id, $now, $number
        ));

        if(!$cards || count($cards) < $number){
            // 库存不足,建议做异常处理
            return;
        }

        $card_data = [];
        foreach($cards as $card){
            $card_data[] = [
                'code' => $card->code,
                'expire_at' => $card->expire_at,
                // 可加更多字段
            ];
            // 标记为已售出
            $wpdb->update($items_table, [
                'status' => 1,
                'order_id' => $order->ID,
                'sold_at' => current_time('mysql')
            ], ['ID' => $card->ID]);
        }

        // 2. 写入订单data字段
        $order_item_table = $wpdb->prefix . 'wpcom_order_items';

        // 读取原有 data 字段
        $old_data = $wpdb->get_var($wpdb->prepare(
            "SELECT data FROM $order_item_table WHERE ID = %d", $item->ID
        ));
        $old_data_arr = [];
        if ($old_data) {
            $old_data_arr = is_array($old_data) ? $old_data : json_decode($old_data, true);
            if (!is_array($old_data_arr)) $old_data_arr = [];
        }

        // 合并新卡密信息
        $old_data_arr['cards'] = $card_data;
        $old_data_arr['desc'] = $this->get_card_product($product_id)->description ?? '';

        // 写回
        $wpdb->update($order_item_table, [
            'data' => wp_json_encode($old_data_arr, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
        ], ['ID' => $item->ID]);

        // 3. 重新统计库存
        self::update_stock($product_id);

        // 4. 邮件通知
        if($order->user){
            $user = get_user_by('ID', $order->user);
            if($user && $user->user_email && !wpcom_is_empty_mail($user->user_email)) {
                $email = $user->user_email;
            }
        }else if(isset($old_data_arr['email']) && is_email($old_data_arr['email'])){
            $email = $old_data_arr['email'];
        }

        if(isset($email) && $email) $this->send_card_mail($email, $card_data, $old_data_arr['desc']);
    }

    function send_card_mail($email, $card_data, $desc){
        if(empty($email) || empty($card_data)) return;

        if ( is_multisite() ) {
            $site_name = get_network()->site_name;
        } else {
            $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
        }
        $title = '卡密购买成功';
        $content = '亲爱的用户:你好!<br>';
        $content .= '感谢您的购买,以下是您的卡密信息:<br>';
        foreach($card_data as $c){
            $content .= '<br>卡密:' . esc_html($c['code']) . '<br>';
            if(!empty($c['expire_at']) && $c['expire_at'] !== '0000-00-00 00:00:00') $content .= '过期时间:' . esc_html($c['expire_at']) . '<br>';
        }
        if($desc) $content .= '<br>使用说明:<br>' . nl2br(esc_html($desc));
        $headers = array('Content-Type: text/html; charset=UTF-8');

        wp_mail( $email, wp_specialchars_decode( '【' . $site_name . '】' . $title ), $content, $headers );
    }

    public function show_in_order($item, $order) {
        if ($item->type_id && $item->data && $data = json_decode($item->data)) { ?>
            <ul class="member-account-order">
                <li class="member-account-order-item">
                    <div class="member-account-order-label">购买卡密:</div>
                    <div class="member-account-order-value">
                        <?php
                        if(isset($data->post_id) && $data->post_id){
                            echo '<a href="' . esc_url(get_permalink($data->post_id)) . '" target="_blank">' . $item->title . '</a>';
                        }else {
                            echo $item->title;
                        } ?>
                    </div>
                </li>
                <li class="member-account-order-item">
                    <div class="member-account-order-label">卡密信息:</div>
                    <div class="member-account-order-value">
                        <?php
                        $content = '';
                        if(isset($data->cards) && $data->cards){
                            foreach($data->cards as $card){
                                $content .= '<div class="card-items-code">卡密:' . esc_html($card->code) . '</div>';
                                if(!empty($card->expire_at) && $card->expire_at !== '0000-00-00 00:00:00') $content .= '<div class="card-items-expire">过期时间:' . esc_html($card->expire_at) . '</div>';
                            }
                        }
                        if($content) { ?>
                            <div class="member-account-card-items">
                                <?php echo $content; ?>
                            </div>
                        <?php } ?>
                    </div>
                </li>
                <li class="member-account-order-item">
                    <div class="member-account-order-label">使用说明:</div>
                    <div class="member-account-order-value">
                        <?php echo isset($data->desc) && $data->desc ? wp_kses_post(wpautop($data->desc)) : '/';?>
                    </div>
                </li>
            </ul>
        <?php }
    }

    function get_card_item($id) {
        global $wpdb;
        $table = $wpdb->prefix . self::$items_table;
        if(!$id) return null;
        return $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE ID = %d", $id));
    }

    public function shortcode($atts) {
        global $wpcom_member;
        $atts = shortcode_atts([
            'id' => ''
        ], $atts, 'wpcom-cards');

        $ids = array_filter(array_map('intval', preg_split('/[,,]/u', $atts['id'])));
        if (!$ids) return '<div>未指定卡密产品ID</div>';

        $attr = [
            'ids' => $ids,
            'products' => []
        ];

        foreach ($ids as $id) {
            $product = $this->get_card_product($id);
            if (!$product) continue;
            $attr['products'][] = $product;
        }

        $output = $wpcom_member->load_template('cards', $attr);
        return '<div class="wp-block-wpcom-cards">' . $output . '</div>';
    }

    private function is_code_exists($code, $product_id, $exclude_id = 0) {
        global $wpdb;
        $table = $wpdb->prefix . self::$items_table;
        $sql = "SELECT COUNT(*) FROM $table WHERE code = %s AND product_id = %d";
        $params = [$code, $product_id];
        if ($exclude_id) {
            $sql .= " AND ID != %d";
            $params[] = $exclude_id;
        }
        return $wpdb->get_var($wpdb->prepare($sql, ...$params)) > 0;
    }

    public static function update_stock($product_id) {
        global $wpdb;
        $products_table = $wpdb->prefix . self::$products_table;
        $items_table = $wpdb->prefix . self::$items_table;

        // 更新过期卡密作废
        $now = current_time('mysql');
        $wpdb->query($wpdb->prepare(
            "UPDATE $items_table SET status = 2 WHERE product_id = %d AND status = 0 AND expire_at IS NOT NULL AND expire_at < %s",
            $product_id, $now
        ));

        // 统计未售出卡密数量
        $stock = (int) $wpdb->get_var(
            $wpdb->prepare(
                "SELECT COUNT(*) FROM $items_table WHERE product_id = %d AND status = 0",
                $product_id
            )
        );

        // 更新产品表的 stock 字段
        $wpdb->update(
            $products_table,
            ['stock' => $stock],
            ['ID' => $product_id]
        );
    }

    private static function init_database(){
        global $wpdb;
        $products_table = $wpdb->prefix . self::$products_table;
        $items_table = $wpdb->prefix . self::$items_table;
        if( $wpdb->get_var("SHOW TABLES LIKE '$products_table'") !== $products_table ){
            $charset_collate = $wpdb->get_charset_collate();
            require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
            $sql = "CREATE TABLE $products_table (
                ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
                title VARCHAR(255) NOT NULL,
                price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
                price_type TINYINT(1) NOT NULL DEFAULT 0,
                stock INT UNSIGNED NOT NULL DEFAULT 0,
                description TEXT NULL,
                status TINYINT(1) NOT NULL DEFAULT 1,
                expire_at DATETIME DEFAULT NULL,
                created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
                updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                PRIMARY KEY (ID)
            ) $charset_collate;";
            dbDelta($sql);
        }
        if( $wpdb->get_var("SHOW TABLES LIKE '$items_table'") !== $items_table ){
            $charset_collate = $wpdb->get_charset_collate();
            require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
            $sql = "CREATE TABLE $items_table (
                ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
                product_id BIGINT(20) UNSIGNED NOT NULL,
                code TEXT NOT NULL,
                status TINYINT(1) NOT NULL DEFAULT 0,
                order_id BIGINT(20) UNSIGNED DEFAULT NULL,
                sold_at DATETIME DEFAULT NULL,
                remark TEXT NULL,
                expire_at DATETIME DEFAULT NULL,
                created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
                PRIMARY KEY (ID),
                KEY product_id (product_id),
                KEY status (status)
            ) $charset_collate;";
            dbDelta($sql);
        }
    }
}

new Cards();

Youez - 2016 - github.com/yon3zu
LinuXploit