By Devport Team | Last updated: 2025-07-12 | 12 min read

Building Subscription Products with WooCommerce

Subscription-based business models provide predictable revenue and stronger customer relationships. This guide explores how to implement subscription products in WooCommerce, from basic recurring payments to complex subscription management systems. Learn how to handle billing cycles, manage renewals, and create flexible subscription options that grow your recurring revenue.

Whether you're selling digital services, physical subscription boxes, or membership access, this guide provides the technical knowledge to build robust subscription systems. We'll cover payment integration, customer management, and retention strategies that maximize subscription lifetime value.

Table of Contents

  1. Subscription Architecture
  2. Creating Subscription Products
  3. Payment and Billing Management
  4. Customer Portal and Management
  5. Retention and Analytics
  6. Advanced Subscription Features

Subscription Architecture

Core Subscription System

// Subscription management system
class WC_Subscription_System {

    /**
     * Subscription statuses
     */
    const STATUS_PENDING = 'pending';
    const STATUS_ACTIVE = 'active';
    const STATUS_ON_HOLD = 'on-hold';
    const STATUS_CANCELLED = 'cancelled';
    const STATUS_EXPIRED = 'expired';
    const STATUS_PENDING_CANCEL = 'pending-cancel';

    /**
     * Initialize subscription system
     */
    public function __construct() {
        // Register subscription post type
        add_action('init', [$this, 'register_subscription_post_type']);

        // Subscription lifecycle hooks
        add_action('woocommerce_order_status_completed', [$this, 'activate_subscription']);
        add_action('woocommerce_scheduled_subscription_payment', [$this, 'process_renewal_payment']);
        add_action('woocommerce_subscription_status_updated', [$this, 'handle_status_change'], 10, 3);

        // Cron events
        add_action('wc_subscriptions_daily', [$this, 'daily_subscription_tasks']);

        // Customer actions
        add_action('init', [$this, 'handle_customer_actions']);
    }

    /**
     * Register subscription post type
     */
    public function register_subscription_post_type() {
        register_post_type('shop_subscription', [
            'labels' => [
                'name' => __('Subscriptions', 'woocommerce'),
                'singular_name' => __('Subscription', 'woocommerce'),
            ],
            'public' => false,
            'show_ui' => true,
            'show_in_menu' => 'woocommerce',
            'capability_type' => 'shop_order',
            'map_meta_cap' => true,
            'supports' => ['title', 'comments', 'custom-fields'],
        ]);
    }

    /**
     * Create new subscription
     */
    public function create_subscription($args) {
        $defaults = [
            'customer_id' => 0,
            'parent_order_id' => 0,
            'product_id' => 0,
            'variation_id' => 0,
            'quantity' => 1,
            'period' => 'month',
            'interval' => 1,
            'length' => 0, // 0 = unlimited
            'trial_period' => '',
            'trial_length' => 0,
            'start_date' => current_time('mysql'),
            'next_payment_date' => '',
            'end_date' => '',
            'status' => self::STATUS_PENDING,
        ];

        $args = wp_parse_args($args, $defaults);

        // Create subscription post
        $subscription_id = wp_insert_post([
            'post_type' => 'shop_subscription',
            'post_status' => 'wc-' . $args['status'],
            'post_author' => 1,
            'post_title' => sprintf(__('Subscription #%s', 'woocommerce'), uniqid()),
        ]);

        if (is_wp_error($subscription_id)) {
            return $subscription_id;
        }

        // Store subscription data
        update_post_meta($subscription_id, '_customer_user', $args['customer_id']);
        update_post_meta($subscription_id, '_parent_order_id', $args['parent_order_id']);
        update_post_meta($subscription_id, '_product_id', $args['product_id']);
        update_post_meta($subscription_id, '_variation_id', $args['variation_id']);
        update_post_meta($subscription_id, '_quantity', $args['quantity']);
        update_post_meta($subscription_id, '_billing_period', $args['period']);
        update_post_meta($subscription_id, '_billing_interval', $args['interval']);
        update_post_meta($subscription_id, '_subscription_length', $args['length']);
        update_post_meta($subscription_id, '_trial_period', $args['trial_period']);
        update_post_meta($subscription_id, '_trial_length', $args['trial_length']);
        update_post_meta($subscription_id, '_start_date', $args['start_date']);

        // Calculate dates
        $this->calculate_subscription_dates($subscription_id);

        // Copy billing/shipping from parent order
        if ($args['parent_order_id']) {
            $this->copy_order_address($args['parent_order_id'], $subscription_id);
        }

        do_action('woocommerce_subscription_created', $subscription_id, $args);

        return $subscription_id;
    }

