403Webshell
Server IP : 43.141.50.122  /  Your IP : 113.219.202.173
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/vip.php
<?php
namespace WPCOM\Member;

defined( 'ABSPATH' ) || exit;

class VIP {
    public $vip_action_type = array(
        'new' => '开通',
        'renew' => '续费',
        'upgrade' => '升级'
    );
    public $vip_lifetime = array(
        'week' => '周付',
        'month' => '月付',
        'season' => '季付',
        'halfyear' => '半年付',
        'year' => '年付',
        'lifetime' => '终身'
    );
    function __construct(){
        add_filter( 'wpcom_user_display_name', array( $this, 'display_name' ), 8, 3);
        add_filter( 'wpcom_is_login', array( $this, 'show_vip_flag' ));
        add_filter( 'wpcom_init_plugin_options', array( $this, 'vip_settings' ));
        add_filter( 'wpcom_setup_exists_order', array( $this, 'check_exists_order'), 10, 3);
        add_filter( 'wpcom_order_types', array( $this, 'vip_order_types') );
        add_filter( 'wpcom_themer_get_taxs', array( $this, 'get_roles'), 10, 2 );
        add_filter( 'wp_insert_post_data', array( $this, 'pre_insert_post'), 10 ,2 );
        add_filter( 'rest_pre_insert_post', array( $this, 'rest_pre_insert_post') );
        add_filter( 'authenticate', array( $this, 'authenticate' ), 100 );
        add_filter( 'wpcom_tougao_notice', array( $this, 'tougao_notice'), 10, 2);
        add_filter( 'get_comment_author', array($this, 'comment_author_vip_flag'), 20, 3);
        add_filter( 'comment_author', array($this, 'comment_author_vip_flag'), 20, 2);
        add_filter( 'the_author', array($this, 'author_vip_flag'), 20);
        add_filter( 'manage_pages_columns', array($this, 'change_author_column') );
        add_filter( 'manage_posts_columns', array($this, 'change_author_column') );

        add_action( 'init', array($this, 'init') );
        add_action( 'admin_init', array($this, 'schedule_event') );
        add_action( 'wpcom_vip_schedule_event', array( $this, 'cron_action') );
        add_action( 'show_user_profile', array( $this, 'edit_user_vip' ) );
        add_action( 'edit_user_profile', array( $this, 'edit_user_vip' ) );
        add_action( 'personal_options_update', array( $this, 'save_user_vip' ) );
        add_action( 'edit_user_profile_update', array( $this, 'save_user_vip' ) );
        add_action( 'wpcom_member_account_after_dio', array( $this, 'show_vip_info' ), 8 );
        add_action( 'wpcom_member_account_after_dio', array( $this, 'show_vip_info_close' ), 20 );
        add_action( 'wp_ajax_wpcom_vip_modal', array($this, 'vip_modal'));
        add_action( 'wp_ajax_nopriv_wpcom_vip_modal', array($this, 'vip_modal'));
        add_action( 'wp_ajax_wpcom_get_vip_data', array($this, 'get_vip_data'));
        add_action( 'wp_ajax_nopriv_wpcom_get_vip_data', array($this, 'get_vip_data'));
        add_action( 'wp_ajax_wpcom_vip_unlock', array($this, 'vip_unlock'));
        add_action( 'wp_ajax_nopriv_wpcom_vip_unlock', array($this, 'vip_unlock'));
        add_action( 'wpcom_order_item_detail_vip-new', array($this, 'show_in_order'));
        add_action( 'wpcom_order_item_detail_vip-renew', array($this, 'show_in_order'));
        add_action( 'wpcom_order_item_detail_vip-upgrade', array($this, 'show_in_order'));
        add_action( 'wpcom_vip-new_payment_complete', array($this, 'payment_complete'), 10, 2);
        add_action( 'wpcom_vip-renew_payment_complete', array($this, 'payment_complete'), 10, 2);
        add_action( 'wpcom_vip-upgrade_payment_complete', array($this, 'payment_complete'), 10, 2);
        add_action( 'admin_notices', array($this, 'post_limit_error') );
        add_action( 'manage_pages_custom_column', array($this, 'author_column'), 10, 2 );
        add_action( 'manage_posts_custom_column', array($this, 'author_column'), 10, 2 );

        if(defined('WWA_VERSION')){
            add_action('rest_api_init', array($this, 'for_rest_api') );
            add_filter('WWA_rest_user_metas', array($this, 'rest_user_metas'), 10, 2);
        }
    }

    function init(){
        register_block_type('wpcom/vip', array(
            'render_callback' => function ($attr, $content) {
                global $wpcom_member;
                $user_id = get_current_user_id();
                $attr['is_vip'] = $user_id && VIP::get_user_vip($user_id);
                $attr['content'] = $attr['is_vip'] && isset($attr['vip_content']) && trim($attr['vip_content']) !== '' ? $attr['vip_content'] : $attr['content'];
                $output = $wpcom_member->load_template('vip-block', $attr);
                return '<div class="wp-block-wpcom-vip'.(trim($attr['content']) === '' ? ' without-desc' : '').'">' . $output . '</div>';
            }
        ));
    }

    function schedule_event(){
        if (!wp_next_scheduled ( 'wpcom_vip_schedule_event' )) wp_schedule_event(time(), 'hourly', 'wpcom_vip_schedule_event');
    }

    function cron_action(){
        global $wpdb;
        // 重置昨天过期会员的用户分组
        $args = array(
            'meta_key'     => $wpdb->get_blog_prefix() . 'vip_end_date',
            'meta_value'   => date('Y-m-d', time() - 86400),
            'meta_compare' => '=',
            'number' => 200
        );
        $users = get_users($args);
        if($users && isset($users[0])){
            foreach($users as $user){
                if(isset($user->ID)) $this->update_role($user->ID, true);
            }
        }
    }

    public static function vip_btn_text($type = 'new'){
        global $wpmx_options;
        $texts = [
            'new' => '开通会员',
            'renew' => '会员续费',
            'upgrade' => '会员升级'
        ];
        if($type && $wpmx_options && isset($wpmx_options['vip_btn_'. $type]) && trim($wpmx_options['vip_btn_' . $type]) !== ''){
            return $wpmx_options['vip_btn_' . $type];
        }else if($type && isset($texts[$type])) {
            return $texts[$type];
        }
    }

    public static function display_name($name, $user_id, $type=''){
        if($type === 'full' || $type === 'vip'){
            $vip = self::get_user_vip($user_id);
            if ( $vip && $vip['icon']) {
                $name .= '<span class="user-vip-flag"><span class="user-vip-flag-inner"><img class="user-vip-flag-icon j-lazy" src="'.esc_url($vip['icon']).'" alt="'.esc_attr($vip['title']).'" title="'.esc_attr($vip['title']).'"></span></span>';
            }
        }
        return $name;
    }

    function show_vip_flag($res){
        $user_id = get_current_user_id();
        if($user_id && $vip = $this->get_user_vip($user_id)){
            $res['vip'] = array(
                'id' => $vip['id'],
                'icon' => $vip['icon'],
                'title' => $vip['title']
            );
        }
        return $res;
    }

    // 同时兼容 get_comment_author 和 comment_author 过滤器
    function comment_author_vip_flag($anthor, $comment_ID, $comment = null){
        $comment = $comment ?? get_comment($comment_ID);
        if($comment->user_id){
            $comment_author = preg_match('/class=&quot;user-vip-flag&quot;/i', $anthor) ? $comment->comment_author : $anthor;
            $anthor = $this->display_name($comment_author, $comment->user_id, 'full');
        }
        return $anthor;
    }

    function author_vip_flag($display_name){
        global $authordata;
        if ( is_admin() && ! wp_doing_ajax() ) {
            return $display_name;
        }

        if($display_name !== null && is_object( $authordata )){
            $display_name = $this->display_name($display_name, $authordata->ID, 'full');
        }

        return $display_name;
    }

    function change_author_column($columns){
        if($columns && isset($columns['author'])){
            $_columns = [];
            foreach($columns as $key => $val){
                if($key === 'author'){
                    $_columns['custom_author'] = $val;
                }else{
                    $_columns[$key] = $val;
                }
            }
            $columns = $_columns;
        }
        return $columns;
    }

