Available — Local Digital Products
HCA · Studio
TR — Contact ↗
Technical Note 12 · WooCommerce

Enabled in settings, missing at checkout

The plugin is active, the settings are saved, there is no error even in test mode — yet customers never see the payment method at checkout. The cause is usually not the plugin but the type of checkout page.

Platform
WooCommerce 8.3+
Language
PHP + JS
Symptom
Method missing at checkout
Short answer

Since WooCommerce 8.3 new stores get a block-based checkout, which renders payment methods in the browser with React. The block checkout does not automatically list classic gateways that extend WC_Payment_Gateway; each method must also be registered through an AbstractPaymentMethodType class on the woocommerce_blocks_payment_method_type_registration hook, and with registerPaymentMethod in JavaScript. Without that registration there is no error — the method is simply not shown. Because the same plugin works on the shortcode ([woocommerce_checkout]) checkout, the problem is easy to miss.

Symptom

Under WooCommerce → Settings → Payments the method shows as enabled. Settings are saved, the error log is clean. At checkout, though, only bank transfer or cash on delivery is listed; your custom gateway is not.

We ran into exactly this while reviewing a QNB virtual POS integration for Castor Coffee: in the same multisite install, the retail store used the classic shortcode checkout and the wholesale store used the block checkout. The plugin code was correct and its hash calculations matched what the bank expects — yet on the wholesale site the method would never have appeared, even with the settings completed.

Which checkout?

The first question is about the page, not the plugin. Look at the checkout page content:

wp post get $(wp option get woocommerce_checkout_page_id) --field=post_content | head -c 200
  • Starts with <!-- wp:woocommerce/checkout --> — a block checkout
  • Contains [woocommerce_checkout] — a classic checkout

On multisite, check each site separately; sites set up at different times can have different kinds of checkout page.

Why it is missing

The classic checkout is rendered on the server in PHP: WooCommerce walks the available gateways and prints each one's payment_fields(). The block checkout is a React application running in the browser that reads cart and checkout data from the Store API. To know how to display a method it needs a definition registered on the JavaScript side.

WooCommerce does not derive that definition from the classic gateway. The result: your gateway is registered on the server, is_available() returns true, and the block checkout knows nothing about it.

PHP registration

First, a class that introduces the gateway to the block side. The $name must be identical to the gateway's id:

use Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType;

final class My_Gateway_Blocks extends AbstractPaymentMethodType {
    protected $name = 'my_gateway';   // same as WC_Payment_Gateway::$id

    public function initialize() {
        $this->settings = get_option( 'woocommerce_my_gateway_settings', array() );
    }

    public function is_active() {
        $gateways = WC()->payment_gateways()->payment_gateways();
        return isset( $gateways[ $this->name ] ) && $gateways[ $this->name ]->is_available();
    }