    /**
     * Calculate subscription dates
     */
    private function calculate_subscription_dates($subscription_id) {
        $start_date = get_post_meta($subscription_id, '_start_date', true);
        $trial_period = get_post_meta($subscription_id, '_trial_period', true);
        $trial_length = get_post_meta($subscription_id, '_trial_length', true);
        $billing_period = get_post_meta($subscription_id, '_billing_period', true);
        $billing_interval = get_post_meta($subscription_id, '_billing_interval', true);
        $subscription_length = get_post_meta($subscription_id, '_subscription_length', true);

        // Calculate trial end date
        if ($trial_period && $trial_length) {
            $trial_end = $this->calculate_next_date($start_date, $trial_period, $trial_length);
            update_post_meta($subscription_id, '_trial_end_date', $trial_end);
            $first_payment = $trial_end;
        } else {
            $first_payment = $start_date;
        }

        // Calculate next payment date
        $next_payment = $this->calculate_next_date($first_payment, $billing_period, $billing_interval);
        update_post_meta($subscription_id, '_next_payment_date', $next_payment);

        // Calculate end date
        if ($subscription_length > 0) {
            $end_date = $this->calculate_next_date($start_date, $billing_period, $subscription_length);
            update_post_meta($subscription_id, '_end_date', $end_date);
        }
    }

    /**
     * Calculate next date based on period and interval
     */
    private function calculate_next_date($from_date, $period, $interval) {
        $from_timestamp = strtotime($from_date);

        switch ($period) {
            case 'day':
                $next_timestamp = strtotime("+{$interval} days", $from_timestamp);
                break;
            case 'week':
                $next_timestamp = strtotime("+{$interval} weeks", $from_timestamp);
                break;
            case 'month':
                $next_timestamp = strtotime("+{$interval} months", $from_timestamp);
                break;
            case 'year':
                $next_timestamp = strtotime("+{$interval} years", $from_timestamp);
                break;
            default:
                $next_timestamp = $from_timestamp;
        }

        return date('Y-m-d H:i:s', $next_timestamp);
    }
}

// Initialize subscription system
new WC_Subscription_System();

Creating Subscription Products

Subscription Product Type

// Subscription product implementation
class WC_Product_Subscription extends WC_Product {

    protected $product_type = 'subscription';

    /**
     * Get subscription price
     */
    public function get_price($context = 'view') {
        return $this->get_prop('subscription_price', $context);
    }

    /**
     * Get subscription period
     */
    public function get_subscription_period() {
        return $this->get_meta('_subscription_period') ?: 'month';
    }

    /**
     * Get subscription interval
     */
    public function get_subscription_interval() {
        return $this->get_meta('_subscription_interval') ?: 1;
    }

    /**
     * Get subscription length
     */
    public function get_subscription_length() {
        return $this->get_meta('_subscription_length') ?: 0;
    }

    /**
     * Get trial period
     */
    public function get_trial_period() {
        return $this->get_meta('_trial_period') ?: '';
    }

    /**
     * Get trial length
     */
    public function get_trial_length() {
        return $this->get_meta('_trial_length') ?: 0;
    }

    /**
     * Get sign-up fee
     */
    public function get_sign_up_fee() {
        return $this->get_meta('_subscription_sign_up_fee') ?: 0;
    }

    /**
     * Get formatted price string
     */
    public function get_price_html($deprecated = '') {
        $price = $this->get_price();
        $sign_up_fee = $this->get_sign_up_fee();

        $price_string = wc_price($price);

        // Add period
        $period = $this->get_subscription_period();
        $interval = $this->get_subscription_interval();

        if ($interval == 1) {
            $price_string .= ' / ' . $period;
        } else {
            $price_string .= sprintf(' every %d %ss', $interval, $period);
        }

        // Add sign-up fee
        if ($sign_up_fee > 0) {
            $price_string .= sprintf(' + %s sign-up fee', wc_price($sign_up_fee));
        }

        // Add trial
        if ($this->get_trial_length() > 0) {
            $trial_string = $this->get_trial_string();
            $price_string .= ' ' . $trial_string;
        }

        return apply_filters('woocommerce_subscription_price_html', $price_string, $this);
    }

