How to Synchronize Orders with Shopify Webhooks
Shopify is a broad platform but for any ambitious tech team, it doesn't do it all. There are literally thousands of valid use cases for Shopify Webhooks. Today I'll run you through how you can leverage the order/created
, order/updated
, and order/deleted
webhook topics to synchronize your Shopify orders with your database and services.
I had to rely on it to at Rachel. We implemented a webhook integration to synchronize all our orders in an Elasticsearch index that we would use to do complex segmentation queries to optimize the fulfillment sequence of orders and operational efficacy.
For our rundown of how this set this up, I'll be using Shopify REST API, Javascript, and ExpressJS.
You can also take a look at this general tutorial to create your first shopify webhook.
Building Shopify webhook integration to synchronize orders in Elasticsearch index
Shopify webhooks are configured with a given set of topics that represent which action on which resource we want to be notified for an URL. Shopify will make an HTTP POST
request to that URL with the data of the resource. In our case, the topics order/created
and order/updated
will receive a request with the newly created or updated order. The order/deleted
will receive a request with the ID of the delete order.
There are a few important things we will need to do to be properly setup.
Create a HTTP endpoint to receive the webhooks
Our endpoint need to accept unauthenticated POST
request and parses the JSON
body data of the webhook. This endpoint will need to be available via HTTPS.
const express = require("express");
const app = express();
const port = 3000;
// Shopify will send data as JSON, register the express JSON parser
app.use(express.json());
// Expose our endpoint
app.post("/webhooks/order", (req, res) => {
console.log(req.body);
res.send("Server receive a order!");
});
app.listen(port, () => {
console.log(`Order service listening at http://localhost:${port}`);
});
Register a webhook on Shopify
To register your webhook, we'll need to expose the server publicly via HTTPS. Hookdeck CLI is a great tool just for that.
hookdeck listen 3000
// npm install shopify-api-node
const ShopifyApi = require("shopify-api-node");
const shopify = new ShopifyApi({
shopName: "SHOPIFY_STORE_NAME",
apiKey: "SHOPIFY_API_KEY",
password: "SHOPIFY_PASSWORD",
apiVersion: "2020-01",
});
shopify.webhook
.create({
topic: ["order/created", "order/updated", "order/deleted"],
address: "HOOKDECK_HTTPS_HOSTNAME" + "/webhooks/order",
})
.then(console.log)
.catch((e) => {
console.error("Shopify - Registration failed for:", topic);
});
Verify the signature
Because webhook endpoints are unauthenticated, we need to verify the request Signature by using the header X-Shopify-Hmac-Sha256
to verify Shopify is the original sender. Your secret will be found in your app or private app configuration.
const crypto = require("crypto");
// Secret provided by Shopify when creating the webhook
const secret = "YOUR_SECRET";
// Middleware to verify the signature
const verifyShopifyWebhookSignature = (req, res, next) => {
// Retrieve hmac signature from headers
const hmacHeader = req.get("X-Shopify-Hmac-Sha256");
// Calculate hash from body and secret value using sha256
const hash = crypto
.createHmac("sha256", secret)
.update(Buffer.from(req.body))
.digest("base64");
// Fail with 401, Unauthorize
if (hash !== hmacHeader) {
res.sendStatus(401);
} else {
// Proceed
next();
}
};
// Register the middleware for the endpoint
app.post("/webhooks/order", verifyShopifyWebhookSignature, (req, res) => {
console.log(req.body);
res.send("Server receive a order!");
});
Implementing the synchronize method
That's all well and good but we aren't actually doing anything yet other then logging the data we received. Let's send synchronize to Elasticsearch.
Ensuring our synchronization method is idempotent
Shopify may (and will) send the same webhook more than once as they operate on an at-least-once strategy. While deleting and updating an order is by nature idempotent (the action can be repeated multiple times with the same results), creating an order is not. To make sure we don't accidentally end up duplicating orders, we will use the Shopify order id as our idempotency key by setting the elastic search entry id to be our Shopify order id.
// npm install @elastic/elasticsearch
const elasticsearch = require("@elastic/elasticsearch");
const client = new elasticsearch.Client({
node: "ELASTICSEARCH_SEARCH_URL",
auth: {
username: "ELASTICSEARCH_USER",
password: "ELASTICSEARCH_PASSWORD",
},
});
const index = "ELASTICSEARCH_INDEX";
const syncToElastic = (topic, order) => {
if (topic === "order/deleted") {
return client.delete({ index, id }).catch((err) => {
if (err.meta && err.meta.statusCode === 404) {
// Order was not found, and therefore already deleted.
// That's to be expected since you can receive the same webhook multiple times
return null;
}
throw err;
});
}
// Set order id as elastic entry id to make the request idempotent.
// Subsequent create with the same id will act as an update
return client.index({ index, body: order, id: order.id });
};
app.post("/webhooks/order", verifyShopifyWebhookSignature, (req, res) => {
syncToElastic(req.headers["X-Shopify-Topic"], req.data)
.then(() => {
res.send("Server receive a order!");
})
.catch((e) => {
console.error(e);
res.sendStatus(500);
});
});
Before going to production
Now that we have a working integration, there are a few things we must talk about. Going to production has its challenges specifically with webhooks. Missing one or failing to properly synchronize has consequences. We published a whole guide to help you with this, you can find it right here.
Conclusion
There you have it! Once deployed, you will now be synchronizing all your order data to your elastic search index. The same logic can be applied to index your own databases like Postgres or MongoDB and perform any custom logic that applies to your company. Rachel has much more use cases for this webhook such as setting custom computed tracking value in our CRM and creating special gift cards when a specific product is purchased. It's up to you to be creative and leverage Shopify webhooks to simplify your operations or create new opportunities.