【问题标题】:trying to test UPS "Quantum" interface - The XML document is not well formed尝试测试 UPS“Quantum”接口 - XML 文档格式不正确
【发布时间】:2014-09-16 17:03:54
【问题描述】:

我们正在尝试测试联合包裹服务(UPS)“Quantum”接口测试获取订单状态信息。我们让跟踪号 API 正常工作,但 QVEvents 出现了问题。

UPS“出境订阅帐户”已设置、激活并与我的帐号相关联。用户访问 Quantum View 数据视图在用户设置屏幕中被选中。顺便说一句,我已经让 Tracking API 使用类似的代码,所以认为我可以排除用户名或密码问题。这两个 API 在操作上有什么不同吗? (例如:SSL 要求、HTTP 标头设置?)

以下是对我有用的跟踪 API(“Tack by Waybill”)代码:

 <?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);

这里是给出错误消息的 Quantum API 代码...

<?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);        

这是实际发送给 UPS 的 XML... [注意双 xml 标头是他们要求的,它适用于所有其他 API,所以不要怪我]

<?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>

错误信息如下所示:

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>

我没有使用 UPS SCA_SDO 库,因为它不会安装在我的 PC 上。也许我应该再看一遍,但我的程序员向我保证这不是必要的,也与这个问题无关。双重堆叠的 XML 看起来很可疑,但手册说这是 UPS 想要的方式。我猜 UPS 会根据请求运行预处理器。

【问题讨论】:

  • 发送给 UPS 的 XML 是什么样的?你能把那个贴出来吗?不过我注意到一件事,你的$data 变量中有两次&lt;?xml version=\"1.0\"?&gt; 。一次在开头,一次在您的 AccessRequest 节点之后。
  • 将在 $data 中添加 XML.... 双 XML:这是标准的 UPS 愚蠢行为。他们似乎对它进行了预处理。见stackoverflow.com/questions/20294168/…
  • 正如@Andy 指出的那样, $data =" 会给你一个格式错误的错误,因为你的引号搞砸了。格式错误基本上意味着你的开始和结束标签不匹配

标签: php xml web-services ups


【解决方案1】:

根据 2014 年 7 月的 Quantum View Developers Guide,未使用 IntegrationIndicator

从您的 Request 节点中删除它,它应该可以按您的预期运行:

<Request>
    <TransactionReference>
        <CustomerContext>Test XML</CustomerContext>
        <XpciVersion>1.0007</XpciVersion>
     </TransactionReference>
     <RequestAction>QVEvents</RequestAction>
</Request> 

【讨论】:

  • 这应该如何与错误代码 10001 相关,错误代码是关于格式正确而不是验证?
【解决方案2】:

问题

我不熟悉 UPS Api,但错误消息提示不是well-formed XML document

错误代码 10001: XML 文档格式不正确

根据Quantum View Package - XML Developers Guide,您在 HTTP 请求中使用了错误的后数据编码和内容类型,这会破坏 XML,从而使其不再格式良好。这会导致错误代码 10001。

改为使用 HTTP 请求正文的正确内容类型(您使用的是错误的 multipart/form-data,正确的内容类型是 application/x-www-form-urlencoded) 将您的 HTTP POST 请求发送到 UPS XML API 端点,并正确编码 XML 数据,您应该使用 XML 库来创建 XML(不是您的具体问题,但是当您知道 XML 是正确的并且创建不像在您的字符串连接中那样容易出错,这有助于排除同一错误代码的大量错误情况。

如何解决?

要解决代码中内容类型错误的具体问题,请替换以下行:

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

$postData = $data;

这确保 curl 库将使用 "application/x-www-form-urlencoded" 作为 Content-Type 而不是 "multipart/form-data"。然后您会收到正确的错误消息,即许可证号无效:

错误代码 250003:无效的访问许可证号

替代代码示例

作为替代方案,这里是另一个小示例,展示了如何使用标准 PHP HTTP 包装器和用于 XML 处理的 SimpleXML 库以及使用 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();

此示例使用您在示例中提供的数据,而不是 10001(您的代码生成),它提供了有关数据样本中真正已知错误的更精确的错误信息:

错误代码 250003:无效的访问许可证号

<?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>

我坚信这是正确的错误消息。

为简洁起见,我已将 file_get_contentsDOMDocument::loadXML() 的错误处理留在了外面。

【讨论】:

  • 嗨 - 我为此悬赏,但没有看到奖励它的按钮!这个答案是正确的。安迪和安倍也是对的!
  • @MustaphaGeorge:赏金会运行一段时间。如果您没有在特定时间段内分配赏金(通常在赏金开始两天后到赏金结束后 24 小时),它将被自动处理。在这种情况下,它就消失了。您需要自己分配它。
【解决方案3】:

IntegrationIndicator 已被弃用。也不要将数据作为数组传递。

【讨论】:

    猜你喜欢
    • 2015-09-11
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    • 2020-01-04
    • 1970-01-01
    • 2016-03-27
    • 2011-05-19
    • 1970-01-01
    相关资源
    最近更新 更多