    function author_column($column, $post_id){
        if ( $column === 'custom_author' ) {
            global $authordata;
            $display_name = $authordata->display_name ?? '';

            if ( ! empty( $display_name ) ) {
                $post = get_post($post_id);
                $args = array(
                    'post_type' => $post->post_type,
                    'author'    => get_the_author_meta( 'ID' ),
                );

                if($display_name !== null && is_object( $authordata )){
                    $display_name = $this->display_name($display_name, $authordata->ID, 'full');
                }

                echo $this->get_edit_link( $args, $display_name );
            } else {
                echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">' . __( '(no author)' ) . '</span>';
            }
        }
    }

    protected function get_edit_link( $args, $link_text, $css_class = '' ) {
		$url = add_query_arg( $args, 'edit.php' );

		$class_html   = '';
		$aria_current = '';

		if ( ! empty( $css_class ) ) {
			$class_html = sprintf(
				' class="%s"',
				esc_attr( $css_class )
			);

			if ( 'current' === $css_class ) {
				$aria_current = ' aria-current="page"';
			}
		}

		return sprintf(
			'<a href="%s"%s%s>%s</a>',
			esc_url( $url ),
			$class_html,
			$aria_current,
			$link_text
		);
	}

    public static function show_vip_info($tougao = true){
        global $options, $tougao, $is_lifetime;
        $user_id = get_current_user_id();
        if($user_id && !empty(self::get_vip_types())){
            $vip = self::get_user_vip($user_id);
            $is_lifetime = $vip && isset($vip['expired']) && self::is_lifetime_vip($vip['expired']);
            $tougao = $tougao && isset($options['tougao_on']) && $options['tougao_on']=='1';
            if ( $vip ) {
                echo '<div class="member-account-vip"><div class="member-account-vip-name">'.($vip['icon']?'<span class="user-vip-flag"><span class="user-vip-flag-inner"><img class="user-vip-flag-icon j-lazy" src="'.esc_url($vip['icon']).'" alt="'.esc_attr($vip['title']).'" title="'.esc_attr($vip['title']).'"></span></span>':'').$vip['title'].'</div><span class="member-account-vip-expired">有效期至<b>'. ($is_lifetime ? '终身' : $vip['expired']) . '</b></span></div>';
                if(self::vip_can_upgrade($vip) && !$is_lifetime){
                    echo '<div class="member-account-vip-action"><a class="wpcom-btn btn-danger btn-block j-vip-renew" href="#">'. self::vip_btn_text('renew').'</a><a class="member-account-vip-btn wpcom-btn btn-vip-upgrade btn-block j-vip-upgrade" href="#">'. self::vip_btn_text('upgrade') .'</a></div>';
                }else if(self::vip_can_upgrade($vip)){
                    if($tougao) echo '<div class="member-account-vip-action">';
                    echo '<a class="member-account-vip-btn wpcom-btn btn-vip-upgrade btn-block j-vip-upgrade" href="#">'. self::vip_btn_text('upgrade') .'</a>';
                }else{
                    if($tougao && !$is_lifetime) echo '<div class="member-account-vip-action">';
                    if(!$is_lifetime) echo '<a class="wpcom-btn member-account-vip-btn btn-danger btn-block j-vip-renew" href="#">'. self::vip_btn_text('renew').'</a>';
                }
            }else{
                if($tougao) echo '<div class="member-account-vip-action">';
                echo '<a class="member-account-vip-btn wpcom-btn btn-danger btn-block j-vip-new" href="#">'.wpmx_icon('vip-crown', false).self::vip_btn_text('new').'</a>';
            }
        }
    }

    public static function show_vip_info_close(){
        global $tougao, $is_lifetime;
        $user_id = get_current_user_id();
        if($user_id && isset($tougao) && $tougao && !empty(self::get_vip_types())){
            $vip = self::get_user_vip($user_id);
            if($vip){
                if( (self::vip_can_upgrade($vip) && $is_lifetime) || (!self::vip_can_upgrade($vip) && !$is_lifetime)){
                    echo '</div>';
                }
            }else if(!$vip){
                echo '</div>';
            }
        }
    }

    function vip_modal(){
        global $wpcom_member;
        $res = array('result' => 0);
        $user_id = get_current_user_id();
        $type = isset($_POST['type']) ? sanitize_text_field($_POST['type']) : 'new';
        if($user_id || $type === 'new'){
            $current_vip = self::get_user_vip();
            if($current_vip && $type === 'new'){
                $res['result'] = -3;
                $res['msg'] = '您已是会员,无需重复开通!';
            }else if(!$current_vip && $type !== 'new'){
                $res['result'] = -4;
                $res['msg'] = '您还不是会员,无法执行此操作!';
            }else if($type === 'upgrade' && !$this->vip_can_upgrade($current_vip)){
                $res['result'] = -5;
                $res['msg'] = '抱歉,当前会员类型无法执行升级操作!';
            }else if($current_vip && $type === 'renew' && $this->is_lifetime_vip($current_vip['expired'])){
                $res['result'] = -6;
                $res['msg'] = '抱歉,您已经是终身会员了!';
            }else{
                $vip_types = self::get_vip_types();
                if($vip_types && !empty($vip_types)){
                    if($current_vip && isset($current_vip['expired'])){
                        $datetime = date_create($current_vip['expired'], wp_timezone());
                        $current_vip['expired'] = $datetime;
                    }
                    if($type === 'upgrade'){
                        $pass = false;
                        foreach($vip_types as $key => $val){
                            if($key === $current_vip['id'] || !$pass){
                                unset($vip_types[$key]);
                                $pass = true;
                            } else {
                                // 计算升级差价
                                $vip_types[$key]['upgrade_price'] = $this->vip_upgrade_price($current_vip, $val);
                            }
                        }
                    }
                    $args = array(
                        'type' => $type,
                        'current_vip' => $current_vip,
                        'vip_types' => $vip_types
                    );
                    $output = $wpcom_member->load_template('vip-modal', $args);
                    foreach($vip_types as $i => $_type){
                        if(isset($vip_types[$i]['privileges'])) unset($vip_types[$i]['privileges']);
                    }
                    $res['tpl'] = $output;
                    $res['data'] = $vip_types;
                    if($type === 'new'){
                        $res['timestamp'] = current_time('timestamp', 1);
                    }else if($current_vip['expired']){
                        $res['timestamp'] = $current_vip['expired']->getTimestamp();
                    }
                }else{
                    $res['result'] = -2;
                    $res['msg'] = '暂无可开通的会员类型,请联系网站管理员';
                }
            }
        }else{
            $res['result'] = -1;
        }
        wp_send_json($res);
    }

    function get_vip_data($return = false){
        $user_id = get_current_user_id();
        $res = array('result' => 0);
        if ($user_id) {
            $action_type = isset($_POST['action_type']) ? sanitize_text_field($_POST['action_type']) : '';
            $vip_type = isset($_POST['vip_type']) ? sanitize_text_field($_POST['vip_type']) : '';
            $vip_lifetime = isset($_POST['vip_lifetime']) ? sanitize_text_field($_POST['vip_lifetime']) : '';
            if (in_array($action_type, array('new', 'renew', 'upgrade')) && $vip_type && ($action_type === 'upgrade' || $vip_lifetime) ) {
                $vip_types = self::get_vip_types();
                $vip = $this->get_user_vip($user_id);
                if($action_type === 'upgrade' && isset($vip_types[$vip_type]) && $vip){
                    $upgrade_price = $this->vip_upgrade_price($vip, $vip_types[$vip_type]);
                    if($upgrade_price && is_numeric($upgrade_price)){
                        $expired = date_create($vip['expired'], wp_timezone());
                        $expired_text = $expired && date_format($expired, 'Y') === '9999' ? '终身' : date_format($expired, 'Y年m月d日');
                        $items = array(
                            array(
                                'title' => $vip['title'] . $this->vip_action_type[$action_type] . $vip_types[$vip_type]['title'] . '-' . $expired_text,
                                'price' => number_format($upgrade_price ?: 0, 2, '.', ''),
                                'number' => 1,
                                'type' => 'vip-'.$action_type,
                                'type_id' => $vip_type.'::'.$vip['id']
                            )
                        );
                        $res['total'] = number_format($upgrade_price ?: 0, 2, '.', '');
                        $res['items'] = $items;
                        $res = Order::setup_order_data($res);
                    }
                }else if(!empty($vip_types) && isset($vip_types[$vip_type]) && isset($vip_types[$vip_type]['time']) && in_array($vip_lifetime, $vip_types[$vip_type]['time'])){
                    $price = $vip_types[$vip_type][$vip_lifetime.'_price'];
                    if($action_type === 'new'){
                        $begin = current_time('Y-m-d');
                    }else if($action_type === 'renew'){
                        $end_date = $vip ? get_user_option('vip_end_date', $user_id) : '';
                        $begin = $end_date ? $end_date : current_time('Y-m-d');
                    }
                    $data = array(
                        'begin_date' => $begin,
                        'end_date' => $this->get_expired_date($begin, $vip_lifetime)
                    );
                    $items = array(
                        array(
                            'title' => $this->vip_action_type[$action_type] . $vip_types[$vip_type]['title'] . '-' . $this->vip_lifetime[$vip_lifetime],
                            'price' => number_format($price ?: 0, 2, '.', ''),
                            'number' => 1,
                            'type' => 'vip-'.$action_type,
                            'type_id' => $vip_type.'::'.$vip_lifetime,
                            'data' => $data
                        )
                    );
                    $res['total'] = number_format($price ?: 0, 2, '.', '');
                    $res['items'] = $items;
                    $res = Order::setup_order_data($res);
                }else{
                    $res['result'] = -3;
                    $res['msg'] = '会员类型出错,请稍后再试';
                }
            } else {
                $res['result'] = -2;
            }
        } else { // 未登录
            $res['result'] = -1;
        }
        if($return){
            return $res;
        }else{
            wp_send_json($res);
        }
    }

