The service program extract below contains a few subprocedures used to validate a given city/state/zip combination using HTTPAPI to access the UPS Web Services. You will need to get a UPS username and account, and apply for a developer access key. A good page to start learning about what is available and how to get started is here.
Overview of code structure:
- Near the top is a group of global D-Specs that would normally be in a copy source for production use to allow them to be easily added to any program that needs to call the exported subprocedures.
- The service program has some local subprocedures to handle the parsing of the returned information from the UPS web service. These are referenced by procedure pointers used in the HTTPAPI call.
- For each UPS service you add to the service program, it would have it's own pair of start and end procedures to handle the parsing.
For now, I will just post the code and can expand more on this write-up if people have questions.
H NOMAIN OPTION(*SRCSTMT:*NODEBUGIO:*NOUNREF:*NOSHOWCPY) DEBUG(*YES)
H BNDDIR('QC2LE':'HTTPAPI')
//----------------------------------------------------------------
// PROGRAM NAME.. UPS Web Services
//
// DESCRIPTION... Collection of procedures to handle various
// online interactions with UPS. There will be
// exported procedures for primary services and
// non-exported procedures that support those
// primary services soley in this service program.
//----------------------------------------------------------------
// Cels D Specs & HTTPAPI
//include CECOPYSRC,CELSDSPECS
/include LIBHTTP/QRPGLESRC,HTTPAPI_H
// D-Specs for this Service Program (would usually be in a copy source
// to make sharing of the exported procedures simpler. Included
// inline in this example code.
//--------------------------------------------------------------------
// Procedure: UPS Web Services D-Specs
// Date.....: 09/22/11
// Author...: David Wright
//----------------------------------------------------------------
// Misc constants used below
D true c *on
D false c *off
D Upper c 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
D Lower c 'abcdefghijklmnopqrstuvwxyz'
// UPS Account Info
D UPS_USERID c 'username'
D UPS_PASSWD c 'password'
D UPS_LICENSE c 'UpsDeveloperKey '
// City,State,Zip Data Structure
D l_UpsCSZds ds template qualified
d iRank 2s 0
d fQuality 7s 4
D sCity 20a
D sState 2a
D sZipLow 5a
D sZipHigh 5a
D l_UpsCSZHdrDs ds inz qualified
D iRanks 5u 0
D sStatus 1a
D sError 50a
D dsCsz likeds(l_UpsCSZds) dim(20)
//--------------------------------------------------------------------
// Procedure: Validate City State Zip with UPS
// Proc Name: IsValidCSZ
// Date.....: 09/22/11
// Author...: David Wright
//----------------------------------------------------------------
DIsValidCSZ pr n
D sCity 40a const
D sState 2a const
D sZip 9a const
//--------------------------------------------------------------------
// Procedure: Get City State Zip Options from UPS
// Proc Name: GetCityStateZip
// Date.....: 09/22/11
// Author...: David Wright
//----------------------------------------------------------------
DGetCityStateZip pr likeds(l_UpsCSZHdrDs)
D sCity 40a const
D sState 2a const
D sZip 9a const
D p#ptrLines * const options(*nopass)
//--------------------------------------------------------------------
// Procedure: Cleanup Address Elements to UPS Standards
// Proc Name: CleanupUpsAddr
// Date.....: 09/22/11
// Author...: David Wright
//----------------------------------------------------------------
DCleanupUpsAddr pr 50a
D sIn 50a const
//--------------------------------------------------------------------
// Procedure: Validate City State Zip with UPS
// Proc Name: IsValidCSZ
// Date.....: 09/22/11
// Author...: David Wright
//----------------------------------------------------------------
// USAGE NOTE: Parms are NOT constant. Values could be corrected
//----------------------------------------------------------------
// Modification Control.
// ~~~~~~~~~~~~~~~~~~~~~
// Code By Date Description
// ~~~~ ~~ ~~~~ ~~~~~~~~~~~
// 25632 DW 9/22/11 * Initial release
//----------------------------------------------------------------
PIsValidCSZ b export
DIsValidCSZ pi n
D p#sCity 40a const
D p#sState 2a const
D p#sZip 9a const
// Work Data Structure
D dsOptions ds inz likeds(l_UpsCSZHdrDs)
// Work Variables
D sCity s 40a inz
D sState s 2a inz
D sZip s 9a inz
/free
// Use parms
sCity = CleanupUpsAddr(p#sCity);
sState = CleanupUpsAddr(p#sState);
sZip = CleanupUpsAddr(p#sZip);
// Get CSZ Options
dsOptions = GetCityStateZip(sCity: sState: sZip);
// If they match option 1, then if is valid
return ((dsOptions.dsCsz(1).fQuality = 1) and
(sCity = dsOptions.dsCsz(1).sCity) and
(sState = dsOptions.dsCsz(1).sState) and
(sZip >= dsOptions.dsCsz(1).sZipLow) and
(sZip <= dsOptions.dsCsz(1).sZipHigh));
/end-free
PIsValidCSZ e
//--------------------------------------------------------------------
// Procedure: Get City State Zip Options from UPS
// Proc Name: GetCityStateZip
// Date.....: 09/22/11
// Author...: David Wright
//----------------------------------------------------------------
// Modification Control.
// ~~~~~~~~~~~~~~~~~~~~~
// Code By Date Description
// ~~~~ ~~ ~~~~ ~~~~~~~~~~~
// 25632 DW 9/22/11 * Initial release
//----------------------------------------------------------------
PGetCityStateZip b export
DGetCityStateZip pi likeds(l_UpsCSZHdrDs)
D p#sCity 40a const
D p#sState 2a const
D p#sZip 9a const
D p#ptrLines * const options(*nopass)
// HTTP Vars
D rc s 10I 0
D postData s 750A varying
// Work Data Structure
D dsOptions ds inz likeds(l_UpsCSZHdrDs)
// Misc Work Variables
D ptrLines s *
D sCity s 40a inz
D sState s 2a inz
D sZip s 9a inz
/free
// Use parms
sCity = p#sCity;
sState = p#sState;
sZip = p#sZip;
// Cleanup Addresses
sCity = CleanupUpsAddr(sCity);
sState = CleanupUpsAddr(sState);
sZip = CleanupUpsAddr(sZip);
// Use lines pointer if passed
if %parms = 4;
ptrLines = p#ptrLines;
endif;
// Build Request
postData =
'<?xml version="1.0"?>' +
'<AccessRequest>' +
'<AccessLicenseNumber>' + UPS_LICENSE + '</AccessLicenseNumber>' +
'<UserId>' + UPS_USERID + '</UserId>' +
'<Password>' + UPS_PASSWD + '</Password>' +
'</AccessRequest>' +
'<?xml version="1.0"?>' +
'<AddressValidationRequest xml:lang="en-US">' +
'<Request>' +
'<TransactionReference>' +
'<CustomerContext>CELS Enterprises Inc.</CustomerContext>' +
'<XpciVersion>1.0001</XpciVersion>' +
'</TransactionReference>' +
'<RequestAction>AV</RequestAction>' +
'</Request>' +
'<Address>' +
'<City>' + %trim(sCity) + '</City>' +
'<StateProvinceCode>' + sState + '</StateProvinceCode>' +
'<PostalCode>' + sZip + '</PostalCode>' +
'</Address>' +
'</AddressValidationRequest>' ;
// Send Request
rc = http_url_post_xml('https://www.ups.com/ups.app/xml/AV'
: %addr(postData) + 2
: %len(postData)
: %paddr(StartGetCSZ)
: %paddr(EndGetCSZ)
: %addr(dsOptions));
// Exit
return dsOptions;
/end-free
PGetCityStateZip e
//--------------------------------------------------------------------
// Procedure: Get City State Zip Options from UPS - Start of Element
// Proc Name: StartGetCSZ
// Date.....: 09/22/11
// Author...: David Wright
//----------------------------------------------------------------
// Modification Control.
// ~~~~~~~~~~~~~~~~~~~~~
// Code By Date Description
// ~~~~ ~~ ~~~~ ~~~~~~~~~~~
// 25632 DW 9/22/11 * Initial release
//----------------------------------------------------------------
PStartGetCSZ b
DStartGetCSZ pi
D pUserData * value
D iDepth 10i 0 value
D sName 1024a varying const
D sPath 24576a varying const
D pAttrs * dim(32767)
D const options(*varsize)
// Work Data Structure
D dsOptions ds likeds(l_UpsCSZHdrDs)
D based(pUserData)
/free
if sName = 'Rank';
dsOptions.iRanks += 1;
endif;
/end-free
PStartGetCSZ e
//--------------------------------------------------------------------
// Procedure: Get City State Zip Options from UPS - End of Element
// Proc Name: EndGetCSZ
// Date.....: 09/22/11
// Author...: David Wright
//----------------------------------------------------------------
// Modification Control.
// ~~~~~~~~~~~~~~~~~~~~~
// Code By Date Description
// ~~~~ ~~ ~~~~ ~~~~~~~~~~~
// 25632 DW 9/22/11 * Initial release
//----------------------------------------------------------------
PEndGetCSZ b
DEndGetCSZ pi
D pUserData * value
D iDepth 10I 0 value
D sName 1024A varying const
D sPath 24576A varying const
D sValue 32767A varying const
D pAttrs * dim(32767)
D const options(*varsize)
// Work Data Structure
D dsOptions ds likeds(l_UpsCSZHdrDs)
D based(pUserData)
D bNoRank s n inz
/free
// If no ranks yet, position to first array element (which will be empty)
if dsOptions.iRanks = 0;
bNoRank = true;
dsOptions.iRanks = 1;
endif;
select;
when sPath = '/AddressValidationResponse/Response' and
sName = 'ResponseStatusCode';
dsOptions.sStatus = sValue;
if dsOptions.sStatus <> '1';
if dsOptions.dsCsz(dsOptions.iRanks).sCity = *blanks;
dsOptions.sError = 'No Address Cadidate Found...';
else;
dsOptions.sError = 'No Address Cadidate Found for ' +
%trim(dsOptions.dsCsz(dsOptions.iRanks).sCity) +
' ' + dsOptions.dsCsz(dsOptions.iRanks).sState;
endIf;
endIf;
when sPath = '/AddressValidationResponse/AddressValidationResult'
and sName = 'Rank' and dsOptions.sStatus = '1';
dsOptions.dsCsz(dsOptions.iRanks).iRank = %Dec(sValue:2:0);
when sPath = '/AddressValidationResponse/AddressValidationResult' and
sName = 'Quality' and dsOptions.sStatus = '1';
dsOptions.dsCsz(dsOptions.iRanks).fQuality = %Dec(sValue:7:4);
when sPath = '/AddressValidationResponse/AddressValidationResult/Address'
and sName = 'City' and dsOptions.sStatus = '1';
dsOptions.dsCsz(dsOptions.iRanks).sCity = sValue;
when sPath = '/AddressValidationResponse/AddressValidationResult/Address'
and sName = 'StateProvinceCode' and dsOptions.sStatus = '1';
dsOptions.dsCsz(dsOptions.iRanks).sState = sValue;
when sPath = '/AddressValidationResponse/AddressValidationResult'
and sName = 'PostalCodeLowEnd' and dsOptions.sStatus = '1';
dsOptions.dsCsz(dsOptions.iRanks).sZipLow = sValue;
when sPath = '/AddressValidationResponse/AddressValidationResult'
and sName = 'PostalCodeHighEnd' and dsOptions.sStatus = '1';
dsOptions.dsCsz(dsOptions.iRanks).sZipHigh = sValue;
endsl;
// If rank was faked, put back to zero
if bNoRank;
dsOptions.iRanks = 0;
endif;
/end-free
PEndGetCSZ e
//--------------------------------------------------------------------
// Procedure: Cleanup Address Elements to UPS Standards
// Proc Name: CleanupUpsAddr
// Date.....: 09/22/11
// Author...: David Wright
//----------------------------------------------------------------
// Modification Control.
// ~~~~~~~~~~~~~~~~~~~~~
// Code By Date Description
// ~~~~ ~~ ~~~~ ~~~~~~~~~~~
// 25632 DW 9/22/11 * Initial release
//----------------------------------------------------------------
PCleanupUpsAddr b export
DCleanupUpsAddr pi 50a
D sIn 50a const
// Work Data Structure
D sOut s inz like(sIn)
/free
// Upper Case
sOut = %xlate(lower: upper: sIn);
// Remove Punctuation
sOut = %xlate(''',-.~`"\/':' ':sOut);
// Remove Double Spaces
dow %len(%trim(sOut)) <> %len(%trim(%scanrpl(' ':' ':sOut)));
sOut = %scanrpl(' ':' ':sOut);
enddo;
// Return cleaned value
return sOut;
/end-free
PCleanupUpsAddr e