When I first launched my Shopify store, I genuinely thought the hardest part would be getting that first sale. I spent weeks tweaking product pages, optimizing images, and running ads. I was wrong. The hardest part was actually what happened after the sale.
A few days after launch, my inbox started filling up. "Where is my order?" "Did it ship yet?" "Why hasn't my tracking updated?" The WISMO (Where Is My Order) emails were relentless. I was spending hours every day manually checking carrier websites and replying to customers. It was exhausting, and it was taking time away from actually growing the business.
I looked at existing tracking apps, but many felt bloated, expensive, or forced a generic, unbranded experience on my customers. I just wanted a clean, reliable way to show tracking data directly on my store without breaking the bank. So I decided to build something.
The Problem with Post-Purchase
The post-purchase experience is often an afterthought in e-commerce, but it is critical for customer retention. When a customer buys something, they enter a state of high anticipation. If you just hand them a raw tracking number and a link to a carrier's website, you lose control of the experience.
The cost of ignoring this phase is tangible. Every WISMO email requires time to resolve. If you are a solo founder, that is your most valuable asset. If you have a support team, the ticket volume scales linearly with your order volume. Furthermore, sending customers off-site to a carrier's tracking page breaks the immersion. They leave your ecosystem, and you miss an opportunity to reinforce your brand or keep them engaged.
The goal wasn't just to stop the emails; it was to create a seamless, branded tracking experience that kept the customer engaged with the store while providing real-time updates.
Technical Approach and Architecture
Building this required a few key architectural decisions. The biggest challenge with Shopify apps is injecting UI into the storefront without requiring merchants to manually edit their Liquid theme files.
I decided to use Shopify App Embed blocks. While legacy ScriptTags work on older themes, App Embeds are the modern standard for Online Store 2.0. They allow the widget to be toggled on and off directly from the theme editor, keeping the installation frictionless and future-proof.
Here is the core frontend snippet that renders the widget on the order status page:
{% comment %}
Renders shipment tracking widget on order status pages
{% endcomment %}
<div id="parcelglance-widget"
data-order-id="{{ order.id }}"
data-order-number="{{ order.name }}"
data-store-name="{{ shop.name }}"
style="min-height: 200px;">
</div>
<script>
(function() {
const script = document.createElement('script');
script.src = 'https://cdn.parcelglance.com/widget.js';
script.async = true;
script.onload = function() {
Parcelglance.init({
orderId: document.getElementById('parcelglance-widget').dataset.orderId,
orderNumber: document.getElementById('parcelglance-widget').dataset.orderNumber,
storeName: document.getElementById('parcelglance-widget').dataset.storeName,
container: 'parcelglance-widget',
});
};
document.head.appendChild(script);
})();
</script>
This approach uses Liquid to pass the necessary order context (like order.id and order.name) directly into the DOM as data attributes. The script then loads asynchronously, preventing any render-blocking issues, and initializes the widget once the DOM element is ready.
But a frontend widget is useless without the backend data. To get real-time tracking status from major carriers, I needed a reliable way to ingest fulfillment data. Relying solely on frontend polling to carrier APIs is a bad idea due to rate limits and CORS issues. Instead, I built a backend webhook handler.
const crypto = require('crypto');
function verifyShopifyWebhook(rawBody, hmacHeader, secret) {
const hash = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('base64');
return hash === hmacHeader;
}
// Handle fulfillment update webhook
app.post('/webhooks/orders/fulfilled',
express.raw({ type: 'application/json' }),
async (req, res) => {
const verified = verifyShopifyWebhook(
req.body,
req.headers['x-shopify-hmac-sha256'],
process.env.SHOPIFY_WEBHOOK_SECRET
);
if (!verified) return res.status(401).send('Invalid');
const order = JSON.parse(req.body.toString('utf8'));
// Store fulfillment data for tracking lookup
await db.fulfillments.upsert({
orderId: order.id,
trackingNumber: order.fulfillments[0]?.tracking_number,
carrier: order.fulfillments[0]?.tracking_company,
status: 'shipped',
});
res.status(200).send('OK');
}
);
Security is paramount here. The verifyShopifyWebhook function ensures that the payload actually came from Shopify by comparing the HMAC-SHA256 hash. Once verified, we parse the fulfillment data and upsert it into our database. This means when the frontend widget requests tracking info, it queries our fast, internal database rather than hammering external carrier APIs.
Practical Results and Lessons Learned
Moving to this architecture changed everything. The App Embed approach meant that installation was just a few clicks in the Shopify admin. No code edits required for the merchant, which was a huge relief.
What surprised me most was the impact on the customer experience. By keeping the tracking page on the store's domain and allowing the widget to be customized to match the store's theme, customers felt much more secure. They weren't bounced to a confusing third-party logistics site. For example, support inquiries dropped significantly because customers could easily see their real-time status without needing to ask.
If I were to do it again, I would optimize the backend polling earlier. Initially, I was polling carrier APIs for every single order update. I quickly realized I needed to implement smarter caching and exponential backoff for stale shipments to keep server costs down and respect carrier rate limits.
Wrapping Up
I ended up packaging this entire workflow into a free tool called Parcelglance. It adds a branded, customizable tracking widget to your Shopify store, pulling real-time data from major carriers to keep your customers informed and reduce those endless WISMO tickets.
You can check out the free Shipment Tracking Widget at parcelglance.com.
Full disclosure: I'm the developer of Parcelglance. I built this because I needed it myself.
Top comments (0)