# easy-invoice/2.4.0/includes/Controllers/VatCheckController.php

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.4.0. 173 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.4.0/code/includes/Controllers/VatCheckController.php
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.4.0/raw/includes/Controllers/VatCheckController.php
- Modified: 2026-09-15T12:31:20+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.0/code/includes/Controllers/VatCheckController.php#L10-L20`.

```php
<?php
/**
 * The "check this VAT number" affordance on the invoice form.
 *
 * @package Easy_Invoice
 * @subpackage Controllers
 */

namespace EasyInvoice\Controllers;

use EasyInvoice\Services\ViesValidator;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Puts a VIES check next to the customer VAT field.
 *
 * On demand rather than on save: VIES is a network call to somebody else's
 * service, and it is slow and occasionally down. Wiring it into saving would
 * mean an invoice that will not save because a foreign government's register is
 * having an afternoon. The merchant asks when they want to know.
 */
class VatCheckController {

    /** AJAX action. */
    const ACTION = 'easy_invoice_check_vat';

    /**
     * Wire it up.
     *
     * @return void
     */
    public static function init(): void {
        add_action( 'wp_ajax_' . self::ACTION, [ __CLASS__, 'handle' ] );
        add_action( 'admin_footer', [ __CLASS__, 'printScript' ] );
    }

    /**
     * Answer a check request.
     *
     * @return void
     */
    public static function handle(): void {
        $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
        if ( ! wp_verify_nonce( $nonce, self::ACTION ) ) {
            wp_send_json_error( [ 'message' => __( 'Security check failed.', 'easy-invoice' ) ], 403 );
        }

        // The check reveals whether a given business is VAT-registered, and it
        // consumes a shared public service on the site's behalf. Both are
        // reasons not to leave it open to any logged-in user.
        if ( ! easy_invoice_user_can( 'ei_view_invoices' ) ) {
            wp_send_json_error( [ 'message' => __( 'You do not have permission to do that.', 'easy-invoice' ) ], 403 );
        }

        $vat     = isset( $_POST['vat'] ) ? sanitize_text_field( wp_unslash( $_POST['vat'] ) ) : '';
        $country = isset( $_POST['country'] ) ? sanitize_text_field( wp_unslash( $_POST['country'] ) ) : '';

        $result = ViesValidator::check( $vat, $country );

        if ( is_wp_error( $result ) ) {
            // Deliberately a success response carrying an "unknown" state. This
            // is not an error in the request — it is the register declining to
            // answer, and the difference matters at the other end.
            wp_send_json_success( [
                'state'   => 'unknown',
                'message' => $result->get_error_message(),
            ] );
        }

        if ( empty( $result['valid'] ) ) {
            wp_send_json_success( [
                'state'   => 'invalid',
                'message' => __( 'The EU VAT register does not recognise this number. Check it with your customer before treating this as a reverse-charge supply.', 'easy-invoice' ),
            ] );
        }

        $message = __( 'Registered in the EU VAT register.', 'easy-invoice' );
        if ( '' !== $result['name'] ) {
            $message = sprintf(
                /* translators: %s: registered trader name. */
                __( 'Registered: %s', 'easy-invoice' ),
                $result['name']
            );
        }

        wp_send_json_success( [
            'state'   => 'valid',
            'message' => $message,
        ] );
    }

    /**
     * Add the control beside the VAT field.
     *
     * Injected from the footer rather than added to the field registration
     * because that layer describes data, not behaviour, and every field type it
     * knows about renders the same way. This attaches to whatever the form
     * produced.
     *
     * @return void
     */
    public static function printScript(): void {
        $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
        if ( ! $screen || false === strpos( (string) $screen->id, 'easy-invoice' ) ) {
            return;
        }

        if ( ! easy_invoice_user_can( 'ei_view_invoices' ) ) {
            return;
        }

        $nonce = wp_create_nonce( self::ACTION );
        ?>
        <script>
        (function () {
            var field = document.querySelector('[name="customer_vat_number"]');
            if (!field || field.dataset.eiVatCheck) { return; }
            field.dataset.eiVatCheck = '1';

            var wrap = document.createElement('div');
            wrap.style.cssText = 'margin-top:6px;font-size:12px;display:flex;align-items:center;gap:8px;flex-wrap:wrap';

            var button = document.createElement('button');
            button.type = 'button';
            button.textContent = <?php echo wp_json_encode( __( 'Check with VIES', 'easy-invoice' ) ); ?>;
            button.style.cssText = 'cursor:pointer;border:1px solid #d1d5db;background:#fff;border-radius:4px;padding:3px 9px;font-size:12px';

            var out = document.createElement('span');
            wrap.appendChild(button);
            wrap.appendChild(out);
            field.parentNode.appendChild(wrap);

            var COLOURS = { valid: '#047857', invalid: '#b91c1c', unknown: '#92400e' };

            button.addEventListener('click', function () {
                var vat = (field.value || '').trim();
                if (!vat) { return; }

                var countryField = document.querySelector('[name="customer_country"]');
                button.disabled = true;
                out.style.color = '#6b7280';
                out.textContent = <?php echo wp_json_encode( __( 'Checking…', 'easy-invoice' ) ); ?>;

                var body = new FormData();
                body.append('action', <?php echo wp_json_encode( self::ACTION ); ?>);
                body.append('nonce', <?php echo wp_json_encode( $nonce ); ?>);
                body.append('vat', vat);
                body.append('country', countryField ? (countryField.value || '') : '');

                fetch(<?php echo wp_json_encode( admin_url( 'admin-ajax.php' ) ); ?>, {
                    method: 'POST', body: body, credentials: 'same-origin'
                })
                    .then(function (r) { return r.json(); })
                    .then(function (r) {
                        var d = (r && r.data) || {};
                        out.style.color = COLOURS[d.state] || '#6b7280';
                        out.textContent = d.message || <?php echo wp_json_encode( __( 'The check could not be completed.', 'easy-invoice' ) ); ?>;
                    })
                    .catch(function () {
                        out.style.color = COLOURS.unknown;
                        out.textContent = <?php echo wp_json_encode( __( 'The check could not be completed.', 'easy-invoice' ) ); ?>;
                    })
                    .finally(function () { button.disabled = false; });
            });
        })();
        </script>
        <?php
    }
}

```
