Posted by admin on 7/23/2010 7:27 AM | Comments (0)

I am using the USPS shipping apis (http://www.usps.com/webtools/shipping.htm) to create USPS priority mail labels from ecommerce orders.  I thought I’d share some things I learned.

  • First you have to sign up for web services here (https://secure.shippingapis.com/registration/).  You’ll get your userid emailed to you; you don’t need your password as far as I can tell.
  • I used the wrapper here in my code  (http://www.codeproject.com/KB/cs/USPS_Web_Tools_Wrapper.aspx).  It isn’t rocket science, and you could roll your own, but he already created all of the xml, so why reinvent the wheel?
  • If you’re using DeliveryConfirmationV3 to generate your labels (why do they call the api that can generate a label “Delivery confirmation” – but I digress), you might get an error like the following:

    <Error>
        <Number>80040b1a</Number>
        <Description>API Authorization failure. DeliveryConfirmationV3 is not a valid API name for this protocol.</Description>
        <Source>UspsCom::DoAuth</Source>
    </Error>
     
    The solution is to test against their production servers, not their test servers.  Maybe there’s a way to make it work on their test servers, but I saw some posts on the web (not sure as to their accuracy) that stated you can’t.  Anyway, to be able to use the production system, you have to call with your user id that you got when you signed up, and explain the situation.  Here’s the contact information.

    USPS Internet Customer Care Center (ICCC). The ICCC is staffed from 7:00AM to 11:00PM Eastern Time.

    Email: icustomercare@usps.com

    Telephone: 1-800-344-7779 (7:00AM to 11:00PM ET)


    • Here’s the code I borrowed/wrote to create a label and save it as a tif to a file.  Again, it’s using the library I referenced above.  The key is that you have to convert the byte array from base 64, or else it won’t be a valid tif (or PDF, if you choose that option) when you save it.

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using NUnit.Framework;
      using MAX.USPS;
      using System.IO;
      using System.Drawing;
       
      namespace TestHarness
      {
          [TestFixture]
          class USPSTests
          {
       
              [Test]
              public void CreateLabel()
              {
                  USPSManager m = new USPSManager("468WESTC3009", false);
                  Package p = new Package();
                  p.FromAddress.Contact = "John Smith";
                  p.FromAddress.Address2 = "475 L'Enfant Plaza, SW";
                  p.FromAddress.City = "Washington";
                  p.FromAddress.State = "DC";
                  p.FromAddress.Zip = "20260";
                  p.ToAddress.Contact = "Joe Customer";
                  p.ToAddress.Address1 = "STE 201";
                  p.ToAddress.Address2 = "6060 PRIMACY PKWY";
                  p.ToAddress.City = "Memphis";
                  p.ToAddress.State = "TN";
                  p.WeightInOunces = 2;
                  p.ServiceType = ServiceType.Priority;
                  p.SeparateReceiptPage = false;
                  p.LabelImageType = LabelImageType.TIF;
                  p.PackageSize = PackageSize.Regular;
                  p.PackageType = PackageType.Flat_Rate_Box;
                  p = m.GetDeliveryConfirmationLabel(p);
       
                  //important - the byte array is base 64, so you have to convert it
                  //there may be a better way than the code below, but it gets the job done
                  System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
                  var str = enc.GetString(p.ShippingLabel);
                  var bytes = Convert.FromBase64String(str);
                  File.WriteAllBytes("c:\\temp\\label.tif", bytes);
              }
          }
      }

    That’s all.  Hopefully this will help someone.

    Update after using the library above a couple more days:

    The values passed need to be XML encoded.  In addition, since they’re passed via URL, they need to be URL encoded.  To XML encode a string, there’s a little-known method called System.Security.SecurityElement.Escape in the .net framework.

    Also, the library above wasn’t retrieving the delivery confirmation number.  I updated the project to build against the 4.0 framework, and then used XML parsing to get the delivery confirmation and return it via a new property on Package (you’ll need to add it to the library above).

    The new code for USPSManager in the library above is here.

    //////////////////////////////////////////////////////////////////////////
    ///This software is provided to you as-is and with not warranties!!!
    ///Use this software at your own risk.
    ///This software is Copyright by Scott Smith 2006
    ///You are free to use this software as you see fit.
    //////////////////////////////////////////////////////////////////////////
     
     
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Net;
    using System.Xml;
    using System.Xml.Linq;
    using System.Security;
    using System.Web;
     
    namespace MAX.USPS
    {
        public class USPSManager
        {
            #region Private Members
            private const string ProductionUrl = "https://secure.shippingapis.com/ShippingAPI.dll";
            private const string TestingUrl = "https://secure.shippingapis.com/ShippingAPITest.dll";//"http://testing.shippingapis.com/ShippingAPITest.dll";
            private WebClient web;
            private string _userid;
            #endregion
     
            #region Constructors
     
            /// <summary>
            /// Creates a new USPS Manager instance
            /// </summary>
            /// <param name="USPSWebtoolUserID">The UserID required by the USPS Web Tools</param>
            public USPSManager(string USPSWebtoolUserID)
            {
                web = new WebClient();
                _userid = USPSWebtoolUserID;
                _TestMode = false;
                
            }
            /// <summary>
            /// Creates a new USPS Manager instance
            /// </summary>
            /// <param name="USPSWebtoolUserID">The UserID required by the USPS Web Tools</param>
            /// <param name="testmode">If True, then the USPS Test URL will be used.</param>
            public USPSManager(string USPSWebtoolUserID, bool testmode)
            {
                _TestMode = testmode;
                web = new WebClient();
                _userid = USPSWebtoolUserID;
            }
     
            #endregion
     
            #region Properties
            private bool _TestMode;
            /// <summary>
            /// Determines if the Calls to the USPS server is made to the Test or Production server.
            /// </summary>
            public bool TestMode
            {
                get { return _TestMode; }
                set { _TestMode = value; }
            }
     
            #endregion
     
            #region Address Methods
            /// <summary>
            /// Validate a single address
            /// </summary>
            /// <param name="address">Address object to be validated</param>
            /// <returns>Validated Address</returns>
            public Address ValidateAddress(Address address)
            {
                try
                {
                    string validateUrl = "?API=Verify&XML=<AddressValidateRequest USERID=\"{0}\"><Address ID=\"{1}\"><Address1>{2}</Address1><Address2>{3}</Address2><City>{4}</City><State>{5}</State><Zip5>{6}</Zip5><Zip4>{7}</Zip4></Address></AddressValidateRequest>";
                    string url = GetURL() + validateUrl;
                    url = String.Format(url, _userid, address.ID.ToString(), address.Address1, address.Address2, address.City, address.State, address.Zip, address.ZipPlus4);
                    string addressxml = web.DownloadString(url);
                    if (addressxml.Contains("<Error>"))
                    {
                        int idx1 = addressxml.IndexOf("<Description>") + 13;
                        int idx2 = addressxml.IndexOf("</Description>");
                        int l = addressxml.Length;
                        string errDesc = addressxml.Substring(idx1, idx2 - idx1);
                        throw new USPSManagerException(errDesc);
                    }
                    
                    return Address.FromXml(addressxml);
                }
                catch(WebException ex)
                {
                    throw new USPSManagerException(ex);
                }
            }
            /// <summary>
            /// Get the zip code by providing an Address object with a city and state
            /// </summary>
            /// <param name="city">City</param>
            /// <param name="state">State</param>
            public Address GetZipcode(string city, string state)
            {
                Address a = new Address();
                a.City = city;
                a.State = state;
                return GetZipcode(a);
            }
     
            /// <summary>
            /// Get the zip code by providing an Address object with a city and state
            /// </summary>
            /// <param name="address">Address Object</param>
            /// <returns>Address Object</returns>
            public Address GetZipcode(Address address)
            {
                try
                {
                    //The address must contain a city and state
                    if (address.City == null || address.City.Length < 1 || address.State == null || address.State.Length < 1)
                        throw new USPSManagerException("You must supply a city and state for a zipcode lookup request.");
     
                    string zipcodeurl = "?API=ZipCodeLookup&XML=<ZipCodeLookupRequest USERID=\"{0}\"><Address ID=\"{1}\"><Address1>{2}</Address1><Address2>{3}</Address2><City>{4}</City><State>{5}</State></Address></ZipCodeLookupRequest>";
                    string url = GetURL() + zipcodeurl;
                    url = String.Format(url, _userid, address.ID.ToString(), address.Address1, address.Address2, address.City, address.State, address.Zip, address.ZipPlus4);
                    string addressxml = web.DownloadString(url);
                    if (addressxml.Contains("<Error>"))
                    {
                        int idx1 = addressxml.IndexOf("<Description>") + 13;
                        int idx2 = addressxml.IndexOf("</Description>");
                        int l = addressxml.Length;
                        string errDesc = addressxml.Substring(idx1, idx2 - idx1);
                        throw new USPSManagerException(errDesc);
                    }
     
                    return Address.FromXml(addressxml);
                }
                catch (WebException ex)
                {
                    throw new USPSManagerException(ex);
                }
            }
     
            /// <summary>
            /// Get the city and state by proving the zip code.
            /// </summary>
            /// <param name="zipcode">Zipcode</param>
            public Address GetCityState(string zipcode)
            {
                Address a = new Address();
                a.Zip = zipcode;
                return GetCityState(a);
            }
     
            /// <summary>
            /// Get the city and state by proving the zip code.
            /// </summary>
            /// <param name="address">Address object</param>
            /// <returns>Address Object</returns>
            public Address GetCityState(Address address)
            {
                try
                {
                    //The address must contain a city and state
                    if (address.Zip == null || address.Zip.Length < 1)
                        throw new USPSManagerException("You must supply a zipcode for a city/state lookup request.");
                    
                    string citystateurl = "?API=CityStateLookup&XML=<CityStateLookupRequest USERID=\"{0}\"><ZipCode ID= \"{1}\"><Zip5>{2}</Zip5></ZipCode></CityStateLookupRequest>";
                    string url = GetURL() + citystateurl;
                    url = String.Format(url, _userid, address.ID.ToString(), address.Zip);
                    string addressxml = web.DownloadString(url);
                    if (addressxml.Contains("<Error>"))
                    {
                        int idx1 = addressxml.IndexOf("<Description>") + 13;
                        int idx2 = addressxml.IndexOf("</Description>");
                        int l = addressxml.Length;
                        string errDesc = addressxml.Substring(idx1, idx2 - idx1);
                        throw new USPSManagerException(errDesc);
                    }
     
                    return Address.FromXml(addressxml);
                }
                catch (WebException ex)
                {
                    throw new USPSManagerException(ex);
                }
            }
     
            #endregion
     
            #region Tracking Methods
            public TrackingInfo GetTrackingInfo(string TrackingNumber)
            {
                try
                {
                    string trackurl = "?API=TrackV2&XML=<TrackRequest USERID=\"{0}\"><TrackID ID=\"{1}\"></TrackID></TrackRequest>";
                    string url = GetURL() + trackurl;
                    url = String.Format(url, _userid, TrackingNumber);
                    string xml = web.DownloadString(url);
                    if (xml.Contains("<Error>"))
                    {
                        int idx1 = xml.IndexOf("<Description>") + 13;
                        int idx2 = xml.IndexOf("</Description>");
                        int l = xml.Length;
                        string errDesc = xml.Substring(idx1, idx2 - idx1);
                        throw new USPSManagerException(errDesc);
                    }
     
                    return TrackingInfo.FromXml(xml);
                }
                catch (WebException ex)
                {
                    throw new USPSManagerException(ex);
                }
            }
            #endregion
     
            #region Label Methods
            /// <summary>
            /// Fills a package's ShippingLabel with a Byte{} containing the Image for the label
            /// </summary>
            /// <param name="package">Package with From and To addresses provided</param>
            /// <returns>The same package with the ShippingLabel</returns>
            public Package GetDeliveryConfirmationLabel(Package package)
            {
                string labeldate = package.ShipDate.ToShortDateString();
                if (package.ShipDate.ToShortDateString() == DateTime.Now.ToShortDateString())
                    labeldate = "";
                string url = "?API=DeliveryConfirmationV3&XML=<DeliveryConfirmationV3.0Request USERID=\"{0}\"><Option>{1}</Option><ImageParameters></ImageParameters><FromName>{2}</FromName><FromFirm>{3}</FromFirm><FromAddress1>{4}</FromAddress1><FromAddress2>{5}</FromAddress2><FromCity>{6}</FromCity><FromState>{7}</FromState><FromZip5>{8}</FromZip5><FromZip4>{9}</FromZip4><ToName>{10}</ToName><ToFirm>{11}</ToFirm><ToAddress1>{12}</ToAddress1><ToAddress2>{13}</ToAddress2><ToCity>{14}</ToCity><ToState>{15}</ToState><ToZip5>{16}</ToZip5><ToZip4>{17}</ToZip4><WeightInOunces>{18}</WeightInOunces><ServiceType>{19}</ServiceType><SeparateReceiptPage>{29}</SeparateReceiptPage><POZipCode>{20}</POZipCode><ImageType>{21}</ImageType><LabelDate>{22}</LabelDate><CustomerRefNo>{23}</CustomerRefNo><AddressServiceRequested>{24}</AddressServiceRequested><SenderName>{25}</SenderName><SenderEMail>{26}</SenderEMail><RecipientName>{27}</RecipientName><RecipientEMail>{28}</RecipientEMail></DeliveryConfirmationV3.0Request>";
                url = GetURL() + url;
                //url = String.Format(url,this._userid, (int)package.LabelType, package.FromAddress.Contact, package.FromAddress.FirmName, package.FromAddress.Address1, package.FromAddress.Address2, package.FromAddress.City, package.FromAddress.State, package.FromAddress.Zip, package.FromAddress.ZipPlus4, package.ToAddress.Contact, package.ToAddress.FirmName, package.ToAddress.Address1, package.ToAddress.Address2, package.ToAddress.City, package.ToAddress.State, package.ToAddress.Zip, package.ToAddress.ZipPlus4, package.WeightInOunces.ToString(), package.ServiceType.ToString().Replace("_", " "), package.OriginZipcode, package.LabelImageType.ToString(), labeldate, package.ReferenceNumber, package.AddressServiceRequested.ToString(),  package.FromAddress.Contact, package.FromAddress.ContactEmail, package.ToAddress.Contact, package.ToAddress.ContactEmail);
                url = String.Format(url, this._userid, (int)package.LabelType
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.FromAddress.Contact))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.FromAddress.FirmName))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.FromAddress.Address1))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.FromAddress.Address2))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.FromAddress.City))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.FromAddress.State))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.FromAddress.Zip))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.FromAddress.ZipPlus4))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.ToAddress.Contact))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.ToAddress.FirmName))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.ToAddress.Address1))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.ToAddress.Address2))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.ToAddress.City))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.ToAddress.State))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.ToAddress.Zip))
                    , HttpUtility.UrlEncode(SecurityElement.Escape(package.ToAddress.ZipPlus4))
                    , package.WeightInOunces.ToString()
                    , package.ServiceType.ToString().Replace("_", " ")
                    , HttpUtility.UrlEncode(package.OriginZipcode)
                    , package.LabelImageType.ToString()
                    , labeldate
                    , HttpUtility.UrlEncode(package.ReferenceNumber)
                    , package.AddressServiceRequested.ToString(), "", "", "", ""
                    , package.SeparateReceiptPage.ToString().ToUpper());
                string xml = web.DownloadString(url);
                if (xml.Contains("<Error>"))
                {
                    int idx1 = xml.IndexOf("<Description>") + 13;
                    int idx2 = xml.IndexOf("</Description>");
                    int l = xml.Length;
                    string errDesc = xml.Substring(idx1, idx2 - idx1);
                    throw new USPSManagerException(errDesc);
                }
                int i1 = xml.IndexOf("<DeliveryConfirmationLabel>") + 27;
                int i2 = xml.IndexOf("</DeliveryConfirmationLabel>");
                package.ShippingLabel = StringToUTF8ByteArray(xml.Substring(i1, i2 - i1));
     
                var doc = XDocument.Parse(xml);
     
                try
                {
                    package.DeliveryConfirmationNumber = doc.Element("DeliveryConfirmationV3.0Response").Element("DeliveryConfirmationNumber").Value;
                }
                catch (Exception ex)
                {
                    package.DeliveryConfirmationNumber = "Error retrieving conf #";
                }
     
                return package;
            }
     
            /// <summary>
            /// Fills a package's ShippingLabel with a Byte{} containing the Image for the label
            /// </summary>
            /// <param name="package">Package with From and To addresses provided</param>
            /// <returns>The same package with the ShippingLabel</returns>
            public Package GetSignatureConfirmationLabel(Package package)
            {
                string url = "?API=SignatureConfirmationV3&XML=<SignatureConfirmationV3.0Request USERID=\"{0}\"><Option>{1}</Option><ImageParameters></ImageParameters><FromName>{2}</FromName><FromFirm>{3}</FromFirm><FromAddress1>{4}</FromAddress1><FromAddress2>{5}</FromAddress2><FromCity>{6}</FromCity><FromState>{7}</FromState><FromZip5>{8}</FromZip5><FromZip4>{9}</FromZip4><ToName>{10}</ToName><ToFirm>{11}</ToFirm><ToAddress1>{12}</ToAddress1><ToAddress2>{13}</ToAddress2><ToCity>{14}</ToCity><ToState>{15}</ToState><ToZip5>{16}</ToZip5><ToZip4>{17}</ToZip4><WeightInOunces>{18}</WeightInOunces><ServiceType>{19}</ServiceType><POZipCode>{20}</POZipCode><ImageType>{21}</ImageType><LabelDate>{22}</LabelDate><CustomerRefNo>{23}</CustomerRefNo><AddressServiceRequested>{24}</AddressServiceRequested></SignatureConfirmationV3.0Request>";
                url = GetURL() + url;
                url = String.Format(url, this._userid, (int)package.LabelType, package.FromAddress.Contact, package.FromAddress.FirmName, package.FromAddress.Address1, package.FromAddress.Address2, package.FromAddress.City, package.FromAddress.State, package.FromAddress.Zip, package.FromAddress.ZipPlus4, package.ToAddress.Contact, package.ToAddress.FirmName, package.ToAddress.Address1, package.ToAddress.Address2, package.ToAddress.City, package.ToAddress.State, package.ToAddress.Zip, package.ToAddress.ZipPlus4, package.WeightInOunces.ToString(), package.ServiceType.ToString().Replace("_", " "), package.OriginZipcode, package.LabelImageType.ToString(), package.ShipDate.ToShortDateString(), package.ReferenceNumber, package.AddressServiceRequested.ToString(), package.FromAddress.Contact, package.FromAddress.ContactEmail, package.ToAddress.Contact, package.ToAddress.ContactEmail);
                string xml = web.DownloadString(url);
                if (xml.Contains("<Error>"))
                {
                    int idx1 = xml.IndexOf("<Description>") + 13;
                    int idx2 = xml.IndexOf("</Description>");
                    int l = xml.Length;
                    string errDesc = xml.Substring(idx1, idx2 - idx1);
                    throw new USPSManagerException(errDesc);
                }
                int i1 = xml.IndexOf("<SignatureConfirmationLabel>") + 28;
                int i2 = xml.IndexOf("</DeliveryConfirmationLabel>");
                package.ShippingLabel = StringToUTF8ByteArray(xml.Substring(i1, i2 - i1));
                return package;
            }
     
         
            #endregion
     
            #region Rates
     
            public RateResponse GetRate(Package package)
            {
                try
                {
                    string url = "?API=RateV2&XML=<RateV2Request USERID=\"{0}\"><Package ID=\"0\"><Service>{1}</Service><ZipOrigination>{2}</ZipOrigination><ZipDestination>{3}</ZipDestination><Pounds>{4}</Pounds><Ounces>{5}</Ounces><Container>{6}</Container><Size>{7}</Size></Package></RateV2Request>";
     
                    int lb = package.WeightInOunces / 16;
                    int oz = package.WeightInOunces % 16;
                    string container = package.PackageType.ToString().Replace("_", " ");
                    if (container == "None")
                        url = url.Replace("<Container>{6}</Container>", "");
                    string fromZip = package.FromAddress.Zip;
                    if (package.OriginZipcode != null && package.OriginZipcode.Length > 0)
                        fromZip = package.OriginZipcode;
     
                    
                    url = GetURL() + url;
                    url = String.Format(url, _userid, package.ServiceType.ToString(), fromZip, package.ToAddress.Zip, lb.ToString(), oz.ToString(), container, package.PackageSize.ToString().Replace("_", " "));
                    string xml = web.DownloadString(url);
                    if (xml.Contains("<Error>"))
                    {
                        int idx1 = xml.IndexOf("<Description>") + 13;
                        int idx2 = xml.IndexOf("</Description>");
                        int l = xml.Length;
                        string errDesc = xml.Substring(idx1, idx2 - idx1);
                        throw new USPSManagerException(errDesc);
                    }
     
                    return RateResponse.FromXml(xml);
                }
                catch (WebException ex)
                {
                    throw new USPSManagerException(ex);
                }
            }
            #endregion
     
            #region TextConversions
            /// <summary>
            /// To convert a Byte Array of Unicode values (UTF-8 encoded) to a complete String.
            /// </summary>
            /// <param name="characters">Unicode Byte Array to be converted to String</param>
            /// <returns>String converted from Unicode Byte Array</returns>
            private String UTF8ByteArrayToString(Byte[] characters)
            {
                UTF8Encoding encoding = new UTF8Encoding();
                String constructedString = encoding.GetString(characters);
                return (constructedString);
            }
     
            /// <summary>
            /// Converts the String to UTF8 Byte array and is used in De serialization
            /// </summary>
            /// <param name="pXmlString"></param>
            /// <returns></returns>
            private Byte[] StringToUTF8ByteArray(String pXmlString)
            {
                UTF8Encoding encoding = new UTF8Encoding();
                Byte[] byteArray = encoding.GetBytes(pXmlString);
                return byteArray;
            }
            #endregion
     
            #region Private methods
            private string GetURL()
            {
                string url = ProductionUrl;
                if (TestMode)
                    url = TestingUrl;
                return url;
            }
            #endregion
        }
    }

    Pingbacks and trackbacks (1)+

    Add comment

      Country flag

    biuquote
    • Comment
    • Preview
    Loading