    /**
     * Get trial string
     */
    public function get_trial_string() {
        $trial_length = $this->get_trial_length();
        $trial_period = $this->get_trial_period();

        if ($trial_length > 0) {
            return sprintf(
                'with %d %s free trial',
                $trial_length,
                $trial_length == 1 ? $trial_period : $trial_period . 's'
            );
        }

        return '';
    }
}

// Register subscription product type
add_filter('product_type_selector', function($types) {
    $types['subscription'] = __('Subscription product', 'woocommerce');
    return $types;
});

add_filter('woocommerce_product_class', function($classname, $product_type) {
    if ($product_type === 'subscription') {
        return 'WC_Product_Subscription';
    }
    return $classname;
}, 10, 2);

Subscription Product Admin

// Admin interface for subscription products
class WC_Subscription_Product_Admin {

    public function __construct() {
        add_action('woocommerce_product_options_general_product_data', [$this, 'subscription_fields']);
        add_action('woocommerce_process_product_meta_subscription', [$this, 'save_subscription_fields']);
        add_filter('woocommerce_product_data_tabs', [$this, 'add_subscription_tab']);
        add_action('woocommerce_product_data_panels', [$this, 'subscription_data_panel']);
    }

    /**
     * Add subscription fields to general tab
     */
    public function subscription_fields() {
        global $post;

        echo '<div class="options_group subscription_pricing show_if_subscription">';

        // Subscription price
        woocommerce_wp_text_input([
            'id' => '_subscription_price',
            'label' => __('Subscription price', 'woocommerce') . ' (' . get_woocommerce_currency_symbol() . ')',
            'data_type' => 'price',
        ]);

        // Subscription period
        woocommerce_wp_select([
            'id' => '_subscription_period',
            'label' => __('Subscription period', 'woocommerce'),
            'options' => [
                'day' => __('day', 'woocommerce'),
                'week' => __('week', 'woocommerce'),
                'month' => __('month', 'woocommerce'),
                'year' => __('year', 'woocommerce'),
            ],
        ]);

        // Subscription interval
        woocommerce_wp_text_input([
            'id' => '_subscription_interval',
            'label' => __('Subscription interval', 'woocommerce'),
            'type' => 'number',
            'custom_attributes' => [
                'min' => '1',
                'step' => '1',
            ],
            'value' => get_post_meta($post->ID, '_subscription_interval', true) ?: 1,
        ]);

        // Subscription length
        woocommerce_wp_text_input([
            'id' => '_subscription_length',
            'label' => __('Subscription length', 'woocommerce'),
            'type' => 'number',
            'custom_attributes' => [
                'min' => '0',
                'step' => '1',
            ],
            'description' => __('The length of the subscription. Leave blank for unlimited length.', 'woocommerce'),
            'desc_tip' => true,
        ]);

        echo '</div>';

        echo '<div class="options_group show_if_subscription">';

        // Sign-up fee
        woocommerce_wp_text_input([
            'id' => '_subscription_sign_up_fee',
            'label' => __('Sign-up fee', 'woocommerce') . ' (' . get_woocommerce_currency_symbol() . ')',
            'data_type' => 'price',
            'description' => __('One-time fee charged at the beginning of the subscription.', 'woocommerce'),
            'desc_tip' => true,
        ]);

        echo '</div>';

        echo '<div class="options_group show_if_subscription">';

        // Trial period
        woocommerce_wp_select([
            'id' => '_trial_period',
            'label' => __('Trial period', 'woocommerce'),
            'options' => [
                '' => __('No trial', 'woocommerce'),
                'day' => __('day', 'woocommerce'),
                'week' => __('week', 'woocommerce'),
                'month' => __('month', 'woocommerce'),
                'year' => __('year', 'woocommerce'),
            ],
        ]);

        // Trial length
        woocommerce_wp_text_input([
            'id' => '_trial_length',
            'label' => __('Trial length', 'woocommerce'),
            'type' => 'number',
            'custom_attributes' => [
                'min' => '0',
                'step' => '1',
            ],
        ]);

        echo '</div>';
    }
}

new WC_Subscription_Product_Admin();

Payment and Billing Management

Recurring Payment Processing

// Recurring payment handler
class WC_Subscription_Payment_Handler {

