I still remember the exact moment I realized how much time I was wasting on post-purchase support. It was a Tuesday evening, and I was manually copying tracking numbers from my fulfillment dashboard, pasting them into email replies, and trying to explain to a customer why their package was sitting at a regional sorting facility.
This was the dreaded "WISMO" (Where Is My Order?) ticket.
At the time, my post-purchase experience was practically non-existent. Once a customer checked out, they got a generic Shopify confirmation email, and then... silence. When they inevitably emailed to ask about their package, I would send them to the carrier's website. But those carrier pages were ugly, riddled with ads, and completely disconnected from my store's branding. It felt like a bait-and-switch. The customer had just given me their money, and I was sending them to a spammy third-party site to check on their purchase.
I was spending hours every week just answering the same tracking questions, and my customers were getting anxious. So, I decided to build something to automate this and give both of us a better experience.
The Problem with Post-Purchase
When you are building an e-commerce store, it is easy to obsess over the checkout flow. You A/B test your product pages, you optimize your cart drawer, and you smooth out the payment gateway. But the moment the payment succeeds, the relationship often hits a wall.
Ignoring the post-purchase experience has a real cost. First, it drives up support overhead. WISMO tickets are usually the highest volume of support requests for any physical product store. Second, it breaks trust. If your store looks premium, but your tracking page looks like a 2005 Geocities site, the customer's perception of your brand drops.
I needed a way to keep customers engaged on a branded experience with real-time updates, without me having to manually update them every time a package moved.
The Technical Approach
Building a Shopify app comes with its own set of architectural quirks. You cannot just inject custom HTML into a theme without risking breaking the merchant's layout.
My first major decision was to use Shopify's Theme App Extensions (specifically App Embed blocks). This allows the tracking widget to be injected into the order status page dynamically, without the merchant needing to edit their theme.liquid file. It keeps the installation clean and upgrade-proof.
Here is the core frontend snippet that renders the widget on the order status page. It passes the necessary Liquid variables into the DOM, which the client-side script then picks up:
{% 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>
To keep the theme load times fast, I opted for a lightweight client-side JavaScript widget rather than server-side rendering the tracking HTML. The script reads the data attributes, fetches the latest tracking status from my backend, and renders the UI. I also made sure the widget inherits the store's CSS variables for fonts and colors, so it feels like a native part of the theme.
But the frontend is only half the battle. You need to know when an order is actually fulfilled to start tracking it. This is where Shopify webhooks come in.
When a merchant fulfills an order in Shopify, we need to catch that event, verify it, and store the tracking data so the frontend widget can display it. Security is critical here; you never want to accept a webhook without verifying the HMAC signature.
Here is the backend handler I wrote to securely ingest fulfillment data:
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 Lessons Learned
Looking back, the development process taught me a lot about building for the Shopify ecosystem.
What I got wrong:
My initial prototype tried to scrape carrier websites directly to get tracking updates. This was a massive mistake. Carrier sites change their HTML structures constantly, and they aggressively rate-limit scrapers. I spent weeks fixing broken parsers. I eventually swallowed my pride and integrated with proper carrier APIs and unified tracking services. It costs a bit more, but the reliability is worth every penny.
What worked well:
Leaning heavily into Shopify App Embed blocks was the best architectural decision I made. Merchants hate editing theme code. By using App Embeds, they can just toggle a switch in their theme settings to enable the tracking widget. It made onboarding frictionless.
What surprised me:
I was surprised by how much customers actually care about the branding of the tracking page. When you provide a clean, branded post-purchase experience, it feels like a premium feature. It turns a moment of anxiety (waiting for a package) into a moment of brand reinforcement.
Wrapping Up
Building this tool solved my immediate problem of drowning in WISMO tickets, but it also highlighted how much the post-purchase experience matters in e-commerce.
I ended up packaging this solution into a free tool called Parcelglance. If you are running a Shopify store and want to add a branded, real-time shipment tracking widget to your order status pages without touching theme code, you can check it out at parcelglance.com.
It is completely free to use, and I built it specifically to solve the exact pain points I faced when I was manually replying to tracking emails at 10 PM on a Tuesday.
Full disclosure: I'm the developer of Parcelglance. I built this because I needed it myself.
Top comments (0)