How can I add urn to SoapClient on PHP? - php

I have this xml generated by SoapUI from this wsdl:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:safetypay:mws:api:messages">
<soapenv:Header/>
<soapenv:Body>
<urn:CommunicationTestRequest>
<urn:ApiKey>?</urn:ApiKey>
<urn:RequestDateTime>?</urn:RequestDateTime>
<urn:Signature>?</urn:Signature>
</urn:CommunicationTestRequest>
</soapenv:Body>
</soapenv:Envelope>
It Works ok, but when I create it with the client for PHP, SoapClient, it fails... I have found that the error its because it is slightly different, it is only missing the urn (which I have no idea what it is). The xml from SoapClient looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<CommunicationTestRequest>
<RequestDateTime>2012-04-17T23:21:54</RequestDateTime>
<ApiKey>XXXXX</ApiKey>
<Signature>XXXXX</Signature>
</CommunicationTestRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The three main diferences are:
1) In the definition the code generated by SoapClient is lacking of this:
xmlns:urn="urn:safetypay:mws:api:messages"
2) In the xml generated by SoapUI I have soapenv, while in the one generated by SoapClient it is SOAP-ENV
3) In the SoapUI's I have urn before each child, the SoapClient's not.
I've spend almost the whole day changing configurations and moving backward from objects to array (since in the past it works with other wsdl's), and my research on the internet didn't gave me nothing... so... can anyone have any idea of what's my mistake?
The request is this:
$std = new RequestDateTime();
$std->RequestDateTime = date ( 'Y-m-d\TH:i:s', time () );
$std->ApiKey = SafetyPaySoap::API_KEY_SAND;
$std->Signature = SafetyPaySoap::SIG_KEY_SAND;
$this->_wsdl->CommunicationTest($std);
where the configuration for the SoapClient is this one
$config = array ();
$config ['exceptions'] = true;
$config ['trace'] = true;
$config ['cache_wsdl'] = WSDL_CACHE_NONE;
$config ['soap_version'] = SOAP_1_1;
parent::SoapClient ( $url . '?wsdl', $config );
Edit:
I was able to fix this error. I had to rewrite the __doRequest method. And it now looks like this:
public function __doRequest($request, $location, $action, $version, $one_way = null) {
$request = str_ireplace("<{$this->_method}Request>", "<{$this->_method}Request xmlns='urn:safetypay:mws:api:messages'>", $request);
return parent::__doRequest ( $request, $location, $action, $version, $one_way );
}
So, what I'm doing is add the namespace at the request, and with that it generates this xml:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<CommunicationTestRequest xmlns='urn:safetypay:mws:api:messages'>
<ApiKey>XXXX</ApiKey>
<RequestDateTime>2012-04-18T20:22:51</RequestDateTime>
<Signature>XXXXX</Signature>
</CommunicationTestRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The urn: is actually a XML namespace. Looking at the documentation for SoapClient, it looks like it passes in the target namespace as a 'uri' entry in the configuration array. It might be worth trying the below and seeing if that makes a difference.
$config = array();
$config['uri'] = "urn:safetypay:mws:api:messages";
// your other configurations
parent::SoapClient('https://mws2.safetypay.com/sandbox/express/ws/v.2.4/MerchantWS.asmx?wsdl', $config);

Related

PHP SoapServer return empty response on function without return

i have a soap with a method that return nothing
class mySoapService {
foo(){
}
}
$wsdl = ...;
$soapService = new mySoapService();
$soapServer = new \SoapServer($wsdl);
$soapServer->setObject($soapService);
$soapServer->handle(); // write nothing on call foo
some soap client raise an exception because they expect a proper xml-soap empty response, like this:
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
the questions are:
why PHP SoapServer return an empty response and not a xml-soap empty response?
what say the soap standard for method without return a value?
Changing
$soapServer->handle();
to
$soapServer->handle(file_get_contents("php://input"));
worked for me

PHP SoapServer formatting