    /**
     * Process renewal payment
     */
    public function process_renewal_payment($subscription_id) {
        $subscription = $this->get_subscription($subscription_id);

        if (!$subscription || $subscription->get_status() !== 'active') {
            return;
        }

        try {
            // Create renewal order
            $renewal_order = $this->create_renewal_order($subscription);

            // Get payment method
            $payment_method = $subscription->get_payment_method();
            $payment_gateway = WC()->payment_gateways()->payment_gateways()[$payment_method];

            if (!$payment_gateway || !$payment_gateway->supports('subscriptions')) {
                throw new Exception('Payment gateway does not support subscriptions');
            }

            // Process payment
            $result = $payment_gateway->process_subscription_payment(
                $renewal_order,
                $subscription
            );

            if ($result['result'] === 'success') {
                // Payment successful
                $renewal_order->payment_complete($result['transaction_id']);
                $subscription->update_dates(['next_payment' => $this->calculate_next_payment_date($subscription)]);

                // Send success email
                do_action('woocommerce_subscription_renewal_payment_complete', $subscription, $renewal_order);

            } else {
                // Payment failed
                $renewal_order->update_status('failed', $result['message']);
                $this->handle_failed_payment($subscription, $renewal_order);
            }

        } catch (Exception $e) {
            $this->log_error('Renewal payment failed: ' . $e->getMessage());
            $this->handle_failed_payment($subscription, $renewal_order ?? null);
        }
    }

    /**
     * Create renewal order
     */
    private function create_renewal_order($subscription) {
        $order = wc_create_order([
            'customer_id' => $subscription->get_customer_id(),
            'created_via' => 'subscription',
        ]);

        // Add subscription items
        $product = wc_get_product($subscription->get_product_id());
        $order->add_product($product, $subscription->get_quantity());

        // Copy addresses
        $order->set_address($subscription->get_address('billing'), 'billing');
        $order->set_address($subscription->get_address('shipping'), 'shipping');

        // Set payment method
        $order->set_payment_method($subscription->get_payment_method());

        // Link to subscription
        $order->update_meta_data('_subscription_renewal', $subscription->get_id());

        // Calculate totals
        $order->calculate_totals();
        $order->save();

        return $order;
    }

    /**
     * Handle failed payment
     */
    private function handle_failed_payment($subscription, $renewal_order = null) {
        $retry_count = $subscription->get_failed_payment_count();
        $max_retries = get_option('woocommerce_subscriptions_max_retry_count', 3);

        if ($retry_count < $max_retries) {
            // Schedule retry
            $retry_date = $this->calculate_retry_date($retry_count + 1);
            $subscription->update_status('on-hold', 'Payment failed - retry scheduled');
            $subscription->update_meta_data('_failed_payment_count', $retry_count + 1);

            as_schedule_single_action(
                strtotime($retry_date),
                'woocommerce_scheduled_subscription_payment_retry',
                [$subscription->get_id()]
            );

        } else {
            // Max retries reached - suspend subscription
            $subscription->update_status('suspended', 'Payment failed - max retries reached');
            do_action('woocommerce_subscription_payment_failed', $subscription, $renewal_order);
        }
    }
}

Payment Method Management

// Customer payment method management
class WC_Subscription_Payment_Methods {

    /**
     * Update payment method
     */
    public function update_payment_method($subscription_id, $payment_method_token) {
        $subscription = wcs_get_subscription($subscription_id);
        $token = WC_Payment_Tokens::get($payment_method_token);

        // Validate token
        if (!$token || $token->get_user_id() !== $subscription->get_customer_id()) {
            return new WP_Error('invalid_token', 'Invalid payment method');
        }

        // Update subscription
        $subscription->set_payment_method($token->get_gateway_id());
        $subscription->update_meta_data('_payment_token', $token->get_id());
        $subscription->update_meta_data('_payment_token_type', $token->get_type());

        // Store card details for display
        if ($token->get_type() === 'card') {
            $subscription->update_meta_data('_payment_card_last4', $token->get_last4());
            $subscription->update_meta_data('_payment_card_type', $token->get_card_type());
        }

        $subscription->save();

        // Log change
        $subscription->add_order_note(
            sprintf('Payment method updated to %s ending in %s',
                $token->get_card_type(),
                $token->get_last4()
            )
        );

        return true;
    }
}

Customer Portal and Management

My Account Subscription Management

// Customer subscription interface
class WC_Subscription_My_Account {