    public function vip_unlock(){
        $user_id = get_current_user_id();
        $res = array('result' => 0);
        if ($user_id) {
            if(Order::check_sign($_POST)){
                $type = isset($_POST['type']) ? sanitize_text_field($_POST['type']) : '';
                $post_id = isset($_POST['post_id']) ? sanitize_text_field($_POST['post_id']) : 0;
                $id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : '';
                $type_id = $id ? $post_id.'::'.$id : $post_id;
                $items = isset($_POST['items']) ? map_deep( $_POST['items'], 'sanitize_text_field' ) : array();
                $total = isset($_POST['total']) ? sanitize_text_field($_POST['total']) : 0;
                if(($total == 0 || $total === '0.00') && isset($items[0]) && isset($items[0]['type']) && $items[0]['type'] === $type && $items[0]['type_id'] == $type_id){
                    if(self::is_vip_visable($type . '/' . $type_id)){ // 可见,比如不限次数或者已经解锁
                        $res['result'] = 1;
                    }else{ // 不可见
                        $free_times = self::get_vip_unlock_times($user_id);
                        if($free_times > 0){
                            $flag = $user_id.'_' . current_time('Ymd');
                            $name = '_vip_' . $flag . '_' . $type . '/' . $type_id;
                            $value = '_vip_free_' . $flag;
                            if(!self::add_vip_unlock_log($name, $value)){
                                $res['result'] = -5;
                                $res['msg'] = '解锁失败,请稍后再试!';
                            }
                        }else{
                            $res['result'] = -4;
                            $res['msg'] = '今日的免费解锁次数已用尽';
                        }
                    }
                }else{
                    $res['result'] = -3;
                    $res['total'] = $items[0]['type_id'] == $id;
                    $res['ww'] = $items[0]['type_id'];
                    $res['ee'] = $id;
                    $res['msg'] = '解锁信息校验失败,请重试';
                }
            }else{
                $res['result'] = -2;
                $res['msg'] = '解锁信息校验失败,请重试';
            }
        } else { // 未登录
            $res['result'] = -1;
            $res['msg'] = '请登录后操作!';
        }
        wp_send_json($res);
    }

    static public function get_vip_types(){
        if(isset($GLOBALS['vip_types'])) return $GLOBALS['vip_types'];
        $options = $GLOBALS['wpmx_options'];
        $types = [];
        if(isset($options['vip_id']) && !empty($options['vip_id']) && is_array($options['vip_id'])){
            foreach($options['vip_id'] as $i => $id){
                $privileges = [];
                $_privileges = isset($options['vip_privileges']) && isset($options['vip_privileges'][$i]) ? $options['vip_privileges'][$i] : '';
                if(!empty($_privileges)){
                    // 处理二选一选项
                    if(in_array('download_free', $_privileges) && in_array('download_discount', $_privileges)){
                        $key = array_search('download_discount', $_privileges);
                        unset($_privileges[$key]);
                    }
                    if(in_array('content_free', $_privileges) && in_array('content_discount', $_privileges)){
                        $key = array_search('content_discount',$_privileges);
                        unset($_privileges[$key]);
                    }
                    if(in_array('copy_free', $_privileges) && in_array('copy_discount', $_privileges)){
                        $key = array_search('copy_discount',$_privileges);
                        unset($_privileges[$key]);
                    }
                }
                if(!empty($_privileges)){
                    $free_limit = false;
                    foreach($_privileges as $v){
                        if($v){
                            switch($v){
                                case 'download_free':
                                    $privileges['download'] = 0;
                                    $free_limit = true;
                                    break;
                                case 'download_discount':
                                    $privileges['download'] = isset($options['vip_download_discount']) && isset($options['vip_download_discount'][$i]) ? (double)$options['vip_download_discount'][$i] : 10;
                                    $privileges['download'] = $privileges['download'] / 10;
                                    break;
                                case 'content_free':
                                    $privileges['content'] = 0;
                                    $free_limit = true;
                                    break;
                                case 'content_discount':
                                    $privileges['content'] = isset($options['vip_content_discount']) && isset($options['vip_content_discount'][$i]) ? (double)$options['vip_content_discount'][$i] : 10;
                                    $privileges['content'] = $privileges['content'] / 10;
                                    break;
                                case 'copy_free':
                                    $privileges['copy'] = 0;
                                    $free_limit = true;
                                    break;
                                case 'copy_discount':
                                    $privileges['copy'] = isset($options['vip_copy_discount']) && isset($options['vip_copy_discount'][$i]) ? (double)$options['vip_copy_discount'][$i] : 10;
                                    $privileges['copy'] = $privileges['copy'] / 10;
                                    break;
                                case 'publish_post':
                                    $default_month_post_limit = isset($options['default_month_post_limit']) ? $options['default_month_post_limit'] : -1;
                                    $default_day_post_limit = isset($options['default_day_post_limit']) ? $options['default_day_post_limit'] : -1;
                                    $privileges['month_post_limit'] = isset($options['vip_month_post_limit']) && isset($options['vip_month_post_limit'][$i]) ? (int)$options['vip_month_post_limit'][$i] : $default_month_post_limit;
                                    $privileges['day_post_limit'] = isset($options['vip_day_post_limit']) && isset($options['vip_day_post_limit'][$i]) ? (int)$options['vip_day_post_limit'][$i] : $default_day_post_limit;
                                    break;
                            }
                        }
                    }
                }
                $type = [
                    'index' => $i,
                    'id' => $id,
                    'title' => isset($options['vip_title']) && isset($options['vip_title'][$i]) ? $options['vip_title'][$i] : '',
                    'icon' => isset($options['vip_icon']) && isset($options['vip_icon'][$i]) && $options['vip_icon'][$i] ? wp_get_attachment_url($options['vip_icon'][$i]) : '',
                    'time' => isset($options['vip_time']) && isset($options['vip_time'][$i]) ? $options['vip_time'][$i] : '',
                    'week_price' => isset($options['vip_week_price']) && isset($options['vip_week_price'][$i]) && is_numeric($options['vip_week_price'][$i]) ? number_format($options['vip_week_price'][$i], 2, '.', '') : '',
                    'month_price' => isset($options['vip_month_price']) && isset($options['vip_month_price'][$i]) && is_numeric($options['vip_month_price'][$i]) ? number_format($options['vip_month_price'][$i], 2, '.', '') : '',
                    'season_price' => isset($options['vip_season_price']) && isset($options['vip_season_price'][$i]) && is_numeric($options['vip_season_price'][$i]) ? number_format($options['vip_season_price'][$i], 2, '.', '') : '',
                    'halfyear_price' => isset($options['vip_halfyear_price']) && isset($options['vip_halfyear_price'][$i]) && is_numeric($options['vip_halfyear_price'][$i]) ? number_format($options['vip_halfyear_price'][$i], 2, '.', '') : '',
                    'year_price' => isset($options['vip_year_price']) && isset($options['vip_year_price'][$i]) && is_numeric($options['vip_year_price'][$i]) ? number_format($options['vip_year_price'][$i], 2, '.', '') : '',
                    'lifetime_price' => isset($options['vip_lifetime_price']) && isset($options['vip_lifetime_price'][$i]) && is_numeric($options['vip_lifetime_price'][$i]) ? number_format($options['vip_lifetime_price'][$i], 2, '.', '') : '',
                    'privileges' => $privileges,
                    'description' => isset($options['vip_desc']) && isset($options['vip_desc'][$i]) ? wp_kses_post(wpautop($options['vip_desc'][$i])) : ''
                ];
                if($privileges && isset($privileges['day_post_limit'])){
                    $type['group'] = isset($options['vip_group']) && isset($options['vip_group'][$i]) ? $options['vip_group'][$i] : '';
                    $type['role'] = isset($options['vip_role']) && isset($options['vip_role'][$i]) ? $options['vip_role'][$i] : '';
                }
                if(isset($options['aff']) && $options['aff'] == '1'){
                    $aff_rate = isset($options['vip_aff_rate']) && isset($options['vip_aff_rate'][$i]) ? $options['vip_aff_rate'][$i] : '';
                    $type['aff_rate'] = trim($aff_rate) === '' ? '' : (float)$aff_rate;
                }
                if(isset($free_limit) && $free_limit){
                    $type['free_limit'] = isset($options['vip_free_limit']) && isset($options['vip_free_limit'][$i]) ? (int)$options['vip_free_limit'][$i] : 0;
                }
                $types[$id] = $type;
            }
        }
        $GLOBALS['vip_types'] = $types;
        return $types;
    }

