id); try { $success = SubscriptionManager::renew($subscription->id); if ($success) { do_action('woonoow/subscription/renewal_completed', $subscription->id); } else { // Auto-debit failed (returns false), so schedule retry // Note: 'manual' falls into a separate bucket in SubscriptionManager and returns true (handled) self::schedule_retry($subscription->id); } } catch (\Exception $e) { // Log error error_log('[WooNooW Subscription] Renewal failed for subscription #' . $subscription->id . ': ' . $e->getMessage()); do_action('woonoow/subscription/renewal_error', $subscription->id, $e); // Also schedule retry on exception self::schedule_retry($subscription->id); } } } /** * Check for expired subscriptions */ public static function check_expirations() { global $wpdb; if (!ModuleRegistry::is_enabled('subscription')) { return; } $table = $wpdb->prefix . 'woonoow_subscriptions'; $now = current_time('mysql'); // Find subscriptions that have passed their end date $expired = $wpdb->get_results($wpdb->prepare( "SELECT id FROM $table WHERE status = 'active' AND end_date IS NOT NULL AND end_date <= %s", $now )); foreach ($expired as $subscription) { SubscriptionManager::update_status($subscription->id, 'expired'); do_action('woonoow/subscription/expired', $subscription->id, 'end_date_reached'); } // Also check pending-cancel subscriptions that need to be finalized $pending_cancel = $wpdb->get_results($wpdb->prepare( "SELECT id FROM $table WHERE status = 'pending-cancel' AND next_payment_date IS NOT NULL AND next_payment_date <= %s", $now )); foreach ($pending_cancel as $subscription) { SubscriptionManager::update_status($subscription->id, 'cancelled'); do_action('woonoow/subscription/cancelled', $subscription->id, 'pending_cancel_completed'); } } /** * Send renewal reminder emails */ public static function send_reminders() { global $wpdb; if (!ModuleRegistry::is_enabled('subscription')) { return; } // Check if reminders are enabled $settings = ModuleRegistry::get_settings('subscription'); if (empty($settings['send_renewal_reminder'])) { return; } $days_before = $settings['reminder_days_before'] ?? 3; $reminder_date = date('Y-m-d H:i:s', strtotime("+$days_before days")); $tomorrow = date('Y-m-d H:i:s', strtotime('+' . ($days_before + 1) . ' days')); $table = $wpdb->prefix . 'woonoow_subscriptions'; // Find subscriptions due for reminder (that haven't had reminder sent for this billing cycle) $due_reminders = $wpdb->get_results($wpdb->prepare( "SELECT * FROM $table WHERE status = 'active' AND next_payment_date IS NOT NULL AND next_payment_date >= %s AND next_payment_date < %s AND (reminder_sent_at IS NULL OR reminder_sent_at < last_payment_date OR (last_payment_date IS NULL AND reminder_sent_at < start_date))", $reminder_date, $tomorrow )); foreach ($due_reminders as $subscription) { // Trigger reminder email do_action('woonoow/subscription/renewal_reminder', $subscription); // Mark reminder as sent in database $wpdb->update( $table, ['reminder_sent_at' => current_time('mysql')], ['id' => $subscription->id], ['%s'], ['%d'] ); } } /** * Get retry schedule for failed payments * * @param int $subscription_id * @return string|null Next retry datetime or null if no more retries */ public static function get_next_retry_date($subscription_id) { $subscription = SubscriptionManager::get($subscription_id); if (!$subscription) { return null; } $settings = ModuleRegistry::get_settings('subscription'); if (empty($settings['renewal_retry_enabled'])) { return null; } $retry_days_str = $settings['renewal_retry_days'] ?? '1,3,5'; $retry_days = array_map('intval', array_filter(explode(',', $retry_days_str))); $failed_count = $subscription->failed_payment_count; if ($failed_count >= count($retry_days)) { return null; // No more retries } $days_to_add = $retry_days[$failed_count] ?? 1; return date('Y-m-d H:i:s', strtotime("+$days_to_add days")); } /** * Schedule a retry for failed payment * * @param int $subscription_id */ public static function schedule_retry($subscription_id) { global $wpdb; $next_retry = self::get_next_retry_date($subscription_id); if ($next_retry) { $table = $wpdb->prefix . 'woonoow_subscriptions'; $wpdb->update( $table, ['next_payment_date' => $next_retry], ['id' => $subscription_id], ['%s'], ['%d'] ); } } }