    public function __construct() {
        // Add menu items
        add_filter('woocommerce_account_menu_items', [$this, 'add_menu_items']);
        add_action('woocommerce_account_subscriptions_endpoint', [$this, 'subscriptions_content']);

        // Register endpoints
        add_action('init', [$this, 'add_endpoints']);

        // Handle actions
        add_action('template_redirect', [$this, 'handle_subscription_actions']);
    }

    /**
     * Add subscription menu items
     */
    public function add_menu_items($items) {
        $items['subscriptions'] = __('Subscriptions', 'woocommerce');
        return $items;
    }

    /**
     * Display subscriptions
     */
    public function subscriptions_content() {
        $customer_id = get_current_user_id();
        $subscriptions = $this->get_customer_subscriptions($customer_id);

        wc_get_template('myaccount/subscriptions.php', [
            'subscriptions' => $subscriptions,
        ]);
    }

    /**
     * Handle subscription actions
     */
    public function handle_subscription_actions() {
        if (!is_account_page() || !isset($_GET['subscription_action'])) {
            return;
        }

        $action = sanitize_key($_GET['subscription_action']);
        $subscription_id = absint($_GET['subscription_id']);

        // Verify nonce
        if (!wp_verify_nonce($_GET['_wpnonce'], 'subscription_action')) {
            wc_add_notice(__('Invalid request', 'woocommerce'), 'error');
            return;
        }

        // Verify ownership
        $subscription = wcs_get_subscription($subscription_id);
        if (!$subscription || $subscription->get_customer_id() !== get_current_user_id()) {
            wc_add_notice(__('Invalid subscription', 'woocommerce'), 'error');
            return;
        }

        switch ($action) {
            case 'pause':
                $this->pause_subscription($subscription);
                break;

            case 'resume':
                $this->resume_subscription($subscription);
                break;

            case 'cancel':
                $this->cancel_subscription($subscription);
                break;

            case 'reactivate':
                $this->reactivate_subscription($subscription);
                break;
        }

        wp_safe_redirect(wc_get_account_endpoint_url('subscriptions'));
        exit;
    }
}

new WC_Subscription_My_Account();

Retention and Analytics

Subscription Analytics

// Subscription analytics and reporting
class WC_Subscription_Analytics {

    /**
     * Get subscription metrics
     */
    public function get_metrics($date_from = null, $date_to = null) {
        global $wpdb;

        $metrics = [
            'mrr' => $this->calculate_mrr(),
            'arr' => $this->calculate_arr(),
            'churn_rate' => $this->calculate_churn_rate($date_from, $date_to),
            'ltv' => $this->calculate_ltv(),
            'new_subscriptions' => $this->count_new_subscriptions($date_from, $date_to),
            'cancelled_subscriptions' => $this->count_cancelled_subscriptions($date_from, $date_to),
            'active_subscriptions' => $this->count_active_subscriptions(),
            'trial_conversions' => $this->calculate_trial_conversion_rate($date_from, $date_to),
        ];

        return $metrics;
    }

