# easy-invoice/2.4.3/easy-invoice.php

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.4.3. 336 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.4.3/code/easy-invoice.php
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.4.3/raw/easy-invoice.php
- Modified: 2026-09-29T06:14:16+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.4.3/code/easy-invoice.php#L10-L20`.

```php
<?php
/**
 * Plugin Name: Easy Invoice
 * Plugin URI: https://matrixaddons.com/plugins/easy-invoice
 * Description: A beautiful, full-featured invoicing solution for WordPress. Create professional invoices, quotes, and manage payments with ease.
 * Version: 2.4.3
 * Author: MatrixAddons
 * Author URI: https://matrixaddons.com
 * License: GPL v2 or later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: easy-invoice
 * Domain Path: /languages
 * Requires at least: 6.0
 * Tested up to: 7.1
 * Requires PHP: 7.4
 *
 * @package EasyInvoice
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}


// Define plugin constants.
define( 'EASY_INVOICE_VERSION', '2.4.3' );
define( 'EASY_INVOICE_PLUGIN_FILE', __FILE__ );
define( 'EASY_INVOICE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
define( 'EASY_INVOICE_PLUGIN_URL', plugin_dir_url( __FILE__ ) );

// Load composer autoloader if it exists.
if ( file_exists( EASY_INVOICE_PLUGIN_DIR . 'vendor/autoload.php' ) ) {
    require_once EASY_INVOICE_PLUGIN_DIR . 'vendor/autoload.php';
}

// Include standalone function files (not autoloaded by Composer)
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/NotificationHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Admin/Helpers/Utility.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/GeneralHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/ToastHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/CapabilityHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/LogoHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/AuditHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/AutoloadOptimizer.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/BrandHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/TaxHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/FormattingHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/EmailHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/MoneyHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/EnqueueHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/PdfHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/QuoteInvoiceHelper.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Helpers/TemplateLocator.php';

// Addons system — Free plugin owns the listing UI + license gate;
// Pro plugin owns the executable addon code.
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Addons/LicensePlanResolver.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Addons/AddonRegistry.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Addons/AddonManager.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Addons/ProAddonMigration.php';
require_once EASY_INVOICE_PLUGIN_DIR . 'includes/Controllers/AddonsController.php';

// Register the Pro→addon migration. Only fires inside admin (admin_init
// hook), only when Pro is active, only once per MIGRATION_VERSION bump.
\EasyInvoice\Addons\ProAddonMigration::register();

/**
 * Initialize the plugin.
 *
 * @since 1.0.0
 * @return void
 */
function easy_invoice_init() {
    // Initialize the main plugin class.
    $plugin = \EasyInvoice\EasyInvoice::getInstance();
    $plugin->init();

    // Initialize service providers.
    $invoice_service_provider = new \EasyInvoice\Providers\InvoiceServiceProvider();
    $invoice_service_provider->register();

    $invoice_item_service_provider = new \EasyInvoice\Providers\InvoiceItemServiceProvider();
    $invoice_item_service_provider->register();

    $client_service_provider = new \EasyInvoice\Providers\ClientServiceProvider();
    $client_service_provider->register();

    $invoice_number_service_provider = new \EasyInvoice\Providers\InvoiceNumberServiceProvider();
    $invoice_number_service_provider->register();

    $quote_number_service_provider = new \EasyInvoice\Providers\QuoteNumberServiceProvider();
    $quote_number_service_provider->register();

    $quote_service_provider = new \EasyInvoice\Providers\QuoteServiceProvider();
    $quote_service_provider->register();

    // Initialize EmailManager
    $email_manager = \EasyInvoice\Services\EmailManager::getInstance();

    // Initialize controllers.
    $payment_controller = new \EasyInvoice\Controllers\PaymentController();
    $payment_controller->init();

    $settings_controller = new \EasyInvoice\Controllers\SettingsController();
    $settings_controller->init();

    $quote_controller = new \EasyInvoice\Controllers\QuoteController();
    $quote_controller->init();

    // Initialize shortcode manager
    $shortcode_manager = new \EasyInvoice\Shortcodes\ShortcodeManager();

    // Initialize review notice service
    \EasyInvoice\Services\ReviewNoticeService::init();

    // One-time notice explaining that document links now require an access key.
    // Registered here rather than on activation because WordPress updates a plugin
    // by replacing files, without firing the activation hook — so an upgrading site
    // would otherwise get no warning that previously-emailed links stop working.
    \EasyInvoice\Services\DocumentAccessNoticeService::init();
    \EasyInvoice\Services\ProCompatibilityNotice::init();
    \EasyInvoice\Services\TaxTreatment::init();
    \EasyInvoice\Rest\RestController::init();
    \EasyInvoice\Services\PdfDownload::init();
    \EasyInvoice\Services\DocumentPage::init();
    \EasyInvoice\Services\DocumentViews::init();
    \EasyInvoice\Services\DocumentAttachments::init();
    \EasyInvoice\Services\InvoiceRetention::init();
    \EasyInvoice\Controllers\CreditNoteController::init();
    \EasyInvoice\Controllers\VatCheckController::init();
    \EasyInvoice\Controllers\StatementController::init();
    \EasyInvoice\Controllers\ImportController::init();

    // Initialize promotion service
    \EasyInvoice\Services\PromotionService::init();

    // Initialize Addons controller (admin page + AJAX toggle)
    (new \EasyInvoice\Controllers\AddonsController())->init();

	do_action('easy_invoice_loaded');

}
add_action( 'init', 'easy_invoice_init' );

// Any plugin option change invalidates the per-request settings cache the
// money formatter reads (see SettingsController::getSettings()).
foreach ( [ 'updated_option', 'added_option', 'deleted_option' ] as $ei_opt_hook ) {
	add_action( $ei_opt_hook, static function ( $option ) {
		if ( is_string( $option ) && 0 === strpos( $option, 'easy_invoice_' ) ) {
			\EasyInvoice\Controllers\SettingsController::flushSharedCache();
			// The builder field set depends on settings (tax fields are only registered
			// while tax is on), so the models' cached definitions go with it.
			\EasyInvoice\Models\Invoice::flushFieldDefinitionsCache();
			\EasyInvoice\Models\Quote::flushFieldDefinitionsCache();
		}
	} );
}
unset( $ei_opt_hook );

// Payment and credit-note writes invalidate the balance totals primed for
// dashboard and report screens (see InvoiceBalance::prime()).
add_action( 'save_post', static function ( $post_id, $post ) {
	if ( $post instanceof WP_Post && in_array( $post->post_type, [ 'easy_invoice_payment', 'easy_invoice_credit', 'easy_invoice', 'easy_invoice_quote' ], true ) ) {
		\EasyInvoice\Services\InvoiceBalance::forget();
		\EasyInvoice\Services\InvoiceTotalsCache::bumpVersion();
	}
}, 10, 2 );
add_action( 'updated_post_meta', static function ( $meta_id, $post_id, $meta_key ) {
	if ( in_array( $meta_key, [ '_amount', '_status', '_easy_invoice_total', '_invoice_id', '_easy_invoice_status', '_easy_invoice_due_date', '_easy_invoice_issue_date', '_easy_invoice_client_id', '_easy_invoice_currency_code', '_easy_invoice_quote_status', '_easy_invoice_quote_currency_code' ], true ) ) {
		\EasyInvoice\Services\InvoiceBalance::forget();
		\EasyInvoice\Services\InvoiceTotalsCache::bumpVersion();
	}
}, 10, 3 );
add_action( 'added_post_meta', static function ( $meta_id, $post_id, $meta_key ) {
	if ( in_array( $meta_key, [ '_amount', '_status', '_easy_invoice_total', '_invoice_id', '_easy_invoice_status', '_easy_invoice_due_date', '_easy_invoice_issue_date', '_easy_invoice_client_id', '_easy_invoice_currency_code', '_easy_invoice_quote_status', '_easy_invoice_quote_currency_code' ], true ) ) {
		\EasyInvoice\Services\InvoiceBalance::forget();
		\EasyInvoice\Services\InvoiceTotalsCache::bumpVersion();
	}
}, 10, 3 );

// Persisted invoice totals (InvoiceTotalsCache): written on save, backfilled by cron,
// dropped when a total's inputs change.
add_action( 'easy_invoice_totals_backfill', [ \EasyInvoice\Services\InvoiceTotalsCache::class, 'cronBackfill' ] );
add_action( \EasyInvoice\Services\QuoteTotals::CRON_HOOK, [ \EasyInvoice\Services\QuoteTotals::class, 'cronBackfill' ] );
foreach ( [ 'updated_post_meta', 'added_post_meta', 'deleted_post_meta' ] as $ei_meta_hook ) {
	add_action( $ei_meta_hook, static function ( $meta_id, $post_id, $meta_key ) {
		if ( in_array( $meta_key, \EasyInvoice\Services\InvoiceTotalsCache::DEPENDENT_META, true ) && 'easy_invoice' === get_post_type( $post_id ) ) {
			\EasyInvoice\Services\InvoiceTotalsCache::invalidate( (int) $post_id );
		}
		if ( in_array( $meta_key, \EasyInvoice\Services\QuoteTotals::DEPENDENT_META, true ) && 'easy_invoice_quote' === get_post_type( $post_id ) ) {
			\EasyInvoice\Services\QuoteTotals::invalidate( (int) $post_id );
		}
	}, 10, 3 );
}
unset( $ei_meta_hook );
foreach ( [ 'trashed_post', 'untrashed_post', 'deleted_post', 'before_delete_post' ] as $ei_post_hook ) {
	add_action( $ei_post_hook, static function ( $post_id ) {
		if ( in_array( get_post_type( $post_id ), [ 'easy_invoice', 'easy_invoice_payment', 'easy_invoice_credit', 'easy_invoice_quote' ], true ) ) {
			\EasyInvoice\Services\InvoiceBalance::forget();
			\EasyInvoice\Services\InvoiceTotalsCache::bumpVersion();
		}
	} );
}
unset( $ei_post_hook );
foreach ( [ 'updated_option', 'added_option', 'deleted_option' ] as $ei_opt_hook ) {
	add_action( $ei_opt_hook, static function ( $option ) {
		if ( in_array( $option, \EasyInvoice\Services\InvoiceTotalsCache::DEPENDENT_OPTIONS, true ) ) {
			\EasyInvoice\Services\InvoiceTotalsCache::invalidateAll();
		}
	} );
}
unset( $ei_opt_hook );
add_action( 'easy_invoice_loaded', static function () {
	$want = \EasyInvoice\Services\InvoiceTotalsCache::CACHE_VERSION;
	if ( $want !== get_option( 'easy_invoice_totals_cache_version' ) ) {
		update_option( 'easy_invoice_totals_cache_version', $want, false );
		\EasyInvoice\Services\InvoiceTotalsCache::invalidateAll(); // drops stale totals and queues the backfill
	}
	if ( '1' !== get_option( 'easy_invoice_quote_totals_version' ) ) {
		update_option( 'easy_invoice_quote_totals_version', '1', false );
		\EasyInvoice\Services\QuoteTotals::scheduleBackfill();
	}
	// Versioned memo keys from the first 2.4.0 builds left one transient per write behind.
	if ( '1' !== get_option( 'easy_invoice_stats_memo_keys' ) ) {
		global $wpdb;
		$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_ei_stats_%' OR option_name LIKE '_transient_timeout_ei_stats_%'" );
		update_option( 'easy_invoice_stats_memo_keys', '1', false );
	}
} );

/**
 * Handle payment gateway logging
 */
function easy_invoice_payment_gateway_log_handler($message, $level, $gateway_name) {
    $log_message = sprintf('[%s] Easy Invoice %s Gateway: %s',
        gmdate('Y-m-d H:i:s'),
        ucfirst($gateway_name),
        $message
    );

    if (defined('WP_DEBUG') && WP_DEBUG) {
        error_log($log_message);
    }
}
add_action('easy_invoice_payment_gateway_log', 'easy_invoice_payment_gateway_log_handler', 10, 3);

// Initialize migration system
if ( class_exists( 'EasyInvoice\Migration\MigrationInit' ) ) {

    EasyInvoice\Migration\MigrationInit::init();
}
/**
 * Register post types early to ensure they're available
 */
function easy_invoice_register_post_types() {
    // Initialize the main plugin class and register post types
    $plugin = \EasyInvoice\EasyInvoice::getInstance();
    $plugin->registerPostTypes();
    $plugin->registerCustomStatuses();
}
add_action( 'init', 'easy_invoice_register_post_types', 0 ); // Priority 0 to run early

/**
 * Load textdomain early to avoid translation loading warnings.
 *
 * @since 1.0.0
 * @return void
 */
function easy_invoice_load_textdomain() {
    load_plugin_textdomain('easy-invoice', false, dirname(plugin_basename(__FILE__)) . '/languages');
}
add_action( 'init', 'easy_invoice_load_textdomain', 1 );





/**
 * Plugin activation hook.
 *
 * @since 1.0.0
 * @return void
 */
register_activation_hook( __FILE__, 'easy_invoice_activate' );
function easy_invoice_activate() {
    // Initialization of post types so rewrite rules can be flushed properly.
    \EasyInvoice\EasyInvoice::getInstance()->registerPostTypes();

    // Initialize default settings
    $settings_controller = new \EasyInvoice\Controllers\SettingsController();
    $settings_controller->initializeSettings();

    // Record first install timestamp (do not overwrite if it already exists)
    if (!get_option('easy_invoice_installed')) {
        update_option('easy_invoice_installed', current_time('mysql'));
    }

    // Check if this is a fresh installation or an upgrade
    $stored_version = get_option('easy_invoice_version', '');

    // If no version is stored, this is a fresh installation
    if (empty($stored_version)) {
        update_option('easy_invoice_version', EASY_INVOICE_VERSION);
        update_option('easy_invoice_migration_completed', true);
    } else {
        // This is an upgrade. Do NOT bump version here; migration will set it when completed.
    }

    // Flush rewrite rules.
    flush_rewrite_rules();
}

/**
 * Plugin deactivation hook.
 *
 * @since 1.0.0
 * @return void
 */
register_deactivation_hook( __FILE__, 'easy_invoice_deactivate' );
function easy_invoice_deactivate() {
    // Flush rewrite rules.
    flush_rewrite_rules();

    // Clear scheduled events
    \EasyInvoice\Services\QuoteExpirationService::clear_schedule();
    foreach ( [ 'easy_invoice_payment_reminder', 'easy_invoice_send_payment_reminders', 'easy_invoice_quote_expiration_check' ] as $hook ) {
        wp_clear_scheduled_hook( $hook );
    }
}

/**
 * Toast notification helper functions
 */


```