    static public function get_user_vip($user_id = ''){
        global $user_vip;
        $user_id = $user_id ? $user_id : get_current_user_id();
        if($user_id && $user_vip && isset($user_vip[$user_id])) return $user_vip[$user_id];
        if(!$user_vip) $user_vip = array();
        $types = self::get_vip_types();
        if($user_id && !empty($types)){
            $vip_type = (string) get_user_option( 'vip_type', $user_id );
            $vip_expired = $vip_type ? get_user_option( 'vip_end_date', $user_id ) : '';
            if($vip_expired && current_time('Y-m-d') <= $vip_expired){
                foreach($types as $id => $type){
                    if((string) $id === $vip_type){
                        $type['begin'] = get_user_option( 'vip_begin_date', $user_id );
                        $type['expired'] = $vip_expired;
                        $user_vip[$user_id] = $type;
                        return $type;
                    }
                }
            }
        }
        $user_vip[$user_id] = false;
        return false;
    }

    static public function get_vip_price($price, $vip_price, $type, $vip = ''){
        $types = self::get_vip_types();
        if(!empty($types)){
            if($vip){
                if($vip_price && isset($vip_price[$vip.'_price']) && $vip_price[$vip.'_price'] !== ''){
                    return (float) $vip_price[$vip.'_price'];
                }else if(isset($types[$vip]) && isset($types[$vip]['privileges'][$type])){
                    return (float) $price * $types[$vip]['privileges'][$type];
                } else {
                    return (float) $price;
                }
            }else{
                $prices = array();
                foreach($types as $id => $vip){
                    $_price = array(
                        'info' => $vip
                    );
                    if($vip_price && isset($vip_price[$id.'_price']) && $vip_price[$id.'_price'] !== ''){
                        $_price['price'] = (float) $vip_price[$id.'_price'];
                    }else if($vip['privileges'] && isset($vip['privileges'][$type])){
                        $_price['price'] = (float) $price * $vip['privileges'][$type];
                    } else {
                        $_price['price'] = (float) $price;
                    }
                    $prices[$id] = $_price;
                }
                $_vip_price = false;
                if(!empty($prices)){
                    foreach($prices as $_p){
                        if(isset($_p['price']) && $_p['price'] != (float) $price){
                            $_vip_price = true;
                            break;
                        }
                    }
                    // VIP专属商品价格会传0,避免会员价格也是0的情况
                    if((float) $price == 0 && !$_vip_price) $_vip_price = true;
                }
                return $_vip_price ? $prices : array();
            }
        }
        return $price;
    }

    static public function get_vip_price_by_user($user_id, $price, $vip_price, $type){
        $vip = self::get_user_vip($user_id);
        if($vip && isset($vip['id']) && $vip_price && isset($vip_price[$vip['id'].'_price']) && $vip_price[$vip['id'].'_price'] !== ''){
            return (float) $vip_price[$vip['id'].'_price'];
        }else if($vip && isset($vip['id'])){
            $types = self::get_vip_types();
            if(isset($types[$vip['id']]) && isset($types[$vip['id']]['privileges'][$type])){
                return (float) $price * $types[$vip['id']]['privileges'][$type];
            }
        }
        return (float) $price;
    }

    function vip_upgrade_price($vip, $upgrade_vip){
        if($vip['expired'] && !is_object($vip['expired'])) $vip['expired'] = date_create($vip['expired'], wp_timezone());
        $is_lifetime = $this->is_lifetime_vip($vip['expired']);
        if($is_lifetime){ // 终身会员直接计算两种会员终身价格的差价
            if($upgrade_vip['time'] && in_array('lifetime', $upgrade_vip['time']) && isset($upgrade_vip['lifetime_price']) && $upgrade_vip['lifetime_price'] && isset($vip['lifetime_price']) && $vip['lifetime_price']){
                $upgrade_price = $upgrade_vip['lifetime_price'] - $vip['lifetime_price'];
                if($upgrade_price && is_numeric($upgrade_price) && $upgrade_price > 0){
                    return $upgrade_price;
                }
            }
        }else if(!empty($upgrade_vip['time'])  && ( !in_array('lifetime', $upgrade_vip['time']) || count($upgrade_vip['time']) >= 2) ){
            if(in_array('lifetime', $upgrade_vip['time'])){
                foreach($upgrade_vip['time'] as $i => $t){
                    if($t === 'lifetime') unset($upgrade_vip['time'][$i]);
                }
            }
            $upgrade_vip['time'] = array_values($upgrade_vip['time']);

            $lifes = array(
                'week' => 7,
                'month' => 31,
                'season' => 91,
                'halfyear' => 183,
                'year' => 365
            );

            // 多种付款周期则根据开通时长计算最佳差价适合的周期
            if(count($upgrade_vip['time']) >= 2){
                $begin = $vip['begin'] ? date_create($vip['begin'], wp_timezone()) : date_create('now', wp_timezone());
                foreach(array_reverse($lifes) as $key => $val){
                    $need_days = intval($val * 0.85);
                    if(in_array($key, $upgrade_vip['time']) &&  in_array($key, $vip['time'])){
                        $vip_days = ($vip['expired']->getTimestamp() - $begin->getTimestamp()) / 86400;
                        if($vip_days && $vip_days >= $need_days && $upgrade_vip[$key . '_price'] && $vip[$key . '_price']) {
                            $price = ($upgrade_vip[$key . '_price'] - $vip[$key . '_price']) / $val;
                            break;
                        }
                    }
                }
                if(!isset($price)) {
                    $_life = $upgrade_vip['time'][count($upgrade_vip['time'])-1];
                    if($_life && isset($lifes[$_life]) && in_array($_life, $vip['time']) && $upgrade_vip[$_life . '_price'] && $vip[$_life . '_price']){
                        $price = ($upgrade_vip[$_life . '_price'] - $vip[$_life . '_price']) / $lifes[$_life];
                    }
                }
            }else{
                $_time = $upgrade_vip['time'][0];
                if($_time && isset($lifes[$_time]) && in_array($_time, $vip['time'])){
                    $price = ($upgrade_vip[$_time . '_price'] - $vip[$_time . '_price']) / $lifes[$_time];
                }
            }
            if(isset($price) && is_numeric($price) && $price > 0){
                $today = date_create(current_time('Y-m-d'), wp_timezone());
                $days = ($vip['expired']->getTimestamp() - $today->getTimestamp()) / 86400;
                if(is_numeric($days)){
                    $days = round($days, 0);
                    return number_format($days * $price, 2, '.', '');
                }
            }
        }
        return false;
    }

    static public function get_vip_price_html($price, $vip_price, $type, $points = false){
        $price = $price ? $price : 0;
        $vip_price = $vip_price && is_array($vip_price) ? $vip_price : array();
        $prices = self::get_vip_price($price, $vip_price, $type);
        $html = '';
        if($prices && !empty($prices) && is_array($prices)){
            $icon = $points ? '<span class="vip-price-icon" aria-label="积分">' . wpmx_icon('points', false) . '</span>' : '<span>¥</span>';
            foreach($prices as $_price){
                if(isset($_price['price'])){
                    $_vip_price = is_numeric($_price['price']) ? (float) $_price['price'] : $price;
                    $_vip_price = $_vip_price == 0 ? '免费' : number_format($_vip_price, $points ? 0 : 2, '.', '');
                    $html .= '<div class="vip-price-item" vip-index="'.$_price['info']['index'].'" vip-id="'.$_price['info']['id'].'">';
                    if(isset($_price['info']) && $_price['info']['icon']){
                        $html .= '<img class="vip-flag-icon j-lazy no-lightbox" src="'.esc_url($_price['info']['icon']).'" alt="'.esc_attr($_price['info']['title']).'">';
                    }
                    $html .= $_price['info']['title'] . '<span class="vip-price">' . ($_vip_price !== '免费' ? $icon : '') . $_vip_price . '</span></div>';
                }
            }
        }
        return apply_filters('wpcom_get_vip_price_html', $html, $price, $vip_price, $type);
    }