I am new to SOAP and trying to format a SOAP response using PHP's SoapServer class.
It might be useful to know that this is for an EWS Push Subscription callback service and that I am using the php-ews library. I have managed to subscribe to the EWS Push Subscription but am having trouble formatting the reply which EWS is expecting from my callback service.
I need the following SOAP response returned by my PHP SOAP service:
<s:Envelope xmlns:s= "http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<SendNotificationResult xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
<SubscriptionStatus>OK</SubscriptionStatus>
</SendNotificationResult>
</s:Body>
</s:Envelope>
Instead I am getting the following output:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://localhost/ioc/ebooking_v4/services/ews/notification.php" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:SendNotificationResponse>
<return xsi:type="SOAP-ENC:Struct">
<SubscriptionStatus xsi:type="xsd:string">OK</SubscriptionStatus>
</return>
</ns1:SendNotificationResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
My PHP service code:
class ewsService {
public function SendNotification( $arg ) {
$result = new EWSType_SendNotificationResultType();
$result->SubscriptionStatus = 'OK';
return $result;
}
}
$server = new soapServer( null, array(
'uri' => $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'],
));
$server->setObject( new ewsService() );
$server->handle();
The SOAP call to my service is as follows:
<soap11:Envelope xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<soap11:Header>
<t:RequestServerVersion xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" Version="Exchange2010_SP2" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" />
</soap11:Header>
<soap11:Body>
<m:SendNotification xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:ResponseMessages>
<m:SendNotificationResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:Notification>
<t:SubscriptionId>GQBjaGNpb2ExODEuY2lvLm9seW1waWMub3JnEAAAAFgvEpJcS3JDkpvHOMgnX60FhmhpPInRCA==</t:SubscriptionId>
<t:PreviousWatermark>AQAAAIiCiXu/q1ZPvYPyvRVVh5cajSoKAAAAAAA=</t:PreviousWatermark>
<t:MoreEvents>false</t:MoreEvents>
<t:StatusEvent>
<t:Watermark>AQAAAIiCiXu/q1ZPvYPyvRVVh5cajSoKAAAAAAA=</t:Watermark>
</t:StatusEvent>
</m:Notification>
</m:SendNotificationResponseMessage>
</m:ResponseMessages>
</m:SendNotification>
</soap11:Body>
</soap11:Envelope>
So I suppose my question is how do I to change the formatting of the SOAP response, using the PHP SoapServer class? Do I need to add a WSDL or pass a classmap to the SoapServer constructor?
If there is no way to do this using the SoapServer class, is there any other way to do this other than creating a string buffer and passing that back manually?
Any help would be greatly appreciated.
I figured this out and posted the solution on a more generic question of this problem here
For completeness I have included the answer here as well...
It would seem that I was close but needed to include the NotificationService.wsdl when instantiating the SoapServer class. The WSDL then allows that SoapServer class to format the response accordingly.
$server = new SoapServer( PHPEWS_PATH.'/wsdl/NotificationService.wsdl', array(
The WSDL was not included in the php-ews library download, however it is included with the Exchange Server installation. If like me you don't have access to the Exchange Server installation you can find the file here. I also had to add the following to the end of the WSDL because I was storing the WSDLs locally and not using autodiscovery:
<wsdl:service name="NotificationServices">
<wsdl:port name="NotificationServicePort" binding="tns:NotificationServiceBinding">
<soap:address location="" />
</wsdl:port>
</wsdl:service>
So the full working PHP code is as follows:
class ewsService {
public function SendNotification( $arg ) {
$result = new EWSType_SendNotificationResultType();
$result->SubscriptionStatus = 'OK';
//$result->SubscriptionStatus = 'Unsubscribe';
return $result;
}
}
$server = new SoapServer( PHPEWS_PATH.'/wsdl/NotificationService.wsdl', array(
'uri' => $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'],
));
$server->setObject( $service = new ewsService() );
$server->handle();
Which gives the following output:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://schemas.microsoft.com/exchange/services/2006/messages">
<SOAP-ENV:Body>
<ns1:SendNotificationResult>
<ns1:SubscriptionStatus>OK</ns1:SubscriptionStatus>
</ns1:SendNotificationResult>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Hope that helps someone else because that took me a while to figure out!

Sending Raw XML via PHP SoapClient request

I am trying to simply send RAW xml to a webservice via PHP and SoapClient. The problem is when I encode my XML it changes the order of elements in the XML that is converted to an associative array.
// Initialize the Soap Client:
$this->_transactionServicesClient = new SoapClient($soapWSDLUrl);
How or what would be the best way to send the following XML as a string to my SoapClient?
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.micros.com/pos/les/TransactionServices">
<SOAP-ENV:Body>
<ns1:PostTransaction>
<ns1:REQ>
<ns1:RequestHeader>
<ns1:InterfaceVersion>3.0.7</ns1:InterfaceVersion>
<ns1:ClientName>TRANS_SERVICES</ns1:ClientName>
</ns1:RequestHeader>
<ns1:CheckDetailEntries>
<ns1:MenuItem>
<ns1:ReferenceEntry>Pizza4</ns1:ReferenceEntry>
<ns1:Count>1</ns1:Count>
<ns1:Price>10.00</ns1:Price>
<ns1:ItemNumber>112001</ns1:ItemNumber>
<ns1:PriceLevel>1</ns1:PriceLevel>
<ns1:Seat xsi:nil="true"/>
</ns1:MenuItem>
</ns1:CheckDetailEntries>
<ns1:CheckHeaderRequest>
<ns1:CheckId>03:21:05.050505</ns1:CheckId>
<ns1:GuestCount>1</ns1:GuestCount>
<ns1:GuestInformation>
<ns1:ID>001</ns1:ID>
<ns1:FirstName>xxx</ns1:FirstName>
<ns1:LastName>xxx</ns1:LastName>
<ns1:Address1>xxx Rd</ns1:Address1>
<ns1:Address2>xx</ns1:Address2>
<ns1:Address3>xx</ns1:Address3>
<ns1:PhoneNum>xx</ns1:PhoneNum>
<ns1:UserText1>None</ns1:UserText1>
<ns1:UserText2>None</ns1:UserText2>
<ns1:UserText3>None</ns1:UserText3>
<ns1:GUID></ns1:GUID></ns1:GuestInformation>
</ns1:CheckHeaderRequest>
<ns1:OrderTypeNumber>1</ns1:OrderTypeNumber>
</ns1:REQ>
</ns1:PostTransaction>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Update/Resolution: Here is the code I used to extend the SOAP Client and send my raw Soap Envelope: My answer below
Update/Resolution: Here is the code I used to extend the SOAP Client and send my raw Soap Envelope
Here is how I extended SoapClient:
<?php
class MySoapClient extends SoapClient {
function __construct($wsdl, $options) {
parent::__construct($wsdl, $options);
$this->server = new SoapServer($wsdl, $options);
}
public function __doRequest($request, $location, $action, $version)
{
$result = parent::__doRequest($request, $location, $action, $version);
return $result;
}
function __myDoRequest($array,$op) {
$request = $array;
$location = 'http://xxxxx:xxxx/TransactionServices/TransactionServices6.asmx';
$action = 'http://www.micros.com/pos/les/TransactionServices/'.$op;
$version = '1';
$result =$this->__doRequest($request, $location, $action, $version);
return $result;
}
}
// To invoke my new custom method with my Soap Envelope already prepared.
$soapClient = new MySoapClient("http://xxxx:xxxx/TransactionServices/TransactionServices6.asmx?WSDL", array("trace" => 1));
$PostTransaction = $soapClient->__myDoRequest($orderRequest,$op);
?>
Also posted on pastie.org: http://pastie.org/3687935 before I turned this into the answer.
For testing purposes, you can subclass SoapClient and override the __doRequest method - it receives the generated XML and you can pre-process it.
But better find what's going wrong with the XML encoding. You can use SoapVar and SoapParam instances to specify the exact way given object has to be represented. Those saved my life when namespaces had to be given

simple php SoapClient example for paypal needed

Could I get a simple example of using PHP's SoapClient class to make an empty call to Paypal with nothing but the version number? I have the correct WSDL url and server url, so that's not what I need help with. This is what I have:
public function SOAPcall($function, $args=array()) {
$args['Version'] = '63.0';
$args = new SoapVar($args, SOAP_ENC_ARRAY, $function.'_Request');
$args = array(new SoapVar($args, SOAP_ENC_ARRAY, $function.'_Req', 'urn:ebay:api:PayPalAPI'));
$results = $this->soapClient->__soapCall($function, $args, array('location' => $this->activeKeys['certificate']), $this->soapOptions);
}
I hope it's okay I am not showing everything. The body of the request comes out completely wrong, as you can see below:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="urn:ebay:api:PayPalAPI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="urn:ebay:apis:eBLBaseComponents">
<SOAP-ENV:Header>
<ns1:RequesterCredentials>
<ns2:Credentials>
<ns2:Username>xxx</ns2:Username>
<ns2:Password>xxx</ns2:Password>
<ns2:Signature>xxx</ns2:Signature>
</ns2:Credentials>
</ns1:RequesterCredentials>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:GetBalanceReq xsi:type="ns1:GetBalance_Req">
<xsd:string>63.0</xsd:string>
</ns1:GetBalanceReq>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
It should look like this:
<?xml version=”1.0” encoding=”UTF-8”?>
<SOAP-ENV:Envelope xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xmlns:SOAP-ENC=”http://schemas.xmlsoap.org/soap/encoding/”
xmlns:SOAP-ENV=”http://schemas.xmlsoap.org/soap/envelope/”
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”
SOAP-ENV:encodingStyle=”http://schemas.xmlsoap.org/soap/encoding/”
><SOAP-ENV:Header>
<RequesterCredentials xmlns=”urn:ebay:api:PayPalAPI”>
<Credentials xmlns=”urn:ebay:apis:eBLBaseComponents”>
<Username>api_username</Username>
<Password>api_password</Password>
<Signature/>
<Subject/>
</Credentials>
</RequesterCredentials>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<specific_api_name_Req xmlns=”urn:ebay:api:PayPalAPI”>
<specific_api_name_Request>
<Version xmlns=urn:ebay:apis:eBLBaseComponents”>service_version
</Version>
<required_or_optional_fields xsi:type=”some_type_here”> data
</required_or_optional_fields>
</specific_api_name_Request>
</specific_api_name_Req>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Of course, Paypal throws a "Version is not supported" error.
This is the cleanest solution I could come up with:
$client = new SoapClient( 'https://www.sandbox.paypal.com/wsdl/PayPalSvc.wsdl',
array( 'soap_version' => SOAP_1_1 ));
$cred = array( 'Username' => $username,
'Password' => $password,
'Signature' => $signature );
$Credentials = new stdClass();
$Credentials->Credentials = new SoapVar( $cred, SOAP_ENC_OBJECT, 'Credentials' );
$headers = new SoapVar( $Credentials,
SOAP_ENC_OBJECT,
'CustomSecurityHeaderType',
'urn:ebay:apis:eBLBaseComponents' );
$client->__setSoapHeaders( new SoapHeader( 'urn:ebay:api:PayPalAPI',
'RequesterCredentials',
$headers ));
$args = array( 'Version' => '71.0',
'ReturnAllCurrencies' => '1' );
$GetBalanceRequest = new stdClass();
$GetBalanceRequest->GetBalanceRequest = new SoapVar( $args,
SOAP_ENC_OBJECT,
'GetBalanceRequestType',
'urn:ebay:api:PayPalAPI' );
$params = new SoapVar( $GetBalanceRequest, SOAP_ENC_OBJECT, 'GetBalanceRequest' );
$result = $client->GetBalance( $params );
echo 'Balance is: ', $result->Balance->_, $result->Balance->currencyID;
This produces the following XML request document, which, at the time of writing, was being successfully accepted and processed by PayPal:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:ebay:apis:eBLBaseComponents" xmlns:ns2="urn:ebay:api:PayPalAPI"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Header>
<ns2:RequesterCredentials>
<ns1:Credentials xsi:type="Credentials">
<Username>***</Username>
<Password>***</Password>
<Signature>***</Signature>
</ns1:Credentials>
</ns2:RequesterCredentials>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns2:GetBalanceReq xsi:type="GetBalanceRequest">
<GetBalanceRequest xsi:type="ns2:GetBalanceRequestType">
<ns1:Version>71.0</ns1:Version>
<ns2:ReturnAllCurrencies>1</ns2:ReturnAllCurrencies>
</GetBalanceRequest>
</ns2:GetBalanceReq>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
In response to some of the other comments on this page:
I'm fairly certain the OP has read the API doc, because that's where the example XML came from that he is trying to reproduce using PHP SOAP library.
The PayPal PHP API has some shortcomings, the biggest one being that it fails to work with E_STRICT warnings turned on. It also requires PEAR, so if you are not currently using PEAR in your project it means dragging in quite a lot of new code, which means more complexity and potentially more risk, in order to achieve what should be two or three fairly simple XML exchanges for a basic implementation.
The NVP API looks pretty good too, but I'm a glutten for punishment, so I chose the hard path. :-)
Did you check out the API's provided by PayPal here? And a direct link to the PHP SOAP API download.
And here is a link to the NVP API which PayPal recommends you use.
PHP has a very easy to use soapClass.
Which can be found here.
http://www.php.net/manual/en/class.soapclient.php

How to add attributes to soapVars

I'd like to create soapVars with attributes like this:
<tag attr="xxx">yyy</tag>
Is this possible with the SoapVar constructor, but without using XSD_ANYXML and raw xml strings?
The best way to do it is:
<?php
$tag['_'] = 'yyy';
$tag['attr'] = 'xxx';
$tagVar = new SoapVar($tag, SOAP_ENC_OBJECT);
?>
the result would be:
<tag attr="xxx">yyy</tag>
After spending many hours searching for a solution, I only found this workaround. Works in my case.
/**
* A SoapClient derived class that sets the namespace correctly in the input parameters
*/
class SoapClientNS extends SoapClient {
// return xml request
function __doRequest($request, $location, $action, $version, $one_way = NULL) {
//Replace each <Typename> with <ns1:Typename> or
$request = str_replace('RequestBase', 'ns1:RequestBase', $request);
return parent::__doRequest($request, $location, $action, $version, $one_way);
}
}
$client = new SoapClientNS($wsdlURL);
$client->getAllBooks(array('RequestBase' => array('code' => 'AAAA', 'password' => '234234fdf')));
The request XML was something like this:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://www.travco.co.uk/trlink/xsd/country/request">
<env:Body>
<ns1:getAllBooks>
<RequestBase code="AAAA" password="234234fdf"/>
</ns1:getAllBooks>
</env:Body>
</env:Envelope>
pcmind's answer did not work for me, and also not if you try it in PhpFiddle.
I stumbled on this article, which basically creates xml with XMLWriter:
http://eosrei.net/articles/2012/01/php-soap-xml-attributes-namespaces-xmlwriter
This totally works for my case.

Categories