Category: PHP Programming

USPS Rate V4 This mail service requires Ground Transportation Only.

In March 2014 USPS required a set of new tags if wanting a quote for Parcel Post (now being called Standard Post).

To get rate quotes for Parcel Post, and Standard Post you need to make 3 changes.

  1. You need a tag of <Container> Within <Container> you also need a new tag of <ContentType>
  2. You need a new tag of <GroundOnly> immediately after <Container>
  3. You need a new tag of <Revision> right after the <RateV4Request> header.

A properly formatted RateV4Request for Parcel / Standard Post is below. Bold Entries are what are required as of 7 March 2014 to quote USPS Parcel Post. Note Parcel is acceptable (in place of Standard Post) until July 2014. You may also omit the Content tags if shipping LARGE packages (ones too large for Priority Mail). See the USPS Webtool API docs for the required values. I’m writing this up because I added the Content and GroundOnly tags and fought for hours until I realized the Revision tags was also required as the Error Message of:

This mail service requires Ground Transportation Only. HelpContext 1000440

was not very informative.

USPS RateV4Request for Standard Post and Parcel Post

<RateV4Request USERID="xxxxxx" PASSWORD="xxxx">
<Revision>2</Revision>
<Package ID="0">
<Service>STANDARD POST</Service>
<ZipOrigination>10001</ZipOrigination>
<ZipDestination>21201</ZipDestination>
<Pounds>6</Pounds>
<Ounces>2</Ounces>
<Container>RECTANGULAR</Container>
<Size>REGULAR</Size>
<Content><ContentType>HAZMAT</ContentType></Content>
<GroundOnly>TRUE</GroundOnly>

<Machinable>TRUE</Machinable>
….

PHP Script for Calculating UPC Code Check Digit

Here’s a php function (function calculate_upc_check_digit($upc_code)) I wrote today for calculating the check digit of a UPC-A (12 digit) UPC Code. You can use it to validate a UPC Code or to calculate the check digit when the manufacturer only supplies 11 digits of the UPC code.

The script will accept a UPC Code of length 11 or 12, and return the calulated check digit. If the length of the UPC Code is other then 11 or 12 it returns -1.


// ----------------------------------------------------
// calculate check digit for upc
// parameter UPC can be either 11 digits (to calculate)
// or full 12 digits to use to validate
// returns -1 on failure
// or check digit 0 - 9 on success
// to validate upc send 12 digit upc code in
// and compare returned value with the 12th
// digit of UPC code you are validating
// --------------------------------------------------
function calculate_upc_check_digit($upc_code) {
$checkDigit = -1; // -1 == failure
$upc = substr($upc_code,0,11);
// send in a 11 or 12 digit upc code only
if (strlen($upc) == 11 && strlen($upc_code) <= 12) { $oddPositions = $upc[0] + $upc[2] + $upc[4] + $upc[6] + $upc[8] + $upc[10]; $oddPositions *= 3; $evenPositions= $upc[1] + $upc[3] + $upc[5] + $upc[7] + $upc[9]; $sumEvenOdd = $oddPositions + $evenPositions; $checkDigit = (10 - ($sumEvenOdd % 10)) % 10; } return $checkDigit; }

Here's a sample of a script (omitting the mysql connection code) that uses this function to validate UPC codes from a mysql database containing products_id (int), products_name (varchar[40]) and products_upc (varchar[12]).


<?php
$sql = "select products_id, products_name, products_upc from products\n";
$sql .= "where products_status = '1' and products_upc <> ''\n";
$sql .= "order by products_id";
$result = mysql_db_query($sql);
$count = 0;
echo "<html>\n";
echo "<head><title>Validate UPC Codes</title></head>\n";
echo "<body>\n<table border='0' cellpadding='2' cellspacing='0'>\n";
echo "<tr>\n";
echo '<td>Reference</td>';
echo '<td>Product Id</td>';
echo '<td>Product Name</td>';
echo '<td>UPC Code<br />w/o Check Digit</td>';
echo '<td>UPC Check Digit</td>';
echo '<td>Calculated<br />Check Digit</td>';
echo '<td>Result</td>';
echo "</tr>\n";
while ($row = mysql_db_fetch_assoc($result)) {
$count++; $check_digit = calculate_upc_check_digit($row['products_upc']);
$match = 'Ok';
if ($check_digit == -1) {
$match = '<strong>Failure</strong>';
} elseif ($check_digit != substr($row['products_upc'],11,1)) {
$match = '<strong>Error</strong>';
}
echo "<tr>\n";
echo '<td>' . $count . '</td>';
echo '<td>' . $row['products_id'] . '</td>';
echo '<td>' . tep_get_products_name($row['products_id']) . '</td>';
echo '<td>' . substr($row['products_upc'],0,11) . '</td>';
echo '<td>' . substr($row['products_upc'],11,1) . '</td>';
echo '<td>' . $check_digit . '</td>';
echo '<td>' . $match . '</td>';
echo "</tr>\n";
}
echo "</table>\n";
echo "</body>\n";
echo "</html>\n"
?>

USPS Web Tools API Issue with Regional Rate Boxes

If you’re programming for the USPS Web Tools for the Regional Rate A, B and C boxes and get the error:
REGIONALRATEBOXA is an invalid container type for a REGULAR package and PRIORITY service.

You need to change the Service to PRIORITY COMMERCIAL. This isn’t in the documentation and no-one at the technical support center will know the answer. It took three weeks of fighting this error to figure it out.

Sample with error:
<RateV4Request USERID="SECRET" PASSWORD="xxxxxxxxx">
<Package ID="0">
<Service>PRIORITY</Service>
<ZipOrigination>20852</ZipOrigination>
<ZipDestination>53110</ZipDestination>
<Pounds>15</Pounds><Ounces>11</Ounces>
<Container>REGIONALRATEBOXA</Container>
<Size>REGULAR</Size>
<Machinable>True</Machinable>
</Package>
</RateV4Request>

<RateV4Response>
<Package ID="0">
<Error>
<Number>-2147219407</Number>
<Source>DomesticRatesV4;clsRateV4.ValidateContainer;RateEngineV4.ProcessRequest</Source>
<Description>REGIONALRATEBOXA is an invalid container type for a REGULAR package and PRIORITY service.</Description>
<HelpFile></HelpFile>
<HelpContext>1000440</HelpContext>
</Error>
</Package>
</RateV4Response>


If done with priority commercial will get a price:
<RateV4Request USERID="SECRET" PASSWORD="xxxxxxxxx">
<Package ID="0">
<Service>PRIORITY COMMERCIAL</Service>
<ZipOrigination>20852</ZipOrigination>
<ZipDestination>53110</ZipDestination>
<Pounds>15</Pounds><Ounces>11</Ounces>
<Container>REGIONALRATEBOXA</Container>
<Size>REGULAR</Size>
<Machinable>True</Machinable>
</Package>
</RateV4Request>


Solves the problem with these two related errors as well:
REGIONALRATEBOXB is an invalid container type for a REGULAR package and PRIORITY service.
REGIONALRATEBOXC is an invalid container type for a REGULAR package and PRIORITY service.