    static public function vip_can_upgrade($vip){
        global $wpmx_options;
        if(isset($wpmx_options['vip_upgrade']) && $wpmx_options['vip_upgrade'] && $vip && isset($vip['id'])){
            $vips = self::get_vip_types();
            if($vips && is_array($vips)){
                return end($vips)['id'] !== $vip['id'];
            }
            return true;
        }
        return false;
    }

    static public function is_vip_free($price, $vip_price, $type, $user_id=''){
        $user_id = $user_id ? $user_id : get_current_user_id();
        if($user_id && self::get_user_vip($user_id)){
            $_price = (float)self::get_vip_price_by_user($user_id, $price, $vip_price, $type);
            return $_price == 0;
        }
        return false;
    }

    static public function is_vip_visable($content_id, $user_id = ''){
        global $wpdb;
        $user_id = $user_id ? $user_id : get_current_user_id();
        if($user_id && $content_id && $vip = self::get_user_vip($user_id)){
            if((isset($vip['free_limit']) && $vip['free_limit'] <= 0) || !isset($vip['free_limit'])){
                return true;
            }
            $table = $wpdb->prefix . 'wpcom_sessions';
            $flag = $user_id . '_' . current_time('Ymd');
            $value = '_vip_free_' . $flag;
            $name = '_vip_' . $flag . '_' . $content_id;
            $option = @$wpdb->get_row( $wpdb->prepare("SELECT * FROM `$table` WHERE name = %s AND value = %s", $name, $value) );
            if($option && isset($option->value)) {
                return true;
            }
        }
        return false;
    }

    static function get_vip_unlock_count($user_id=''){
        global $wpdb;
        $user_id = $user_id ? $user_id : get_current_user_id();
        $table = $wpdb->prefix . 'wpcom_sessions';
        $value = '_vip_free_' . $user_id . '_' . current_time('Ymd');
        return $wpdb->get_var( $wpdb->prepare("SELECT count(*) FROM `$table` WHERE value = %s", $value) );
    }

    function add_vip_unlock_log($name, $value){
        global $wpdb;
        $table = $wpdb->prefix . 'wpcom_sessions';
        $session = [];
        $session['name'] = $name;
        $session['value'] = $value;
        $session['expired'] = 24*60*60;
        $session['time'] = current_time( 'mysql', 1 );
        $option = @$wpdb->get_row( $wpdb->prepare("SELECT * FROM `$table` WHERE name = %s AND value = %s", $name, $value) );
        if($option && isset($option->value)) {
            $res = $wpdb->update($table, $session, ['ID' => $option->ID]);
        }else{
            $res = $wpdb->insert($table, $session);
        }
        return $res;
    }

    static public function get_vip_unlock_times($user_id = ''){
        $user_id = $user_id ? $user_id : get_current_user_id();
        if($user_id){
            $vip = self::get_user_vip($user_id);
            $total = isset($vip['free_limit']) && $vip['free_limit'] ? (int) $vip['free_limit'] : 0;
            $used = (int) self::get_vip_unlock_count($user_id);
            $unlock_times = $total - $used;
            $unlock_times = $unlock_times > 0 ? $unlock_times : 0;
            return apply_filters('wpcom_vip_unlock_times', $unlock_times, $user_id);
        }
    }

    function get_expired_date($begin, $lifetime){
        $end_date = '';
        if($lifetime === 'lifetime'){
            $end_date = '9999-09-09';
        }else if($begin = date_create($begin, wp_timezone())){
            $begin = $begin->getTimestamp();
            if($lifetime === 'week'){
                $end_date = $begin + 7*24*60*60;
                $end_date = wp_date('Y-m-d', $end_date);
            }if($lifetime === 'month'){
                $end_date = $begin + 31*24*60*60;
                $end_date = wp_date('Y-m-d', $end_date);
            }if($lifetime === 'season'){
                $end_date = $begin + 91*24*60*60;
                $end_date = wp_date('Y-m-d', $end_date);
            }if($lifetime === 'halfyear'){
                $end_date = $begin + 183*24*60*60;
                $end_date = wp_date('Y-m-d', $end_date);
            }else if($lifetime === 'year'){
                $end_date = (1 + wp_date('Y', $begin)) . wp_date('-m-d', $begin);
            }
        }
        return $end_date;
    }

    public function payment_complete($item, $order){
        if($order && $order->user && $order->status === 'paid' && isset($item->type) && $actin_type = str_replace('vip-', '', $item->type)){
            $type_id = explode('::', $item->type_id);
            $user_id = (int)$order->user;
            if($type_id && isset($type_id[0]) && isset($type_id[1])){
                $vip_type = $type_id[0];
                $vip_lifetime = $type_id[1]; // 付费周期,升级的话此参数为升级前会员类型
                if($actin_type === 'new' || $actin_type === 'renew'){
                    $data = $item->data ? json_decode($item->data) : null;
                    if($data && $data->begin_date && $data->end_date){
                        $begin_date = $data->begin_date;
                        $end_date = $data->end_date;
                        if($actin_type === 'renew'){
                            $vip_begin_date = get_user_option('vip_begin_date', $user_id);
                            $begin_date = $vip_begin_date ? $vip_begin_date : $begin_date;
                        }
                    }else{
                        if($actin_type === 'new'){
                            $begin_time = current_time('timestamp', 1);
                            $begin_date = wp_date('Y-m-d', $begin_time);
                            $end_date = $this->get_expired_date($begin_date, $vip_lifetime);
                        }else{
                            $vip_begin_date = get_user_option('vip_begin_date', $user_id);
                            $vip_end_date = get_user_option('vip_end_date', $user_id);
                            $begin_date = $vip_begin_date ? $vip_begin_date : current_time('Y-m-d');
                            $_begin_time = $vip_end_date ? $vip_end_date : current_time('Y-m-d');
                            $end_date = $this->get_expired_date($_begin_time, $vip_lifetime);
                        }
                    }
                    update_user_option($user_id, 'vip_type', $vip_type);
                    update_user_option($user_id, 'vip_begin_date', $begin_date);
                    update_user_option($user_id, 'vip_end_date', $end_date);
                    $this->update_role($user_id);
                }else if($actin_type === 'upgrade'){
                    // 升级仅更新等级信息
                    update_user_option($user_id, 'vip_type', $vip_type);
                    $this->update_role($user_id);
                }
            }
        }
    }

    function update_role($user_id, $force = false){
        if(!$user_id || (!$force && user_can( $user_id, 'publish_posts' ))) return false; // 已有权限用户跳过

        $vip = $this->get_user_vip($user_id);
        if(class_exists(User_Groups::class) && $vip && isset($vip['group']) && $vip['group']){ // 会员,用户分组
            $user_group = wpcom_get_user_group($user_id);
            wp_set_object_terms($user_id, array((int)$vip['group']), 'user-groups', false);
            clean_object_term_cache($user_id, 'user-groups');
            $_role = get_term_meta((int)$vip['group'], 'wpcom_sys_role', true);
            if($user_group && isset($user_group->term_id)){
                update_user_option($user_id, '__user_group', $user_group->term_id);
            }
        }else if($vip && isset($vip['role']) && $vip['role']){ // 会员,用户角色
            $_role = $vip['role'];
            update_user_option($user_id, '__user_role', $_role);
        }else if(!$vip && $force){ // 不是会员,重置
            $__user_group = get_user_option('__user_group', $user_id);
            if($__user_group && is_numeric($__user_group)){
                wp_set_object_terms($user_id, array((int)$__user_group), 'user-groups', false);
                clean_object_term_cache($user_id, 'user-groups');
                $_role = get_term_meta((int)$__user_group, 'wpcom_sys_role', true);
                delete_user_option($user_id, '__user_group');
            }else if($__user_role = get_user_option('__user_role', $user_id)){
                $_role = $__user_role;
                delete_user_option($user_id, '__user_role');
            }
        }
        if(isset($_role) && $_role){
            $user = get_user_by('ID', $user_id);
            $default_roles = array('subscriber', 'contributor', 'author', 'editor', 'administrator');
            $roles = $user->roles;
            if (!$roles) $roles = array();
            if (in_array($_role, $default_roles) && !in_array($_role, $roles)) { // 权限和当前用户组权限不一样
                foreach ($roles as $role) {
                    if (in_array($role, $default_roles)) {
                        $user->remove_role($role);
                    }
                }
                if (in_array($_role, $default_roles)) $user->add_role($_role);
            }
        }
    }

