Exchanging AuctionWorx Data with ShipStation

ShipStation pulls invoice data from a specially configured web page (endpoint) on your AuctionWorx site. After the order ships, ShipStation can post the shipment information back to your AuctionWorx site. The code sample is available as a free download.

This topic shows how to:

Exporting The Invoice (Order) Data as XML Markup

A ShipStation request for data includes several query string parameters. These include the intended action (export or shipnotify), the start date and end date for the query, and the page number (used for high volume sites). Here's a sample query string:

SS-UserName=SSAWEdeploy123
&SS-Password=333Rrab1904X
&action=export
&start_date=12%2f05%2f2016+08%3a16
&end_date=12%2f06%2f2016+08%3a34
&page=1

The DoOrderExport() handles the "export" action. The method parses the values, builds an AuctionWorx query, fetches the invoices, and builds the XML string.

Note: There are many object-oriented ways to generate XML in ASP.NET MVC using classes such as XmlDocument, XDocument and Linq-to-XML. For simplicity, this example uses the .NET StringBuilder class and barebones string concatenation.

Lines 3 and 4 capture the start date and end date from the ShipStation query string.

Line 15 instantiates a UserInput object that assembles the core elements (Lines 18-24) of an AuctionWorx query.

The variable retVal on line 25 will hold the returned Page<> of SalesInvoicesReportResult objects.

The SalesInvoicesReport() method (Line 30-32) gets a list of SalesInvoicesReportResult objects that match the query parameters.

On Line 42 we use the variable sb (a StringBuilder object) to start building the XML output - including the XML declaration on line 43.

Line 45 starts the main foreach loop that examines each item in the List<> of SalesInvoicesReportResult objects. Because the SalesInvoicesReportResult doesn't contain all the sales data required, Line 47 passes the InvoiceID value to the GetInvoiceByID() method to get a complete Invoice object.

For each invoice, the routine concatenates the opening XML tag, the invoice values (ID, status, etc.), and the closing tag. Text values are wrapped inside the XML schema's built-in CDATA datatype.

On Lines 125 to 133, notice the special check for an empty ShippingCountry value to be inserted into the ShipStation <ShipTo><Country> element. The ShipStation XML schema for order information declares the Country element as a field of exactly two characters.

  <xs:simpleType name="StringExactly2">
    <xs:restriction base="xs:string">
      <xs:minLength value="2"/>
      <xs:maxLength value="2"/>
    </xs:restriction>
  </xs:simpleType>

Note: An invoice object may not contain shipping information. To ensure that ShipStation gets valid <ShipTo> address values, this routine has been updated to provide the Billing address instead of the Shipping address.

Line 137 gets the line items (individual listing items) in this invoice. The foreach loop examines each line item and creates the <Item> markup.

Finally, the routine emits the complete XML content (Line 172) and sets the HTTP status code to 200.

The use of the AuctionWorx LogManager throughout is optional, but entries in the event log are helpful for analysis and debugging.

