3

UPS Ratings Web サービスを利用するために、PHP と SoapClient を使用しようとしています。サービス リクエストを作成するための開始点ライブラリを作成する 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 を使用して、RateRequest および UPSSecurity 部分の独自の (インスタンス) データ構造を除く ProcessRate メソッドを使用して RateService クラスを引き出しますが、上記のすべてで同じエラーが生成されます。

インタープリターによって定義されたクラスを呼び出すために使用しているコードのサンプルを次に示します。


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 の実装は、私には感銘を与えません!

4

1 に答える 1

2

この回答はおそらく遅すぎることはわかっていますが、とにかくフィードバックを提供します。カスタム SOAP ヘッダーを作成するには、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));
于 2011-09-18T15:13:16.080 に答える