    function authenticate($user){
        if( $user instanceof \WP_User){
            $this->update_role($user->ID);
        }
        return $user;
    }

    function check_exists_order($res, $order, $items){
        if(!empty($items) && isset($items[0]) && $items[0]['order_id'] === $order->ID && in_array($items[0]['type'], array('vip-new', 'vip-renew', 'vip-upgrade'))){
            // 已有订单需要重新检查并更新下开通和过期时间
            $order_time = date_create($order->time, wp_timezone());
            $vip = $this->get_user_vip($order->user);
            $vip_types = $this->get_vip_types();
            $type_id = explode('::', $items[0]['type_id']);
            if(empty($vip_types) || !isset($vip_types[$type_id[0]])){
                $res['result'] = -8;
                $res['msg'] = '订单已过期,会员信息获取失败!';
            }else if($vip && $items[0]['type'] === 'vip-new'){ // 已经开通订单,无法重复开通
                $res['result'] = -8;
                $res['msg'] = '订单已过期,无法重复开通会员!';
            }else if($items[0]['type'] === 'vip-new'){
                $begin_date = current_time('Y-m-d');
                $items[0]['data'] = array(
                    'begin_date' => $begin_date,
                    'end_date' => $this->get_expired_date($begin_date, $type_id[1])
                );
            }else if(!$vip && $items[0]['type'] === 'vip-renew'){ // 会员过期,无法续费
                $res['result'] = -8;
                $res['msg'] = '检测到您当前并无会员,无法进行续费操作!';
            }else if($vip && $items[0]['type'] === 'vip-renew'){
                if($type_id && $type_id[0] && $type_id[0] === $vip['id']){
                    if(current_time('Y-m-d') !== date_format($order_time, 'Y-m-d')){ // 时间不一致,需要更新下时间
                        $end_date = $vip ? get_user_option('vip_end_date', $order->user) : '';
                        $begin_date = $end_date ? $end_date : current_time('Y-m-d');
                        $items[0]['data'] = array(
                            'begin_date' => $begin_date,
                            'end_date' => $this->get_expired_date($begin_date, $type_id[1])
                        );
                    }
                }else{
                    $res['result'] = -8;
                    $res['msg'] = '订单已过期,当前无法续费会员!';
                }
            }else if($items[0]['type'] === 'vip-upgrade'){
                if(!$vip){
                    $res['result'] = -8;
                    $res['msg'] = '检测到您当前并无会员,无法进行升级操作!';
                }else if(current_time('Y-m-d') !== date_format($order_time, 'Y-m-d') || $vip['id'] !== $type_id[1]){ // 时间或者当前会员类型不一致,无法升级
                    $res['result'] = -8;
                    $res['msg'] = '订单已过期,请重新创建升级订单进行操作!';
                }
            }
            if($res['result'] == 0 && $items[0]['data'] && $_items = Order::get_order_items($order->ID)){
                global $wpdb;
                if(isset($_items[0]) && isset($_items[0]->ID)){
                    if ($items[0]['data'] && version_compare(PHP_VERSION, '5.4.0', '<')) {
                        $data = wp_json_encode($items[0]['data']);
                    } else if ($items[0]['data']) {
                        $data = wp_json_encode($items[0]['data'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
                    }
                    $wpdb->update($wpdb->order_item_table, array('data' => $data), array('ID' => $_items[0]->ID));
                }
            }
        }
        return $res;
    }

    public function show_in_order($item) {
        if ($item->type_id && $ids = explode('::', $item->type_id)) {
            $actin_type = str_replace('vip-', '', $item->type);
            $vip_type = $ids[0];
            $vip_lifetime = $ids[1];
            $vip_types = $this->get_vip_types();
            if ($vip_type && isset($this->vip_action_type[$actin_type])) { ?>
                <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 echo $this->vip_action_type[$actin_type];?>会员</div>
                    </li>
                    <li class="member-account-order-item">
                        <div class="member-account-order-label">会员类型:</div>
                        <div class="member-account-order-value"><?php echo isset($vip_types[$vip_type]) ? $vip_types[$vip_type]['title'] : '-';?></div>
                    </li>
                    <?php if($actin_type === 'upgrade'){ ?>
                        <li class="member-account-order-item">
                        <div class="member-account-order-label">升级前会员类型:</div>
                        <div class="member-account-order-value"><?php echo isset($vip_types[$vip_lifetime]) ? $vip_types[$vip_lifetime]['title'] : '-';?></div>
                    </li>
                    <?php }else{ ?>
                        <li class="member-account-order-item">
                            <div class="member-account-order-label">付费周期:</div>
                            <div class="member-account-order-value"><?php echo $this->vip_lifetime[$vip_lifetime];?></div>
                        </li>
                        <li class="member-account-order-item">
                            <div class="member-account-order-label">有效期至:</div>
                            <div class="member-account-order-value"><?php
                            $data = $item->data ? json_decode($item->data) : '';
                            $date = $data && isset($data->end_date) ? $data->end_date : '-';
                            $date = preg_match('/^9999-/', $date) ? '终身' : $date;
                            echo $date;?></div>
                        </li>
                    <?php } ?>
                </ul>
            <?php }
        }
    }

    public static function is_lifetime_vip($expired){
        if($expired && !is_object($expired)) $expired = date_create($expired, wp_timezone());
        if($expired && is_object($expired)){
            $year = date_format($expired, 'Y');
            return $year && $year == '9999';
        }
        return false;
    }

    function edit_user_vip($user){
        if (!current_user_can('edit_user')) return;
        $vip_type = get_user_option( 'vip_type', $user->ID );
        $vip_begin_date = get_user_option( 'vip_begin_date', $user->ID );
        $vip_end_date = get_user_option( 'vip_end_date', $user->ID );
        $types = $this->get_vip_types(); ?>
        <h3>会员设置</h3>
        <table class="form-table">
        <tr>
                <th><label for="vip_type">会员类型</label></th>
                <td>
                    <select name="vip_type">
                        <option value="">选择会员</option>
                        <?php
                        if (!empty($types)) {
                            foreach ($types as $id => $type) { ?>
                                <option value="<?php echo esc_attr($id); ?>" <?php echo($id===$vip_type ? 'selected' : '') ?>><?php echo $type['title']; ?></option>
                            <?php }
                        } ?>
                    </select>
                </td>
            </tr>
            <tr>
                <th><label>会员有效期</label></th>
                <td>
                    <span>从</span>
                    <label for=""><input type="date" name="vip_begin_date" value="<?php echo $vip_begin_date;?>"></label>
                    <span> 至 </span>
                    <label for=""><input type="date" name="vip_end_date" value="<?php echo $vip_end_date;?>"></label>
                    <p class="description">分别为开通时间和到期时间;终身/无到期时间时请将到期时间设置为<b>9999年的任意日期</b>即可(日期控件不好选择的话可以直接手动输入)</p>
                </td>
            </tr>
        </table>
    <?php }

    function save_user_vip($user_id){
        if (!current_user_can('edit_user', $user_id)) return false;
        if (isset($_POST['vip_type'])) update_user_option($user_id, 'vip_type', sanitize_text_field($_POST['vip_type']));
        if (isset($_POST['vip_begin_date'])) update_user_option($user_id, 'vip_begin_date', sanitize_text_field($_POST['vip_begin_date']));
        if (isset($_POST['vip_end_date'])) update_user_option($user_id, 'vip_end_date', sanitize_text_field($_POST['vip_end_date']));
    }

    function vip_settings($res){
        if($res && isset($res['type']) && $res['type'] === 'post' && isset($res['plugin-slug']) && $res['plugin-slug'] === 'wpcom-member-pro'){
            $vips = $this->get_vip_types();
            if($vips && is_array($vips)){
                $res['hooks'] = $res['hooks'] ?? [];
                $res['hooks']['vip_price'] = $res['hooks']['vip_price'] ?? [];
                $res['hooks']['copy_vip_price'] = $res['hooks']['copy_vip_price'] ?? [];

                $options = $GLOBALS['wpmx_options'];
                if(isset($options['points_on']) && $options['points_on'] == '1'){
                    $res['hooks']['vip_price'][] = array(
                        'f' => 'unlock_type:5',
                        'n' => 'pay_type',
                        'l' => '使用积分购买',
                        't' => 't',
                        'ux' => 1,
                        's' => 0,
                        'd' => '如果开启积分功能则可设置使用积分的方式购买;开启后下列价格选项单位均为积分',
                    );
                }

                foreach($vips as $vip){
                    $res['hooks']['vip_price'][] = array(
                        'f' => 'unlock_type:5',
                        'n' => $vip['id'] . '_price',
                        'l' => $vip['title'] . '价格',
                        'd' => '设置为0或者留空表示当前会员类型免费'
                    );
                    $res['hooks']['vip_price'][] = array(
                        'f' => 'unlock_type:3',
                        'n' => $vip['id'] . '_price',
                        'l' => $vip['title'] . '价格',
                        'd' => '设置为0表示免费,留空则按会员类型里面会员特权选项设置的免费或者折扣自动计算'
                    );
                    $res['hooks']['copy_vip_price'][] = array(
                        'n' => $vip['id'] . '_copy_post_price',
                        'l' => $vip['title'] . '价格',
                        'd' => '设置为0表示免费,留空则按会员类型里面会员特权选项设置的免费或者折扣自动计算'
                    );
                }
            }
        }
        return $res;
    }

    public function vip_order_types($types){
        $_types = self::get_vip_types();
        if(!empty($_types)){
            $types['vip-new'] = '会员开通';
            $types['vip-renew'] = '会员续费';
            $types['vip-upgrade'] = '会员升级';
        }
        return $types;
    }

    public function get_roles($res, $taxs){
        if( current_user_can( 'edit_posts' ) ){
            foreach ($taxs as $tax){
                if($tax && $tax === 'wp_roles') {
                    $_roles = wp_roles();
                    $roles = $_roles->role_names;
                    if(!empty($roles)){
                        foreach ($roles as $name => $role) {
                            $roles[$name] = translate_user_role($role);
                        }
                    }
                    $res[$tax] = $roles;
                }
            }
        }
        return $res;
    }

    public function for_rest_api(){
        register_rest_route('wpcom/v1', '/vip', array(
            array(
                'methods'             => \WP_REST_Server::READABLE,
                'callback'            => array($this, 'rest_get_vip_data'),
                'args'                => $this->rest_get_params(),
                'permission_callback' => array( $this, 'rest_permission_check' )
            ),
            array(
                'methods'             => \WP_REST_Server::CREATABLE,
                'callback'            => array($this, 'rest_create_vip_order'),
                'args'                => $this->rest_post_params(),
                'permission_callback' => array( $this, 'rest_permission_check' )
            )
        ));
        register_rest_route('wpcom/v1', '/vip-unlock', array(
            array(
                'methods'             => \WP_REST_Server::CREATABLE,
                'callback'            => array($this, 'rest_vip_unlock'),
                'args'                => array(
                    'type' => array(
                        'type' => 'string',
                        'required' => true
                    ),
                    'post_id' => array(
                        'type' => 'integer',
                        'required' => true
                    ),
                    'id' => array(
                        'type' => 'string'
                    ),
                    'items' => array(
                        'required'           => true,
                        'type'              => 'array',
                        'validate_callback' => 'rest_validate_request_arg',
                        'items' => array(
                            'type' => 'object',
                            'properties' => array(
                                'title' => array(
                                    'type' => 'string'
                                ),
                                'number' => array(
                                    'type' => 'integer',
                                    'default' => 1
                                ),
                                'price' => array(
                                    'type' => 'string',
                                    'required' => true
                                ),
                                'type' => array(
                                    'type' => 'string',
                                    'required' => true
                                ),
                                'type_id' => array(
                                    'type' => 'string',
                                    'required' => true
                                ),
                                'url' => array(
                                    'type' => 'string'
                                )
                            )
                        )
                    ),
                    'total' => array(
                        'type' => 'string',
                        'required' => true
                    ),
                    'sign' => array(
                        'type' => 'string',
                        'required' => true
                    ),
                    'timestamp' => array(
                        'type' => 'integer',
                        'required' => true
                    )
                ),
                'permission_callback' => array( $this, 'rest_permission_check' )
            )
        ));
    }

    function rest_get_params(){
        return array(
            'type' => array(
                'required'          => true,
                'type'              => 'string',
                'enum'              => array('new', 'renew', 'upgrade'),
                'validate_callback' => 'rest_validate_request_arg',
            )
        );
    }

    function rest_post_params(){
        return array(
            'action_type' => array(
                'required'          => true,
                'type'              => 'string',
                'enum'              => array('new', 'renew', 'upgrade'),
                'validate_callback' => 'rest_validate_request_arg',
            ),
            'vip_type' => array(
                'required'          => true,
                'default'           => '',
                'type'              => 'string',
                'validate_callback' => 'rest_validate_request_arg',
            ),
            'vip_lifetime' => array(
                'default'           => '',
                'type'              => 'string',
                'validate_callback' => 'rest_validate_request_arg',
            )
        );
    }

    function rest_get_vip_data($request){
        $res = array('result' => 0);
        $user_id = get_current_user_id();
        $type = isset($request['type']) ? sanitize_text_field($request['type']) : 'new';
        if($user_id){
            $current_vip = self::get_user_vip();
            if($current_vip && $type === 'new'){
                $res['result'] = -3;
                $res['msg'] = '您已是会员,无需重复开通!';
            }else if(!$current_vip && $type !== 'new'){
                $res['result'] = -4;
                $res['msg'] = '您还不是会员,无法执行此操作!';
            }else if($type === 'upgrade' && !$this->vip_can_upgrade($current_vip)){
                $res['result'] = -5;
                $res['msg'] = '抱歉,当前会员类型无法执行升级操作!';
            }else if($current_vip && $type === 'renew' && $this->is_lifetime_vip($current_vip['expired'])){
                $res['result'] = -6;
                $res['msg'] = '抱歉,您已经是终身会员了!';
            }else{
                $vip_types = self::get_vip_types();
                if($vip_types && !empty($vip_types)){
                    if($current_vip && isset($current_vip['expired'])){
                        $datetime = date_create($current_vip['expired'], wp_timezone());
                        $current_vip['expired'] = $datetime;
                    }
                    if($type === 'upgrade'){
                        $pass = false;
                        foreach($vip_types as $key => $val){
                            if($key === $current_vip['id'] || !$pass){
                                unset($vip_types[$key]);
                                $pass = true;
                            } else {
                                // 计算升级差价
                                $vip_types[$key]['upgrade_price'] = $this->vip_upgrade_price($current_vip, $val);
                            }
                        }
                    }
                    if($type === 'renew' || $type === 'upgrade') {
                        $res['current_vip'] = $current_vip;
                        $res['current_vip']['expired'] = $current_vip['expired']->getTimestamp();
                    }
                    $_vip_types = array();
                    foreach($vip_types as $vtype){
                        $_vip_types[] = $vtype;
                    }
                    $res['data'] = $_vip_types;
                    if($type === 'new'){
                        $res['timestamp'] = current_time('timestamp', 1);
                    }else if($current_vip['expired']){
                        $res['timestamp'] = $current_vip['expired']->getTimestamp();
                    }
                }else{
                    $res['result'] = -2;
                    $res['msg'] = '暂无可开通的会员类型,请联系网站管理员';
                }
            }
        }else{
            $res['result'] = -1;
        }
        return rest_ensure_response( $res );
    }

    function rest_create_vip_order($request){
        $_POST = $_POST ?: array();
        $_POST['action_type'] = $request['action_type'];
        $_POST['vip_type'] = $request['vip_type'];
        $_POST['vip_lifetime'] = $request['vip_lifetime'];
        $data = $this->get_vip_data(true);
        $data['nonce'] = wp_create_nonce('wpcom_payment_form');
        return rest_ensure_response( $data );
    }

    function rest_vip_unlock($request){
        $_POST = $request;
        $this->vip_unlock();
    }

    function rest_permission_check(){
        if ( get_current_user_id() ) {
            return true;
        } else {
            return new \WP_Error( 'rest_user_cannot_view', '请登录后操作!', array( 'status' => 200 ) );
        }
    }

    function rest_user_metas($metas, $object){
        if($object['id'] && $object['id'] === get_current_user_id()){
            $vip = self::get_user_vip($object['id']);
            $metas['vip'] = false;
            if($vip && isset($vip['id'])){
                $metas['vip'] = array(
                    'index' => $vip['index'],
                    'id' => $vip['id'],
                    'icon' => $vip['icon'],
                    'title' => $vip['title'],
                    'expired' => $vip['expired'],
                    'upgrade' => self::vip_can_upgrade($vip)
                );
            }
        }
        return $metas;
    }

    function pre_insert_post($data, $attr, $rest = 0){
        global $wpdb, $post_notice;
        $user = wp_get_current_user();
        if($user && $user->ID && $data && isset($data['post_status']) && in_array($data['post_status'], ['pending', 'publish']) && !current_user_can('edit_others_posts')){
            $options = $GLOBALS['wpmx_options'];
            $vip = $this->get_user_vip($user->ID);
            $privileges = $vip && isset($vip['privileges']) ? $vip['privileges'] : false;
            $default_day_post_limit = isset($options['default_day_post_limit']) ? $options['default_day_post_limit'] : -1;
            $default_month_post_limit = isset($options['default_month_post_limit']) ? $options['default_month_post_limit'] : -1;
            $per_day = $privileges && isset($privileges['day_post_limit']) ? $privileges['day_post_limit'] : $default_day_post_limit;
            $per_month = $privileges && isset($privileges['month_post_limit']) ? $privileges['month_post_limit'] : $default_month_post_limit;

            $stop = 0;
            $post_id = isset($attr['ID']) ? $attr['ID'] : 0;

            // 月或者日额度为0则为不允许发布
            if($per_day == 0 || $per_month == 0) $stop = 1;

            if($stop === 0 && $per_day > 0 && is_numeric($per_day)){
                $sql_day = $wpdb->prepare("SELECT count(ID) FROM $wpdb->posts WHERE post_status IN ('pending', 'publish') AND post_type = 'post' AND post_author = %d AND ID != %d AND DATE_FORMAT(post_date, '%%Y-%%m-%%d') = %s", array($user->ID, $post_id, wp_date('Y-m-d', current_time('timestamp', 1))));
                $_day = $wpdb->get_var( $sql_day );
                if($_day && $_day >= $per_day){
                    $stop = 1;
                }
            }
            if($stop === 0 && $per_month > 0 && is_numeric($per_month)){
                $sql_month = $wpdb->prepare("SELECT count(ID) FROM $wpdb->posts WHERE post_status IN ('pending', 'publish') AND post_type = 'post' AND post_author = %d AND ID != %d AND DATE_FORMAT(post_date, '%%Y-%%m') = %s", array($user->ID, $post_id, wp_date('Y-m', current_time('timestamp', 1))));
                $_month = $wpdb->get_var( $sql_month );
                if($_month && $_month >= $per_month){
                    $stop = 1;
                }
            }

            if ( $stop ){
                $post_notice = isset($options['post_limit_msg']) && $options['post_limit_msg'] ? $options['post_limit_msg'] : '发布失败:超出发帖限额';
                $post_save = isset($options['post_limit_save']) && $options['post_limit_save'] == '1' ? false : true;
                $data['post_status'] = $post_save ? 'draft' : 'inherit';
                if($rest){
                    $err = new \WP_Error( 'vip_publish_limit', $post_notice);
                    $err->add_data( array( 'status' => 400 ) );
                    return $err;
                }else{
                    add_filter('redirect_post_location', array( $this, 'redirect_post_location_filter'), 99);
                }
            }
        }
        return $data;
    }

    function rest_pre_insert_post($post){
        $_post = json_decode(json_encode($post), true);
        $res = $this->pre_insert_post($_post, $_post, 1);
        if(is_wp_error($res)){
            return $res;
        }else{
            return $post;
        }
    }

    function redirect_post_location_filter($location){
        remove_filter('redirect_post_location', __FUNCTION__, 99);
        $location = add_query_arg('message', 8899, $location);
        return $location;
    }

    function post_limit_error(){
        if(isset( $_GET['message'] ) && isset( $_GET['post'] ) && $_GET['message'] == '8899'){
            $options = $GLOBALS['wpmx_options'];
            $post_notice = isset($options['post_limit_msg']) && $options['post_limit_msg'] ? $options['post_limit_msg'] : '发布失败:超出发帖限额'; ?>
            <div class="notice error is-dismissible" >
                <?php echo wp_kses_post(wpautop($post_notice));?>
            </div>
        <?php }
    }

    function tougao_notice($notice, $post){
        global $post_notice;
        $options = $GLOBALS['wpmx_options'];
        if(!isset($post_notice) && $post->post_status === 'inherit'){
            $post_notice = isset($options['post_limit_msg']) && $options['post_limit_msg'] ? $options['post_limit_msg'] : '发布失败:超出发帖限额';
        }
        if($post_notice && $post && isset($post->post_status) && ($post->post_status === 'draft' || $post->post_status === 'inherit')){
            $notice = '<div class="wpcom-alert alert-warning alert-dismissible fade in" role="alert">';
            $notice .= '<div class="wpcom-close" data-wpcom-dismiss="alert" aria-label="Close">' . wpmx_icon('close', 0) . '</div>';
            $notice .= $post_notice;
            $notice .= '</div>';
        }
        return $notice;
    }

    public static function get_total_vip($period = 'total', $start = '', $end = '') {
        global $wpdb;
        $prefix = $wpdb->get_blog_prefix();
        $today = current_time('Y-m-d');

        $cache_key = 'wpcom_total_vip_' . substr(md5(serialize([$period, $start, $end])), 0, 12);

        $cached = get_transient($cache_key);
        if ($cached !== false) {
            return $cached;
        }

        $types = self::get_vip_types();
        if (empty($types)) {
            return $period === 'total' ? ['total' => 0, 'active' => 0] : 0;
        }

        $vip_type_ids = array_map('strval', array_keys($types));

        // 构建 SQL 条件
        $conditions = [
            $wpdb->prepare("vt.meta_value IN (" . implode(',', array_fill(0, count($vip_type_ids), '%s')) . ")", ...$vip_type_ids)
        ];

        if ($period !== 'total' || $start || $end) {
            if (!$start && !$end) {
                if ($period === 'today') {
                    $start = $today;
                    $end = $today;
                } elseif ($period === 'month') {
                    $start = date_i18n('Y-m-01');
                    $end = $today;
                } elseif ($period === 'year') {
                    $start = date_i18n('Y-01-01');
                    $end = $today;
                }
            }

            $start_full = $start && strlen($start) === 10 ? $start . ' 00:00:00' : $start;
            $end_full = $end && strlen($end) === 10 ? $end . ' 23:59:59' : $end;

            if ($start_full && $end_full) {
                $conditions[] = $wpdb->prepare("vb.meta_value BETWEEN %s AND %s", $start_full, $end_full);
            } elseif ($start_full) {
                $conditions[] = $wpdb->prepare("vb.meta_value >= %s", $start_full);
            } elseif ($end_full) {
                $conditions[] = $wpdb->prepare("vb.meta_value <= %s", $end_full);
            }
        }

        $where = implode(' AND ', $conditions);

        // 构建 SQL:JOIN vip_type, vip_begin_date, vip_end_date
        $sql = "
            SELECT
                COUNT(DISTINCT vt.user_id) as total,
                COUNT(DISTINCT CASE WHEN ve.meta_value >= %s THEN ve.user_id ELSE NULL END) as active
            FROM {$wpdb->usermeta} vt
            LEFT JOIN {$wpdb->usermeta} vb ON vb.user_id = vt.user_id AND vb.meta_key = %s
            LEFT JOIN {$wpdb->usermeta} ve ON ve.user_id = vt.user_id AND ve.meta_key = %s
            WHERE vt.meta_key = %s AND $where
        ";

        $result = $wpdb->get_row(
            $wpdb->prepare(
                $sql,
                $today,
                $prefix . 'vip_begin_date',
                $prefix . 'vip_end_date',
                $prefix . 'vip_type'
            ),
            ARRAY_A
        );

        $data = $period === 'total'
            ? ['total' => (int) ($result['total'] ?? 0), 'active' => (int) ($result['active'] ?? 0)]
            : (int) ($result['total'] ?? 0);

        // 缓存 10 分钟
        set_transient($cache_key, $data, MINUTE_IN_SECONDS * 10);

        return $data;
    }
}

class_alias(VIP::class, 'WPCOM_VIP');
new VIP();

Youez - 2016 - github.com/yon3zu
LinuXploit