Integration guide 8 min read

GST Number Verification for WooCommerce and WordPress

Add live GSTIN validation at checkout on WooCommerce and WordPress with a lightweight API integration.

Written and reviewed by the GSTIN API team Last reviewed

GST number verification on WooCommerce means adding a GSTIN field to checkout, validating it server-side against a live API before the order is created, and storing the confirmed legal name on the order for B2B invoicing. This is a small, self-contained plugin — a few WordPress hooks and one outbound API call — not a heavyweight extension.

Why validate a GSTIN at WooCommerce checkout

A B2B customer checking out on WooCommerce will often type their GST number into a free-text field so they can claim input tax credit against the purchase. If that field is never validated, you end up with typos, made-up numbers, and GSTINs that were valid once but are now cancelled — all of which produce a GST invoice the customer's accountant will reject. Verifying the GSTIN against live GSTN data at the point of checkout catches this before the order ships, not after a support ticket arrives.

Where the API key actually belongs

Define the key as a constant in wp-config.php, outside the web root's document tree if your hosting allows it. This keeps it out of the database entirely, which matters because WordPress post meta and the wp_options table are both readable by any plugin with database access, and are a common target if the site is ever compromised.

// wp-config.php
define('GSTINAPI_KEY', 'YOUR_API_KEY');

Adding a GST number field to checkout

Hook into woocommerce_checkout_fields to add an optional field in the billing section. Keep it optional at this layer — the server-side hook below is what actually enforces correctness for customers who do provide one:

add_filter('woocommerce_checkout_fields', function ($fields) {
    $fields['billing']['billing_gstin'] = [
        'label'       => __('GSTIN (for B2B invoice)', 'gstinapi-woo'),
        'placeholder' => '27AAACR5055K1Z7',
        'required'    => false,
        'class'       => ['form-row-wide'],
        'priority'    => 35,
    ];

    return $fields;
});

Validating server-side, not just in the browser

Client-side format checks are a nice-to-have for instant feedback, but they cannot tell you whether a GSTIN was ever issued. The real check has to happen server-side, on woocommerce_after_checkout_validation, where you can still block the order:

add_action('woocommerce_after_checkout_validation', function ($fields, $errors) {
    $gstin = strtoupper(trim($fields['billing_gstin'] ?? ''));

    if ($gstin === '') {
        return; // field is optional
    }

    if (!preg_match('/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/', $gstin)) {
        $errors->add('validation', __('That GSTIN is not correctly formatted.', 'gstinapi-woo'));
        return;
    }

    $cache_key = 'gstinapi_' . $gstin;
    $result = get_transient($cache_key);

    if ($result === false) {
        $response = wp_remote_get("https://gstinapi.com/api/get-taxpayer-info/{$gstin}", [
            'headers' => ['X-API-Key' => GSTINAPI_KEY],
            'timeout' => 10,
        ]);

        if (is_wp_error($response)) {
            $errors->add('validation', __('Could not verify GSTIN right now — please try again.', 'gstinapi-woo'));
            return;
        }

        $code = wp_remote_retrieve_response_code($response);
        $body = json_decode(wp_remote_retrieve_body($response), true);

        if ($code === 404 || ($body['status'] ?? null) === 'not_found') {
            $errors->add('validation', __('That GSTIN does not exist. Please double-check it.', 'gstinapi-woo'));
            return;
        }

        if ($code !== 200) {
            $errors->add('validation', __('Could not verify GSTIN right now — please try again.', 'gstinapi-woo'));
            return;
        }

        $result = $body;
        set_transient($cache_key, $result, 6 * HOUR_IN_SECONDS);
    }

    if (($result['status'] ?? '') !== 'Active') {
        $errors->add('validation', __('This GSTIN is not currently active.', 'gstinapi-woo'));
        return;
    }

    WC()->session->set('gstinapi_verified_' . $gstin, $result);
}, 10, 2);

Caching lookups with a transient

The transient in the snippet above (set_transient/get_transient) is the whole caching layer, and it matters more than it looks: without it, a customer who fails checkout for an unrelated reason (a card decline, a shipping address issue) and retries will trigger a fresh API call each time, even though the GSTIN itself has not changed. A cache window of a few hours to a day avoids burning extra verification credits on repeat lookups of the same number, while still catching a GSTIN that gets cancelled between orders.

Storing the verified legal name on the order

Once validation passes, save the result as order meta on woocommerce_checkout_update_order_meta so your invoicing template can pull the registered legal name, not whatever the customer typed into their billing name field:

add_action('woocommerce_checkout_update_order_meta', function ($order_id) {
    $gstin = strtoupper(trim($_POST['billing_gstin'] ?? ''));
    if ($gstin === '') {
        return;
    }

    $verified = WC()->session->get('gstinapi_verified_' . $gstin);
    if (!$verified) {
        return;
    }

    $order = wc_get_order($order_id);
    $order->update_meta_data('_gstin', $gstin);
    $order->update_meta_data('_gstin_legal_name', $verified['legalName'] ?? '');
    $order->save();
});

This is what makes the whole flow useful for the B2B GST-invoice use case: your invoice plugin or a small custom template can now print the buyer's registered legal name and GSTIN exactly as they appear in GSTN records, which is what a business customer's accounts team needs to claim input tax credit on the purchase. If you sell at volume and want to check an existing customer list rather than validate one at a time, see our bulk GSTIN verification guide.

Frequently asked questions

Yes. Add a custom checkout field with the woocommerce_checkout_fields filter, then verify it server-side on the woocommerce_after_checkout_validation hook by calling a GSTIN verification API. This blocks a malformed or non-existent GSTIN before the order is placed.
In wp-config.php as a defined constant, never in post meta, options table rows editable from wp-admin, or a plugin settings field saved without encryption. Post meta and unprotected options are readable by any plugin or, in a compromised install, an attacker with database access.
Cache it. Use a WordPress transient keyed by the GSTIN with a TTL of a few hours to a day, so a customer who mistypes and retries, or who checks out twice in a session, does not trigger a duplicate API call and does not consume an extra verification credit for the same lookup.
Reject the checkout with a clear WooCommerce validation error (wc_add_notice with an error type) before payment is taken, rather than accepting the order and finding out later. A well-formed but non-existent GSTIN is a normal, expected API response, not a server error, so handle it as a validation failure.
Yes — this is the main use case. Once a GSTIN is verified, store the registered legal name and address as order meta and pull them into your GST invoice PDF or plugin, so the invoice shows the buyer's correct registered details rather than whatever they typed into a free-text field.

A note on accuracy. GST rules change often. This article reflects our understanding as of 30 August 2026 and is general information, not tax or legal advice. For the authoritative position, check gst.gov.in and cbic-gst.gov.in, or speak to a qualified tax professional about your specific situation.

Check a GSTIN right now, free

Five lookups a day, no account required. Status, filing history, e-invoicing and jurisdiction.

Free GST search tool

Build it into your product

One REST call, JSON back. Get an API key and 20 free verification credits in under a minute.

Start free