eriktobben
eriktobben

Reputation: 119

How to decode WooCommerce Webhook Secret?

I can't find any information on what algorithm to use to decode WooCommerce webhook field X-Wc-Webhook-Signature in PHP. Does anyone know how to decode it?

Thanks!

Upvotes: 11

Views: 9802

Answers (6)

emale999
emale999

Reputation: 21

If all the answers will fail: Your secret (defined in woocommerce) should only contain letters and numbers!

Upvotes: 0

Ian Winstanley
Ian Winstanley

Reputation: 81

To expand on the laravel solution this is how I created middleware to validate the incoming webhook.

Create middleware. The application that I am using keeps the WooCommerce consumer key and secret in a table assigned to a given store.

class ValidateWebhook
{
    /**
     * Validate that the incoming request has been signed by the correct consumer key for the supplied store id
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        $signature = $request->header('X-WC-Webhook-Signature');
        if (empty($signature)) {
            return response(['Invalid key'], 401);
        }

        $store_id = $request['store'];
        $consumer_key = ConsumerKeys::fetchConsumerSecretByStoreId($store_id);

        $payload = $request->getContent();
        $calculated_hmac = base64_encode(hash_hmac('sha256', $payload, $consumer_key, true));

        if ($signature != $calculated_hmac) {
            return response(['Invalid key'], 401);
        }

        return $next($request);
    }
}

Register the middleware in Kernel.php

        'webhook' => \App\Http\Middleware\ValidateWebhook::class,

Protect the webhook route with the middleware

Route::post('webhook', 'PrintController@webhook')->middleware('webhook');

Upvotes: 8

Micheal C Wallas
Micheal C Wallas

Reputation: 525

Here's my laravel / lumen function to verify the webhook request, hope it helps someone!

private function verifyWebhook(Request $request) {
    $signature = $request->header('x-wc-webhook-signature');

    $payload = $request->getContent();
    $calculated_hmac = base64_encode(hash_hmac('sha256', $payload, 'WOOCOMMERCE_KEY', true));

    if($signature != $calculated_hmac) {
      abort(403);
    }

    return true;
}

Upvotes: 0

Alexandru Burca
Alexandru Burca

Reputation: 427

Here is my solution for this question. You need to generate a sha256 hash for the content that was sent [payload] and encode it in base64. Then just compare the generated hash with the received one.

$secret = 'your-secret-here';
$payload = file_get_contents('php://input');
$receivedHeaders = apache_request_headers();
$receivedHash = $receivedHeaders['X-WC-Webhook-Signature'];
$generatedHash = base64_encode(hash_hmac('sha256', $payload, $secret, true));
if($receivedHash === $generatedHash):
        //Verified, continue your code
else:
        //exit
endif;

Upvotes: 3

DarkNeuron
DarkNeuron

Reputation: 8692

According to the docs: "A base64 encoded HMAC-SHA256 hash of the payload"

I'm guessing that the payload in this instances is the secret you've supplied in the webhook properties.

Source: https://woocommerce.github.io/woocommerce-rest-api-docs/v3.html#webhooks-properties

EDIT

Further digging indicates that the payload is the body of the request. So you'd use a HMAC library to create a sha256 hash of the payload using your webhook secret, and then base64 encode the result, and do the comparison with X-Wc-Webhook-Signature and see if they match.

You can generate a key pair from the following URL:

/wc-auth/v1/authorize?app_name=my_app&scope=read_write&user_id=<a_unique_id_that_survives_the_roundtrip>&return_url=<where_to_go_when_the_flow_completes>&callback_url=<your_server_url_that_will_receieve_the_following_params>

These are the params the callback URL receieves in the request body:

{
  consumer_key: string,
  consumer_secret: string,
  key_permissions: string,
  user_id: string,
}

Upvotes: 1

user2542365
user2542365

Reputation: 91

Expanding on the current answers, this is the PHP code snippet you need:

$sig = base64_encode(hash_hmac('sha256', $request_body, $secret, true));

Where $secret is your secret, $request_body is the request body, which can be fetched with file_get_contents('php://input'); The $sig value should then be equal to the X-Wc-Webhook-Signature request header.

Upvotes: 9

Related Questions