When I first started helping a friend manage their Shopify store, I assumed the hardest part was driving initial traffic and getting those first sales. I was wrong. The real headache started right after checkout. My inbox was quickly flooded with "Where is my order?" (WISMO) emails. We were spending hours manually checking tracking numbers and replying to anxious customers.
We looked at existing tracking apps on the Shopify App Store, but they were either prohibitively expensive for our thin margins, bloated with features we didn't need, or forced customers onto ugly, generic carrier pages that felt disconnected from our brand. So I decided to build something.
The Post-Purchase Problem
The post-purchase experience is a critical part of the customer journey, yet it is often treated as an afterthought. When a customer completes a purchase, their excitement peaks, then inevitably drops while they wait for the physical product. If you redirect them to a raw FedEx or USPS tracking page, the brand experience is broken. The carrier's page is full of ads, confusing navigation, and zero context about your store.
Worse, if there is a shipping delay, the customer blames your store, not the carrier. Ignoring the post-purchase experience means high WISMO ticket volume, which drains your support resources. It can also lead to lower customer lifetime value, as a frustrating delivery experience makes buyers hesitant to return.
Comparing Technical Approaches
To solve this, I needed to evaluate the main architectural approaches for implementing tracking on a Shopify store. Here is how they compare:
1. Carrier Redirects
The simplest approach is to just email the customer a direct link to the carrier's tracking page.
- Pros: Zero development time, no app fees.
- Cons: Terrible user experience, no brand control, high support overhead.
2. Heavy Third-Party SaaS Apps
Installing a robust tracking app from the Shopify App Store.
- Pros: Feature-rich, includes marketing modules, SMS notifications.
- Cons: Monthly fees that scale with order volume. These apps often inject heavy JavaScript bundles into your storefront, which can negatively impact your Core Web Vitals and page load speeds.
3. Custom Shopify App Embeds
Building a lightweight, custom widget using Shopify's native App Embed blocks.
- Pros: Keeps the experience fully on-brand, lightweight, no recurring SaaS fees, native integration with the theme editor.
- Cons: Requires custom development and maintenance.
I chose the third approach. The key was using Shopify's App Embed blocks to inject the widget cleanly without modifying the core theme Liquid files. This keeps the theme upgrade-safe and allows merchants to toggle the widget on or off directly from the theme editor.
Here is the frontend implementation for the App Embed block. It uses data- attributes to pass order information securely, and utilizes an async immediately-invoked function expression (IIFE) to load the tracking script without blocking the main thread:
{% 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>
Backend Data Sync: Polling vs. Webhooks
For the backend, I had to decide how to keep the tracking data updated. The two main options are client-side polling or server-side webhooks.
Polling carrier APIs directly from the frontend is not a practical approach due to CORS restrictions and strict API rate limits. Instead, relying on Shopify webhooks is the standard for keeping data in sync efficiently. When an order is fulfilled, Shopify sends a webhook, allowing us to capture the tracking number and carrier, and then query the carrier API securely from our backend.
Here is a simplified look at how the backend handles the fulfillment webhook, including the crucial HMAC verification step to ensure the request actually came from Shopify:
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');
}
);
Practical Results and Trade-offs
The App Embed approach worked well. It kept the theme files clean and didn't bloat the storefront with unnecessary dependencies. The webhook-driven backend meant tracking updates were processed reliably without hammering carrier APIs on every page load.
What surprised me was the qualitative impact on the customer experience. For example, a store processing a few hundred orders a month saw a noticeable drop in WISMO emails simply because customers had a centralized, branded place to check their status, rather than digging through their inbox for a shipping confirmation email.
If I were to start over, I would spend more time on the initial carrier API mapping during the planning phase. Every carrier formats their tracking events slightly differently, and normalizing those status updates into a unified timeline took more effort than I initially anticipated.
Moving Forward
If you are dealing with WISMO tickets and want a lightweight, branded solution that doesn't bog down your storefront, I have packaged this architecture into a free tool called Parcelglance. You can check out the Shipment Tracking Widget and install it via App Embeds at parcelglance.com.
Full disclosure: I'm the developer of Parcelglance. I built this because I needed it myself.
Top comments (0)