Friday, October 5, 2012

Microsoft Dynamics AX 2012 WS Integration – USPS (Address Standartization API)

Microsoft Dynamics AX 2012
WS Integration – USPS (Address Standartization API)
Purpose: The purpose of this document is to illustrate how to integrate Microsoft Dynamics AX 2012 with USPS Address Standardization API.
 
Walkthrough:
 
First off you have to register on USPS website to get access to USPS Web Tools: https://secure.shippingapis.com/registration/
 
Thank you for registering to use USPS Web Tools™. You will receive an email within 24 hours containing your unique Web Tools User ID and instructions for next steps. Once you receive your User ID, you can begin your Web Tools API integration. Please note that additional permission is required for our Address Information APIs. If you wish to use these tools, please register your Web Tools User ID at: https://www.usps.com/business/webtools-address-information.htm.
 
Once registered you will receive email with your Username and Password
 
Thank you for registering for the U. S. Postal Service's Web Tools Application Program Interfaces (APIs).  We are providing you with a User ID that serves multiple purposes, as explained below.
 
Your Username is ABC
Your Password is XYZ
 
Your Web Tools User ID, shown above, is required to test and integrate USPS Web Tools APIs.  With this ID, you may begin sending calls to the test server.  Depending on the API, the address to the test server is either http://testing.shippingapis.com/ShippingAPITest.dll or https://secure.shippingapis.com/ShippingAPITest.dll.  Use this information in combination with your User ID and your XML string to send a request to the USPS servers.  For more details, refer to the programming guides (located at http://www.usps.com/webtools) for the specific API you are integrating.
 
 
When you have completed your testing, email the USPS Internet Customer Care Center (ICCC).  They will switch your profile to allow you access to the production server and will provide you with the production URLs.
 
USPS provides a lot of different Address related APIs and for the purpose of this demo we are interested in Address Standardization API
 
Address Standardization
Eliminate addressing errors and help ensure accurate and timely delivery. This tool corrects errors in street addresses, including abbreviations and missing information. It also supplies a ZIP+4 Code.
 
ZIP Code Lookup
Find matching ZIP Codes or ZIP+4 Codes for any given address, city, and state in the U.S.
 
City/State Lookup
Use a ZIP Code to get accurate city and state information.
 
The idea is that we sent the info about address as it is (as we have it) to USPS Address Standardization API and as receive a response with potentially amended and/or corrected address
For example,
Request
Response
6406 Ivy Lane
Greenbelt
MD
6406 IVY LN
GREENBELT
MD
20770
1440
 
Please see examples of request and response XML messages when using USPS Address Standardization API for Success and Failure scenarios 
Success
Request
Response
http://testing.shippingapis.com/ShippingAPITest.dll?API=Verify&XML=<AddressValidateRequest%20USERID="ABC"><Address ID="0"><Address1></Address1><Address2>6406 Ivy Lane</Address2><City>Greenbelt</City><State>MD</State><Zip5></Zip5><Zip4></Zip4></Address></AddressValidateRequest>
<?xml version="1.0"?>
<Address ID="0"><Address2>6406 IVY LN</Address2><City>GREENBELT</City><State>MD</State><Zip5>20770</Zip5><Zip4>1440</Zip4></Address>
</AddressValidateResponse>
 
Failure
Request
Response
http://testing.shippingapis.com/ShippingAPITest.dll?API=Verify&XML=<AddressValidateRequest USERID="ABC"><Address ID="0"><Address1></Address1><Address2>One Microsoft Way</Address2><City>Redmond</City><State>WA</State><Zip5>98052</Zip5><Zip4>7329</Zip4></Address></AddressValidateRequest>
 
<?xml version="1.0"?>
<Address ID="0"><Error><Number>-2147219040</Number><Source>SOLServerTest;SOLServerTest.CallAddressDll</Source><Description>This Information has not been included in this Test Server.</Description><HelpFile/><HelpContext/></Error></Address>
</AddressValidateResponse>
 
Once you know the name of the API and the format of request and response of XML messages you are ready to implement the integration
 
C#.NET Class Library (DLL)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Xml.Linq;
using System.IO;
 
namespace USPS
{
    public class USPS_AV
    {
        private string AX_Street;
        private string AX_City;
        private string AX_County;
        private string AX_State;
        private string AX_Country;
        private string AX_Zip;
       
        private string USPS_Address1;
        private string USPS_Address2;
        private string USPS_City;
        private string USPS_State;
        private string USPS_Zip5;
        private string USPS_Zip4;
 
        private bool result;
 
