# easy-invoice/2.1.2/includes/Controllers/PaymentController.php

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.1.2. 1,436 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.1.2/code/includes/Controllers/PaymentController.php
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.1.2/raw/includes/Controllers/PaymentController.php
- Modified: 2025-08-19T12:00:36+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/easy-invoice/2.1.2/code/includes/Controllers/PaymentController.php#L10-L20`.

```php
<?php
/**
 * Payment Controller
 *
 * @package Easy_Invoice
 */

namespace EasyInvoice\Controllers;

use EasyInvoice\PaymentGatewayManager;
use EasyInvoice\EasyInvoice;
use EasyInvoice\Models\Invoice;
use EasyInvoice\Models\Payment;
use EasyInvoice\Traits\TemplateTrait;
use EasyInvoice\Traits\PaymentCalculationTrait;
use EasyInvoice\Constants\PagesSlugs;
use EasyInvoice\Constants\InvoiceFields;
use EasyInvoice\Constants\InvoiceMetaKeys;
use EasyInvoice\Helpers\Sanitization;
use EasyInvoice\Providers\InvoiceServiceProvider; // Assuming this is used elsewhere or for future

/**
 * Class PaymentController
 * 
 * @package EasyInvoice\Controllers
 */
class PaymentController extends BaseController {
    use TemplateTrait;
    use PaymentCalculationTrait;

    /**
     * Payment gateway manager instance
     * 
     * @var PaymentGatewayManager
     */
    private $gatewayManager;

    /**
     * Constructor
     */
    public function __construct() {
        $this->gatewayManager = EasyInvoice::getInstance()->getGatewayManager();
    }

    /**
     * Initialize the controller
     */
    public function init() {
        add_action('admin_enqueue_scripts', [$this, 'enqueueAssets']);
        add_action('wp_ajax_easy_invoice_process_payment', [$this, 'processPayment']);
        add_action('wp_ajax_nopriv_easy_invoice_process_payment', [$this, 'processPayment']);
        add_action('wp_ajax_easy_invoice_update_payment', [$this, 'updatePayment']);
        add_action('wp_ajax_easy_invoice_payment_callback', [$this, 'handleCallback']);
        add_action('wp_ajax_nopriv_easy_invoice_payment_callback', [$this, 'handleCallback']);
        add_action('wp_ajax_easy_invoice_verify_manual_payment', [$this, 'verifyManualPayment']);
        add_action('wp_ajax_easy_invoice_reject_manual_payment', [$this, 'rejectManualPayment']);


        
        // Handler for submitting payment proof for manual gateways
        add_action('wp_ajax_easy_invoice_submit_payment_proof', [$this, 'submitPaymentProof']);
        add_action('wp_ajax_nopriv_easy_invoice_submit_payment_proof', [$this, 'submitPaymentProof']);

        // Handler for getting payment instructions for manual gateways
        add_action('wp_ajax_easy_invoice_get_payment_instructions', [$this, 'getPaymentInstructions']);
        add_action('wp_ajax_nopriv_easy_invoice_get_payment_instructions', [$this, 'getPaymentInstructions']);

        // Handler for admin to mark an invoice as paid
        add_action('wp_ajax_easy_invoice_approve_payment', [$this, 'mark_invoice_paid_ajax']);
        
        // Stripe payment handlers moved to Pro plugin

        add_action('wp_enqueue_scripts', [$this, 'enqueueScripts']);
        
        // Add filter to show pending payments in admin
        add_filter('easy_invoice_admin_payment_statuses', [$this, 'addPendingPaymentStatuses']);
        
        // Add custom columns to payments list
        add_filter('manage_easy-payment_posts_columns', [$this, 'addPaymentMethodColumn']);
        add_action('manage_easy-payment_posts_custom_column', [$this, 'renderPaymentMethodColumn'], 10, 2);
        
        // Add reminder CRON job for pending payments
        add_action('easy_invoice_payment_reminder', [$this, 'sendPaymentReminders']);
        if (!wp_next_scheduled('easy_invoice_payment_reminder')) {
            wp_schedule_event(time(), 'daily', 'easy_invoice_payment_reminder');
        }
        
        // Handle bulk actions
        add_action('admin_init', [$this, 'handleBulkActions']);
    }

    /**
     * Get payment instructions for manual gateways
     */
    public function getPaymentInstructions() {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'], 'easy_invoice_payment')) {
            wp_send_json_error(['message' => 'Security check failed']);
            return;
        }
        
        $gateway = sanitize_text_field($_POST['gateway']);
        $invoice_id = intval($_POST['invoice_id']);
        
        if (!$gateway || !$invoice_id) {
            wp_send_json_error(['message' => 'Missing required parameters']);
            return;
        }
        
        // Get invoice
        $invoice_post = get_post($invoice_id);
        if (!$invoice_post || $invoice_post->post_type !== 'easy_invoice') {
            wp_send_json_error(['message' => 'Invalid invoice']);
            return;
        }
        
        $invoice = new \EasyInvoice\Models\Invoice($invoice_post);
        
        // Get gateway instance
        $gateway_instance = $this->gatewayManager->getGateway($gateway);
        
        if (!$gateway_instance) {
            wp_send_json_error(['message' => 'Gateway not found']);
            return;
        }
        
        // Get instructions using the hook system
        ob_start();
        do_action('easy_invoice_payment_gateways_after', $invoice, $gateway);
        $instructions = ob_get_clean();
        
        if ($instructions) {
            wp_send_json_success(['instructions' => $instructions]);
        } else {
            wp_send_json_error(['message' => 'No instructions available']);
        }
    }

    /**
     * Enqueue admin assets
     */
    public function enqueueAssets() {
        $screen = get_current_screen();
        if (!$screen || !property_exists($screen, 'id') || strpos($screen->id, 'easy-invoice') === false) {
            return;
        }

    
    }

    /**
     * Display method implementation
     * 
     * @param array $args Display arguments
     */
    public function display(array $args = []) {
        $page = isset($args['page']) ? $args['page'] : '';
        
        switch ($page) {
            case PagesSlugs::PAYMENTS:
                $this->displayPaymentsPage();
                break;
                
            case PagesSlugs::PAYMENT_NEW:
                $this->displayTemplate(EASY_INVOICE_PLUGIN_DIR . 'templates/payments/new.php');
                break;
                
            case 'view':
                $payment_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
                if ($payment_id) {
                    try {
                        $payment = new Payment($payment_id);
                        $this->displayTemplate(EASY_INVOICE_PLUGIN_DIR . 'templates/payments/view.php', ['payment' => $payment]);
                    } catch (\Exception $e) {
                        wp_die(__('Invalid payment ID', 'easy-invoice'));
                    }
                } else {
                    wp_die(__('Payment ID is required', 'easy-invoice'));
                }
                break;

            case 'edit':
                $payment_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
                if ($payment_id) {
                    try {
                        $payment = new Payment($payment_id);
                        $this->displayTemplate(EASY_INVOICE_PLUGIN_DIR . 'templates/payments/edit.php', ['payment' => $payment]);
                    } catch (\Exception $e) {
                        wp_die(__('Invalid payment ID', 'easy-invoice'));
                    }
                } else {
                    wp_die(__('Payment ID is required', 'easy-invoice'));
                }
                break;
                
            default:
                $this->displayPaymentsPage();
                break;
        }
    }

    /**
     * Display payments page with pagination
     */
    protected function displayPaymentsPage() {
        // Get current view (all, trash)
        $current_view = isset($_GET['view']) ? sanitize_text_field($_GET['view']) : 'all';
        
        // Get status filter
        $status_filter = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : '';
        
        // Pagination settings
        $per_page = 20;
        $current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
        
        // Build query arguments
        $args = array(
            'post_type' => 'easy_invoice_payment',
            'posts_per_page' => $per_page,
            'paged' => $current_page,
            'orderby' => 'ID',
            'order' => 'DESC',
            'no_found_rows' => false, // We need this for pagination
        );
        
        // Set post status based on current view
        if ($current_view === 'trash') {
            $args['post_status'] = 'trash';
        } else {
            $args['post_status'] = 'publish';
        }
        
        // Add status filter if set
        if (!empty($status_filter)) {
            $args['meta_query'] = array(
                array(
                    'key' => '_status',
                    'value' => $status_filter,
                ),
            );
        }
        
        // Allow plugins to modify query arguments
        $args = apply_filters('easy_invoice_payment_controller_query_args', $args, $current_view, $status_filter);
        
        
        // Get paginated payments using WordPress query
        $wp_query = new \WP_Query($args);
        
        
        $payments = [];
        
        if ($wp_query->have_posts()) {
            while ($wp_query->have_posts()) {
                $wp_query->the_post();
                $post = get_post();
                $payment = new Payment($post);
                $payments[] = $payment;
            }
        }
        
        wp_reset_postdata();
        
        // Allow plugins to modify the payments array
        $payments = apply_filters('easy_invoice_payment_controller_payments_list', $payments, $wp_query);
        
        // Get pagination info from WordPress query
        $total_payments = $wp_query->found_posts;
        $total_pages = $wp_query->max_num_pages;
        
        // Calculate statistics from ALL payments (not just current page)
        $stats_args = array(
            'post_type' => 'easy_invoice_payment',
            'posts_per_page' => -1, // Get all payments
            'meta_query' => array(
                array(
                    'key' => '_status',
                    'compare' => 'EXISTS',
                ),
            ),
        );
        
        // Set post status for stats based on current view
        if ($current_view === 'trash') {
            $stats_args['post_status'] = 'trash';
        } else {
            $stats_args['post_status'] = 'publish';
        }
        
        $stats_query = new \WP_Query($stats_args);
        
        $stats = [
            'total_payments' => $stats_query->found_posts,
            'total_amount' => 0,
            'completed_payments' => 0,
            'pending_payments' => 0,
            'failed_payments' => 0
        ];
        
        // Calculate stats from the query results
        if ($stats_query->have_posts()) {
            while ($stats_query->have_posts()) {
                $stats_query->the_post();
                $payment = new Payment(get_post());
                
                $amount = floatval($payment->getAmount());
                $status = $payment->getStatus();
                
                $stats['total_amount'] += $amount;
                
                switch ($status) {
                    case 'completed':
                        $stats['completed_payments']++;
                        break;
                    case 'pending':
                        $stats['pending_payments']++;
                        break;
                    case 'failed':
                        $stats['failed_payments']++;
                        break;
                }
            }
        }
        wp_reset_postdata();
        
        // Ensure all required keys exist with default values
        $stats = array_merge([
            'total_payments' => 0,
            'total_amount' => 0,
            'completed_payments' => 0,
            'pending_payments' => 0,
            'failed_payments' => 0
        ], $stats);
        
        // Get trash count for tab display
        $trash_args = array(
            'post_type' => 'easy_invoice_payment',
            'post_status' => 'trash',
            'posts_per_page' => -1
        );
        $trash_query = new \WP_Query($trash_args);
        $trash_count = $trash_query->found_posts;
        
        // Define available status filters
        $status_filters = array(
            'completed' => 'Completed',
            'pending' => 'Pending',
            'failed' => 'Failed'
        );
        
        // Prepare template data
        $template_data = [
            'payments' => $payments,
            'current_view' => $current_view,
            'status_filter' => $status_filter,
            'status_filters' => $status_filters,
            'trash_count' => $trash_count,
            'stats' => $stats,
            'current_page' => $current_page,
            'per_page' => $per_page,
            'total_payments' => $total_payments,
            'total_pages' => $total_pages,
            'wp_query' => $wp_query
        ];
        
        // Allow plugins to modify template data
        $template_data = apply_filters('easy_invoice_payment_controller_template_data', $template_data);
        
        // Display the template
        $this->displayTemplate(
            EASY_INVOICE_PLUGIN_DIR . 'templates/payments/list.php',
            $template_data
        );
        
        // Allow plugins to perform actions after displaying payments page
        do_action('easy_invoice_payment_controller_after_display_payments_page', $template_data);
    }

    /**
     * Enqueue required scripts and styles
     */
    public function enqueueScripts(): void {
        // Check if scripts are already enqueued
        if (wp_script_is('easy-invoice-payment', 'enqueued')) {
            return;
        }

        // Enqueue our custom scripts
        wp_enqueue_script(
            'easy-invoice-payment',
            EASY_INVOICE_URL . 'assets/js/payment.js',
            ['jquery'],
            EASY_INVOICE_VERSION,
            true
        );

        // Get currency settings
        $settings_controller = new \EasyInvoice\Controllers\SettingsController();
        $settings = $settings_controller->getSettings();
        $currency_code = $settings['easy_invoice_currency_code'] ?? 'USD';
        $currency_symbol = \EasyInvoice\Helpers\CurrencyHelper::getCurrencySymbol($currency_code);

        // Localize script variables for payment form
        wp_localize_script('easy-invoice-payment', 'easy_invoice_vars', [
            'ajax_url' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('easy_invoice_payment'),
            'currency_symbol' => $currency_symbol,
            'currency_code' => $currency_code
        ]);
    }

    // Stripe methods moved to Pro plugin

    /**
     * Process payment via AJAX
     */
    public function processPayment() {
        check_ajax_referer('easy_invoice_payment', 'payment_nonce');

        $invoice_id = isset($_POST['invoice_id']) ? intval($_POST['invoice_id']) : 0;
        $payment_method_slug = isset($_POST['payment_method']) ? sanitize_text_field($_POST['payment_method']) : '';

        // Add filter for extensions to handle custom payment logic (e.g., partial payments)
        $custom_result = apply_filters('easy_invoice_before_process_payment', null, $invoice_id, $_POST);
        
        if (is_array($custom_result) && isset($custom_result['handled']) && $custom_result['handled']) {
            if ($custom_result['success']) {
                wp_send_json_success($custom_result);
            } else {
                wp_send_json_error(['message' => $custom_result['message'] ?? __('Payment failed.', 'easy-invoice')]);
            }
            return;
        }

        if (!$invoice_id || !$payment_method_slug) {
            wp_send_json_error(['message' => __('Missing required fields.', 'easy-invoice')]);
            return;
        }

        $invoice_post = get_post($invoice_id);
        if (!$invoice_post || $invoice_post->post_type !== \EasyInvoice\Constants\PostTypes::EASY_INVOICE_POST_TYPE) {
            wp_send_json_error(['message' => __('Invalid invoice.', 'easy-invoice')]);
            return;
        }
        
        $invoice = new \EasyInvoice\Models\Invoice($invoice_post);
        $amount = $invoice->total ?? 0;
        
        // Log the payment processing details

        $gateway_instance = $this->gatewayManager->getGateway($payment_method_slug);

        if (!$gateway_instance || !$gateway_instance->isEnabled() || !$gateway_instance->isAvailable()) {
            wp_send_json_error(['message' => __('Selected payment gateway is not available or configured correctly.', 'easy-invoice')]);
            return;
        }

        try {
            // Pass the entire $_POST array to the gateway
            $result = $gateway_instance->processPayment($amount, $_POST);

            if (isset($result['success']) && $result['success']) {
                wp_send_json_success($result); 
            } else {
                wp_send_json_error(['message' => $result['message'] ?? __('Payment processing failed with the gateway.', 'easy-invoice')]);
            }

        } catch (\Exception $e) {
            error_log('Easy Invoice Payment Error: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine());
            wp_send_json_error(['message' => __('An unexpected error occurred during payment processing. Please check plugin logs or contact support.', 'easy-invoice')]);
        }
    }

    /**
     * Handle payment callback/webhook
     */
    public function handleCallback(): void {
        check_ajax_referer('easy_invoice_payment', 'payment_nonce');

        $invoice_id = isset($_POST['invoice_id']) ? intval($_POST['invoice_id']) : 0;
        $gateway = isset($_POST['gateway']) ? sanitize_text_field($_POST['gateway']) : '';

        if (!$invoice_id || !$gateway) {
            wp_send_json_error(['message' => __('Invalid request', 'easy-invoice')]);
        }

        $gateway_instance = $this->gatewayManager->getGateway($gateway);
        if (!$gateway_instance) {
            wp_send_json_error(['message' => __('Invalid payment gateway', 'easy-invoice')]);
        }

        $result = $gateway_instance->handleCallback($_POST);
        
        // Send admin notification for manual payments
        if ($result['success'] && in_array($gateway, ['bank', 'cheque'])) {
            do_action('easy_invoice_manual_payment_submitted', $invoice_id, $gateway);
        }

        if ($result['success']) {
            wp_send_json_success($result);
        } else {
            wp_send_json_error($result);
        }
    }

    /**
     * Get available payment gateways for an invoice
     * 
     * @param int $invoice_id
     * @return array
     */
    public function getAvailableGateways(int $invoice_id): array {
        $post = get_post($invoice_id);
        if (!$post || $post->post_type !== \EasyInvoice\Constants\PostTypes::EASY_INVOICE_POST_TYPE) {
            return [];
        }

        $invoice = new \EasyInvoice\Models\Invoice($post);
        $invoice_status = $invoice->getStatus();
        
        if (!in_array($invoice_status, [ 'unpaid', 'available'])) {
            return [];
        }

        $enabled_gateways = $this->gatewayManager->getEnabledGateways();
        
        if (empty($enabled_gateways)) {
            return [];
        }

        // Get invoice-specific gateways (comma-separated string or empty)
        $invoice_gateways = $invoice->getPaymentGateways();
        $selected_gateways = [];
        
        // Handle both string and array formats
        if (!empty($invoice_gateways)) {
            if (is_string($invoice_gateways)) {
                // If it's a string, split by comma
                $selected_gateways = array_filter(array_map('trim', explode(',', $invoice_gateways)));
            } elseif (is_array($invoice_gateways)) {
                // If it's already an array, use it directly
                $selected_gateways = array_filter($invoice_gateways);
            }
        }

        $available_gateways = [];
        $gateway_manager = \EasyInvoice\EasyInvoice::getInstance()->getGatewayManager();
        
        // $enabled_gateways is an associative array with gateway_id as key and gateway object as value
        foreach ($enabled_gateways as $gateway_id => $gateway) {
            // If invoice has custom gateways selected, only show those
            // If no custom gateways are selected (empty array), show all enabled gateways
            if (!empty($selected_gateways) && !in_array($gateway_id, $selected_gateways, true)) {
                continue;
            }
            
            $is_available = $gateway->isAvailable();
            
            if ($is_available) {
                $available_gateways[] = [
                    'id' => $gateway_id,
                    'title' => $gateway_manager->getGatewayDisplayName($gateway_id),
                    'icon' => $gateway->getIcon(),
                    'description' => $gateway->getDescription()
                ];
            }
        }

        return $available_gateways;
    }

    /**
     * Update payment via AJAX
     */
    public function updatePayment() {
        check_ajax_referer('easy_invoice_payment', 'payment_nonce');

        $payment_id = isset($_POST['payment_id']) ? intval($_POST['payment_id']) : 0;
        $invoice_id = isset($_POST['invoice_id']) ? intval($_POST['invoice_id']) : 0;
        $amount = isset($_POST['amount']) ? floatval($_POST['amount']) : 0;
        $payment_method = isset($_POST['payment_method']) ? sanitize_text_field($_POST['payment_method']) : '';
        $payment_date = isset($_POST['payment_date']) ? sanitize_text_field($_POST['payment_date']) : date('Y-m-d');
        $status = isset($_POST['status']) ? sanitize_text_field($_POST['status']) : 'pending';
        $notes = isset($_POST['notes']) ? sanitize_textarea_field($_POST['notes']) : '';

        if (!$payment_id || !$invoice_id || !$amount || !$payment_method) {
            wp_send_json_error(['message' => __('Missing required fields', 'easy-invoice')]);
            return;
        }

        try {
            $payment = new Payment($payment_id);
            if (!$payment->exists()) {
                wp_send_json_error(['message' => __('Invalid payment', 'easy-invoice')]);
                return;
            }

            $post = get_post($invoice_id);
            if (!$post || $post->post_type !== \EasyInvoice\Constants\PostTypes::EASY_INVOICE_POST_TYPE) {
                wp_send_json_error(['message' => __('Invalid invoice', 'easy-invoice')]);
                return;
            }
            $invoice = new Invoice($post);

            $payment_data = [
                'invoice_id' => $invoice_id,
                'amount' => $amount,
                'payment_method' => $payment_method,
                'payment_date' => $payment_date,
                'status' => $status,
                'notes' => $notes,
                'gateway_response' => [
                    'method' => $payment_method,
                    'date' => $payment_date,
                    'notes' => $notes
                ]
            ];

            $result = $payment->update($payment_data);

            if ($result) {
                wp_send_json_success([
                    'message' => __('Payment updated successfully', 'easy-invoice'),
                    'redirect' => admin_url('admin.php?page=easy-invoice-payments')
                ]);
            } else {
                wp_send_json_error(['message' => __('Failed to update payment', 'easy-invoice')]);
            }
        } catch (\Exception $e) {
            wp_send_json_error(['message' => $e->getMessage()]);
        }
    }

    /**
     * Verify manual payment
     */
    public function verifyManualPayment(): void {
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('You do not have permission to perform this action', 'easy-invoice')]);
            return;
        }
        
        // Verify nonce
        check_ajax_referer('easy_invoice_admin', 'nonce');
        
        $invoice_id = isset($_POST['invoice_id']) ? intval($_POST['invoice_id']) : 0;
        $amount = isset($_POST['amount']) ? floatval($_POST['amount']) : 0;
        $payment_method = isset($_POST['payment_method']) ? sanitize_text_field($_POST['payment_method']) : '';
        $notes = isset($_POST['notes']) ? sanitize_textarea_field($_POST['notes']) : '';
        $transaction_id = isset($_POST['transaction_id']) ? sanitize_text_field($_POST['transaction_id']) : '';
        
        if (!$invoice_id || !$amount || !$payment_method) {
            wp_send_json_error(['message' => __('Missing required fields', 'easy-invoice')]);
            return;
        }
        
        // Get the invoice
        $post = get_post($invoice_id);
        if (!$post || $post->post_type !== \EasyInvoice\Constants\PostTypes::EASY_INVOICE_POST_TYPE) {
            wp_send_json_error(['message' => __('Invalid invoice', 'easy-invoice')]);
            return;
        }
        
        $invoice = new Invoice($post);
        
        // Get currency settings
        $settings_controller = new \EasyInvoice\Controllers\SettingsController();
        $settings = $settings_controller->getSettings();
        $currency_code = $settings['easy_invoice_currency_code'] ?? 'USD';
        $currency_symbol = \EasyInvoice\Helpers\CurrencyHelper::getCurrencySymbol($currency_code);
        
        $payment_data = [
            'invoice_id' => $invoice_id,
            'amount' => $amount,
            'payment_method' => $payment_method,
            'payment_date' => current_time('mysql'),
            'notes' => $notes,
            'status' => 'completed',
            'payment_type' => 'full',
            'transaction_id' => $transaction_id,
            'recurring_id' => '',
            'parent_payment_id' => '',
            'currency' => $currency_code,
            'currency_symbol' => $currency_symbol,
            'gateway_response' => [
                'admin_verified' => true,
                'verification_date' => current_time('mysql'),
                'verification_user' => get_current_user_id()
            ]
        ];
        
        try {
            $payment = Payment::create($payment_data);
            
            // Update invoice status to paid only if total payments are sufficient
            $this->updateInvoiceStatusIfPaid($invoice_id, $invoice, 'manual');
            
            // Send confirmation email to customer
            $this->sendPaymentConfirmationEmail($invoice_id, $payment->getId());
            
            wp_send_json_success([
                'message' => __('Payment verified successfully', 'easy-invoice'),
                'payment_id' => $payment->getId()
            ]);
        } catch (\Exception $e) {
            wp_send_json_error(['message' => $e->getMessage()]);
        }
    }
    
    /**
     * Reject manual payment
     */
    public function rejectManualPayment(): void {
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('You do not have permission to perform this action', 'easy-invoice')]);
            return;
        }
        
        // Verify nonce
        check_ajax_referer('easy_invoice_admin', 'nonce');
        
        $invoice_id = isset($_POST['invoice_id']) ? intval($_POST['invoice_id']) : 0;
        $reason = isset($_POST['reason']) ? sanitize_textarea_field($_POST['reason']) : '';
        
        if (!$invoice_id) {
            wp_send_json_error(['message' => __('Invoice ID is required', 'easy-invoice')]);
            return;
        }
        
        // Get the invoice
        $post = get_post($invoice_id);
        if (!$post || $post->post_type !== \EasyInvoice\Constants\PostTypes::EASY_INVOICE_POST_TYPE) {
            wp_send_json_error(['message' => __('Invalid invoice', 'easy-invoice')]);
            return;
        }
        
        $invoice = new Invoice($post);
        
        // Update invoice status
        update_post_meta($invoice_id, '_payment_status', 'rejected');
        
        // Add rejection reason
        update_post_meta($invoice_id, '_payment_rejection_reason', $reason);
        update_post_meta($invoice_id, '_payment_rejection_date', current_time('mysql'));
        update_post_meta($invoice_id, '_payment_rejection_user', get_current_user_id());
        
        // Send rejection email to customer
        $this->sendPaymentRejectionEmail($invoice_id, $reason);
        
        wp_send_json_success([
            'message' => __('Payment rejected successfully', 'easy-invoice')
        ]);
    }
    

    
    /**
     * Send payment confirmation email to customer
     * 
     * @param int $invoice_id
     * @param int $payment_id
     */
    private function sendPaymentConfirmationEmail($invoice_id, $payment_id): void {
        $invoice = new Invoice(get_post($invoice_id));
        $customer_email = $invoice->getCustomerEmail();
        
        if (!$customer_email) {
            return;
        }
        
        // Get currency settings
        $settings_controller = new \EasyInvoice\Controllers\SettingsController();
        $settings = $settings_controller->getSettings();
        $currency_code = $settings['easy_invoice_currency_code'] ?? 'USD';
        $currency_symbol = \EasyInvoice\Helpers\CurrencyHelper::getCurrencySymbol($currency_code);
        
        $site_name = get_bloginfo('name');
        $invoice_number = $invoice->getNumber();
        $amount = $invoice->getTotal();
        $formatted_amount = $currency_symbol . number_format($amount, 2);
        
        $subject = sprintf(__('[%s] Payment Confirmed - Invoice #%s', 'easy-invoice'), $site_name, $invoice_number);
        
        $message = sprintf(
            __('Dear %s,', 'easy-invoice'),
            $invoice->getCustomerName()
        );
        $message .= "\n\n";
        $message .= sprintf(
            __('We are pleased to confirm that your payment of %s for Invoice #%s has been received and processed successfully.', 'easy-invoice'),
            $formatted_amount,
            $invoice_number
        );
        $message .= "\n\n";
        $message .= __('Thank you for your business.', 'easy-invoice');
        $message .= "\n\n";
        $message .= sprintf(__('Regards,', 'easy-invoice'));
        $message .= "\n";
        $message .= get_option('easy_invoice_company_name', $site_name);
        
        wp_mail($customer_email, $subject, $message);
    }
    
    /**
     * Send payment rejection email to customer
     * 
     * @param int $invoice_id
     * @param string $reason
     */
    private function sendPaymentRejectionEmail($invoice_id, $reason): void {
        $invoice = new Invoice(get_post($invoice_id));
        $customer_email = $invoice->getCustomerEmail();
        
        if (!$customer_email) {
            return;
        }
        
        // Get currency settings
        $settings_controller = new \EasyInvoice\Controllers\SettingsController();
        $settings = $settings_controller->getSettings();
        $currency_code = $settings['easy_invoice_currency_code'] ?? 'USD';
        $currency_symbol = \EasyInvoice\Helpers\CurrencyHelper::getCurrencySymbol($currency_code);
        
        $site_name = get_bloginfo('name');
        $invoice_number = $invoice->getNumber();
        $amount = $invoice->getTotal();
        $formatted_amount = $currency_symbol . number_format($amount, 2);
        
        $subject = sprintf(__('[%s] Payment Issue - Invoice #%s', 'easy-invoice'), $site_name, $invoice_number);
        
        $message = sprintf(
            __('Dear %s,', 'easy-invoice'),
            $invoice->getCustomerName()
        );
        $message .= "\n\n";
        $message .= sprintf(
            __('We regret to inform you that we could not process your payment of %s for Invoice #%s.', 'easy-invoice'),
            $formatted_amount,
            $invoice_number
        );
        $message .= "\n\n";
        
        if ($reason) {
            $message .= __('Reason:', 'easy-invoice') . "\n";
            $message .= $reason;
            $message .= "\n\n";
        }
        
        $message .= __('Please contact us to arrange an alternative payment method.', 'easy-invoice');
        $message .= "\n\n";
        $message .= sprintf(__('Regards,', 'easy-invoice'));
        $message .= "\n";
        $message .= get_option('easy_invoice_company_name', $site_name);
        
        wp_mail($customer_email, $subject, $message);
    }
    
    /**
     * Add pending payment statuses to admin filters
     * 
     * @param array $statuses
     * @return array
     */
    public function addPendingPaymentStatuses($statuses): array {
        $statuses['pending-bank'] = __('Pending Bank Transfer', 'easy-invoice');
        $statuses['pending-cheque'] = __('Pending Cheque', 'easy-invoice');
        return $statuses;
    }
    
    /**
     * Add payment method column to payments list
     * 
     * @param array $columns
     * @return array
     */
    public function addPaymentMethodColumn($columns): array {
        $new_columns = [];
        
        foreach ($columns as $key => $value) {
            $new_columns[$key] = $value;
            
            if ($key === 'title') {
                $new_columns['payment_method'] = __('Payment Method', 'easy-invoice');
            }
        }
        
        return $new_columns;
    }
    
    /**
     * Render payment method column
     * 
     * @param string $column
     * @param int $post_id
     */
    public function renderPaymentMethodColumn($column, $post_id): void {
        if ($column === 'payment_method') {
            $payment_method = get_post_meta($post_id, '_payment_method', true);
            $payment_methods = [
                            'paypal' => __('PayPal', 'easy-invoice')
            ];
            
            echo isset($payment_methods[$payment_method]) ? esc_html($payment_methods[$payment_method]) : esc_html($payment_method);
        }
    }
    
    /**
     * Send payment reminders for pending manual payments
     */
    public function sendPaymentReminders(): void {
        // Get invoices with pending manual payments
        $pending_invoices = get_posts([
            'post_type' => \EasyInvoice\Constants\PostTypes::EASY_INVOICE_POST_TYPE,
            'posts_per_page' => -1,
            'meta_query' => [
                'relation' => 'AND',
                [
                    'key' => '_payment_status',
                    'value' => ['pending-bank', 'pending-cheque'],
                    'compare' => 'IN'
                ],
                [
                    'key' => '_payment_reminder_sent',
                    'compare' => 'NOT EXISTS'
                ]
            ]
        ]);

        if (!empty($pending_invoices)) {
            // Get currency settings
            $settings_controller = new \EasyInvoice\Controllers\SettingsController();
            $settings = $settings_controller->getSettings();
            $currency_code = $settings['easy_invoice_currency_code'] ?? 'USD';
            $currency_symbol = \EasyInvoice\Helpers\CurrencyHelper::getCurrencySymbol($currency_code);
            
            foreach ($pending_invoices as $post) {
                $invoice = new Invoice($post);
                $customer_email = $invoice->getCustomerEmail();
                
                if (!$customer_email) {
                    continue;
                }
                
                $site_name = get_bloginfo('name');
                $invoice_number = $invoice->getNumber();
                $amount = $invoice->getTotal();
                $formatted_amount = $currency_symbol . number_format($amount, 2);
                $payment_method = get_post_meta($invoice->getId(), '_payment_method', true);
                $payment_method_label = $payment_method === 'bank' ? __('Bank Transfer', 'easy-invoice') : __('Cheque', 'easy-invoice');
                
                $subject = sprintf(__('[%s] Payment Reminder - Invoice #%s', 'easy-invoice'), $site_name, $invoice_number);
                
                $message = sprintf(
                    __('Dear %s,', 'easy-invoice'),
                    $invoice->getCustomerName()
                );
                $message .= "\n\n";
                $message .= sprintf(
                    __('This is a friendly reminder that we are still awaiting your %s payment of %s for Invoice #%s.', 'easy-invoice'),
                    $payment_method_label,
                    $formatted_amount,
                    $invoice_number
                );
                $message .= "\n\n";
                $message .= __('If you have already sent the payment, please disregard this reminder. If not, please arrange for payment at your earliest convenience.', 'easy-invoice');
                $message .= "\n\n";
                $message .= sprintf(__('Regards,', 'easy-invoice'));
                $message .= "\n";
                $message .= get_option('easy_invoice_company_name', $site_name);
                
                wp_mail($customer_email, $subject, $message);
                
                // Mark reminder as sent
                update_post_meta($invoice->getId(), '_payment_reminder_sent', current_time('mysql'));
            }
            
            wp_reset_postdata();
        }
    }

    /**
     * Handle submission of payment proof for manual gateways (Bank Transfer, Cheque)
     */
    public function submitPaymentProof(): void {
        $gateway_name = isset($_POST['gateway']) ? sanitize_text_field($_POST['gateway']) : '';
        $invoice_id = isset($_POST['invoice_id']) ? intval($_POST['invoice_id']) : 0;

        if (empty($gateway_name) || empty($invoice_id)) {
            wp_send_json_error(['message' => __('Invalid request. Missing gateway or invoice ID.', 'easy-invoice')]);
            return;
        }

        // Nonce verification (make nonce name consistent or check based on gateway)
        $nonce_action = 'easy_invoice_payment_proof_' . $invoice_id; // Bank transfer nonce
        $nonce_value = isset($_POST['payment_proof_nonce']) ? sanitize_text_field($_POST['payment_proof_nonce']) : '';
        if ($gateway_name === 'cheque') {
            $nonce_action = 'easy_invoice_cheque_notification_' . $invoice_id; // Cheque nonce
            $nonce_value = isset($_POST['cheque_notification_nonce']) ? sanitize_text_field($_POST['cheque_notification_nonce']) : '';
        }

        if (!wp_verify_nonce($nonce_value, $nonce_action)) {
            wp_send_json_error(['message' => __('Nonce verification failed. Please try again.', 'easy-invoice')]);
            return;
        }

        // Optional: Add capability check if this can be submitted by logged-in users only from frontend
        // if (is_user_logged_in() && !current_user_can('read_invoice', $invoice_id)) { // Example capability
        //     wp_send_json_error(['message' => __('You do not have permission to submit proof for this invoice.', 'easy-invoice')]);
        //     return;
        // }

        $gateway = $this->gatewayManager->getGateway($gateway_name);

        if (!$gateway || !method_exists($gateway, 'handleProofSubmission')) {
            wp_send_json_error(['message' => __('Invalid payment gateway or submission handler not found.', 'easy-invoice')]);
            return;
        }

        // Prepare data for the gateway handler
        $post_data = stripslashes_deep($_POST);
        $files_data = $_FILES;

        $result = $gateway->handleProofSubmission($post_data, $files_data);

        if ($result['success']) {
            wp_send_json_success(['message' => $result['message']]);
        } else {
            wp_send_json_error(['message' => $result['message']]);
        }
    }

    /**
     * AJAX handler for admin to mark an invoice as paid.
     */
    public function mark_invoice_paid_ajax(): void {
        $invoice_id = isset($_POST['invoice_id']) ? intval($_POST['invoice_id']) : 0;
        $nonce = isset($_POST['nonce']) ? sanitize_text_field($_POST['nonce']) : '';
        $notes = isset($_POST['notes']) ? sanitize_textarea_field($_POST['notes']) : '';

        if (empty($invoice_id) || !wp_verify_nonce($nonce, 'easy_invoice_approve_payment')) {
            easy_invoice_toast_error(__('Invalid request or security check failed.', 'easy-invoice'));
            return;
        }

        // Use manage_options capability which administrators have
        if (!current_user_can('manage_options')) {
            easy_invoice_toast_error(__('You do not have permission to perform this action.', 'easy-invoice'));
            return;
        }

        $invoice_post = get_post($invoice_id);
        if (!$invoice_post || $invoice_post->post_type !== \EasyInvoice\Constants\PostTypes::EASY_INVOICE_POST_TYPE) {
            wp_send_json_error(['message' => __('Invalid invoice.', 'easy-invoice')]);
            return;
        }

        $invoice = new Invoice($invoice_post);
        // For manual approval, always use 'manual' as payment method
        $payment_method = 'manual';

        // Update invoice post status to 'publish' (or your primary paid status)
        wp_update_post(['ID' => $invoice_id, 'post_status' => 'publish']);
        update_post_meta($invoice_id, '_payment_status', 'completed'); // General completed status for payments
        
        // Allow plugins to control invoice status update
        $should_update_invoice_status = apply_filters('easy_invoice_should_update_invoice_status', true, $invoice_id);
        if ($should_update_invoice_status) {
            update_post_meta($invoice_id, InvoiceFields::STATUS, 'paid'); // Specific invoice status field if used by model
        }

        // Use submitted notes or default note
        $payment_notes = !empty($notes) 
            ? $notes 
            : __('Payment manually verified by admin.', 'easy-invoice');

        // Find existing pending payment records for this invoice
        $existing_payment_args = [
            'post_type' => 'easy_invoice_payment',
            'posts_per_page' => 1,
            'meta_query' => [
                'relation' => 'AND',
                [
                    'key' => '_invoice_id',
                    'value' => $invoice_id,
                ],
                [
                    'key' => '_status',
                    'value' => ['pending-bank', 'pending-cheque', 'pending'], // Check against pending statuses
                    'compare' => 'IN'
                ]
            ]
        ];
        $existing_payments = get_posts($existing_payment_args);
        $payment_id = null;

        if (!empty($existing_payments)) {
            // Update existing pending payment instead of creating new one
            $payment_id = $existing_payments[0]->ID;
            update_post_meta($payment_id, '_status', 'completed'); // Update status to completed
            update_post_meta($payment_id, '_payment_method', 'manual'); // Set payment method to manual
            update_post_meta($payment_id, '_transaction_id', 'MANUAL-' . $invoice_id . '-' . time());
            update_post_meta($payment_id, '_payment_date', current_time('mysql'));
            update_post_meta($payment_id, '_notes', $payment_notes); // Update notes on existing payment
        } else {
            // Only create a new payment if no pending payments exist
            // This prevents creating duplicate payment records
            $existing_payments = get_posts([
                'post_type' => 'easy_invoice_payment',
                'posts_per_page' => -1,
                'meta_query' => [
                    [
                        'key' => '_invoice_id',
                        'value' => $invoice_id,
                    ]
                ]
            ]);
            
            if (!empty($existing_payments)) {
                // If payments exist but none are pending, don't create a new one
                // Just update the invoice status
                easy_invoice_toast_success(__('Invoice marked as paid successfully.', 'easy-invoice'));
                return;
            }
            
            // Get currency from invoice
            $currency_code = get_post_meta($invoice_id, '_easy_invoice_currency_code', true);
            if (empty($currency_code) || $currency_code === 'global') {
                $currency_code = get_option('easy_invoice_currency_code', 'USD');
            }
            $currency_symbol = \EasyInvoice\Helpers\CurrencyHelper::getCurrencySymbol($currency_code);
            
            $payment_data = [
                'invoice_id' => $invoice_id,
                'amount' => $invoice->getTotal(), // Or get amount from proof submission if it varies
                'payment_method' => $payment_method,
                'status' => 'completed',
                'transaction_id' => get_post_meta($invoice_id, '_' . $payment_method . '_transaction_id', true) ?: 'MANUAL-' . $invoice_id,
                'payment_date' => current_time('mysql'),
                'notes' => $payment_notes, // Use provided notes
                'payment_type' => 'manual',
                'currency' => $currency_code,
                'currency_symbol' => $currency_symbol,
                'gateway_response' => json_encode([
                    'admin_verified' => true, 
                    'user' => get_current_user_id(),
                    'verification_date' => current_time('mysql'),
                    'notes' => $payment_notes // Store notes in response JSON as well
                ])
            ];
            try {
                // Create payment record using WordPress post creation
                $payment_post_data = [
                    'post_title' => sprintf('Manual Payment for Invoice #%s', $invoice->getNumber()),
                    'post_type' => 'easy_invoice_payment',
                    'post_status' => 'publish',
                    'post_author' => get_current_user_id(),
                    'meta_input' => [
                        '_invoice_id' => $invoice_id,
                        '_amount' => $invoice->getTotal(),
                        '_payment_method' => $payment_method,
                        '_status' => 'completed',
                        '_transaction_id' => get_post_meta($invoice_id, '_' . $payment_method . '_transaction_id', true) ?: 'MANUAL-' . $invoice_id,
                        '_payment_date' => current_time('mysql'),
                        '_notes' => $payment_notes,
                        '_payment_type' => 'manual',
                        '_currency' => $currency_code,
                        '_currency_symbol' => $currency_symbol,
                        '_gateway_response' => json_encode([
                            'admin_verified' => true, 
                            'user' => get_current_user_id(),
                            'verification_date' => current_time('mysql'),
                            'notes' => $payment_notes
                        ])
                    ]
                ];
                
                $payment_id = wp_insert_post($payment_post_data);
                if (is_wp_error($payment_id)) {
                    easy_invoice_toast_error(__('Error creating payment record:', 'easy-invoice') . ' ' . $payment_id->get_error_message());
                    return;
                }
            } catch (\Exception $e) {
                easy_invoice_toast_error(__('Error creating payment record:', 'easy-invoice') . ' ' . $e->getMessage());
                return;
            }
        }

        // Trigger email confirmation and actions only if we have a payment_id
        if ($payment_id) {
            $this->sendPaymentConfirmationEmail($invoice_id, $payment_id);
            do_action('easy_invoice_manual_payment_confirmed', $invoice_id, $payment_id, $payment_method);
        }

        easy_invoice_toast_success(__('Invoice marked as paid successfully.', 'easy-invoice'));
    }

    /**
     * Handle bulk actions for payments
     */
    public function handleBulkActions() {
        // Check if we're processing a bulk action
        if (!isset($_POST['action']) || $_POST['action'] !== 'easy_invoice_payment_bulk_action') {
            return;
        }
        
        // Check nonce and capability
        if (!wp_verify_nonce($_POST['easy_invoice_payment_bulk_nonce'], 'easy_invoice_payment_bulk_action')) {
            wp_die(__('Security check failed.', 'easy-invoice'));
        }
        
        if (!current_user_can('manage_options')) {
            wp_die(__('You do not have permission to perform this action.', 'easy-invoice'));
        }
        
        // Check if we have payment IDs
        if (!isset($_POST['payment_ids']) || !is_array($_POST['payment_ids']) || empty($_POST['payment_ids'])) {
            wp_redirect(admin_url('admin.php?page=easy-invoice-payments&bulk_error=no_selection'));
            exit;
        }
        
        // Get bulk action and payment IDs
        $bulk_action = isset($_POST['bulk_action']) ? sanitize_text_field($_POST['bulk_action']) : '';
        $payment_ids = array_map('intval', $_POST['payment_ids']);
        
        // Process based on action
        $processed = 0;
        $invoice_updates = array(); // Track invoice updates needed
        
        switch ($bulk_action) {
            case 'trash':
                foreach ($payment_ids as $id) {
                    // Get payment info before trashing for invoice status update
                    $payment = new Payment($id);
                    $payment_status = $payment->getStatus();
                    $invoice_id = $payment->getInvoiceId();
                    $payment_amount = $payment->getAmount();
                    
                    if (wp_trash_post($id)) {
                        $processed++;
                        
                        // Track invoice updates needed for completed payments
                        if ($payment_status === 'completed' && $invoice_id) {
                            if (!isset($invoice_updates[$invoice_id])) {
                                $invoice_updates[$invoice_id] = 0;
                            }
                            $invoice_updates[$invoice_id] += $payment_amount;
                        }
                    }
                }
                
                // Update invoice statuses for completed payments that were trashed
                foreach ($invoice_updates as $invoice_id => $deleted_amount) {
                    $this->updateInvoiceStatusAfterPaymentDeletion($invoice_id, $deleted_amount);
                }
                
                wp_redirect(admin_url('admin.php?page=easy-invoice-payments&bulk_trashed=' . $processed));
                break;
                
            case 'restore':
                foreach ($payment_ids as $id) {
                    // Get payment info before restoring for invoice status update
                    $payment = new Payment($id);
                    $payment_status = $payment->getStatus();
                    $invoice_id = $payment->getInvoiceId();
                    $payment_amount = $payment->getAmount();
                    
                    if (wp_untrash_post($id)) {
                        // Also set status to publish (since WordPress sets it to draft by default)
                        wp_update_post(array(
                            'ID' => $id,
                            'post_status' => 'publish'
                        ));
                        $processed++;
                        
                        // Track invoice updates needed for completed payments
                        if ($payment_status === 'completed' && $invoice_id) {
                            if (!isset($invoice_updates[$invoice_id])) {
                                $invoice_updates[$invoice_id] = 0;
                            }
                            $invoice_updates[$invoice_id] += $payment_amount;
                        }
                    }
                }
                
                // Update invoice statuses for completed payments that were restored
                foreach ($invoice_updates as $invoice_id => $restored_amount) {
                    $this->updateInvoiceStatusAfterPaymentRestoration($invoice_id, $restored_amount);
                }
                
                wp_redirect(admin_url('admin.php?page=easy-invoice-payments&bulk_restored=' . $processed));
                break;
                
            case 'delete':
                foreach ($payment_ids as $id) {
                    // Get payment info before deletion for invoice status update
                    $payment = new Payment($id);
                    $payment_status = $payment->getStatus();
                    $invoice_id = $payment->getInvoiceId();
                    $payment_amount = $payment->getAmount();
                    
                    if (wp_delete_post($id, true)) {
                        $processed++;
                        
                        // Track invoice updates needed for completed payments
                        if ($payment_status === 'completed' && $invoice_id) {
                            if (!isset($invoice_updates[$invoice_id])) {
                                $invoice_updates[$invoice_id] = 0;
                            }
                            $invoice_updates[$invoice_id] += $payment_amount;
                        }
                    }
                }
                
                // Update invoice statuses for completed payments that were deleted
                foreach ($invoice_updates as $invoice_id => $deleted_amount) {
                    $this->updateInvoiceStatusAfterPaymentDeletion($invoice_id, $deleted_amount);
                }
                
                wp_redirect(admin_url('admin.php?page=easy-invoice-payments&bulk_deleted=' . $processed));
                break;
                
            default:
                wp_redirect(admin_url('admin.php?page=easy-invoice-payments&bulk_error=invalid_action'));
        }
        
        exit;
    }
    
    /**
     * Update invoice status after payment deletion
     */
    private function updateInvoiceStatusAfterPaymentDeletion($invoice_id, $deleted_amount) {
        $invoice = new Invoice($invoice_id);
        
        if (!$invoice->getId()) {
            return;
        }
        
        // Get all remaining payments for this invoice
        $remaining_payments = get_posts(array(
            'post_type' => 'easy_invoice_payment',
            'post_status' => 'publish',
            'meta_query' => array(
                array(
                    'key' => '_invoice_id',
                    'value' => $invoice_id,
                    'compare' => '='
                ),
                array(
                    'key' => '_status',
                    'value' => 'completed',
                    'compare' => '='
                )
            ),
            'posts_per_page' => -1
        ));
        
        // Calculate total remaining payments
        $total_remaining = 0;
        foreach ($remaining_payments as $payment_post) {
            $payment = new Payment($payment_post);
            $total_remaining += floatval($payment->getAmount());
        }
        
        $invoice_total = floatval($invoice->getTotal());
        
        // Update invoice status based on remaining payments
        if ($total_remaining >= $invoice_total) {
            // Still fully paid
            update_post_meta($invoice_id, '_status', 'paid');
        } elseif ($total_remaining > 0) {
            // Partially paid
            update_post_meta($invoice_id, '_status', 'partial');
        } else {
            // No payments remaining
            update_post_meta($invoice_id, '_status', 'unpaid');
        }
    }

    /**
     * Update invoice status after payment restoration
     */
    private function updateInvoiceStatusAfterPaymentRestoration($invoice_id, $restored_amount) {
        $invoice = new Invoice($invoice_id);
        
        if (!$invoice->getId()) {
            return;
        }
        
        // Get all payments for this invoice (including the restored one)
        $all_payments = get_posts(array(
            'post_type' => 'easy_invoice_payment',
            'post_status' => 'publish',
            'meta_query' => array(
                array(
                    'key' => '_invoice_id',
                    'value' => $invoice_id,
                    'compare' => '='
                ),
                array(
                    'key' => '_status',
                    'value' => 'completed',
                    'compare' => '='
                )
            ),
            'posts_per_page' => -1
        ));
        
        // Calculate total payments (including restored ones)
        $total_payments = 0;
        foreach ($all_payments as $payment_post) {
            $payment = new Payment($payment_post);
            $total_payments += floatval($payment->getAmount());
        }
        
        $invoice_total = floatval($invoice->getTotal());
        
        // Update invoice status based on total payments
        if ($total_payments >= $invoice_total) {
            // Fully paid
            update_post_meta($invoice_id, '_status', 'paid');
        } elseif ($total_payments > 0) {
            // Partially paid
            update_post_meta($invoice_id, '_status', 'partial');
        } else {
            // No payments
            update_post_meta($invoice_id, '_status', 'unpaid');
        }
    }

    // Stripe payment recording moved to Pro plugin


} 
```
