PHP + SoapClient异常和标题? (UPS评级)

时间:2011-07-29 17:37:36

标签: php soap ups

我正在尝试使用PHP和SoapClient来使用UPS评级网络服务。我发现了一个名为WSDLInterpreter的好工具来创建一个用于创建服务请求的起始库,但无论我尝试什么,我都会得到相同的(非描述性)错误:

EXCEPTION=SoapFault::__set_state(array(
   'message' => 'An exception has been raised as a result of client data.',
   'string' => '',
   'code' => 0,

嗯,这到底是什么意思?

与我实施的其他一些Web服务工具不同,UPS Soap需要在头部放置一个安全块。我尝试做原始关联数组数据,但我不确定100%是否正确注入标题部分。 使用WSDLInterpreter,它使用ProcessRate方法提取RateService类,该方法除了RateRequest和UPSSecurity部分的自己的(实例)数据结构外,但上述所有内容都会产生相同的错误。

以下是我正在使用的代码示例,它调用了解释器定义的类:


require_once "Shipping/UPS/RateService.php"; // WSDLInterpreter class library

class Query
{

    // constants removed for stackoverflow that define (proprietary) security items
    private $_upss;
    private $_shpr;

    // use Interpreter code's RateRequest class to send with ProcessRate
    public function soapRateRequest(RateRequest $req, UPSSecurity $upss = null)
    {
        $res = false;
        if (!isset($upss)) {
            $upss = $this->__getUPSS();
        }
        echo "SECURITY:\n" . var_export($upss, 1) . "\n";
        $upsrs = new RateService(self::UPS_WSDL);
        try {
            $res = $upsrs->ProcessRate($req, $upss);
        } catch (SoapFault $exception) {
            echo 'EXCEPTION=' . var_export($exception, 1) . "\n";
        }
        return $res;
    }

    // create a soap data structure to send to web service from shipment
    public function getRequestSoap(Shipment $shpmnt)
    {
        $qs = new RateRequest();
        // pickup information
        $qs->PickupType->Code = '01';
        $qs->Shipment->Shipper = $this->__getAcctInfo();
        // Ship To address
        $qs->Shipment->ShipTo->Address->AddressLine = $this->__getAddressArray($shpmnt->destAddress->address1, $shpmnt->destAddress->address2);
        $qs->Shipment->ShipTo->Address->City = $shpmnt->destAddress->city;
        $qs->Shipment->ShipTo->Address->StateProvinceCode = $shpmnt->destAddress->state;
        $qs->Shipment->ShipTo->Address->PostalCode = $shpmnt->destAddress->zip;
        $qs->Shipment->ShipTo->Address->CountryCode = $shpmnt->destAddress->country;
        $qs->Shipment->ShipTo->Name = $shpmnt->destAddress->name;
        // Ship From address
        $qs->Shipment->ShipFrom->Address->AddressLine = $this->__getAddressArray($shpmnt->origAddress->address1, $shpmnt->origAddress->address2);
        $qs->Shipment->ShipFrom->Address->City = $shpmnt->origAddress->city;
        $qs->Shipment->ShipFrom->Address->StateProvinceCode = $shpmnt->origAddress->state;
        $qs->Shipment->ShipFrom->Address->PostalCode = $shpmnt->origAddress->zip;
        $qs->Shipment->ShipFrom->Address->CountryCode = $shpmnt->origAddress->country;
        $qs->Shipment->ShipFrom->Name = $shpmnt->origAddress->name;
        // Service type
        // TODO cycle through available services
        $qs->Shipment->Service->Code = "03";
        $qs->Shipment->Service->Description = "UPS Ground";
        // Package information
        $pkg = new PackageType();
        $pkg->PackagingType->Code = "02";
        $pkg->PackagingType->Description = "Package/customer supplied";
        //   dimensions
        $pkg->Dimensions->UnitOfMeasurement->Code = $shpmnt->dimensions->dimensionsUnit;
        $pkg->Dimensions->Length = $shpmnt->dimensions->length;
        $pkg->Dimensions->Width = $shpmnt->dimensions->width;
        $pkg->Dimensions->Height = $shpmnt->dimensions->height;

        $pkg->PackageServiceOptions->DeclaredValue->CurrencyCode = "USD";
        $pkg->PackageServiceOptions->DeclaredValue->MonetaryValue = $shpmnt->dimensions->value;

        $pkg->PackageServiceOptions->DeclaredValue->CurrencyCode = "USD";
        $pkg->PackageWeight->UnitOfMeasurement = $this->__getWeightUnit($shpmnt->dimensions->weightUnit);
        $pkg->PackageWeight->Weight = $shpmnt->dimensions->weight;
        $qs->Shipment->Package = $pkg;
        $qs->CustomerClassification->Code = 123456;
        $qs->CustomerClassification->Description = "test_rate_request";
        return $qs;
    }

    // fill out and return a UPSSecurity data structure
    private function __getUPSS()
    {
        if (!isset($this->_upss)) {
            $unmt = new UsernameToken();
            $unmt->Username = self::UPSS_USERNAME;
            $unmt->Password = self::UPSS_PASSWORD;
            $sat = new ServiceAccessToken();
            $sat->AccessLicenseNumber = self::UPSS_ACCESS_LICENSE_NUMBER;
            $upss = new UPSSecurity();
            $upss->UsernameToken = $unmt;
            $upss->ServiceAccessToken = $sat;
            $this->_upss = $upss;
        }
        return $this->_upss;
    }

    // get our shipper/account info (some items blanked for stackoverflow)
    private function __getAcctInfo()
    {
        if (!isset($this->_shpr)) {
            $shpr = new ShipperType();
            $shpr->Address->AddressLine = array(
                "CONTACT",
                "STREET ADDRESS"
            );
            $shpr->Address->City = "CITY";
            $shpr->Address->StateProvinceCode = "MI";
            $shpr->Address->PostalCode = "ZIPCODE";
            $shpr->Address->CountryCode = "US";

            $shpr = new ShipperType();
            $shpr->Name = "COMPANY NAME";
            $shpr->ShipperNumber = self::UPS_ACCOUNT_NUMBER;
            $shpr->Address = $addr;

            $this->_shpr = $shpr;
        }

        return $this->_shpr;
    }

    private function __getAddressArray($adr1, $adr2 = null)
    {
        if (isset($adr2) && $adr2 !== '') {
            return array($adr1, $adr2);
        } else {
            return array($adr1);
        }
    }

}

它似乎甚至没有达到通过Soap发送任何东西的程度,所以我假设它因为与WSDL信息不匹配而死亡。 (请记住,我已尝试使用相同的WSDL文件向手动创建的SoapClient发送一个正确播种的键/值详细信息数组,并产生相同的错误) 如果有错误让我知道“客户数据”是一个问题,那就太好了。这个PHP soap实现并没有给我留下深刻的印象!

1 个答案:

答案 0 :(得分:2)

我知道这个答案可能为时已晚,但无论如何我都会提供一些反馈。为了创建自定义SOAP Header,您必须覆盖SoapHeader类。

/*
 * Auth Class to extend SOAP Header for WSSE Security
 * Usage:
 * $header = new upsAuthHeader($user, $password);
 * $client = new SoapClient('{...}', array("trace" => 1, "exception" => 0));
 * $client->__setSoapHeaders(array($header));
 */

class upsAuthHeader extends SoapHeader
{
...
    function __construct($user, $password)
    {

        // Using SoapVar to set the attributes has proven nearly impossible; no WSDL.
        // It might be accomplished with a combined SoapVar and stdClass() approach.

        // Security Header - as a combined XSD_ANYXML SoapVar
        // This method is much easier to define all of the custom SoapVars with attributes.
        $security = '<ns2:Security xmlns:ns2="'.$this->wsse.'">'. // soapenv:mustUnderstand="1" 
                        '<ns2:UsernameToken ns3:Id="UsernameToken-49" xmlns:ns3="'.$this->wsu.'">'.
                            '<ns2:Username>'.$user.'</ns2:Username>'.
                            '<ns2:Password Type="'.$this->password_type.'">'.htmlspecialchars($password).'</ns2:Password>'.
                        '</ns2:UsernameToken>'.
                    '</ns2:Security>';
        $security_sv = new SoapVar($security, XSD_ANYXML);
        parent::__construct($this->wsse, 'Security', $security_sv, false);

    }

}

然后在soap调用之前调用upsAuthHeader()。

$client = new SoapClient($this->your_ups_wsdl,
    array('trace' => true,
        'exceptions' => true,
        'soap_version' => SOAP_1_1
    )
);
// Auth Header - Security Header
$header = new upsAuthHeader($user, $password);
// Set Header
$client->__setSoapHeaders(array($header));