5

We are trying to test United Parcel Service (UPS) "Quantum" interface test get order status information. We got the tracking number API working but having trouble with the QVEvents one.

UPS "Outbound subscription account" is set up, active and linked to my account numbers. User access Quantum View Data view is checked off in User Setup screen. I have, incidently, gotten Tracking API working with similar code, so think that I can rule out username or password problems. Is there anything operationally different with these two APIs? (e.g: SSL requirements, HTTP Header settings?)

Here is code for tracking API ("Tack by Waybill") which DOES works for me:

 <?php
  //  UPS Tracker API - track specfic Waybill
  //  DEV server
  $access      = '99999999399999999';
  $userid      = '9999999';
  $passwd      = '999999999999';
  $endpointUrl = 'https://www.ups.com/ups.app/xml/Track';
  $outFileName = './XOLTResult.xml'; 


  // Note: you need at least a UPS DEV account to test this
  $data ="<?xml version=\"1.0\"?><AccessRequest xml:lang='en-US'>
    <AccessLicenseNumber>$access</AccessLicenseNumber>
    <UserId>$userid</UserId>
    <Password>$passwd</Password>
    </AccessRequest>
    <?xml version=\"1.0\"?>
    <TrackRequest>
        <Request>
            <TransactionReference>
                <CustomerContext>
                    <InternalKey>hello</InternalKey>
                </CustomerContext>
                <XpciVersion>1.0</XpciVersion>
            </TransactionReference>
            <RequestAction>Track</RequestAction>
        </Request>
        <TrackingNumber>9999999999999999</TrackingNumber>
    </TrackRequest>";

    $ch = curl_init("https://www.ups.com/ups.app/xml/Track");
    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch,CURLOPT_POST,1);
    curl_setopt($ch,CURLOPT_TIMEOUT, 60);
    curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
    curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
    $result=curl_exec ($ch);
    $data = strstr($result, '<?');
    $xml=simplexml_load_string($data);
    echo "<pre>";
    print_r($xml);

And here is Quantum API code that gives error message...

<?php
 //  UPS Quantum API ("Show list of recent tracking information")
 //  DEV server
 $access      = '99999999399999999';
 $userid      = '9999999';
 $passwd      = '999999999999';
 $endpointUrl = 'https://wwwcie.ups.com/ups.app/xml/QVEvents';      // URL for testing Quantum
 $outFileName = './XOLTResult.xml'; 


try
{

$data ="<?xml version=\"1.0\"?>
        <AccessRequest xml:lang=\"en-US\">
        <AccessLicenseNumber>$access</AccessLicenseNumber>
        <UserId>$userid</UserId>
        <Password>$passwd</Password>
        </AccessRequest>
        <?xml version=\"1.0\"?>    
        <QuantumViewRequest xml:lang=\"en-US\">
            <Request>
                <TransactionReference>
                    <CustomerContext>Test XML</CustomerContext>
                    <XpciVersion>1.0007</XpciVersion>
                 </TransactionReference>
                 <RequestAction>QVEvents</RequestAction>
                 <IntegrationIndicator></IntegrationIndicator>
            </Request> 
        </QuantumViewRequest>";

  $postData = array
    (
      'content' =>  $data
    );


        $ch = curl_init();

        curl_setopt($ch, CURLOPT_HEADER, 1);
        curl_setopt($ch, CURLOPT_POST,1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 60);      
        curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
        curl_setopt($ch, CURLOPT_URL,$endpointUrl);
        curl_setopt($ch, CURLOPT_VERBOSE, 1 );
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));     
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);                // disable SSL verification if not installed
        //curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0);             
        curl_setopt($ch, CURLOPT_SSLVERSION, 3);                        // use Secure Socket v3 SSL3
        curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'SSLv3');             
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);        
        curl_setopt($ch,CURLOPT_POSTFIELDS,$postData);


        if( ! $result = curl_exec($ch))
        {
            trigger_error(curl_error($ch));
        } 


        echo $result;

        $data = strstr($result, '<?');
        $xml=simplexml_load_string($data);


        echo "<pre>";
        print_r($xml);

}
catch(Exception $ex)
{
   echo ($ex . "!");
}

curl_close($ch);        

