Adding a Crypto Payment Method to the WooCommerce Checkout Block
October 10, 2026 · 5 min read
To show a payment method in the WooCommerce Checkout block, a classic WC_Payment_Gateway is not enough. You also need a small PHP class that extends AbstractPaymentMethodType, and a JavaScript file that calls registerPaymentMethod(). If the gateway redirects to a hosted payment page, which is common for crypto, your existing process_payment() keeps working unchanged. This guide shows the complete integration as we built it for the Clearqo USDT gateway, plus the pitfalls we hit along the way.
Why doesn’t my gateway appear in the Checkout block?
New WooCommerce stores use the block-based Cart and Checkout by default. The block checkout is a React app that talks to the Store API, so it only lists payment methods that register themselves on the JavaScript side. A gateway that only extends WC_Payment_Gateway still works on the classic shortcode checkout, but in the block checkout it is simply missing. Store owners usually report this as “no payment methods available”.
The three parts of a block integration
- Declare compatibility, so WooCommerce doesn’t flag your plugin as incompatible.
- A PHP integration class that tells the block which script to load and which settings to pass to it.
- A JavaScript file that registers the method with the block’s payment registry.
1. Declare compatibility
add_action( 'before_woocommerce_init', function () {
if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', __FILE__, true );
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true ); // HPOS
}
} );
Only declare HPOS (custom_order_tables) compatibility if you read and write order data through the WC_Order API (get_meta(), update_meta_data()) and never through get_post_meta() on orders.
2. The PHP integration class
add_action( 'woocommerce_blocks_loaded', function () {
if ( ! class_exists( '\Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType' ) ) {
return;
}
final class My_Crypto_Blocks extends \Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType {
protected $name = 'my_crypto'; // must equal your gateway ID
public function initialize() {
$this->settings = get_option( 'woocommerce_my_crypto_settings', array() );
}
public function is_active() {
return 'yes' === $this->get_setting( 'enabled' );
}
public function get_payment_method_script_handles() {
wp_register_script(
'my-crypto-blocks',
plugins_url( 'assets/blocks.js', __FILE__ ),
array( 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities' ),
'1.0.0',
true
);
return array( 'my-crypto-blocks' );
}
public function get_payment_method_data() {
return array(
'title' => $this->get_setting( 'title' ),
'description' => $this->get_setting( 'description' ),
'supports' => array( 'products' ),
);
}
}
add_action( 'woocommerce_blocks_payment_method_type_registration', function ( $registry ) {
$registry->register( new My_Crypto_Blocks() );
} );
} );
Whatever get_payment_method_data() returns is passed to your script. Only include what the browser may see: never API keys or secrets.
3. The JavaScript file
No build step is needed. Plain ES5 with wp.element.createElement works and keeps the plugin simple:
( function () {
'use strict';
var registry = window.wc && window.wc.wcBlocksRegistry;
var wcSettings = window.wc && window.wc.wcSettings;
// Newer WooCommerce: getPaymentMethodData(); older versions: the my_crypto_data setting.
var settings = ! wcSettings ? {} : ( wcSettings.getPaymentMethodData
? wcSettings.getPaymentMethodData( 'my_crypto', {} )
: wcSettings.getSetting( 'my_crypto_data', {} ) ) || {};
if ( ! registry || ! window.wp || ! window.wp.element ) {
return;
}
var el = window.wp.element.createElement;
var decode = window.wp.htmlEntities ? window.wp.htmlEntities.decodeEntities : function ( s ) { return s; };
var title = decode( settings.title || 'Pay with USDT' );
var Content = function () {
return el( 'div', null, decode( settings.description || '' ) );
};
registry.registerPaymentMethod( {
name: 'my_crypto',
label: el( 'span', null, title ),
ariaLabel: title,
content: el( Content, null ),
edit: el( Content, null ),
canMakePayment: function () { return true; },
supports: { features: settings.supports || [ 'products' ] }
} );
} )();
What about process_payment()?
For redirect-style gateways, nothing changes. When the customer clicks Place Order, the Store API creates the order and calls your gateway’s process_payment(). Return the usual array and the block redirects the browser:
return array( 'result' => 'success', 'redirect' => $payment_page_url );
Errors work too. Call wc_add_notice( $message, 'error' ) and return array( 'result' => 'failure' ), and the block shows the notice above the form. Keep customer-facing messages neutral (“temporarily unavailable”) and write the technical reason to an order note for the store owner.
Pitfalls we hit
- The name must match.
$namein PHP, thenameinregisterPaymentMethod()and your gateway ID must be identical, or the method shows up but the order is never processed. - Reading settings. Recent WooCommerce versions expose method data through
wcSettings.getPaymentMethodData( name ). Older versions only hadgetSetting( name + '_data' ). Support both, as above, so the title doesn’t fall back to a default on one of them. - HTML entities in titles. A title saved as “USDT — Binance Pay” arrives encoded. Use
wp.htmlEntities.decodeEntitiesand render it as text, not withdangerouslySetInnerHTML. - Script dependencies. List
wc-blocks-registryandwc-settingsas dependencies. If your script runs before them,window.wcis undefined and nothing registers. is_active()vsis_available().is_active()only decides whether your script loads. Your gateway’sis_available()still decides whether the method can be used. Keep both in sync, for example by returning false when required settings are empty.- Caching plugins. After deploying, purge page and script caches. An old cached checkout page won’t load your new script.
Testing checklist
- The method shows in both the block checkout and the classic checkout (a page with the
[woocommerce_checkout]shortcode). - The title and description match the settings, including special characters.
- Place Order redirects to your payment page, and the order is created as Pending payment.
- A failed API call shows a clean error notice and does not leave the customer stuck.
- No console errors on the checkout page. Under WooCommerce → Settings → Advanced → Features your plugin is not listed as incompatible.
Just want USDT in your checkout block?
If you’d rather not build this yourself, the free Clearqo for WooCommerce plugin already supports the Checkout block, the classic checkout and HPOS. Its source code, including the files above, is on GitHub. Setup guide: How to Accept USDT Payments on WooCommerce.
FAQ
Do I need React or a build tool?
No. wp.element.createElement is React’s createElement, and WordPress already loads it. A plain script file is enough for a simple method.
Can a payment method show its own fields in the block?
Yes. Render inputs in your content component and pass their values on through the checkout’s payment-processing events. Redirect gateways usually need no fields at all.
Will the classic checkout keep working?
Yes. The block integration is added alongside your existing gateway class, so the classic checkout is unchanged.
Accept USDT on your website
Paid straight to your own Binance, verified automatically.