WooCommerce Webhook API: Real-Time Order Notifications with HMAC Verification

WooCommerce Webhooks send HTTP POST requests to your endpoint whenever an order is created, updated, or deleted — eliminating the need to poll the REST API. They support HMAC-SHA256 payload signing so your receiver can verify authenticity.

Problem: WooCommerce sends webhook payloads to external URLs for events like order creation and status changes, but verifying the payload authenticity and processing it reliably requires understanding the signature mechanism and idempotency.

Solution: Enable webhooks in WooCommerce → Settings → Advanced → Webhooks, select the delivery URL and secret key. In the receiving endpoint, verify the X-WC-Webhook-Signature header with hash_equals(base64_encode(hash_hmac('sha256', $body, $secret, true)), $signature) before processing the payload.

The examples below register a webhook programmatically, show how to validate the HMAC signature on the receiving end, and demonstrate a custom webhook topic for a plugin-specific event.

// Create a webhook via WC API (run once)
function myplugin_register_order_webhook() {
    $webhook = new WC_Webhook();
    $webhook->set_name( 'Order Created — CRM Sync' );
    $webhook->set_topic( 'order.created' );          // order.updated | order.deleted | order.restored
    $webhook->set_delivery_url( 'https://crm.example.com/hooks/woo' );
    $webhook->set_secret( wp_generate_password( 32, false ) ); // store this!
    $webhook->set_status( 'active' );
    $webhook->save();
}
add_action( 'init', 'myplugin_register_order_webhook' );

// Available built-in topics:
// coupon.created/updated/deleted
// customer.created/updated/deleted
// order.created/updated/deleted/restored
// product.created/updated/deleted/restored

Validate the webhook signature on your receiving server:

// receiver.php — validate WooCommerce webhook signature
$secret  = 'your_stored_secret';
$payload = file_get_contents( 'php://input' );
$sig     = $_SERVER['HTTP_X_WC_WEBHOOK_SIGNATURE'] ?? '';

$expected = base64_encode( hash_hmac( 'sha256', $payload, $secret, true ) );

if ( ! hash_equals( $expected, $sig ) ) {
    http_response_code( 401 );
    exit( 'Invalid signature' );
}

$data = json_decode( $payload, true );
$order_id = $data['id'] ?? null;
// process order ...
http_response_code( 200 );
echo 'ok';

NOTE: Always use hash_equals() for signature comparison — it prevents timing attacks. WooCommerce will retry delivery up to five times with exponential back-off if your endpoint returns a non-2xx status.

Leave Comment

Your email address will not be published. Required fields are marked *