    public function get_payment_method_script_handles() {
        wp_register_script( 'my-gateway-blocks', plugins_url( 'assets/blocks.js', __FILE__ ),
            array( 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities' ), '1.0', true );
        return array( 'my-gateway-blocks' );
    }

    public function get_payment_method_data() {
        return array( 'title' => 'Credit / Debit Card', 'supports' => array( 'products' ) );
    }
}

add_action( 'woocommerce_blocks_payment_method_type_registration', function ( $registry ) {
    $registry->register( new My_Gateway_Blocks() );
} );

Reusing the gateway's own is_available() inside is_active() matters: with missing credentials or an unsupported currency the method stays hidden on the block checkout too, and both checkout types follow the same rule.

Also declare block compatibility in the main plugin file, or the editor may flag the plugin as incompatible:

add_action( 'before_woocommerce_init', function () {
    \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', __FILE__, true );
} );

JavaScript registration

Data returned by get_payment_method_data() is available in the browser under the <name>_data key. The smallest registration, with no build step:

( function () {
    var settings = window.wc.wcSettings.getSetting( 'my_gateway_data', {} );
    var el = window.wp.element.createElement;
    var label = settings.title || 'Credit / Debit Card';

    window.wc.wcBlocksRegistry.registerPaymentMethod( {
        name: 'my_gateway',                 // same as $name in PHP
        label: el( 'span', null, label ),
        ariaLabel: label,
        content: el( 'p', null, 'You will be redirected to the secure payment page.' ),
        edit: el( 'p', null, 'You will be redirected to the secure payment page.' ),
        canMakePayment: function () { return true; },
        supports: { features: settings.supports || [ 'products' ] }
    } );
} )();

Gateways that redirect the customer to the bank's 3D Secure page need nothing more: when process_payment() returns a redirect URL, the block checkout sends the customer there. Gateways that collect card details on your own page additionally need the onPaymentSetup event to pass payment data to the Store API.

Silent traps

If the method is still missing after registration, check these in order. None of them produces a visible error:

  • Name mismatch: the gateway id, the PHP $name and the JavaScript name must all be the same
  • Deferred script: if a speed plugin defers scripts, the registration runs before wc.wcBlocksRegistry exists, or not at all — exempt the checkout from optimisation
  • Missing dependency: without wc-blocks-registry in the dependency list the console shows wc is not defined
  • is_active() returns false: missing settings, an unsupported currency or a mis-built condition
  • Cache: a page cache may serve the old script list; purge the checkout page after changes

Verification

You can confirm the server-side registration with WP-CLI:

wp eval '
$r = Automattic\WooCommerce\Blocks\Package::container()
      ->get( Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry::class );
do_action( "woocommerce_blocks_payment_method_type_registration", $r );
echo implode( ", ", array_keys( $r->get_all_registered() ) );'

If your method's name is not in the list, the problem is the PHP registration. If it is listed but still not shown, check in the browser that the script loads and look for console errors; finally, check the is_active() decision.

Summary

The block checkout is a separate door for classic payment gateways, and it only opens with a registration. Diagnosis starts with one command: is the checkout a block or a shortcode? If it is a block, write an AbstractPaymentMethodType class and a registerPaymentMethod call for the gateway, keep the name identical in all three places, and do not defer the script. A method that looks ready to take payments but is never shown to customers is one of the most expensive bugs there is — nobody complains, sales simply do not happen.

Quick reference
Diagnosis
Checkout content wp:woocommerce/checkout means block, [woocommerce_checkout] means classic
Cause
The block checkout does not list classic gateways automatically
PHP
AbstractPaymentMethodType + woocommerce_blocks_payment_method_type_registration
JS
wc.wcBlocksRegistry.registerPaymentMethod()
Name
Gateway id = PHP $name = JS name
Dependencies
wc-blocks-registry, wc-settings, wp-element
Compatibility
declare_compatibility( 'cart_checkout_blocks', … )
Trap
A speed plugin deferring the script silently breaks the registration
Frequently Asked Questions

About the block checkout.

My payment method is enabled in settings but does not appear at checkout. Why?

Most often because the checkout page is block-based. Since WooCommerce 8.3 new stores ship with the block checkout, which does not list classic payment gateways automatically. The gateway also has to be registered with an AbstractPaymentMethodType class and with registerPaymentMethod in JavaScript.

Does switching back to the classic checkout fix it?

Yes — converting the checkout block to the classic shortcode version makes the method appear immediately. It is not a lasting fix, though: WooCommerce ships new features through the block checkout. Making the plugin block-compatible is a one-time job.

I added the registration and the method is still missing.

Check that the name is identical in all three places: the gateway id, $name in the PHP class and name in JavaScript. Then check whether a speed plugin defers the script and whether is_active() returns true. None of these produces a visible error.

Does a virtual POS that redirects to the bank's 3D Secure page need extra code?

No. When process_payment() returns a redirect URL, the block checkout sends the customer to it. Extra code is only needed for gateways that collect card details on your own page.

Availability and Quotes

Have an idea?
Half a sentence is enough.