PHP SOAP客户端用于英国邮政WebService API?

9
我正在开发一个电商网站,用户可以在上面下单。为了追踪订单的交付情况,我需要向用户提供一个链接,链接包含用户填写的表单中的所有参数,以便使用UK Mail提供的链接来跟踪订单状态。
我需要使用UK Mail Web Service API,但我对SOAP WSDL不熟悉,请问有人能告诉我如何使用吗?
根据我的理解,我已经完成以下基本操作,现在需要进一步实现以下功能:
1. 验证登录并使用验证令牌 2. 发送参数以创建国内分配任务 3. 追踪交付状态
这是我的更新代码:
<?php 

$LoginWebRequest = new stdClass();
$LoginWebRequest->Username = 'xxx cant show here xxx';
$LoginWebRequest->Password = 'xxx cant show here xxx';

//echo "<pre>";  print_r($LoginWebRequest); "</pre>"; exit;

$Login = new stdClass();
$Login->loginWebRequest = $LoginWebRequest;

//echo "<pre>";  print_r($Login); "</pre>"; exit; 

$soapClient = new SoapClient('somewsdl?wsdl');
$LoginResponse = $soapClient->Login($Login);

//echo "<pre>";  print_r($LoginResponse); "</pre>"; exit; 

$LoginResponse = $soapClient->Login($Login);


// -- till here my code runs fine and also gives the failed output but adding the code //below gives me error cant find out whats wrong 


$AuthenticationToken = $LoginResponse->LoginResult->AuthenticationToken;



$AddDomesticConsignmentWebRequest = new stdClass();
$AddDomesticConsignmentWebRequest->Username = 'xxxxxx';
// setting the Authentication Token from the previous step
$AddDomesticConsignmentWebRequest->AuthenticationToken = $AuthenticationToken ;
// other properties are set here...

$AddDomesticConsignment = new stdClass();
$AddDomesticConsignment->request = $AddDomesticConsignmentWebRequest;


$soapClient = new SoapClient('https://svc?wsdl');
$AddDomesticConsignmentResponse = $soapClient->AddDomesticConsignment($AddDomesticConsignment);


?>

我已经解决了所有问题并获得了我的货运单号,现在只需要跟踪我的API。
我的XML文件如下,或者您可以查看PDF文件。
     Example XML Request:
    <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"        xmlns:thir="http://webapp-cl.internet-delivery.com/ThirdPartyIntegrationService">
    <soap:Header/>
    <soap:Body>
    <thir:ConsignmentTrackingSearchV1>
    <thir:UserName>mail.com</thir:UserName>
    <thir:Password>123</thir:Password>
    <thir:Token></thir:Token>
    <thir:ConsignmentNumber>01161</thir:ConsignmentNumber>
    <thir:IsPartialConsignmentNumber>false</thir:IsPartialConsignmentNumber>
    <thir:CustomerReference></thir:CustomerReference>
    <thir:IsPartialCustomerReference>false</thir:IsPartialCustomerReference>
    <thir:DeliveryPostCode></thir:DeliveryPostCode>
    <thir:MailingID></thir:MailingID>
    <thir:MaxResults>100</thir:MaxResults>
    </thir:ConsignmentTrackingSearchV1>
    </soap:Body>
    </soap:Envelope>