protected void DoOrderExport()
{
string start_date = Request["start_date"];
string end_date = Request["end_date"];
string page = Request["page"];

// Convert ShipStation's page to zero-based for AuctionWorx
int intPage = 0;
int.TryParse(page, out intPage);
if (intPage > 1)
    {
    intPage--;
    }

UserInput input = new UserInput(SystemActors.SystemUserName,
        SystemActors.SystemUserName, "en", "en");

input.Items.Add("dateEnd", end_date);
input.Items.Add("dateStart", start_date);
input.Items.Add("page", "0");
input.Items.Add("isPaid", "0");
input.Items.Add("sort", "DateTime");
input.Items.Add("descending", "True");
input.Items.Add("pageSize", "0");
Page<SalesInvoicesReportResult> retVal = null;
try
    {
    int currencyCount = 0;
    decimal totalAmount = 0;
    retVal = AccountingClient.SalesInvoicesReport
        (SystemActors.SystemUserName, input, ref currencyCount,
        ref totalAmount);
    }
catch (Exception e)
    {
    LogManager.WriteLog(e.Message, "ShipStation Error", "ShipStation");
    SendResponse(500);
    }
int ordercount = 0;
ordercount = retVal.List.Count;
string dateformat = "MM/dd/yyyy HH:mm tt";
StringBuilder sb = new StringBuilder();
sb.AppendLine(@"<?xml version=""1.0"" encoding=""utf-8""?>");
sb.AppendLine("<Orders>");
foreach (var ret in retVal.List)
    {
    var invoice = AccountingClient.GetInvoiceByID(SystemActors
                            .SystemUserName, ret.InvoiceID);
    sb.AppendLine("<Order>");
    sb.AppendLine(" <OrderID><![CDATA[" + invoice.ID.ToString() + "]]></OrderID>");
    sb.AppendLine("    <OrderNumber><![CDATA[" + invoice.ID.ToString()
                                    + " ]]></OrderNumber>");
    sb.AppendLine("    <OrderDate>" + invoice.CreatedDTTM.ToString(dateformat)
                                    + "</OrderDate>");
    sb.AppendLine("    <OrderStatus><![CDATA[" + invoice.Status
                                    + "]]></OrderStatus>");
    if (invoice.UpdatedDTTM != null)
        {
        sb.AppendLine("    <LastModified>" + invoice.UpdatedDTTM.ToString(dateformat)
                                            + "</LastModified>");
        }
    else
        {
        sb.AppendLine("    <LastModified>" + DateTime.Now.ToString(dateformat)
                                            + "</LastModified>");
        }
    sb.AppendLine("    <ShippingMethod></ShippingMethod>");

    var paymentMethod = (from p in invoice.PaymentHistory
                            orderby p.Timestamp descending
                            select p.ProviderIdentifier).FirstOrDefault();

    sb.AppendLine("    <PaymentMethod><![CDATA[" + paymentMethod
                                                    + "]]></PaymentMethod>");
    sb.AppendLine("    <OrderTotal>" + invoice.Total + "</OrderTotal>");
    sb.AppendLine("    <TaxAmount>" + invoice.SalesTax + "</TaxAmount>");
    sb.AppendLine("    <ShippingAmount>" + invoice.ShippingAmount
                                            + "</ShippingAmount>");
    sb.AppendLine("    <CustomerNotes><![CDATA[" + invoice.Comments
                                                    + "]]></CustomerNotes>");
    sb.AppendLine("    <InternalNotes><![CDATA[" + invoice.Comments
                                                    + "]]></InternalNotes>");
    sb.AppendLine("    <Gift>false</Gift>");
    sb.AppendLine("    <GiftMessage></GiftMessage>");
    sb.AppendLine("    <CustomField1><![CDATA[AuctionWorx Status: " +
                            invoice.Status + "]]></CustomField1>");
    sb.AppendLine("    <CustomField2><![CDATA[Status was " +
                            invoice.Status + " when record updated at: " +
                            DateTime.Now.ToString(dateformat)
                                            + "]]></CustomField2>");
    if (invoice.ShippingOption != null)
        {
        sb.AppendLine("    <CustomField3><![CDATA[Shipping selected: " +
                    invoice.ShippingOption.Method.Name + "]]></CustomField3>");
        }
    sb.AppendLine("    <Customer>");
    sb.AppendLine("      <CustomerCode><![CDATA[" + invoice.Payer.UserName
                                                    + "]]></CustomerCode>");
    sb.AppendLine("      <BillTo>");
    sb.AppendLine("        <Name><![CDATA[" + invoice.BillingFirstName + " " +
                                    invoice.BillingLastName + "]]></Name>");
    sb.AppendLine("        <Company><![CDATA[]]></Company>");

    var buyer = UserClient.GetUserByUserName(SystemActors.SystemUserName,
                                            invoice.Payer.UserName);
    sb.AppendLine("        <Email><![CDATA[" + invoice.Payer.Email
                                                + "]]></Email>");
    sb.AppendLine("      </BillTo>");
    sb.AppendLine("      <ShipTo>");
    sb.AppendLine("        <Name><![CDATA[" + invoice.BillingFirstName + " " +
                                        invoice.BillingLastName + "]]></Name>");
    sb.AppendLine("       <Company><![CDATA[]]></Company>");
    sb.AppendLine("       <Address1><![CDATA[" + invoice.BillingStreet1
                                                + "]]></Address1>");
    sb.AppendLine("        <Address2><![CDATA[" + invoice.BillingStreet2
                                                + "]]></Address2>");
    sb.AppendLine("        <City><![CDATA[" + invoice.BillingCity
                                            + "]]></City>");
    sb.AppendLine("        <State><![CDATA[" + invoice.BillingStateRegion
                                                + "]]></State>");
    sb.AppendLine("        <PostalCode><![CDATA[" + invoice.BillingZipPostal
                                                    + "]]></PostalCode>");

    // Shipping country is required and must be exactly 2 characters
    if (string.IsNullOrWhiteSpace(invoice.BillingCountry))
        {
        sb.AppendLine("        <Country><![CDATA[NA]]></Country>");
        }
    else
        {
        sb.AppendLine("        <Country><![CDATA[" + invoice.BillingCountry
                                        + "]]></Country>");
        }
    sb.AppendLine("      </ShipTo>");
    sb.AppendLine("    </Customer>");
    sb.AppendLine("    <Items>");
    var pageOfLineItems = AccountingClient.GetLineItemsByInvoice
            (SystemActors.SystemUserName, invoice.ID, 0, 0,
            RainWorx.FrameWorx.Strings.Fields.DateStamp, false);

    foreach (var lineitem in pageOfLineItems.List)
        {
        if (lineitem.Type != LineItemTypes.Listing)
            {
            continue;
            }
        sb.AppendLine("      <Item>");
        sb.AppendLine("        <SKU><![CDATA[" + lineitem.ListingID + "]]></SKU>");
        sb.AppendLine("        <Name><![CDATA[" + lineitem.Description + "]]></Name>");
        sb.AppendLine("        <ImageUrl><![CDATA[" + lineitem.Listing.
                                        PrimaryImageURI + "]]></ImageUrl>");
        sb.AppendLine("        <Weight>0.00</Weight>");
        sb.AppendLine("       <WeightUnits></WeightUnits>");
        sb.AppendLine("       <Quantity>" + lineitem.Quantity + "</Quantity>");
        sb.AppendLine("        <UnitPrice>" + lineitem.TotalAmount + "</UnitPrice>");
        sb.AppendLine("        <Location><![CDATA[]]></Location>");
        sb.AppendLine("        <Options>");
        sb.AppendLine("          <Option>");
        sb.AppendLine("            <Name><![CDATA[]]></Name>");
        sb.AppendLine("            <Value><![CDATA[]]></Value>");
        sb.AppendLine("            <Weight>0.00</Weight>");
        sb.AppendLine("          </Option>");
        sb.AppendLine("        </Options>");
        sb.AppendLine("      </Item>");
        }
    sb.AppendLine("    </Items>");
    sb.AppendLine("  </Order>");
    }
sb.AppendLine("</Orders>");
Response.Clear();
Response.StatusCode = 200;
Response.Write(sb.ToString());
LogManager.WriteLog("ShipStation Pickup order count: " +
                    ordercount.ToString() + " at " +
                    DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"),
                    "ShipStation DoOrderExport", "ShipStation");
}