    /**
     * Calculate Monthly Recurring Revenue
     */
    private function calculate_mrr() {
        global $wpdb;

        $mrr = $wpdb->get_var("
            SELECT SUM(
                CASE
                    WHEN pm2.meta_value = 'day' THEN pm1.meta_value * 30
                    WHEN pm2.meta_value = 'week' THEN pm1.meta_value * 4.33
                    WHEN pm2.meta_value = 'month' THEN pm1.meta_value
                    WHEN pm2.meta_value = 'year' THEN pm1.meta_value / 12
                    ELSE 0
                END
            ) as mrr
            FROM {$wpdb->posts} p
            LEFT JOIN {$wpdb->postmeta} pm1 ON p.ID = pm1.post_id AND pm1.meta_key = '_subscription_price'
            LEFT JOIN {$wpdb->postmeta} pm2 ON p.ID = pm2.post_id AND pm2.meta_key = '_billing_period'
            WHERE p.post_type = 'shop_subscription'
            AND p.post_status = 'wc-active'
        ");

        return floatval($mrr);
    }

    /**
     * Calculate churn rate
     */
    private function calculate_churn_rate($date_from, $date_to) {
        $start_active = $this->count_active_subscriptions($date_from);
        $cancelled = $this->count_cancelled_subscriptions($date_from, $date_to);

        if ($start_active == 0) {
            return 0;
        }

        return ($cancelled / $start_active) * 100;
    }

    /**
     * Retention cohort analysis
     */
    public function get_retention_cohorts($months = 12) {
        global $wpdb;

        $cohorts = [];

        for ($i = $months; $i >= 0; $i--) {
            $cohort_date = date('Y-m-01', strtotime("-{$i} months"));
            $cohort_end = date('Y-m-t', strtotime("-{$i} months"));

            // Get subscriptions started in this cohort
            $cohort_subscriptions = $wpdb->get_col($wpdb->prepare("
                SELECT ID FROM {$wpdb->posts} p
                JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
                WHERE p.post_type = 'shop_subscription'
                AND pm.meta_key = '_start_date'
                AND pm.meta_value >= %s
                AND pm.meta_value <= %s
            ", $cohort_date, $cohort_end));

            if (empty($cohort_subscriptions)) {
                continue;
            }

            $cohorts[$cohort_date] = [
                'size' => count($cohort_subscriptions),
                'retention' => [],
            ];

            // Calculate retention for each subsequent month
            for ($j = 0; $j <= $i; $j++) {
                $check_date = date('Y-m-d', strtotime("+{$j} months", strtotime($cohort_date)));
                $active_count = $this->count_active_in_cohort($cohort_subscriptions, $check_date);

                $cohorts[$cohort_date]['retention'][$j] = [
                    'count' => $active_count,
                    'percentage' => ($active_count / count($cohort_subscriptions)) * 100,
                ];
            }
        }

        return $cohorts;
    }
}

Advanced Subscription Features

Subscription Switching

// Subscription upgrade/downgrade functionality
class WC_Subscription_Switching {

    /**
     * Switch subscription
     */
    public function switch_subscription($subscription_id, $new_product_id, $args = []) {
        $subscription = wcs_get_subscription($subscription_id);
        $new_product = wc_get_product($new_product_id);

        if (!$subscription || !$new_product || !$new_product->is_type('subscription')) {
            return new WP_Error('invalid_request', 'Invalid subscription or product');
        }

        // Calculate prorated amount
        $proration = $this->calculate_proration($subscription, $new_product);

        // Create switch order
        $switch_order = wc_create_order([
            'customer_id' => $subscription->get_customer_id(),
            'created_via' => 'subscription_switch',
        ]);

        // Add new product
        $switch_order->add_product($new_product, 1);

        // Apply proration
        if ($proration != 0) {
            $fee = new WC_Order_Item_Fee();
            $fee->set_name($proration > 0 ? 'Proration credit' : 'Proration charge');
            $fee->set_total($proration);
            $switch_order->add_item($fee);
        }

        // Process payment if needed
        if ($switch_order->get_total() > 0) {
            // Charge customer
            $payment_result = $this->process_switch_payment($switch_order, $subscription);

            if (!$payment_result) {
                return new WP_Error('payment_failed', 'Switch payment failed');
            }
        }

        // Update subscription
        $subscription->update_meta_data('_product_id', $new_product_id);
        $subscription->update_meta_data('_subscription_price', $new_product->get_price());

        // Recalculate dates
        $this->recalculate_dates($subscription, $args['next_payment_date'] ?? null);

        $subscription->save();

        // Log switch
        $subscription->add_order_note(
            sprintf('Subscription switched from %s to %s',
                wc_get_product($subscription->get_product_id())->get_name(),
                $new_product->get_name()
            )
        );

        do_action('woocommerce_subscription_switched', $subscription, $new_product, $switch_order);

        return $switch_order;
    }

    /**
     * Calculate proration amount
     */
    private function calculate_proration($subscription, $new_product) {
        $current_price = $subscription->get_total();
        $new_price = $new_product->get_price();

        // Get remaining days in current period
        $next_payment = strtotime($subscription->get_next_payment_date());
        $today = current_time('timestamp');
        $days_remaining = max(0, ($next_payment - $today) / DAY_IN_SECONDS);

        // Calculate daily rates
        $period_days = $this->get_period_days($subscription->get_billing_period());
        $current_daily_rate = $current_price / $period_days;
        $new_daily_rate = $new_price / $period_days;

        // Calculate proration
        $current_credit = $current_daily_rate * $days_remaining;
        $new_charge = $new_daily_rate * $days_remaining;

        return $current_credit - $new_charge;
    }
}

Building subscription products with WooCommerce requires careful planning of billing cycles, payment processing, and customer management. By implementing robust subscription architecture and focusing on retention strategies, you can create successful recurring revenue streams that provide predictable income and stronger customer relationships.