示例 XML 响应
      <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       <soap:Body>
       <ConsignmentTrackingSearchV1Response xmlns="http://webapp-cl.internet- delivery.com/ThirdPartyIntegrationService">
        <ConsignmentTrackingSearchV1Result>
        <ResultState>Successful</ResultState>
         <ConsignmentResults>
       <ConsignmentSearchResult>
    <ConsignmentNumber>001161</ConsignmentNumber>
    <CustomerRef1/>
   <CustomerRef2/>
     <SubCustomerRef1/>
     <SubCustomerRef2/>
      <DeliveryType/>
      <ConsignmentStatus>Delivered</ConsignmentStatus>
      <DateTimeDelivered>2010-02-11T12:00:00+00:00</DateTimeDelivered>
      <ItemsDelivered>2</ItemsDelivered>
      <RecipientName>robin</RecipientName>
      <DeliveryComments/>
      <ExpectedDeliveryDate>2010-02-11T00:00:00</ExpectedDeliveryDate>
       <DeliveryService>Next Day</DeliveryService>
      <TotalItems>2</TotalItems>
      <Consignmentkey>22</Consignmentkey>
        </ConsignmentSearchResult>
         </ConsignmentResults>
        </ConsignmentTrackingSearchV1Result>
        </ConsignmentTrackingSearchV1Response>
        </soap:Body>
       </soap:Envelope>

1
几点建议:1)您调用了Login()操作两次,请勿这样做;2)不要将脚本分开,第二部分依赖于第一部分的$AuthenticationToken;3)为$AddDomesticConsignmentWebRequest对象设置所有必需的属性。请参考文档中所需的属性;4)最好在尝试调用操作并希望得到任何合理结果之前获取您的用户名/密码。当我在CLI中尝试运行脚本时,我收到错误消息“SOAP-ERROR: Encoding: object hasn't 'ConfirmationOfDelivery' property”(见第3点)。 - zafarkhaja
谢谢您的回复,我会实现您的建议并删除两次登录,并添加必需的属性。目前我还在等待,所以无法确定。我的客户已经注册了,但尚未获得批准,希望能尽快获得批准 :) - Rinzler
2个回答

20

介绍

很显然,这里缺少文档。不幸的是,$soapClient->__getTypes()并没有提供太多信息。它只显示Web服务支持的可用复杂类型,但它并不显示它们之间的关系。即使您拥有所有可用操作及其输入输出类型的列表,由$soapClient->__getFunctions()返回,也无法保证您能在不知道复杂类型的确切性质或没有任何文档的情况下继续。但幸运的是,这是一个基于SOAP的Web服务,它提供了一个WSDL文档。WSDL文档描述了所有支持的操作和复杂类型以及它们之间的关系。因此,通过仅检查WSDL文档,我们就可以弄清楚如何使用该服务。

检查WSDL文档有两种方法:

  1. 从WSDL文档生成工件(客户端类)并检查工件
  2. 查看WSDL文档和XML模式文档。

1. 工件

工件可以由像Java或C#这样的强类型语言提供的工具生成。 https://qa-api.ukmail.com/Services/UKMAuthenticationServices/页面建议使用svcutil.exe工具为C#编程语言生成工件,或者您也可以使用wsimport工具为Java编程语言生成工件。我怀疑,对于PHP编程语言,可能没有好的工具来生成工件。

2. WSDL文档和XML模式文档

如果您不熟悉C#或Java,您始终可以通过查看WSDL文档和XML模式文档来检查WSDL文档。 XML模式文档可以包含在WSDL文档中或从外部文件导入。虽然WSDL文档描述了可以在Web服务上执行的操作,但XML模式文档描述了复杂类型及其关系。

操作

我撰写了介绍部分,以便您知道如何自行完成。下面我要展示一个示例。为了检查WSDL文档,我使用了两种方法。首先,我使用wsimport工具生成了工件,然后阅读了很多XML。

该服务的WSDL文档使用import语句将其分成几个文件。因此,为了找到所有操作和复杂类型,您必须遵循import语句。

身份验证

如果我们查看认证服务的WSDL文档(位置),我们可以看到它导入了另一个WSDL文档:

<wsdl:import namespace="http://tempuri.org/" location="https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?wsdl=wsdl1"/>

后者(位置),又引入了另一个:

<wsdl:import namespace="http://www.UKMail.com/Services/Contracts/ServiceContracts" location="https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?wsdl=wsdl0"/>

最后一个(位置),导入了所有相关的XML模式:

