WooCommerce Integration
Accept crypto payments on WooCommerce with the Plaidly payment gateway plugin
WooCommerce Integration
Integrate Plaidly into your WooCommerce store using a custom payment gateway plugin. This guide walks through building a minimal gateway plugin against the live POST /v1/payment_sessions endpoint.
Prerequisites
- WordPress + WooCommerce installed
- PHP 8.1+
- A Plaidly API key and webhook secret — see Register a merchant
- SSL/HTTPS on your store (required by WooCommerce for payment gateways)
1. Create the plugin
Create a file at wp-content/plugins/plaidly-woocommerce/plaidly-woocommerce.php:
<?php
/**
* Plugin Name: Plaidly Crypto Payments
* Description: Accept cryptocurrency payments via Plaidly
* Version: 1.0.0
* Requires Plugins: woocommerce
*/
if ( ! defined( 'ABSPATH' ) ) exit;
add_filter( 'woocommerce_payment_gateways', function( $gateways ) {
$gateways[] = 'WC_Plaidly_Gateway';
return $gateways;
} );
add_action( 'plugins_loaded', function() {
if ( ! class_exists( 'WC_Payment_Gateway' ) ) return;
class WC_Plaidly_Gateway extends WC_Payment_Gateway {
public function __construct() {
$this->id = 'plaidly';
$this->method_title = 'Crypto (Plaidly)';
$this->method_description = 'Accept USDC, USDT, and other tokens via Plaidly';
$this->has_fields = false;
$this->supports = [ 'products' ];
$this->init_form_fields();
$this->init_settings();
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
$this->api_key = $this->get_option( 'api_key' );
$this->webhook_secret = $this->get_option( 'webhook_secret' );
$this->chain = $this->get_option( 'chain', 'solana' );
$this->currency = $this->get_option( 'currency', 'USDC' );
$this->network = $this->get_option( 'network', 'mainnet' );
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id,
[ $this, 'process_admin_options' ] );
}
public function init_form_fields() {
$this->form_fields = [
'enabled' => [
'title' => 'Enable',
'type' => 'checkbox',
'label' => 'Enable Plaidly Crypto Payments',
'default' => 'yes',
],
'title' => [
'title' => 'Title',
'type' => 'text',
'default' => 'Pay with Crypto',
],
'description' => [
'title' => 'Description',
'type' => 'text',
'default' => 'Pay with USDC, USDT, or other stablecoins',
],
'api_key' => [
'title' => 'Plaidly API Key',
'type' => 'password',
],
'webhook_secret' => [
'title' => 'Webhook Secret',
'type' => 'password',
],
'chain' => [
'title' => 'Chain',
'type' => 'select',
'options' => [
'solana' => 'Solana',
'ethereum' => 'Ethereum',
'polygon' => 'Polygon',
'bsc' => 'BNB Smart Chain',
'tron' => 'Tron',
'ton' => 'TON',
],
'default' => 'solana',
],
'currency' => [
'title' => 'Token',
'type' => 'select',
'options' => [ 'USDC' => 'USDC', 'USDT' => 'USDT' ],
'default' => 'USDC',
],
];
}
public function process_payment( $order_id ) {
$order = wc_get_order( $order_id );
$amount = (float) number_format( (float) $order->get_total(), 2, '.', '' );
// Live endpoint: POST /v1/payment_sessions (verified against
// plaidly-api/api/openapi.yaml). Note the nested `paymentMethod`
// object -- this is NOT the older flat chain/currency shape.
$response = wp_remote_post( 'https://api.plaidly.io/v1/payment_sessions', [
'headers' => [
'X-API-Key' => $this->api_key,
'Content-Type' => 'application/json',
'Idempotency-Key' => 'wc-order-' . $order_id,
],
'body' => wp_json_encode( [
'amount' => $amount,
'expires_in' => '30m',
'paymentMethod' => [
'methodID' => 0,
'chain' => $this->chain,
'token' => $this->currency,
'network' => $this->network,
],
'metadata' => [
'order_id' => (string) $order_id,
'order_key' => $order->get_order_key(),
],
] ),
] );
if ( is_wp_error( $response ) ) {
wc_add_notice( 'Payment error: unable to reach payment processor', 'error' );
return [ 'result' => 'failure' ];
}
$session = json_decode( wp_remote_retrieve_body( $response ), true );
if ( ! $session || ! isset( $session['session_id'] ) ) {
wc_add_notice( 'Payment error: invalid response from payment processor', 'error' );
return [ 'result' => 'failure' ];
}
// Store session ID on the order
$order->update_meta_data( '_plaidly_session_id', $session['session_id'] );
$order->update_status( 'pending', 'Awaiting crypto payment via Plaidly' );
$order->save();
return [
'result' => 'success',
'redirect' => $session['payment_url'] ??
add_query_arg( 'plaidly_session', $session['session_id'],
wc_get_checkout_url() ),
];
}
}
} );2. Register the webhook endpoint
Add to your plugin file. The signature scheme is t=<unix_seconds>,v1=<hex> where v1 is HMAC-SHA256(webhook_secret, "<t>.<raw_body>") — see Verify a webhook event for the canonical algorithm this mirrors:
add_action( 'rest_api_init', function() {
register_rest_route( 'plaidly/v1', '/webhook', [
'methods' => 'POST',
'callback' => 'plaidly_handle_webhook',
'permission_callback' => '__return_true',
] );
} );
function plaidly_handle_webhook_verify( string $raw_body, string $signature_header, string $secret, int $tolerance_seconds = 300 ): bool {
$parts = [];
foreach ( explode( ',', $signature_header ) as $part ) {
[ $key, $value ] = array_pad( explode( '=', trim( $part ), 2 ), 2, null );
$parts[ $key ] = $value;
}
if ( empty( $parts['t'] ) || empty( $parts['v1'] ) ) {
return false;
}
if ( abs( time() - (int) $parts['t'] ) > $tolerance_seconds ) {
return false;
}
$expected = hash_hmac( 'sha256', $parts['t'] . '.' . $raw_body, $secret );
return hash_equals( $expected, $parts['v1'] );
}
function plaidly_handle_webhook( WP_REST_Request $request ) {
$body = $request->get_body();
$signature = $request->get_header( 'x-plaidly-signature' );
$secret = get_option( 'woocommerce_plaidly_settings' )['webhook_secret'] ?? '';
if ( ! plaidly_handle_webhook_verify( $body, $signature ?? '', $secret ) ) {
return new WP_REST_Response( 'Unauthorized', 401 );
}
$event = json_decode( $body, true );
if ( ( $event['event_type'] ?? '' ) === 'payment_session.completed' ) {
$order_id = $event['metadata']['order_id'] ?? null;
if ( $order_id ) {
$order = wc_get_order( (int) $order_id );
if ( $order && $order->has_status( 'pending' ) ) {
$order->payment_complete( $event['session_id'] ?? '' );
$order->add_order_note(
'Payment confirmed via Plaidly. Session: ' .
( $event['session_id'] ?? 'N/A' )
);
}
}
}
return new WP_REST_Response( 'OK', 200 );
}3. Activate and configure
- Upload the plugin to
wp-content/plugins/plaidly-woocommerce/ - Activate it in WordPress Admin → Plugins
- Go to WooCommerce → Settings → Payments → Crypto (Plaidly)
- Enter the API Key and Webhook Secret you got back from merchant registration — there is currently no dashboard to look these up after the fact; if you lost them, rotate credentials to issue new ones
- Select your preferred Chain and Token
- Enable the gateway and save
4. Set the webhook URL
webhook_url is set once, at merchant registration (POST /v1/merchants) — there is no dashboard or update endpoint to change it afterward today. Register your sandbox/live merchant with the final webhook URL already in place:
https://yourstore.com/wp-json/plaidly/v1/webhookSee Configure a webhook for the current limitation and workaround.
Testing
Set chain to solana, register a sandbox merchant ("sandbox": true at registration — the resulting api_key is prefixed plaidly_sk_test_), and use Simulate a payment to trigger a test confirmation without broadcasting a real transaction.
HPOS compatibility
The plugin is compatible with WooCommerce High-Performance Order Storage (HPOS). The update_meta_data and get_meta calls use WooCommerce's CRUD methods.