DEV Community

seller-mind
seller-mind

Posted on

What I Learned Building a Shopify Post-Purchase Widget

When I first started helping a friend manage their Shopify store, I assumed the hardest part was driving traffic and getting that first sale. I was completely wrong. The real headache didn't start at checkout; it started three days later.

My inbox was suddenly flooded with variations of the same question: "Where is my order?"

At first, I replied to each one manually. But as orders scaled, this became unsustainable. I was spending hours playing delivery detective, querying carrier APIs, and copy-pasting tracking numbers. The post-purchase experience had become a black hole, and my friend's customers were falling into it.

So, I decided to build something.

The Problem with the Post-Purchase Void

In e-commerce, the "WISMO" (Where Is My Order) phenomenon is a massive drain on resources. It’s not just an annoyance; it’s a direct hit to your bottom line and customer trust. When a customer clicks "buy," they enter a state of high anticipation. If you don't provide a transparent, branded way for them to track that anticipation, they will reach out to your support team.

The cost of ignoring the post-purchase experience is twofold. First, there's the literal cost of support hours spent answering repetitive questions. Second, and more importantly, there's the erosion of trust. A clunky, generic, or missing tracking experience makes a store feel unprofessional. If customers feel anxious about their delivery, they are far less likely to become repeat buyers. You need to turn that anxiety into engagement.

Technical Approach and Architecture

My goal was to build a tracking widget that was easy to install, matched the store's branding, and pulled real-time data from major carriers.

The biggest architectural decision was how to inject the widget into the store. In the early days of Shopify, you had to manually edit theme Liquid files, which was a nightmare for merchants and developers alike. With Shopify's OS 2.0, App Embed blocks changed the game. They allow apps to inject scripts and styles without requiring manual theme code edits.

I chose to build a lightweight JavaScript widget that merchants could enable via an App Embed block. The challenge was passing the correct order context from Shopify's Liquid templates to my client-side JavaScript securely and efficiently.

Here is what the App Embed block implementation looks like:

{% 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 what's happening here. We use a standard div container with data attributes populated by Shopify's Liquid variables (order.id, order.name, shop.name). This avoids making extra API calls on the client side to figure out which order we are looking at.

Then, we use an IIFE (Immediately Invoked Function Expression) to dynamically inject the widget script. This keeps the initial page load fast. Once the script loads, we initialize the Parcelglance object, passing the dataset values and the container ID. The widget then takes over, fetching the real-time tracking status and rendering it inside that div.

On the backend, the architecture relies heavily on Shopify webhooks. Instead of polling carrier APIs constantly, we listen to the orders/fulfilled webhook. When an order is marked as fulfilled, Shopify sends a payload to our server. We verify the HMAC signature to ensure the request is actually from Shopify, parse the tracking details, and store them in our database. This event-driven approach keeps infrastructure costs low and ensures tracking data is updated the moment the carrier scans the package.

One of the trickiest parts of the backend was normalizing carrier data. Every carrier has a different API structure and status nomenclature. I had to build a normalization layer that maps these disparate statuses into a unified set of states. This ensures the frontend widget always displays a consistent, predictable UI regardless of who is delivering the package.

Practical Results and Lessons Learned

Looking back at the development process, a few things stood out.

What worked exceptionally well was leaning into the App Embed architecture. Merchants loved that they could toggle the tracking page on and off without touching a single line of theme code. It reduced installation friction to almost zero.

What surprised me was how much merchants cared about the visual design. I initially thought that as long as the tracking data was accurate, the UI didn't matter much. I was wrong. If the widget looked like it belonged to a third-party SaaS and clashed with their store's aesthetic, they didn't like it. I had to pivot and spend significant time building a robust customization engine so the widget could inherit the store's fonts, colors, and button styles.

Another lesson learned was the importance of error handling on the frontend. Early in testing, if a carrier API timed out, the widget would just show a blank space. I had to implement graceful degradation, adding loading skeletons and clear, friendly error messages if tracking data was temporarily unavailable. It’s a small detail, but it prevents customers from panicking and emailing support when the widget is just waiting on a slow API response.

If I were to do it again, I would change my initial approach to the backend. Early on, I tried to build a massive, all-in-one logistics dashboard. It was over-engineered and took months to build. I eventually stripped it back to focus purely on the customer-facing widget and the core webhook ingestion. Keeping the scope tight was the best decision I made.

Wrapping Up

Building this tool taught me that the post-purchase experience is just as critical as the checkout flow. If you are running a Shopify store and want to give your customers a branded, real-time tracking experience without the development headache, I've packaged the core widget logic into a free tool.

You can check out the free Shipment Tracking Widget at parcelglance.com. It's designed to be a simple, effective way to keep your customers informed and reduce those support tickets.


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

Top comments (0)