Receiving Status Updates from ShipStation

Once an order is marked as shipped within ShipStation, ShipStation can post the order's updated status back to your AuctionWorx site. The DoShipNotify() processes the update.

ShipStation sends AuctionWorx the unique ID of the invoice as the value of the order_number parameter. Line 4 parses the value into the invoicenumber variable.

Lines 5-7 get the Invoice object using the GetInvoiceByID() method and confirm that the referenced invoice is not null.

Lines 15-16 use the SetInvoiceShipped() method to update the invoice status.

The remainder of the routine logs the interactions and returns the expected HTTP Response codes.

protected void DoShipNotify()
{
int invoicenumber = 0;
int.TryParse(Request["order_number"], out invoicenumber);
var invoice = AccountingClient.GetInvoiceByID(SystemActors
                    .SystemUserName, invoicenumber);
if (invoice != null)
    {
    try
        {
        LogManager.WriteLog("ShipStation Setting Invoice " +
            invoicenumber.ToString() + " as shipped " +
            DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"),
            "ShipStation DoShipNotify", "ShipStation");
        AccountingClient.SetInvoiceShipped(SystemActors
                        .SystemUserName, invoicenumber, true);
        }
    catch (Exception exc)
        {
        LogManager.WriteLog("SetInvoiceShipped error " +
            invoicenumber.ToString() + exc.Message,
            "ShipStation DoShipNotify", "ShipStation");
        SendResponse(500);
        return;
        }
    }
else
    {
    LogManager.WriteLog("ShipStation did not find Invoice " +
        invoicenumber.ToString(),
        "ShipStation DoShipNotify", "ShipStation");
    }
Response.Clear();
Response.StatusCode = 200;
}

 

Sending the Response Codes

SendResponse() is a simple function that takes an integer as an input and sends it as an HTTP Response code.

protected void SendResponse(int responseCode)
{
Response.Clear();
Response.StatusCode = responseCode;
Response.End();
}

Copyright © 2002-2022. RainWorx Software. All rights reserved.