subscription/yoone-subscriptions.php

458 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Plugin Name: Yoone Subscriptions
* Plugin URI: https://yoone.ca
* Description: 专业的WooCommerce混装产品和订阅管理插件支持Moneris支付网关
* Version: 1.0.0
* Author: Yoone Team
* Author URI: https://yoone.ca
* Text Domain: yoone-subscriptions
* Domain Path: /languages
* Requires at least: 5.0
* Tested up to: 6.4
* Requires PHP: 7.4
* WC requires at least: 5.0
* WC tested up to: 8.0
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Network: false
*
* @package YooneSubscriptions
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 检查WooCommerce是否激活
if (!in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
add_action('admin_notices', function() {
echo '<div class="notice notice-error"><p><strong>Yoone Subscriptions</strong> 需要 WooCommerce 插件才能正常工作。</p></div>';
});
return;
}
// 定义插件常量
define('YOONE_SUBSCRIPTIONS_VERSION', '1.0.0');
define('YOONE_SUBSCRIPTIONS_PLUGIN_FILE', __FILE__);
define('YOONE_SUBSCRIPTIONS_PLUGIN_BASENAME', plugin_basename(__FILE__));
define('YOONE_SUBSCRIPTIONS_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('YOONE_SUBSCRIPTIONS_PLUGIN_URL', plugin_dir_url(__FILE__));
define('YOONE_SUBSCRIPTIONS_INCLUDES_PATH', YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/');
define('YOONE_SUBSCRIPTIONS_TEMPLATES_PATH', YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'templates/');
define('YOONE_SUBSCRIPTIONS_ASSETS_URL', YOONE_SUBSCRIPTIONS_PLUGIN_URL . 'assets/');
/**
* 主插件类
*/
final class Yoone_Subscriptions {
/**
* 单例实例
*/
private static $instance = null;
/**
* 获取单例实例
*/
public static function instance() {
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 构造函数
*/
private function __construct() {
$this->init_hooks();
}
/**
* 初始化钩子
*/
private function init_hooks() {
// 插件激活和停用钩子
register_activation_hook(__FILE__, array($this, 'activate'));
register_deactivation_hook(__FILE__, array($this, 'deactivate'));
// 插件加载完成后初始化
add_action('plugins_loaded', array($this, 'init'), 10);
// 检查WooCommerce依赖
add_action('admin_notices', array($this, 'check_woocommerce_dependency'));
}
/**
* 插件初始化
*/
public function init() {
// 检查WooCommerce是否激活
if (!$this->is_woocommerce_active()) {
return;
}
// 加载文本域
$this->load_textdomain();
// 包含核心文件
$this->includes();
// 初始化类
$this->init_classes();
// 初始化钩子
$this->init_plugin_hooks();
}
/**
* 检查WooCommerce是否激活
*/
private function is_woocommerce_active() {
return class_exists('WooCommerce');
}
/**
* 检查WooCommerce依赖
*/
public function check_woocommerce_dependency() {
if (!$this->is_woocommerce_active()) {
echo '<div class="notice notice-error"><p>';
echo __('Yoone Subscriptions 需要 WooCommerce 插件才能正常工作。', 'yoone-subscriptions');
echo '</p></div>';
}
}
/**
* 加载文本域
*/
private function load_textdomain() {
load_plugin_textdomain(
'yoone-subscriptions',
false,
dirname(YOONE_SUBSCRIPTIONS_PLUGIN_BASENAME) . '/languages'
);
}
/**
* 包含核心文件
*/
private function includes() {
// 核心抽象类和接口
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/abstracts/abstract-yoone-data.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/interfaces/interface-yoone-subscription.php';
// 核心类
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/class-yoone-install.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/class-yoone-ajax.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/class-yoone-api.php';
// 混装产品层
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/bundle/class-yoone-bundle-product.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/bundle/class-yoone-bundle-cart.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/bundle/class-yoone-bundle-admin.php';
// 订阅层
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/subscription/class-yoone-subscription.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/subscription/class-yoone-subscription-manager.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/subscription/class-yoone-subscription-scheduler.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/subscription/class-yoone-subscription-admin.php';
// 支付层
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/payment/class-yoone-moneris-gateway.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/payment/class-yoone-payment-token.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/payment/class-yoone-payment-tokens.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/payment/class-yoone-payment-scheduler.php';
// 前端类
if (!is_admin()) {
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/frontend/class-yoone-frontend.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/frontend/class-yoone-shortcodes.php';
}
// 管理后台类
if (is_admin()) {
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/admin/class-yoone-admin.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/admin/class-yoone-admin-menus.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/admin/class-yoone-admin-settings.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/admin/class-yoone-admin-logs.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/class-yoone-log-analyzer.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'tests/test-config.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'tests/test-subscription-flow.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'tests/class-yoone-test-suite.php';
}
// 工具类
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/class-yoone-logger.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/class-yoone-cache.php';
require_once YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'includes/class-yoone-helper.php';
}
/**
* 初始化类
*/
private function init_classes() {
// 初始化前端类
new Yoone_Frontend();
// 初始化后端类
if (is_admin()) {
new Yoone_Admin();
new Yoone_Admin_Logs();
new Yoone_Test_Suite();
}
// 初始化AJAX类
new Yoone_Ajax();
// 初始化计划任务类
new Yoone_Cron();
// 注册支付网关
add_filter('woocommerce_payment_gateways', array($this, 'add_payment_gateways'));
// 注册产品类型
add_filter('product_type_selector', array($this, 'add_product_types'));
add_action('woocommerce_product_data_tabs', array($this, 'add_product_data_tabs'));
add_action('woocommerce_product_data_panels', array($this, 'add_product_data_panels'));
}
/**
* 初始化插件钩子
*/
private function init_plugin_hooks() {
// 产品类型和数据面板
add_filter('product_type_selector', array($this, 'add_product_types'));
add_filter('woocommerce_product_data_tabs', array($this, 'add_product_data_tabs'));
add_action('woocommerce_product_data_panels', array($this, 'add_product_data_panels'));
// 订阅状态和端点
add_action('init', array($this, 'register_subscription_statuses'));
add_action('init', array($this, 'add_subscription_endpoints'));
// 管理菜单
add_action('admin_menu', array($this, 'add_admin_menus'));
// 订单状态变化处理
add_action('woocommerce_order_status_changed', array($this, 'handle_order_status_change'), 10, 4);
// 产品元数据保存
add_action('woocommerce_process_product_meta', array($this, 'save_product_meta'));
// 我的账户菜单项
add_filter('woocommerce_account_menu_items', array($this, 'add_account_menu_items'));
add_action('woocommerce_account_subscriptions_endpoint', array($this, 'subscriptions_endpoint_content'));
// 订阅续费处理
add_action('yoone_process_subscription_renewals', array($this, 'process_subscription_renewals'));
// 清理过期令牌
add_action('yoone_cleanup_expired_tokens', array($this, 'cleanup_expired_tokens'));
// 文本域加载
add_action('plugins_loaded', array($this, 'load_textdomain'));
}
/**
* 添加Moneris支付网关
*/
public function add_moneris_gateway($gateways) {
$gateways[] = 'Yoone_Moneris_Gateway';
return $gateways;
}
/**
* 初始化核心钩子
*/
private function init_core_hooks() {
// WooCommerce初始化后的钩子
add_action('woocommerce_init', array($this, 'woocommerce_init'));
// 加载模板钩子
add_filter('woocommerce_locate_template', array($this, 'locate_template'), 10, 3);
// 脚本和样式
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
}
/**
* WooCommerce初始化
*/
public function woocommerce_init() {
// 注册自定义产品类型
add_filter('product_type_selector', array($this, 'add_bundle_product_type'));
// 注册自定义订单状态
add_action('init', array($this, 'register_subscription_statuses'));
}
/**
* 添加混装产品类型
*/
public function add_bundle_product_type($types) {
$types['bundle'] = __('混装产品', 'yoone-subscriptions');
return $types;
}
/**
* 注册订阅状态
*/
public function register_subscription_statuses() {
register_post_status('yoone-active', array(
'label' => __('激活', 'yoone-subscriptions'),
'public' => false,
'show_in_admin_status_list' => true,
'label_count' => _n_noop('激活 <span class="count">(%s)</span>', '激活 <span class="count">(%s)</span>', 'yoone-subscriptions')
));
register_post_status('yoone-paused', array(
'label' => __('暂停', 'yoone-subscriptions'),
'public' => false,
'show_in_admin_status_list' => true,
'label_count' => _n_noop('暂停 <span class="count">(%s)</span>', '暂停 <span class="count">(%s)</span>', 'yoone-subscriptions')
));
register_post_status('yoone-cancelled', array(
'label' => __('已取消', 'yoone-subscriptions'),
'public' => false,
'show_in_admin_status_list' => true,
'label_count' => _n_noop('已取消 <span class="count">(%s)</span>', '已取消 <span class="count">(%s)</span>', 'yoone-subscriptions')
));
}
/**
* 定位模板文件
*/
public function locate_template($template, $template_name, $template_path) {
if (strpos($template_name, 'yoone-') === 0) {
$plugin_template = YOONE_SUBSCRIPTIONS_PLUGIN_PATH . 'templates/' . $template_name;
if (file_exists($plugin_template)) {
return $plugin_template;
}
}
return $template;
}
/**
* 加载前端脚本和样式
*/
public function enqueue_scripts() {
wp_enqueue_style(
'yoone-subscriptions-frontend',
YOONE_SUBSCRIPTIONS_PLUGIN_URL . 'assets/css/frontend.css',
array(),
YOONE_SUBSCRIPTIONS_VERSION
);
wp_enqueue_script(
'yoone-subscriptions-frontend',
YOONE_SUBSCRIPTIONS_PLUGIN_URL . 'assets/js/frontend.js',
array('jquery', 'wc-add-to-cart'),
YOONE_SUBSCRIPTIONS_VERSION,
true
);
// 本地化脚本
wp_localize_script('yoone-subscriptions-frontend', 'yoone_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('yoone_nonce'),
'i18n' => array(
'loading' => __('加载中...', 'yoone-subscriptions'),
'error' => __('发生错误,请重试', 'yoone-subscriptions')
)
));
}
/**
* 加载后台脚本和样式
*/
public function admin_enqueue_scripts($hook) {
// 只在相关页面加载
if (strpos($hook, 'yoone') === false && !in_array($hook, array('post.php', 'post-new.php', 'edit.php'))) {
return;
}
wp_enqueue_style(
'yoone-subscriptions-admin',
YOONE_SUBSCRIPTIONS_PLUGIN_URL . 'assets/css/admin.css',
array(),
YOONE_SUBSCRIPTIONS_VERSION
);
wp_enqueue_script(
'yoone-subscriptions-admin',
YOONE_SUBSCRIPTIONS_PLUGIN_URL . 'assets/js/admin.js',
array('jquery', 'wc-enhanced-select'),
YOONE_SUBSCRIPTIONS_VERSION,
true
);
wp_localize_script('yoone-subscriptions-admin', 'yoone_admin', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('yoone_admin_nonce')
));
}
/**
* 插件激活
*/
public function activate() {
// 检查WooCommerce
if (!$this->is_woocommerce_active()) {
deactivate_plugins(YOONE_SUBSCRIPTIONS_PLUGIN_BASENAME);
wp_die(__('Yoone Subscriptions 需要 WooCommerce 插件才能激活。', 'yoone-subscriptions'));
}
// 创建数据库表
Yoone_Install::activate();
// 设置默认选项
$this->set_default_options();
// 刷新重写规则
flush_rewrite_rules();
}
/**
* 插件停用
*/
public function deactivate() {
// 清理定时任务
wp_clear_scheduled_hook('yoone_process_subscriptions');
wp_clear_scheduled_hook('yoone_retry_failed_payments');
// 刷新重写规则
flush_rewrite_rules();
}
/**
* 设置默认选项
*/
private function set_default_options() {
$defaults = array(
'yoone_bundle_min_quantity' => 10,
'yoone_subscription_billing_cycle' => 'monthly',
'yoone_payment_retry_attempts' => 3,
'yoone_payment_retry_interval' => 24
);
foreach ($defaults as $option => $value) {
if (get_option($option) === false) {
add_option($option, $value);
}
}
}
}
/**
* 获取插件主实例
*/
function yoone_subscriptions() {
return Yoone_Subscriptions::instance();
}
// 初始化插件
yoone_subscriptions();