Recently a client requested a challenging project. They wanted to fully automate the processing of tracking numbers pulled from the QuantumView API data feeds.
The majority of the client’s shipments are drop-shipped direct from the manufacturer or distributor who charge my client’s UPS account number. This is a technique known as 3rd-party billing. This works well because my client can leverage all of their shipping volume with UPS instead of just the in-stock shipments. And they know what to expect for shipping charges since it’s their account being billed.
However there is a downside to this shipping process. Normally an integration like ShipStation or WorldShip would post tracking numbers back to AbleCommerce. However with drop-ship orders, these tracking numbers must all be hand-entered on every shipment. A major hassle when your order volume is significant.
The client’s workaround was to create two feeds in their QuantumView account. One feed for in-stock shipments and the other for all 3rd-party (dropship) shipments. Each day the client would download the feed, load it into Excel for readability purposes and then copy/paste tracking number data. A huge pain.
The design for this integration needed to meet the following requirements:
- Download the feed files on a regular basis from the UPS QuantumView API
- Locate the Origin record types and identify the shipment Id for each one
- Post the associated tracking number to the shipment Id
- Set aside any Origin records that could not be automatically processed
- Provide a UI for manually processing Origin records which could not be automatically processed
- Log a summary of each interaction with the QuantumView API for debugging purposes
The first step to the project was to dig into the UPS QuantumView API documentation. The docs clearly show the API supports both a JSON and XML payload. However I was never able to get the JSON payload to work properly. The problem came down to arrays/collections. When a specific element in the API specifications supported multiple items, I would naturally define it as an array i.e. element. But when the element had only one entry, UPS would not send the element as an array with a single entry. UPS would simply send a standard element definition. This broke my class structure in C# – I couldn’t get JSON.Net to handle a element that could arrive as a single piece or as an array of pieces.
After several frustrating hours I was forced to give up and switch to the XML format of the API. The documentation from UPS for the XML API was vastly more detailed. Clearly it seems they added JSON as an afterthought.
I wasn’t looking forward to the prospect of hand-building a class that matched the XML structure of the API response. Fortunately, I found an awesome trick that involved using a command-line tool from Visual Studio. Just hand the XML file to the tool and *poof* it generates the necessary class and child classes to consume that XML layout. Later on I found that feature actually exists right in Visual Studio – just copy the XML to your clipboard and then use Edit/Paste Special/Paste XML as Classes. SWEET!
Once I had the XML importing into strongly typed classes, progress moved much faster. I was able to leverage HangFire to automatically poll the UPS API on schedule controlled through an admin settings page. Now that automation is handled, I moved on to processing the downloads.
Testing the API proved a bit confusing because of one REALLY important detail: The default API endpoints only deliver what transactions have been received by UPS since the last time you asked UPS for them. Helpful in a production scenario since you never have to worry if you’ve already received a specific Origin record. But it makes testing your code a major source of stress. Your first request comes back with data, and then suddenly you get nothing. Over and over again, what you thought was working now appears to be broken. The way around this is to add a date-range criteria (up to 7 days in the past). When a date range is included in the request, QuantumView will respond with all transactions regardless if they’ve already been delivered or not.
Now that I’m pulling down data, the processing became simple. The client was already having each manufacturer put the AbleCommerce Shipment Id in one of the UPS reference fields. UPS manifests support up to five ‘reference’ fields usable by the shipper for any purpose. Having the Able Shipment Id in each Origin record makes it a snap to determine which Able shipment gets the tracking number.