<wsdl:types>
  <xsd:schema targetNamespace="http://www.UKMail.com/Services/Contracts/ServiceContracts/Imports">
    <xsd:import schemaLocation="https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?xsd=xsd0" namespace="http://www.UKMail.com/Services/Contracts/ServiceContracts"/>
    <xsd:import schemaLocation="https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?xsd=xsd1" namespace="http://schemas.microsoft.com/2003/10/Serialization/"/>
    <xsd:import schemaLocation="https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?xsd=xsd2" namespace="http://www.UKMail.com/Services/Contracts/DataContracts"/>
    <xsd:import schemaLocation="https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?xsd=xsd3" namespace="http://schemas.datacontract.org/2004/07/UKMWebAPICommon.WebResponses"/>
  </xsd:schema>
</wsdl:types>

它还描述了可以通过调用$soapClient->__getFunctions()查看的操作:
Array
(
    [0] => LoginResponse Login(Login $parameters)
    [1] => LogoutResponse Logout(Logout $parameters)
)

在这里,我们可以看到Login()操作接受类型为Login$parameters作为其参数,并返回类型为LoginResponse的响应。在WSDL文档中,它看起来是这样的:

<wsdl:operation name="Login">
  <wsdl:input wsaw:Action="http://www.UKMail.com/Services/IUKMAuthenticationService/Login" message="tns:IUKMAuthenticationService_Login_InputMessage"/>
  <wsdl:output wsaw:Action="http://www.UKMail.com/Services/Contracts/ServiceContracts/IUKMAuthenticationService/LoginResponse" message="tns:IUKMAuthenticationService_Login_OutputMessage"/>
</wsdl:operation>

Login 是一个复杂类型,在其中一个导入的 XML Schema 文档(schemaLocation)中可以看到:

<xs:element name="Login">
  <xs:complexType>
    <xs:sequence>
      <xs:element xmlns:q1="http://www.UKMail.com/Services/Contracts/DataContracts" minOccurs="0" name="loginWebRequest" nillable="true" type="q1:LoginWebRequest"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

它有一个名为loginWebRequest的元素,它也是另一个导入的XML模式中描述的名为LoginWebRequest的复杂类型:

<xs:complexType name="LoginWebRequest">
  <xs:sequence>
    <xs:element name="Password" nillable="true" type="xs:string"/>
    <xs:element name="Username" nillable="true" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

LoginWebRequest 更为简单。它只有两个类型为 String简单 类型 UsernamePassword

在 PHP 中,复杂类型由 stdClass 对象表示。因此,为了调用 Login() 操作,我们需要创建两个对象 LoginLoginWebRequest

$LoginWebRequest = new stdClass();
$LoginWebRequest->Username = 'Username';
$LoginWebRequest->Password = 'p@$$w0rd';

$Login = new stdClass();
$Login->loginWebRequest = $LoginWebRequest;

$soapClient = new SoapClient('https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?wsdl');
$LoginResponse = $soapClient->Login($Login);

这将会返回一个类型为LoginResponse的结果:
<xs:element name="LoginResponse">
  <xs:complexType>
    <xs:sequence>
      <xs:element xmlns:q2="http://www.UKMail.com/Services/Contracts/DataContracts" minOccurs="0" name="LoginResult" nillable="true" type="q2:UKMLoginResponse"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

该段落包含一个名为LoginResult的元素,其类型为UKMLoginResponse

<xs:complexType name="UKMLoginResponse">
  <xs:complexContent mixed="false">
    <xs:extension base="tns:UKMWebResponse">
      <xs:sequence>
        <xs:element minOccurs="0" name="Accounts" nillable="true" type="tns:ArrayOfAccountWebModel"/>
        <xs:element name="AuthenticationToken" nillable="true" type="xs:string"/>
      </xs:sequence>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

UKMLoginResponse有两个自己的元素AccountsAuthenticationToken,类型分别为ArrayOfAccountWebModelString,还继承了UKMWebResponse的三个元素(注意extension语句):Errors类型为ArrayOfUKMWebErrorWarnings类型为ArrayOfUKMWebWarningResult类型为UKMResultState