This is XML actually sent to UPS... [Note the double xml header is what they ask for and it works in all their other APIs, so don't blame me]

<?xml version="1.0"?>
<AccessRequest xml:lang="en-US">
<AccessLicenseNumber>999</AccessLicenseNumber>
<UserId>999</UserId>
<Password>999</Password>
</AccessRequest>
<?xml version="1.0"?>    
<QuantumViewRequest xml:lang="en-US">
    <Request>
        <TransactionReference>
            <CustomerContext>Test XML</CustomerContext>
            <XpciVersion>1.0007</XpciVersion>
         </TransactionReference>
         <RequestAction>QVEvents</RequestAction>
         <IntegrationIndicator></IntegrationIndicator>
    </Request> 
</QuantumViewRequest>

Error message look like this:

HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Date: Fri, 25 Jul 2014 22:50:57 GMT
Server: Apache
X-Frame-Options: SAMEORIGIN
Pragma: no-cache
Transfer-Encoding: chunked
Content-Type: application/xml

<QuantumViewResponse><Response><TransactionReference><XpciVersion>1.0</XpciVersion>
</TransactionReference><ResponseStatusCode>0</ResponseStatusCode>
<ResponseStatusDescription>Failure</ResponseStatusDescription><Error>
<ErrorSeverity>Hard</ErrorSeverity><ErrorCode>10001</ErrorCode>
<ErrorDescription>The XML document is not well formed</ErrorDescription></Error>
 </Response></QuantumViewResponse><pre>

In am not using UPS SCA_SDO library because it would not install on my PC. Maybe I should look at it again, but my programmer assured me it was not necessary and not relevant to this problem. The double stacked XML looks suspicious, but manual says this is the way UPS wants it. I guess UPS runs a pre-processor on the request.

MrLore
  • 3,759
  • 2
  • 28
  • 36
Mustapha George
  • 2,497
  • 9
  • 47
  • 79
  • 1
    What does your XML look like that you are sending to UPS? Can you post that? One thing I noticed though, you have ` ` in your `$data` variable twice. Once at the beginning and once after your `AccessRequest` node. – Andy Jul 28 '14 at 00:54
  • will add the XML.... double XML in $data: this is standard UPS silliness. They seem to pre-process it. see http://stackoverflow.com/questions/20294168/ups-shipping-api-shipmentconfirmrequest-error – Mustapha George Jul 28 '14 at 01:44
  • as @Andy pointed out, $data =" will give you a malformed error because your quotes are messed up. malformed basically means your opening and closing tags don't match properly – Jeff Hawthorne Aug 05 '14 at 13:47

3 Answers3

1

According to the July 2014 Quantum View Developers Guide, the IntegrationIndicator is not used.

IntegrationIndicator

Remove that from your Request node and it should function as you expect:

<Request>
    <TransactionReference>
        <CustomerContext>Test XML</CustomerContext>
        <XpciVersion>1.0007</XpciVersion>
     </TransactionReference>
     <RequestAction>QVEvents</RequestAction>
</Request> 
Andy
  • 49,085
  • 60
  • 166
  • 233
  • How should this be related to error code 10001 which is about well-formedness and not validation? – hakre Jul 30 '14 at 13:12
1

The Problem

I'm not fluent with the UPS Api in specific, but the error message hints a not well-formed XML document:

Error Code 10001: The XML document is not well formed

According to the Quantum View Package - XML Developers Guide you're using the wrong encoding and content-type of the post-data in your HTTP request which breaks the XML which makes it not well formed any longer. This causes the error code 10001.

Instead, use the right content-type of the HTTP request body (you're using multipart/form-data which is wrong, the correct content-type is application/x-www-form-urlencoded) with your HTTP POST request to the UPS XML API endpoint and also encode the XML data correct as well for which you should use an XML library to create the XML (not your concrete issue, but when you know that the XML is correct and the creation is not error prone as it is in your string concatenation, this helps to exclude a tons of error cases for the same error code).

How to fix?

To fix the concrete issue of the wrong content-type in your code, replace the following lines:

$postData = array
(
    'content' =>  $data
);

with

$postData = $data;

This ensures that the curl library will use "application/x-www-form-urlencoded" as Content-Type instead of "multipart/form-data". You then get the correct error message that the license number is invalid:

Error Code 250003: Invalid Access License number

Alternative Code-Example

As alternative, here is another little example that shows how this can be done with standard PHP HTTP wrappers and the SimpleXML library for the XML handling as well as nicer output for the response with DOMDocument:

/*
 * UPS API XML PHP Example (Open Source)
 */

# Every UPS XML API request needs an AccessRequest XML payload first
$accessRequest                      = new SimpleXMLElement("<AccessRequest xml:lang='en-US'/>");
$accessRequest->AccessLicenseNumber = '99999999399999999';
$accessRequest->UserId              = '9999999';
$accessRequest->Password            = '999999999999';

# Exemplary testing QuantumViewRequest
$url     = 'https://wwwcie.ups.com/ups.app/xml/QVEvents';
$request = new SimpleXMLElement('<QuantumViewRequest xml:lang="en-US"/>');

# UPS XML API requires two concatenated XML documents send via a HTTP POST
# request with a HTTP request body and the content-type set to 
# application/x-www-form-urlencoded
$options = array(
    'http' => array(
        'header'  => "Content-type: application/x-www-form-urlencoded",
        'method'  => 'POST',
        'content' => $accessRequest->asXML() . $request->asXML(),
    ),
);
$context = stream_context_create($options);
$result  = file_get_contents($url, false, $context);

# output the response nicely formatted
$dom = new DOMDocument();
$dom->loadXML($result);
$dom->preserveWhiteSpace = false;
$dom->formatOutput       = true;
echo $dom->saveXML();

This example is using the data you've provided in your example, and instead of the 10001 (that your code produces) it gives the much more precise error information about the real known error in your data sample:

Error Code 250003: Invalid Access License number

<?xml version="1.0"?>
<QuantumViewResponse>
  <Response>
    <TransactionReference/>
    <ResponseStatusCode>0</ResponseStatusCode>
    <ResponseStatusDescription>Failure</ResponseStatusDescription>
    <Error>
      <ErrorSeverity>Hard</ErrorSeverity>
      <ErrorCode>250003</ErrorCode>
      <ErrorDescription>Invalid Access License number</ErrorDescription>
    </Error>
  </Response>
</QuantumViewResponse>

In which I have good faith that this is the correct error message.

For the brevity of the example, I've left the error handling for file_get_contents and DOMDocument::loadXML() out.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • hi - I put a bounty on this, but don't see button to award it! This answer was correct. Andy and Abe are correct as well! – Mustapha George Aug 16 '14 at 23:12
  • @MustaphaGeorge: The Bounty runs for a certain time. If you don't assign the Bounty within a specific period (normally after two days of Bounty start to 24 hours after Bounty end), it will be automatically handled. In this case, it is just gone. You would have needed to assign it your own. – hakre Aug 22 '14 at 17:22
0

IntegrationIndicator has been deprecated. Also don't pass data as an array.

Abe
  • 298
  • 4
  • 12