wp_create_nonce( 'subscrpt_bulk_action_nonce' ),
'ajaxurl' => admin_url( 'admin-ajax.php' ),
)
);
}
/**
* Create Subscriptions Menu.
*/
public function create_admin_menu() {
$parent_slug = 'wp-subscription';
// Determine if the menu is active
$is_active = isset( $_GET['page'] ) && strpos( sanitize_text_field( wp_unslash( $_GET['page'] ) ), 'wp-subscription' ) === 0;
$icon_url = $is_active
? SUBSCRPT_ASSETS . '/images/icons/subscription-20.png'
: SUBSCRPT_ASSETS . '/images/icons/subscription-20-gray.png';
// Main menu
add_menu_page(
__( 'WPSubscription', 'subscription' ),
__( 'WPSubscription', 'subscription' ),
'manage_woocommerce',
$parent_slug,
array( $this, 'render_subscriptions_page' ),
$icon_url,
40
);
// Subscriptions List
add_submenu_page(
$parent_slug,
__( 'Subscriptions', 'subscription' ),
__( 'Subscriptions', 'subscription' ),
'manage_woocommerce',
$parent_slug,
array( $this, 'render_subscriptions_page' )
);
// Stats Overview
add_submenu_page(
$parent_slug,
__( 'Reports', 'subscription' ),
__( 'Reports', 'subscription' ),
'manage_woocommerce',
'wp-subscription-stats',
array( $this, 'render_stats_page' )
);
// Subscription Health
add_submenu_page(
$parent_slug,
__( 'Health', 'subscription' ),
__( 'Health', 'subscription' ),
'manage_woocommerce',
'wp-subscription-health',
array( $this, 'render_health_page' )
);
/*
! For god's sake, check existing code before implementing!
// Settings
add_submenu_page(
$parent_slug,
__( 'Settings', 'subscription' ),
__( 'Settings', 'subscription' ),
'manage_woocommerce',
'wp-subscription-settings',
array( $this, 'render_settings_page' )
);
*/
// Support
add_submenu_page(
$parent_slug,
__( 'Support', 'subscription' ),
__( 'Support', 'subscription' ),
'manage_woocommerce',
'wp-subscription-support',
array( $this, 'render_support_page' )
);
// Add WPSubscription link under WooCommerce menu
add_submenu_page(
'woocommerce',
__( 'WPSubscription', 'subscription' ),
__( 'WPSubscription', 'subscription' ),
'manage_woocommerce',
'wp-subscription',
array( $this, 'render_subscriptions_page' )
);
}
/**
* Render the admin header
*/
public function render_admin_footer() {
?>
Made with
♥ by the WPSubscription Team
render_admin_header( __( 'Subscriptions', 'subscription' ), __( 'Manage your subscriptions', 'subscription' ) );
// Handle filters
$status = isset( $_GET['subscrpt_status'] ) ? sanitize_text_field( wp_unslash( $_GET['subscrpt_status'] ) ) : '';
$search = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '';
$date_filter = isset( $_GET['date_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['date_filter'] ) ) : '';
$per_page = isset( $_GET['per_page'] ) ? max( 1, intval( $_GET['per_page'] ) ) : 20;
$paged = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
// Handle form submissions (both filters and bulk actions)
$request_method = isset( $_SERVER['REQUEST_METHOD'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : '';
if ( 'POST' === $request_method ) {
// Verify nonce before processing any POST data.
$nonce = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '';
if ( ! wp_verify_nonce( $nonce, 'subscrpt_list_action' ) ) {
wp_die( esc_html__( 'Security check failed.', 'subscription' ) );
}
// Handle bulk actions
if ( isset( $_POST['bulk_action'] ) || isset( $_POST['bulk_action2'] ) ) {
$bulk_action = isset( $_POST['bulk_action'] ) ? sanitize_text_field( wp_unslash( $_POST['bulk_action'] ) ) : sanitize_text_field( wp_unslash( $_POST['bulk_action2'] ?? '' ) );
$action = isset( $_POST['action'] ) ? sanitize_text_field( wp_unslash( $_POST['action'] ) ) : sanitize_text_field( wp_unslash( $_POST['action2'] ?? '' ) );
if ( $bulk_action && $action && $action !== '-1' && isset( $_POST['subscription_ids'] ) && is_array( $_POST['subscription_ids'] ) ) {
$subscription_ids = array_map( 'intval', $_POST['subscription_ids'] );
if ( $action === 'trash' ) {
foreach ( $subscription_ids as $sub_id ) {
wp_trash_post( $sub_id );
}
} elseif ( $action === 'restore' ) {
foreach ( $subscription_ids as $sub_id ) {
wp_untrash_post( $sub_id );
}
} elseif ( $action === 'delete' ) {
foreach ( $subscription_ids as $sub_id ) {
wp_delete_post( $sub_id, true );
}
}
wp_safe_redirect( admin_url( 'admin.php?page=wp-subscription' ) );
exit;
}
}
// Handle filter form submission
if ( isset( $_POST['filter_action'] ) ) {
$filter_params = array();
if ( ! empty( $_POST['subscrpt_status'] ) ) {
$filter_params['subscrpt_status'] = sanitize_text_field( wp_unslash( $_POST['subscrpt_status'] ) );
}
if ( ! empty( $_POST['date_filter'] ) ) {
$filter_params['date_filter'] = sanitize_text_field( wp_unslash( $_POST['date_filter'] ) );
}
if ( ! empty( $_POST['s'] ) ) {
$filter_params['s'] = sanitize_text_field( wp_unslash( $_POST['s'] ) );
}
if ( ! empty( $_POST['per_page'] ) ) {
$filter_params['per_page'] = intval( $_POST['per_page'] );
}
$redirect_url = add_query_arg( $filter_params, admin_url( 'admin.php?page=wp-subscription' ) );
wp_safe_redirect( $redirect_url );
exit;
}
}
// Handle individual actions
if ( isset( $_GET['action'] ) && ! empty( $_GET['sub_id'] ) ) {
$sub_id = intval( $_GET['sub_id'] );
$action = sanitize_text_field( wp_unslash( $_GET['action'] ) );
$nonce = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '';
// Clean trash action.
if ( $action === 'clean_trash' ) {
// Verify nonce for security.
$nonce_action = 'wpsubs_action_clean_trash';
if ( ! wp_verify_nonce( $nonce, $nonce_action ) ) {
echo '' . esc_html__( 'Security check failed. Please try again.', 'subscription' ) . '
';
wp_die();
}
// Clean all trash items.
$trash_posts = get_posts(
[
'post_type' => 'subscrpt_order',
'post_status' => 'trash',
'numberposts' => -1,
'fields' => 'ids',
]
);
foreach ( $trash_posts as $trash_id ) {
wp_delete_post( $trash_id, true );
}
wp_safe_redirect( admin_url( 'admin.php?page=wp-subscription&subscrpt_status=trash' ) );
exit;
} else {
// For other actions, verify nonce with subscription ID.
$nonce_action = 'wpsubs_action_' . $sub_id;
if ( ! wp_verify_nonce( $nonce, $nonce_action ) ) {
echo '' . esc_html__( 'Security check failed. Please try again.', 'subscription' ) . '
';
wp_die();
}
$redirect_url = admin_url( 'admin.php?page=wp-subscription' );
switch ( $action ) {
case 'duplicate':
$post = get_post( $sub_id );
if ( $post && $post->post_type === 'subscrpt_order' ) {
$new_post = [
'post_title' => $post->post_title . ' (Copy)',
'post_content' => $post->post_content,
'post_status' => 'draft',
'post_type' => 'subscrpt_order',
];
$new_id = wp_insert_post( $new_post );
if ( $new_id ) {
$meta = get_post_meta( $sub_id );
foreach ( $meta as $key => $values ) {
foreach ( $values as $value ) {
add_post_meta( $new_id, $key, maybe_unserialize( $value ) );
}
}
}
}
break;
case 'trash':
wp_trash_post( $sub_id );
break;
case 'restore':
wp_untrash_post( $sub_id );
break;
case 'delete':
wp_delete_post( $sub_id, true );
$redirect_url = admin_url( 'admin.php?page=wp-subscription&subscrpt_status=trash' );
break;
}
wp_safe_redirect( $redirect_url );
exit;
}
}
$args = [
'post_type' => 'subscrpt_order',
'post_status' => 'any',
'posts_per_page' => $per_page,
'paged' => $paged,
'orderby' => 'date',
'order' => 'DESC',
];
if ( $status ) {
$args['post_status'] = $status;
}
// Search only by subscription ID
if ( $search !== '' ) {
if ( is_numeric( $search ) ) {
$args['p'] = intval( $search );
} else {
// If not numeric, return no results
$args['post__in'] = array( 0 );
}
}
// Dynamic date filter (YYYY-MM)
if ( $date_filter && preg_match( '/^\d{4}-\d{2}$/', $date_filter ) ) {
$year = substr( $date_filter, 0, 4 );
$month = substr( $date_filter, 5, 2 );
$args['date_query'][] = [
'year' => intval( $year ),
'month' => intval( $month ),
];
}
$query = new \WP_Query( $args );
$subscriptions = $query->posts;
$total = $query->found_posts;
$max_num_pages = $query->max_num_pages;
// Get all possible statuses for filter dropdown
$all_statuses = get_post_stati( [ 'show_in_admin_all_list' => true ], 'objects' );
include __DIR__ . '/views/subscription-list.php';
?>
Made with
♥ by the WPSubscription Team
render_admin_header( __( 'Reports', 'subscription' ), __( 'View your subscription analytics', 'subscription' ) );
if ( ! subscrpt_pro_activated() ) {
$args = [
'preview_image_url' => SUBSCRPT_ASSETS . '/images/previews/reports-page-preview.png',
'cta_title' => __( 'Unlock Subscription Reports', 'subscription' ),
'cta_description' => __( 'Subscription Reports require WPSubscription Pro. Unlock advanced features, priority support, and more with WPSubscription Pro.', 'subscription' ),
];
$preview_content = subscrpt_render_page_preview( $args );
echo $preview_content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Content is already escaped. Re-escaping will break the HTML structure.
} else {
// Allow pro plugin to override the entire stats page content.
do_action( 'subscrpt_render_stats_page' );
}
$this->render_admin_footer();
}
/**
* Render Health page
*/
public function render_health_page() {
$this->render_admin_header( __( 'Health', 'subscription' ), __( 'Monitor your subscription health', 'subscription' ) );
if ( ! subscrpt_pro_activated() ) {
$args = [
'preview_image_url' => SUBSCRPT_ASSETS . '/images/previews/subscrpt-health-preview.png',
'cta_title' => __( 'Unlock Subscription Health', 'subscription' ),
'cta_description' => __( 'Subscription Health requires WPSubscription Pro. Unlock advanced features, priority support, and more with WPSubscription Pro.', 'subscription' ),
];
$preview_content = subscrpt_render_page_preview( $args );
echo $preview_content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Content is already escaped. Re-escaping will break the HTML structure.
} else {
// Allow pro plugin to render the full health page content.
do_action( 'subscrpt_render_health_page' );
}
$this->render_admin_footer();
}
/**
* Render Support page
*/
public function render_support_page() {
$this->render_admin_header( __( 'Support', 'subscription' ), __( 'Get help and resources', 'subscription' ) );
?>
✨
Unlock advanced features, priority support,
and more subscription control and reporting.
UPGRADE TO PRO
Product Overview
WPSubscription helps you to sell products and services on a recurring basis using your existing WooCommerce store. Whether you're offering digital licenses, physical product boxes, or ongoing service plans, this plugin provides the tools to build and manage subscription models.
Create simple or variable subscription products
Set billing intervals (daily, weekly, monthly, yearly)
Offer free trials and sign-up fees
Allow customers to cancel or renew subscriptions manually
View and manage subscriptions from the admin dashboard
Customize subscription behavior and role assignment
Integrate with payment gateways that support recurring billing (Stripe, PayPal)
WPSubscription PRO Features
🔀
Variable Product Offer flexible subscription options for variable products.
🚚
Delivery Schedule Set custom delivery intervals for each subscription.
📜
Subscription History Track all changes and events for every subscription.
⏳
More Subscription Durations Offer more flexible and custom subscription periods.
💶
Sign Up Fee Charge a one-time sign up fee for new subscribers.
⏩
Early Renewal Allow customers to renew their subscription before expiry.
💳
Renewal Price Set a different price for subscription renewals.
Made with
♥ by the WPSubscription Team
__( 'Security check failed.', 'subscription' ) ) );
}
// Check permissions
if ( ! current_user_can( 'manage_woocommerce' ) ) {
wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'subscription' ) ) );
}
// Get action and subscription IDs
$bulk_action = isset( $_POST['bulk_action'] ) ? sanitize_text_field( wp_unslash( $_POST['bulk_action'] ) ) : '';
$subscription_ids = isset( $_POST['subscription_ids'] ) ? array_map( 'intval', $_POST['subscription_ids'] ) : array();
if ( empty( $subscription_ids ) ) {
wp_send_json_error( array( 'message' => __( 'No subscriptions selected.', 'subscription' ) ) );
}
$processed_count = 0;
$errors = array();
foreach ( $subscription_ids as $subscription_id ) {
$post = get_post( $subscription_id );
if ( ! $post || $post->post_type !== 'subscrpt_order' ) {
// translators: Subscription ID.
$errors[] = sprintf( __( 'Subscription #%d not found.', 'subscription' ), $subscription_id );
continue;
}
try {
switch ( $bulk_action ) {
case 'trash':
if ( wp_trash_post( $subscription_id ) ) {
++$processed_count;
} else {
$errors[] = sprintf(
// translators: Subscription ID.
__( 'Failed to move subscription #%d to trash.', 'subscription' ),
$subscription_id
);
}
break;
case 'restore':
if ( wp_untrash_post( $subscription_id ) ) {
++$processed_count;
} else {
$errors[] = sprintf(
// translators: Subscription ID.
__( 'Failed to restore subscription #%d.', 'subscription' ),
$subscription_id
);
}
break;
case 'delete':
if ( wp_delete_post( $subscription_id, true ) ) {
++$processed_count;
} else {
$errors[] = sprintf(
// translators: Subscription ID.
__( 'Failed to delete subscription #%d.', 'subscription' ),
$subscription_id
);
}
break;
default:
$errors[] = sprintf(
// translators: Bulk action.
__( 'Unknown action: %s', 'subscription' ),
$bulk_action
);
break;
}
} catch ( Exception $e ) {
$errors[] = sprintf(
// translators: Subscription ID, Error message.
__( 'Error processing subscription #%1$d: %2$s', 'subscription' ),
$subscription_id,
$e->getMessage()
);
}
}
// Prepare response message
$message = '';
if ( $processed_count > 0 ) {
switch ( $bulk_action ) {
case 'trash':
$message = sprintf(
// translators: Number of subscriptions.
_n( '%d subscription moved to trash.', '%d subscriptions moved to trash.', $processed_count, 'subscription' ),
$processed_count
);
break;
case 'restore':
$message = sprintf(
// translators: Number of subscriptions.
_n( '%d subscription restored.', '%d subscriptions restored.', $processed_count, 'subscription' ),
$processed_count
);
break;
case 'delete':
$message = sprintf(
// translators: Number of subscriptions.
_n( '%d subscription permanently deleted.', '%d subscriptions permanently deleted.', $processed_count, 'subscription' ),
$processed_count
);
break;
}
}
if ( ! empty( $errors ) ) {
$message .= ' ' . __( 'Some errors occurred:', 'subscription' ) . ' ' . implode( ', ', $errors );
}
if ( $processed_count > 0 ) {
wp_send_json_success( array( 'message' => $message ) );
} else {
wp_send_json_error( array( 'message' => $message ? $message : __( 'No subscriptions were processed.', 'subscription' ) ) );
}
}
}