DEV Community

seller-mind
seller-mind

Posted on

How I Compared Shopify Tracking Approaches to Stop WISMO

When I first started helping a friend manage their Shopify store, I thought the hardest part would be driving traffic. I was wrong. The real nightmare began after the checkout.

Our inbox was constantly flooded with "Where is my order?" (WISMO) emails. Customers were anxious, and we were spending hours manually checking carrier websites just to reply. The post-purchase experience had become a total black hole.

I looked at the existing solutions, but they either felt too heavy, too expensive, or too locked down. So I decided to build something myself and objectively compare the different technical approaches to solving this problem.

The Hidden Cost of the Post-Purchase Black Hole

WISMO isn't just an annoying support metric; it's a silent margin killer. Every time a customer emails support about their package, it costs time. If you are handling dozens of these a week, that's essentially a part-time job dedicated to copy-pasting tracking numbers.

Beyond the direct cost of support, there is the hidden cost of customer retention. The post-purchase phase is critical. If a customer feels ignored after handing over their credit card, they aren't coming back. Ignoring the post-purchase experience means you are actively churning customers who already bought from you.

Comparing Technical Approaches

When evaluating how to handle post-purchase tracking, I analyzed three main architectural approaches.

1. Heavy Third-Party SaaS Apps
You install a prominent app, and it injects a fully managed tracking page.
Pros: Zero backend maintenance.
Cons: These apps often inject massive JavaScript bundles that hurt Core Web Vitals. They also restrict your branding unless you are on their highest tier.

2. Pure Custom Backend Build
You write your own integrations directly with USPS, UPS, FedEx, and DHL APIs.
Pros: Ultimate control over data and UI.
Cons: Carrier APIs are notoriously inconsistent. Maintaining them, handling rate limits, and parsing different tracking event formats is a full-time job.

3. Lightweight Embed Widgets (The Hybrid Approach)
You use Shopify's App Embed blocks to inject a lightweight frontend widget that communicates with a centralized, simplified tracking backend.
Pros: Keeps the main theme clean, separates frontend presentation from backend logistics, and allows for deep branding.
Cons: Requires a bit more initial setup and understanding of Shopify's theme architecture.

I chose the third approach. Here is how the frontend integration looks using Shopify's Liquid templating and an async script loader.

{% 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>
Enter fullscreen mode Exit fullscreen mode

Let's break down why this architecture works. First, we use Liquid variables to pass the order context securely to the DOM. The widget container has a min-height to prevent layout shift (CLS) when the script eventually loads.

Second, the script is injected asynchronously. This is crucial for e-commerce. We never want to block the main thread on the order status page. By creating the script element dynamically and setting async = true, the browser can continue parsing the rest of the page while the tracking widget loads in the background.

On the backend side, the comparison also dictated our data flow. Instead of polling carrier APIs on every page load, we use webhooks.

const crypto = require('crypto');

function verifyShopifyWebhook(rawBody, hmacHeader, secret) {
  const hash = crypto
    .createHmac('sha256', secret)
    .update(rawBody, 'utf8')
    .digest('base64');
  return hash === hmacHeader;
}

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'));
    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');
  }
);
Enter fullscreen mode Exit fullscreen mode

By listening to the orders/fulfilled webhook, we capture the tracking data exactly once when the label is created. We verify the HMAC signature to ensure the request actually came from Shopify. This means the frontend widget doesn't need to make complex API calls to carriers; it just asks our lightweight backend for the latest status we've already cached.

Practical Results and Trade-offs

What worked exceptionally well was keeping the widget footprint small. By decoupling the frontend widget from the heavy backend carrier logic, the order status page load times remained completely unaffected.

What surprised me was the psychological impact of the branded experience. Even though the underlying tracking data was identical to what the carriers provided, presenting it in a widget that matched the store's typography and colors drastically reduced customer anxiety. It made the store feel present, even when the package was in a truck three states away.

If I could change one thing, I would abstract the CSS theming variables earlier in the development cycle. Initially, I hardcoded some styles, which meant tweaking the widget for different Shopify themes took longer than it should have. Building a more robust, dynamic theming system from day one would have saved a lot of refactoring.

Moving Forward

If you are dealing with WISMO tickets and want to test a lightweight, developer-friendly approach, I have packaged this solution into a free tool. You can check out the Shipment Tracking Widget at parcelglance.com. It is completely free to use, easy to install via Shopify App Embed blocks, and designed to keep your post-purchase experience branded and fast.


Full disclosure: I'm the developer of Parcelglance. I built this because I needed it myself.

Top comments (0)