Processing webhooks

Webhooks are a way to get notified about events in an Ecwid store. They are not sent in any particular order. So you shouldn't use them as actionable items.

How to process webhooks

A rough description of webhook processing looks like this:

  • Receive a webhook from Ecwid
  • Check the entity that changed with the event in REST API (product, order, etc.)
  • Stop the further procedures or continue, depending on previous step results and your goal

See some processing flow examples below.

📘

Webhooks are sent on any update of an order/product. Even if one field has changed. So make sure to check the details of order/product before processing it further.

product.updated webhook processing flow example:

  • Receive a webhook from Ecwid
  • Respond with 200OK HTTP status
  • Parse the request information: identify productId from entityId, make sure the eventType is correct and get storeId value
  • Get up-to-date information about the product from Ecwid via Ecwid REST API
  • Update your local database with latest stock quantity of that product

order.updated webhook processing flow example:

  • Receive a webhook from Ecwid
  • Respond with 200OK HTTP status
  • Parse the request information: identify orderId from webhook, make sure the eventType is correct and get storeId value, check if newPaymentStatus value is "PAID"
  • Get order details from Ecwid via the Ecwid REST API
  • Check if the order has changed compared to current info you have
  • Send order details to a fulfillment center
  • Set order fulfillment status as "PROCESSING" via Ecwid REST API

See also the webhooks best practices on webhooks security and processing examples.

Responding to webhooks

When a webhook is sent to your URL, your app must return a 200 OK HTTP status code in reply to a webhook. This confirms that you received a webhook.

Any other response (e.g. 3xx), will indicate to Ecwid that the webhook was not received. In this case, Ecwid will re-send a webhook every 15 minutes for 4 more tries.

Ecwid will wait 3 seconds to successfully connect to your server. And 10 seconds to get back a response from it before timing out.

📘

If Ecwid still can't deliver the webhook after that, it will try sending webhooks every hour until 24 hour limit is reached. Once that limit is reached, the webhook will be removed from the queue and will not be sent again.
If you URL fails 90% of consecutive requests Ecwid will stop sending any webhooks for 1 min. Ecwid will keep sending a webhook per minute to check if it can be delivered till your URL start returning a 200 OK status.

Webhook processing example

Here's an example of implementing all of the described guidelines and recommendations in order to process webhooks from Ecwid in the most efficient way.

<?php 
// Get contents of webhook request
$requestBody = file_get_contents('php://input');
$client_secret = 'abcde123456789'; // your client_secret value on https://my.ecwid.com/#develop-apps page; NOT your 'secret_*' access token.

// Parse webhook data
$decodedBody = json_decode($requestBody, true);

$eventId = $decodedBody['eventId'];
$eventCreated = $decodedBody['eventCreated'];
$storeId = $decodedBody['storeId'];
$entityId = $decodedBody['entityId'];
$eventType = $decodedBody['eventType'];
$data = $decodedBody['data'];
$signatureHeaderPresent = false;

// Reply with 200OK to Ecwid
http_response_code(200);

// Filter out the events we're not interested in
if ($eventType !== 'order.updated') {
    exit;
}

if (!function_exists('getallheaders')) {
    function getallheaders()
    {
        foreach ($_SERVER as $name => $value) {
            if (substr($name, 0, 5) == 'HTTP_') {
                $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
            }
        }
        return $headers;
    }
}

// Continue if eventType is order.updated
// Verify the webhook (check that it is sent by Ecwid)
foreach (getallheaders() as $name => $value) {
    if ($name == "X-Ecwid-Webhook-Signature") {
        $headerSignature = "$value";
        $signatureHeaderPresent = true;
        
        $hmac_result = hash_hmac("sha256", "$eventCreated.$eventId", $client_secret, true);
        $generatedSignature = base64_encode($hmac_result);
        
        if ($generatedSignature !== $headerSignature) {
            echo 'Signature verification failed';
            exit;
        }
    }
}

if (!$signatureHeaderPresent) {
    echo 'Signature verification failed';
    exit;
}

// Handle the event
// 
// Update events can be sent about one entity (product/order) multiple times. 
//
// Make sure the changed entity meets your requirements first, before processing further. 
// 
// ...

?>