        public string AXStreet { get { return AX_Street; } set { AX_Street = value; }}
        public string AXCity { get { return AX_City; } set { AX_City = value; }}
        public string AXCounty { get { return AX_County; } set { AX_County = value; }}
        public string AXState { get { return AX_State; } set { AX_State = value; }}
        public string AXCountry { get { return AX_Country; } set { AX_Country = value; } }
        public string AXZip { get { return AX_Zip; } set { AX_Zip = value; }}
 
        public string USPSAddress1 { get { return USPS_Address1; } set { USPS_Address1 = value; } }
        public string USPSAddress2 { get { return AX_Street; } set { USPS_Address2 = value; } }
        public string USPSCity { get { return USPS_City; } set { USPS_City = value; } }
        public string USPSState { get { return USPS_State; } set { USPS_State = value; } }
        public string USPSZip5 { get { return USPS_Zip5; } set { USPS_Zip5 = value; } }
        public string USPSZip4 { get { return USPS_Zip4; } set { USPS_Zip4 = value; } }
 
        public bool Result { get { return result; } set { result = value; } }
 
        public void validate()
        {
            HttpWebRequest request = null;
            HttpWebResponse response = null;
 
            try
            {
                request = WebRequest.Create(String.Format("http://testing.shippingapis.com/ShippingAPITest.dll?API=Verify&XML=<AddressValidateRequest USERID=\"ABC\"><Address ID=\"0\"><Address1>{0}</Address1><Address2>{1}</Address2><City>{2}</City><State>{3}</State><Zip5>{4}</Zip5><Zip4>{5}</Zip4></Address></AddressValidateRequest>",
                    "", AX_Street, AX_City, AX_State, "", "")) as HttpWebRequest;
 
                using (response = request.GetResponse() as HttpWebResponse)
                {
                    StreamReader reader = new StreamReader(response.GetResponseStream());
 
                    XDocument xmlDocument = new XDocument();
 
                    xmlDocument = XDocument.Parse(reader.ReadToEnd());
 
                    XElement address = xmlDocument.Root.Element("Address");
 
                    XElement error = address.Element("Error");
 
                    if (error == null)
                    {
                        XElement address1 = address.Element("Address1");
 
                        if (address1 != null)
                            USPS_Address1 = address1.Value;
 
                        XElement address2 = address.Element("Address2");
 
                        if (address2 != null)
                            USPS_Address2 = address2.Value;
 
                        XElement city = address.Element("City");
 
                        if (city != null)
                            USPS_City = city.Value;
 
                        XElement state = address.Element("State");
 
                        if (state != null)
                            USPS_State = state.Value;
 
                        XElement zip5 = address.Element("Zip5");
 
                        if (zip5 != null)
                            USPS_Zip5 = zip5.Value;
 
                        XElement zip4 = address.Element("Zip4");
 
                        if (zip4 != null)
                            USPS_Zip4 = zip4.Value;
 
                        result = true;
                    }
                    else
                    {
                        result = false;
                    }
                }
            }
            catch
            {
                result = false;
            }
        }
    }
}
 
C#.NET Console Application (.NET Client for testing)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using USPS;
 
namespace USPSClient
{
    class Program
    {
        static void Main(string[] args)
        {
            USPS_AV client = new USPS_AV();
            client.AXStreet = "6406 Ivy Lane";
            client.AXCity = "Greenbelt";
            client.AXCounty = "";
            client.AXState = "MD";
            client.AXCountry = "";
            client.AXZip = "";
 
            client.validate();
 
            Console.WriteLine(client.USPSAddress1);
            Console.WriteLine(client.USPSAddress2);
            Console.WriteLine(client.USPSCity);
            Console.WriteLine(client.USPSState);
            Console.WriteLine(client.USPSZip5);
            Console.WriteLine(client.USPSZip4);
 
            Console.ReadLine();
        }
    }
}
 
Result in .NET:
Please note that ZIP+4 Code was identified based on initial address information provided 
 
Once DLL that implements USPS Address Standardization integration is compiled you can complete a programming part in Microsoft Dynamics AX 2012
 
Please add a reference to USPS.DLL in References in Microsoft Dynamics AX 2012
Please note that you have to either put USPS.DLL in Client Bin directory or register DLL in GAC on Client/Server (depending where the code will be executed)
 
For the sake of simplicity I put USPS.DLL into Client Bin folder, created a reference to it in References node in AOT and restarted the Client
 