<xs:complexType name="UKMWebResponse">
  <xs:sequence>
    <xs:element minOccurs="0" name="Errors" nillable="true" type="tns:ArrayOfUKMWebError"/>
    <xs:element xmlns:q1="http://schemas.datacontract.org/2004/07/UKMWebAPICommon.WebResponses" name="Result" type="q1:UKMResultState"/>
    <xs:element minOccurs="0" name="Warnings" nillable="true" type="tns:ArrayOfUKMWebWarning"/>
  </xs:sequence>
</xs:complexType>
< p > 在由 wsimport 工具生成的工件中,它看起来像这样:

public class UKMLoginResponse extends UKMWebResponse { ... }

因此,为了从LoginResponse获得身份验证令牌,我们执行以下操作:
$LoginResponse = $soapClient->Login($Login);
$AuthenticationToken = $LoginResponse->LoginResult->AuthenticationToken;

调用方法

这里我不会过于具体,因为它与我们上面所做的非常相似。

以一个例子来说,让我们调用一个 AddDomesticConsignment() 方法。根据寄存服务的 WSDL 文档和 $soapClient->__getFunctions() 返回的结果,AddDomesticConsignment() 方法需要一个类型为 AddDomesticConsignment$parameters 参数,并返回一个类型为 AddDomesticConsignmentResponse 的结果。通过分析 AddDomesticConsignment 复杂类型,我们可以看到它有一个名为 request 的元素,其类型为 AddDomesticConsignmentWebRequest,该类型扩展了 AddConsignmentWebRequest,而后者则扩展了 WebRequest。以下是 AddDomesticConsignmentWebRequest 类型的所有元素列表:

// AddDomesticConsignmentWebRequest's own elements
boolean BookIn
decimal CODAmount
string ConfirmationEmail
string ConfirmationTelephone
boolean ExchangeOnDelivery
int ExtendedCover
boolean LongLength
PreDeliveryNotificationType PreDeliveryNotification
string SecureLocation1
string SecureLocation2
boolean SignatureOptional

// elements inhereted from AddConsignmentWebRequest
string AccountNumber
AddressWebModel Address
string AlternativeRef
string BusinessName
string CollectionJobNumber
boolean ConfirmationOfDelivery
string ContactName
string CustomersRef
string Email
int Items
int ServiceKey
string SpecialInstructions1
string SpecialInstructions2
string Telephone
decimal Weight

// elements inhereted from WebRequest
string Username
string AuthenticationToken

请注意,并非所有元素都是必需的。其中可选的元素在 XML Schema 中具有 minOccurs 属性,其设置为 0
<xs:element minOccurs="0" name="PreDeliveryNotification" type="tns:PreDeliveryNotificationType"/>

因此,最终我们这样调用该方法:

$AddDomesticConsignmentWebRequest = new stdClass();
$AddDomesticConsignmentWebRequest->Username = 'Username';
// setting the Authentication Token from the previous step
$AddDomesticConsignmentWebRequest->AuthenticationToken = $AuthenticationToken;
// other properties are set here...

$AddDomesticConsignment = new stdClass();
$AddDomesticConsignment->request = $AddDomesticConsignmentWebRequest;

$soapClient = new SoapClient('https://qa-api.ukmail.com/Services/UKMConsignmentServices/UKMConsignmentService.svc?wsdl');
$AddDomesticConsignmentResponse = $soapClient->AddDomesticConsignment($AddDomesticConsignment);
AddDomesticConsignmentResponse会根据其在XML模式文件中的定义进行解析,就像我们解析LoginResponse一样。
好了,我想这就是全部内容了。虽然我自己没有尝试过,但理论上应该可以工作。 希望这有所帮助。

更新

