Webhooks are a way for web apps to get real-time notifications of some operations in Selz. Once you register a URI to receive webhooks, Selz will send an HTTP request to that URI every time there's a change for any of your app's registered users.

Available events

Type Description
blog_post_created Occurs whenever a blog post is created
blog_post_updated Occurs whenever a blog post is updated
blog_post_deleted Occurs whenever a blog post is deleted
category_created Occurs whenever a category is created
category_updated Occurs whenever a category is updated
category_deleted Occurs whenever a category is deleted
customer_created Occurs whenever a customer is created
customer_updated Occurs whenever a customer is updated
customer_deleted Occurs whenever a customer is deleted
discount_created Occurs whenever a discount is created
discount_updated Occurs whenever a discount is updated
discount_deleted Occurs whenever a discount is deleted
order_payment_succeeded Occurs whenever an order payment succeeded
order_completed Occurs whenever an order payment is completed
product_created Occurs whenever a product is created
product_updated Occurs whenever a product is updated
product_deleted Occurs whenever a product is deleted
product_soldout Occurs whenever a product is sold out
product_variant_soldout Occurs whenever a product variant is sold out

Securing Webhooks

Webhooks can be verified by calculating a digital signature.

Each Webhook request includes a HTTP_X_SELZ_SIGNATURE header along with the data sent in the request. To verify that the request came from Selz, compute the HMAC digest according to the following algorithm and compare it to the value in the HTTP_X_SELZ_SIGNATURE header. If they match, you can be sure that the Webhook was sent from Selz and the data has not been compromised.

PHP Example

<?php
$json = json_decode(file_get_contents('php://input'), TRUE);
$message = $json["timestamp"].$json["token"];

if(!ValidateSignature($message)){
    header("HTTP/1.1 400 BAD REQUEST");
    die();
}

// Do something with webhook payload
$orderId = $json["data"]["id"];

header("HTTP/1.1 200 OK");
die();

function CreateSignature($message) {
    $key = 'your_webhook_verification_key';

    $aMessage = iconv(iconv_get_encoding("internal_encoding"), "ASCII", $message);
    $aKey = iconv(iconv_get_encoding("internal_encoding"), "ASCII", $key);

    $sig = hash_hmac('sha256', $aMessage, $aKey, true);
    return base64_encode($sig);
}

function ValidateSignature($message){
    $signature = $_SERVER["HTTP_X_SELZ_SIGNATURE"];
    $signatureToCompare = CreateSignature($message);

    return ($signature == $signatureToCompare);
}
?>

Node.js Example

const crypto = require('crypto');

app.post('/webhook', (request, response) => {
    // https://github.com/visionmedia/express/wiki/Migrating-from-3.x-to-4.x
    // express 3.x: app.use(express.bodyParser());
    // express 4.x: app.use(bodyParser());
    const json = request.body;
    response.send(200);

    const signature = request.headers['x-selz-signature'];
    const message = `${json['timestamp']}${json['token']}`;

    if (validateSignature(message, signature)) {
        console.log('OK');
    } else {
        console.log('ERROR');
    }
});

const createSignature = message => {
    const key = 'your_webhook_verification_key';

    return crypto
        .createHmac('sha256', key)
        .update(message, 'ascii')
        .digest('base64');
};

const validateSignature = (message, signature) => signature === createSignature(message);

Best practices

Always respond to webhooks quickly

To make sure you can always respond within ten seconds, you should always do your work on a separate thread (as in the simple example above) or asynchronously using a queue.

Manage concurrency

When a user makes a number of changes in rapid succession, your app is likely to receive multiple notifications for the same user at roughly the same time. If you're not careful about how you manage concurrency, your app can end up processing the same changes for the same user more than once. For some applications, this is not a serious issue. Work that can be repeated without changing the outcome is called idempotent. If your app's actions are always idempotent, you don't need to worry much about concurrency. Unfortunately, not every app can be made idempotent easily. For example, suppose you have an app that sends email every time a certain file is changed. To avoid sending duplicate emails, you need to make sure that your app never processes the same user on multiple threads/processes at the same time.