WooCommerce plugin hooks

Reference for version 1.2.3.

WooCommerce plugin

What a hook can change

For a country VIESAC already checks live (EU-27, Northern Ireland, Great Britain, Norway, Switzerland, Liechtenstein, Serbia, Turkey, Bosnia and Herzegovina, North Macedonia, Albania, Iceland, and Australia, including territories that fold into those countries), your code can run an extra step. VIESAC then runs its own registry check and, when the order qualifies, still creates the audit. A hook cannot mark that number valid, cannot skip the registry, and cannot skip the audit.

For a country VIESAC does not check live, a number that contains only letters and digits can remove tax as an export. VIESAC does not call a registry and does not create an audit. Your own check can refuse that exemption. An empty field, or a number that contains any other character, keeps the shop's tax and does not block checkout.

A hook can keep tax. It cannot grant 0% when the decision engine refused it. If any cart item refuses the exemption, the whole cart stays taxed.

Actions

Hook When it runs
viesac_before_vat_validation Before the live check. The return value is ignored. Arguments: $context with country, postcode, vat, billing country, shipping country, and company.
viesac_vat_validated After VIESAC accepts the number. Arguments: full VAT or unverified token, country, result, user ID, source api, provisional (registry confirmation pending), or unverified. Acceptance is not always a confirmed valid registry result.
viesac_vat_validation_failed After a live check rejects or cannot accept the number, including an unconfirmed result kept taxed by the merchant setting. Arguments: full VAT, country, error code, user ID.
viesac_order_tax_decision_recorded After the order stores _viesac_tax_treatment. This does not create or change the audit. Arguments: order, decision, VAT.

Filters

Hook and callback argumentsPurpose and limits
viesac_vat_field_args
$args, $context, $country, $section
Classic VAT field label, placeholder, type and other arguments. Context remains checkout; section is billing or shipping. Country is the corresponding address country.
viesac_company_field_args
$args, $section, $country
Classic company field arguments for billing/shipping, also on saved address forms. Country is the corresponding address country.
viesac_is_vat_required
$required, $country, $context
Classic and saved-address VAT requirement. Context contains section, field_key, location (checkout/my_account), billing_country, shipping_country and shop_country. A required VAT field stays visible when company is empty.
viesac_checkout_field_text
$text, $key, $default, $custom
Global field text after translation, including merchant custom text. This filter has no address country; use field arguments for country-specific labels.
viesac_validation_success_message
$message, $code, $country
AJAX success/status text. Codes: valid, validated_no_tax_change, audit_required, audit_required_no_exemption, unverified_export, unverified_no_exemption. Country is the VAT validation country.
viesac_validation_error_message
$message, $code, $country
Server-side format and country-mismatch messages. Country is the VAT validation country. Does not cover every browser or registry failure.
viesac_unverified_export_eligible
$eligible, $country, $number, $context
Countries without live validation only. Return false to keep tax. Context includes address countries and postcodes. No VIESAC audit is created either way.
viesac_tax_treatment_decision
$decision, $input
Restrict exemption using the decision engine input: shop/VAT/address countries, postcodes, shipping choice, needs_shipping and vat_valid. Cannot grant exemption when the engine refused it.
viesac_cart_item_allows_exemption
$allowed, $product, $decision, $context
Classic and Blocks. Return false for one product to keep tax on the whole cart. Context includes shop_country, vat_country, billing_country, shipping_country and their postcodes, needs_shipping, ship_to_different_address, vat_valid, plus effective supply_country and supply_postcode. Runs only after the engine permits exemption.
viesac_exemption_note
$note, $order, $decision, $vat
Note stored in _viesac_vat_exemption_note when the order is exempt. Address countries are available on the order; invoice plugins can read the stored note.
viesac_vat_cache_ttl
$seconds, $full_vat
Session cache lifetime for valid, invalid and audit_required live results. Default 600 seconds; clamped to 60–86400. Failed requests are not cached.
viesac_territory_aliases
$aliases
Add country pairs. Built-in mappings are preserved: EL → GR, LI → CH, MC → FR, IM → GB.
viesac_supply_zone
$zone, $country, $postcode
Add an outside: zone only when VIESAC has not already assigned one. Built-in excluded zones are preserved.

Register the matching accepted argument count in add_filter. Existing argument positions are retained: the VAT field filter keeps checkout as its second argument; the company filter keeps billing/shipping there. In 1.2.3, viesac_is_vat_required receives the address country instead of an empty string and a context array instead of null.

Country-dependent field arguments and requirements apply to Classic Checkout and saved address forms. Classic fields update on WooCommerce's normal country-change refresh and preserve entered values. Saved address forms use their country when the form is built. Checkout Blocks use separate field registration and do not use these field-definition filters; shared cart exemption restrictions do apply to Blocks.

Examples

Change the existing VAT field by country. This applies to both billing and shipping; no extra VAT field is created. The optional fourth argument lets you restrict the change to one section.

add_filter('viesac_vat_field_args', function ($args, $context, $country, $section) {
    if ($country === 'BA') {
        $args['label'] = 'PDV number';
        $args['placeholder'] = 'BA201133590005';
    } elseif ($country === 'IS') {
        $args['label'] = 'VSK or kennitala';
        $args['placeholder'] = 'IS23148';
    }
    if ($country === 'US') {
        $args['type'] = 'hidden';
        $args['required'] = false;
    }
    // To change billing only: return $args early when $section !== 'billing'.
    return $args;
}, 10, 4);

Customize the company field for billing only. Omitting a section condition applies your rule to each section when its filter runs. An empty section is not an instruction to update both fields.

add_filter('viesac_company_field_args', function ($args, $section, $country) {
    if ($section === 'billing' && $country === 'AL') {
        $args['label'] = 'Registered business name';
    }
    return $args;
}, 10, 3);

Require VAT for billing addresses in a chosen country.

add_filter('viesac_is_vat_required', function ($required, $country, $context) {
    if ($country === 'EE' && $context['section'] === 'billing') {
        return true;
    }
    return $required;
}, 10, 3);

Keep tax for one product when the effective destination matches your rule. Physical orders use the active shipping destination; purely virtual orders use billing.

add_filter('viesac_cart_item_allows_exemption', function ($allowed, $product, $decision, $context) {
    if ($product instanceof WC_Product
        && $product->get_sku() === 'LOCAL-ONLY'
        && $context['supply_country'] === 'NO') {
        return false;
    }
    return $allowed;
}, 10, 4);

Keep tax for one product. The rest of the cart stays taxed as well.

add_filter('viesac_cart_item_allows_exemption', function ($allowed, $product) {
    if ($product instanceof WC_Product && $product->get_sku() === 'LOCAL-ONLY') {
        return false;
    }
    return $allowed;
}, 10, 2);

Refuse an export for a country VIESAC does not check. This does not create an audit.

add_filter('viesac_unverified_export_eligible', function ($eligible, $country, $number) {
    if ($country === 'EG' && strlen($number) < 9) {
        return false;
    }
    return $eligible;
}, 10, 3);

Print the stored note on a PDF invoice. The plugin does not call the PDF plugin itself.

add_action('wpo_wcpdf_after_order_data', function ($document_type, $order) {
    if (!$order instanceof WC_Order) {
        return;
    }
    $note = (string) $order->get_meta('_viesac_vat_exemption_note');
    if ($note !== '') {
        echo '<p>' . esc_html($note) . '</p>';
    }
}, 10, 2);

Checkout field labels can also use the existing viesac_checkout_field_text filter.