根据文档,跟踪托运应该很容易,只需要执行以下操作即可:
// create the SOAP client
$soapClient = new SoapClient('http://web-service/?wsdl');
// call the `ConsignmentTrackingSearchV1` method and pass the search parameters
$ConsignmentTrackingSearchV1Response = $soapClient->ConsignmentTrackingSearchV1(
    'mail.com', // Username
    '123',      // Password
    '',         // Token
    '01161',    // ConsignmentNumber
    'false',    // IsPartialConsignmentNumber
    '',         // CustomerReference
    'false'     // IsPartialCustomerReference
    '',         // DeliveryPostCode
    '',         // MailingID
    100         // MaxResults
);
// parse the response
$ConsignmentTrackingSearchV1Result = $ConsignmentTrackingSearchV1Response->ConsignmentTrackingSearchV1Result;
$ResultState = $ConsignmentTrackingSearchV1Result->ResultState; // Successful
$ConsignmentResults = $ConsignmentTrackingSearchV1Result->ConsignmentResults;
// loop through the `ConsignmentResults`
foreach ($ConsignmentResults as $ConsignmentSearchResult) {
    $ConsignmentNumber = $ConsignmentSearchResult->ConsignmentNumber;
    $ConsignmentStatus = $ConsignmentSearchResult->ConsignmentStatus;
    // other properties
}

就是这样了!


1
@Rinzler,很可能Web服务不需要每次请求时都要求一个新的令牌。然而,有一天令牌可能会过期。因此,根据您的服务规范,您可以将令牌存储在本地某个地方,可以是临时存储(如Sessions)或更永久的配置文件中。 - zafarkhaja
我意识到所有的API都是相互连接的,如果你查看API PDF文件,首先我必须登录,然后我必须预订司机并从他那里获得一个CollectionJobNumber,然后在创建国内寄递时使用它。身份验证令牌在所有服务中都需要输入,因此所有代码可能都在同一页上。 - Rinzler
1
@Rinzler,我已经查看了您的更新并相应地更新了我的答案11小时前... 您不能看到吗? - zafarkhaja
1
@Rinzler,不是的,stdClass对象用于XML模式中描述的复杂类型。但是ConsignmentTrackingSearchV1操作没有任何复杂类型,它的所有输入参数都是标量类型。您可以在文档中看到它,有一个表格列出了所有参数及其类型,例如Alpha(字符串),NumberDateTime等。 - zafarkhaja
1
@Rinzler,“谢谢”并不能增加声望 :) 不客气! - zafarkhaja
显示剩余13条评论

3

我很感谢zafarkhaja先生给了我很多帮助,但是对于像我这样的其他人,我想解释一下对我有用的方法,以便将来也能对其他人有所帮助。

处理复杂类型 例如,在我的英国邮政API中,我是这样登录的

$LoginWebRequest = new stdClass();
$LoginWebRequest->Username = 'username';
$LoginWebRequest->Password = 'password';



$Login = new stdClass();
$Login->loginWebRequest = $LoginWebRequest;



$soapClient = new SoapClient('somewsdl?wsdl', array('cache_wsdl' => WSDL_CACHE_NONE) );


$LoginResponse = $soapClient->Login($Login);

太好了,我成功登录了。系统返回了一些响应信息,需要我去检索获取。

 $AuthenticationToken = $LoginResponse->LoginResult->AuthenticationToken; 

我已经成功获取了认证令牌,当然,您可以根据您的Web服务更改参数。

当你不处理复杂类型时,使用SOAP客户端

   $soapClient = new SoapClient('http://somewsdl?wsdl');

    //create an array whose valuse will be passed has parameters
          $input_array = array (   
               UserName => 'username',
               Password => 'password'

     ) ;

    // passes our array
    $ConsignmentResponse = $soapClient1->TrackingSearch($input_array);

  //retrieve the response like this 

    $ResultState =  $ConsignmentResponse->ResultState; // Successful

我在各处搜索,但没有人发布任何可用的示例。对于我来说,所有的代码都适用于带有复杂类型和不带复杂类型的 PHP SOAP 1.1。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接