X++ (Calling DLL validate method)
void clicked()
{
    CodeAccessPermission permission;
 
    USPS.USPS_AV AV;
 
    LogisticsAddressStreet    AX_Street;// = "6406 Ivy Lane";
    LogisticsAddressCity      AX_City;// = "Greenbelt";
    LogisticsAddressCountyId  AX_County;
    LogisticsAddressStateId   AX_State;// = "MD";
    LogisticsAddressCountryRegionId AX_Country;
    LogisticsAddressZipCodeId AX_Zip;
 
    System.String USPS_Address1;
    System.String USPS_Address2;
    System.String USPS_City;
    System.String USPS_State;
    System.String USPS_Zip5;
    System.String USPS_Zip4;
 
    str strUSPS_Address1;
    str strUSPS_Address2;
    str strUSPS_City;
    str strUSPS_State;
   str strUSPS_Zip5;
    str strUSPS_Zip4;
 
    boolean result = false;
 
    super();
 
    permission = new InteropPermission(InteropKind::CLRInterop);
 
    permission.assert();
 
    AV = new USPS.USPS_AV();
 
    AX_Street = AXStreet.text();
    AX_City = AXCity.text();
    AX_County = AXCounty.text();
    AX_State = AXState.text();
    AX_Country = AXCountry.text();
    AX_Zip = AXZip.text();
 
    AV.set_AXStreet(AX_Street);
    AV.set_AXCity(AX_City);
    AV.set_AXCounty(AX_County);
    AV.set_AXState(AX_State);
    AV.set_AXCountry(AX_Country);
    AV.set_AXZip(AX_Zip);
 
    AV.validate();
 
    //result = AV.get_Result();
 
    USPS_Address1 = AV.get_USPSAddress1();
    USPS_Address2 = AV.get_USPSAddress2();
    USPS_City = AV.get_USPSCity();
    USPS_State = AV.get_USPSState();
    USPS_Zip5 = AV.get_USPSZip5();
    USPS_Zip4 = AV.get_USPSZip4();
 
    if (USPS_Address1 != null)
        strUSPS_Address1 = USPS_Address1.ToString();
 
    if (USPS_Address2 != null)
        strUSPS_Address2 = USPS_Address2.ToString();
 
    if (USPS_City != null)
        strUSPS_City = USPS_City.ToString();
 
    if (USPS_State != null)
        strUSPS_State = USPS_State.ToString();
 
    if (USPS_Zip5 != null)
        strUSPS_Zip5 = USPS_Zip5.ToString();
 
    if (USPS_Zip4 != null)
        strUSPS_Zip4 = USPS_Zip4.ToString();
 
    USPSAddress1.text(strUSPS_Address1);
    USPSAddress2.text(strUSPS_Address2);
    USPSCity.text(strUSPS_City);
    USPSState.text(strUSPS_State);
    USPSZip5.text(strUSPS_Zip5);
    USPSZip4.text(strUSPS_Zip4);
}
 
Please see full XPO and AX model with the source code of the USPS Address Standardization API Integration here:  <Integration Pack v1 link> 
 
Result in Microsoft Dynamics AX 2012:
Microsoft Dynamics AX 2012 – Sales order header
 
Microsoft Dynamics AX 2012 – Sales order line
 
USPS Address Verification dialog
 
 
Summary: In this walkthrough I demonstrated how to implement Integration of USPS Address Standardizing API with Microsoft Dynamics AX 2012. Additional UI elements were added to AX 2012 Sales orders form on header and line level to trigger USPS Address Verification dialog. You can use "Validate address" function to call USPS Address Standardizing API and have USPS API amend and/or correct address initially provided. After address has been verified you can use "Transfer Address" function to transfer verified address to Sales order header or line.  
 
More information about USPS Web Tools can be found here: https://www.usps.com/business/webtools.htm
 
Author: Alex Anikiev, PhD, MCP
 
Tags: Microsoft Dynamics ERP, Microsoft Dynamics AX 2012, Integration, Microsoft .NET, USPS Address Standardization API, Address Verification, Address Validation.
 
Note: This document is intended for information purposes only, presented as it is with no warranties from the author. This document may be updated with more content to better outline the concepts and describe the examples.
 

8 comments:

  1. The link doesn't work on this line - "Please see full XPO and AX model with the source code of the USPS Address Standardization API Integration here: "

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Just want to say your blog is extremely helpful. It gets right down to it. I've learned so much. Please keep it up if you can!

    ReplyDelete
  5. hello Alex, this was really helpful. thanks. is there a link for Integration Pack v1 link. or can you email the zip file.

    ReplyDelete
    Replies
    1. Did you receive the link or a zip file?

      Delete