I am trying to make a non-wsdl SOAP client call using php. My code is something like this:
try {
$URL = 'http://example.com/webservices/security/accesscontrol.asmx';
$sc = new SoapClient(null, array(
'location' => $URL,
'uri' => 'http://example.com/webservices/security/',
'trace' => 1
));
$usertoken = array('UserNameToken' =>
array(
'UserName' => 'test',
'Password' => 'test123'
));
$header = new SoapHeader('http://www.docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', 'Security', $usertoken);
$sc->__setSoapHeaders($header);
$test = $sc->__soapCall("AuthenticateClient",
array(),
array('soapaction' => 'http://example.com/webservices/security/AuthenticateClient')
);
If I debug and see the Last request header part of xml it looks like this:
<SOAP-ENV:Header>
<ns2:Security>
<item><key>UserNameToken</key><value><item><key>UserName</key><value>test</value></item><item><key>Password</key><value>test123</value></item></value></item>
</ns2:Security>
</SOAP-ENV:Header>
But if I use wsdl file, the xml header looks like this:
<SOAP-ENV:Header>
<ns2:Security>
<ns2:UserNameToken>
<ns2:UserName>test</ns2:UserName>
<ns2:Password>test123</ns2:Password>
</ns2:UserNameToken>
</ns2:Security>
</SOAP-ENV:Header>
How can I make the header part like above using non-wsdl SOAP client call? Becasue its not working and giving an error that is caused by "if either the UserName Token or the UserName was not provided in the AuthenticateClient Soap Header Request"
Thanks in advance for your help.
Please note that I have changed the URL and password intentionally as I can not disclose them.
You can create the part of the header manually and insert it into the SoapHeader, try to do something like this:
$URL = 'http://example.com/webservices/security/accesscontrol.asmx';
$soapClient = new SoapClient(null, array(
'location' => $URL,
'uri' => 'http://example.com/webservices/security/',
'trace' => 1
));
$headerPart = '
<SOAP-ENV:Header>
<ns2:Security>
<ns2:UserNameToken>
<ns2:UserName>DASKO</ns2:UserName>
<ns2:Password>welcome1</ns2:Password>
</ns2:UserNameToken>
</ns2:Security>
</SOAP-ENV:Header>
';
$soapVarHeader = new SoapVar($headerPart, XSD_ANYXML, null, null, null);
$header = new SoapHeader(
'http://www.docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', // Namespace - namespace of the WebService
'Security',
$soapVarHeader,
false // mustunderstand
);
$soapClient->__setSoapHeaders($header);
Related
After over a half a day of trying and reading tutorials on creating a simple SOAP client, I am no closer to retrieving a request from API I attempting to work with.
WSDL: http://publicapi.ekmpowershop31.com/v1.1/publicapi.asmx?WSDL
I can make the request from SOAP UI with the following simple SOAP request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pub="http://publicapi.ekmpowershop.com/">
<soapenv:Header/>
<soapenv:Body>
<pub:GetOrders>
<!--Optional:-->
<pub:GetOrdersRequest>
<!--Optional:-->
<pub:APIKey>myApiKey</pub:APIKey>
</pub:GetOrdersRequest>
</pub:GetOrders>
</soapenv:Body>
</soapenv:Envelope>
This above returns the expected data.
When it comes to translating the request into a PHP I have the following:
$wsdl = 'http://publicapi.ekmpowershop31.com/v1.1/publicapi.asmx?WSDL';
$trace = true;
$exceptions = false;
$debug = true;
$client = new SoapClient($wsdl,
array(
'trace' => $trace,
'exceptions' => $exceptions,
'debug' => $debug,
));
$param = array('GetOrdersRequest' => array(
'APIKey' => 'myApiKey'
)
);
$resp = $client->GetOrders();
print_r($param);
print_r($client->__getLastRequest());
print_r($client->__getLastResponse());
If put the $param into the GetOrders function then, it breaks and nothing happens.
Even if I use an array in the $client->GetOrders(array('someArry' => $param)) then response and request still always looks the same and looks like the body of the SOAP request is never created:
Request:
?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://publicapi.ekmpowershop.com/"><SOAP-ENV:Body><ns1:GetOrders/></SOAP-ENV:Body></SOAP-ENV:Envelope>
Response:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><GetOrdersResponse xmlns="http://publicapi.ekmpowershop.com/"><GetOrdersResult><Status>Failure</Status><Errors><string>Object reference not set to an instance of an object.</string></Errors><Date>2017-04-03T16:00:42.9457446+01:00</Date><TotalOrders>0</TotalOrders><TotalCost xsi:nil="true" /></GetOrdersResult></GetOrdersResponse></soap:Body></soap:Envelope>
If anyone can shed some light on what I am doing wrong here that would be real big help?
P.S My experience of SOAP in PHP is limited as I am used to SOAP in a java env. Thanks
You need to pass the parameters into the $client->GetOrders() call. Also the WSDL defines some required parameters, so a minimal example is something like:
$wsdl = 'http://publicapi.ekmpowershop31.com/v1.1/publicapi.asmx?WSDL';
$trace = true;
$exceptions = false;
$debug = true;
$client = new SoapClient($wsdl,
array(
'trace' => $trace,
'exceptions' => $exceptions,
'debug' => $debug,
));
$param = array(
'GetOrdersRequest' => array(
'APIKey' => 'dummy-key',
'CustomerID' => 1,
'ItemsPerPage' => 1,
'PageNumber' => 1,
)
);
$resp = $client->GetOrders($param);
print_r($param);
print_r($client->__getLastRequest());
print_r($client->__getLastResponse());
This gives the error response:
<Errors><string>Invalid character in a Base-64 string.</string></Errors>
which presumably is because my API key is invalid.
I'd like to make a soap call to the eBay api and finds products by keywords. With the help of eBay documentation and other online resources, I came up with this code:
$client = new \SoapClient('http://developer.ebay.com/webservices/finding/latest/FindingService.wsdl');
$soap_headers = array(
new \SoapHeader('X-EBAY-SOA-OPERATION-NAME', 'findItemsByKeywords'),
new \SoapHeader('X-EBAY-SOA-SERVICE-VERSION', '1.3.0'),
new \SoapHeader('X-EBAY-SOA-REQUEST-DATA-FORMAT', 'XML'),
new \SoapHeader('X-EBAY-SOA-GLOBAL-ID', 'EBAY-US'),
new \SoapHeader('X-EBAY-SOA-SECURITY-APPNAME', '<there would be the key>'),
);
$client->__setSoapHeaders($soap_headers);
// Call wsdl function
$result = $client->__soapCall("findItemsByKeywords", array("keywords" => "Potter"));
However, this code results in an error:
"Service operation is unknown,
500 Internal Server Error - SoapFault"
I tried changing the first line into this (don't know why it should make a difference, but I saw it somewhere):
$client = new \SoapClient(NULL, array(
"location" => 'http://svcs.sandbox.ebay.com/services/search/FindingService/v1',
'uri' => 'http://svcs.sandbox.ebay.com/services/search/FindingService/v1')
);
And now this result in this error: Missing SOA operation name header,
500 Internal Server Error - SoapFault
Does anybody know what causes these errors to occur and how to fix them?
Thank you, Mike!
The following code works.
define('APP_ID', '*** Your App ID ***');
$wsdl = 'http://developer.ebay.com/webservices/finding/latest/FindingService.wsdl';
// For sandbox
$endpoint_uri = 'http://svcs.sandbox.ebay.com/services/search/FindingService/v1';
// For production
//$endpoint_uri = 'http://svcs.ebay.com/services/search/FindingService/v1';
// We'll use this namespace explicitly for the 'keywords' tag,
// because the SoapClient doesn't apply it automatically.
$ns = 'http://www.ebay.com/marketplace/search/v1/services';
// The SOAP function
$operation = 'findItemsByKeywords';
$http_headers = implode(PHP_EOL, [
"X-EBAY-SOA-OPERATION-NAME: $operation",
"X-EBAY-SOA-SECURITY-APPNAME: " . APP_ID,
]);
$options = [
'trace' => true,
'cache' => WSDL_CACHE_NONE,
'exceptions' => true,
'location' => $endpoint_uri,
//'uri' => 'ns1',
'stream_context' => stream_context_create([
'http' => [
'method' => 'POST',
'header' => $http_headers,
]
]),
];
$client = new \SoapClient($wsdl, $options);
try {
$wrapper = new StdClass;
$wrapper->keywords = new SoapVar('harry potter', XSD_STRING,
null, null, null, $ns);
$result = $client->$operation(new SoapVar($wrapper, SOAP_ENC_OBJECT));
var_dump($result);
} catch (Exception $e) {
echo $e->getMessage();
}
As already mentioned, one of the issues is that you pass X-EBAY-SOA-* values as SOAP headers. The service expects them as HTTP headers:
HTTP Headers or URL Parameters—Each Finding API call requires certain
HTTP headers or URL parameters. For example, you must specify your
AppID in the X-EBAY-SOA-SECURITY-APPNAME header or SECURITY-APPNAME
URL parameter. Similarly, you must always specify the call name in the
X-EBAY-SOA-OPERATION-NAME header or OPERATION-NAME URL parameter.
Other headers are optional or conditionally required.
The second issue is that the SoapClient location option is not specified. It should contain an URI to one of the service endpoints. Otherwise, the API returns Service operation is unknown error.
Finally, the SoapClient doesn't put the keywords into Ebay's namespace:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.ebay.com/marketplace/search/v1/services">
<SOAP-ENV:Body>
<ns1:findItemsByKeywordsRequest>
<keywords>harry potter</keywords>
</ns1:findItemsByKeywordsRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
As a result, the API returns an error: Keywords value required.. So I've specified the namespace for the keywords tag explicitly.
Also note the use of different endpoint URIs for sandbox and for production.
The thing is, the service expects HTTP Headers (or query string parameters, apparently, by reading their guide a bit).
With __setSoapHeaders you are, well, setting SOAP Headers.
Try this, for instance
$wsdl = 'http://developer.ebay.com/webservices/finding/latest/FindingService.wsdl';
$appID = 'YOUR KEY/ID';
$headers = implode(PHP_EOL, [
"X-EBAY-SOA-OPERATION-NAME: findItemsByKeywords",
"X-EBAY-SOA-SECURITY-APPNAME: $appID"
]);
$options = [
'trace' => true,
'cache' => WSDL_CACHE_NONE,
'exceptions' => true,
'stream_context' => stream_context_create([
'http' => [
'header' => $headers
]
]),
];
$client = new SoapClient($wsdl, $options);
$response = $client->findItemsByKeywords([
'keywords' => 'Potter'
]);
I am making some PHP application and I want to use my WCF service, which is working good. I need to make soap call, and set header to something like this:
<s:Header>
<a:Action s:mustUnderstand="1">GetPage</a:Action>
<a:MessageID>1</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://someurl.com</a:To>
In my php file where I want to make the call I have:
$ns = 'http://www.w3.org/2005/08/addressing'; //Namespace of the WS.
//Body of the Soap Header.
$headerbody = array('MessageID' => '1',
'Action' => 'GetPage',
'To' => 'http://someurl.com');
//Create Soap Header.
$header = new SOAPHeader($ns, $headerbody);
//set the Headers of Soap Client.
$soapClient->__setSoapHeaders($header);
$GetPageResponse = $soapClient->GetPage($GetPageRequest);
When I go to the logs of my service, I see that the message that came in looks like:
<SOAP-ENV:Envelope xmlns:SOAP- ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:Flow/2012/10">
<SOAP-ENV:Header>
<To SOAP-ENV:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://someurl.com</To>
<Action SOAP-ENV:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">urn:Flow/2012/10/GetPage</Action>
</SOAP-ENV:Header>
</SOAP-ENV:Envelope>
$GetPageRequest is for now irrelevant. So, in my header there is no MessageID. What am I missing here? Did I set something wrong? Also, how to correctly set part 'ReplyTo'?
Not sure why MessageID is not getting through in your output yet, but adding ReplyTo->Address is as simple as adding an sub-array to your header array.
$headerbody = array('MessageID' => '1',
'Action' => 'GetPage',
'To' => 'http://someurl.com',
'ReplyTo' => array (
'Address' => 'http://www.w3.org/2005/08/addressing/anonymous'
)
);
I'm trying to make a request with the PHP SoapClient in non-WSDL mode. I'm passing parameters as a multi-dimensional object as shown in the below code snippet:
$params = new stdClass;
$params->Characteristic = new stdClass;
$params->Characteristic->Name = 'PRODUCT_TYPE';
$params->Characteristic->CharacteristicValue = new stdClass;
$params->Characteristic->CharacteristicValue->Value = $type;
$params->Characteristic->CharacteristicValue->Type = 'STRING';
$client = new SoapClient(NULL, array( 'trace' => true, 'exceptions' => true, 'uri' => $uri, 'location' => $location,
'connection_timeout'=>9999,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
'soap_version' => SOAP_1_1, 'encoding' => 'ISO-8859-1',
'use' => SOAP_LITERAL
));
$response = $client->thisIsTheFunction($params);
The generated XML is almost right apart from being wrapped in a tag:
<soap-env:envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://[removed]">
<soap-env:body>
<ns1:thisisthefunction>
<param0>
<characteristic>
<name>PRODUCT_TYPE</name>
<characteristicvalue>
<value>Adhoc</value>
<type>STRING</type>
</characteristicvalue>
</characteristic>
</param0>
</ns1:thisisthefunction>
</soap-env:body>
</soap-env:envelope>
The problem is this is being detected as malformed by the service. Is there any way we can remove this extra tag?
I think if you want remove param0 and put characteristicValue in this place, you need to use a SoapParam (http://www.php.net/manual/en/class.soapparam.php).
In fact, your call must proceed like that :
$response = $client->thisIsTheFunction(new SoapParam($params->Characteristic, 'ns1:Characteristic'));
Now, your generated XML looks like that :
<soap-env:envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://[removed]">
<soap-env:body>
<ns1:thisisthefunction>
<ns1:characteristic>
<name>PRODUCT_TYPE</name>
<characteristicvalue>
<value>Adhoc</value>
<type>STRING</type>
</characteristicvalue>
</ns1:characteristic>
</ns1:thisisthefunction>
</soap-env:body>
Good luck !
I am using PHP SoapClient in WSDL mode.
This is what the expected SOAP request should look like:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns="http://xml.m4u.com.au/2009">
<soapenv:Header/>
<soapenv:Body>
<ns:sendMessages>
<ns:authentication>
<ns:userId>Username</ns:userId>
<ns:password>Password</ns:password>
</ns:authentication>
<ns:requestBody>
<ns:messages>
<ns:message>
<ns:recipients>
<ns:recipient>61400000001</ns:recipient>
</ns:recipients>
<ns:content>Message Content</ns:content>
</ns:message>
</ns:messages>
</ns:requestBody>
</ns:sendMessages>
</soapenv:Body>
</soapenv:Envelope>
And this is what PHP SoapClient is sending:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://xml.m4u.com.au/2009">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns1:sendMessages>
<ns1:authentication>
<userId>Username</userId>
<password>Password</password>
</ns1:authentication>
<ns1:requestBody>
<messages>
<message>
<recipients>
<recipient>61400000001</recipient>
</recipients>
<content>Message Content</content>
</message>
</messages>
</ns1:requestBody>
</ns1:sendMessages>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
This is how I constructed the client and params:
function sendMessages($recipient, $content) {
$authenticationType = new SoapVar(array('userId' => $this->username, 'password' => $this->password), SOAP_ENC_OBJECT);
$recipientsType = new SoapVar(array('recipient' => $recipient), SOAP_ENC_OBJECT);
$messageType = new SoapVar(array('recipients' => $recipientsType, 'content' => $content), SOAP_ENC_OBJECT);
$messagesType = new SoapVar(array('message' => $messageType), SOAP_ENC_OBJECT);
$requestBodyType = new SoapVar(array('messages' => $messagesType), SOAP_ENC_OBJECT);
$params = array(
'authentication' => $authenticationType,
'requestBody' => $requestBodyType
);
try {
$this->soapClient = new SoapClient($this->wsdl, array('trace' => 1));
$this->soapClient->__setSoapHeaders(array());
return $this->soapClient->sendMessages($params);
} catch (SoapFault $fault) {
echo '<h2>Request</h2><pre>' . htmlspecialchars($this->soapClient->__getLastRequest(), ENT_QUOTES) . '</pre>';
trigger_error("SOAP Fault: (faultcode: {$fault->faultcode}, faultstring: {$fault->faultstring})", E_USER_ERROR);
}
}
Why is 'ns1' present for 'authentication' and 'requestBody' but missing for their child nodes?
What am I doing wrong? The WSDL is located here => http://soap.m4u.com.au/?wsdl
Appreciate anyone who can help.
You must specify the URI of the namespace to encode the object. This will include everything necessary (including your missing namespace). The acronym of the namespace name is irrelevant.
This:
$authenticationType = new SoapVar(array('userId' => $this->username, 'password' => $this->password), SOAP_ENC_OBJECT);
should be:
$authenticationType = new SoapVar(array('userId' => $this->username, 'password' => $this->password), SOAP_ENC_OBJECT, "authentication","http://xml.m4u.com.au/2009");
The problem you're seeing with the namespace serialization comes from using soapvar without all the parameters. When it serializes the request prior to sending it assumes the namespace is already included. If, instead, you had created each as a simple array and included them in $params it would include the namespace for the internal parameters correctly. By way of demonstration:
$authenticationType = array('userId' => $this->username, 'password' => $this->password)
try using nusoap library. Below is sample code:
<?php
$wsdl = "http://soap.m4u.com.au/?wsdl";
// generate request veriables
$data = array();
$action = ""; // ws action
$param = ""; //parameters
$options = array(
'location' => 'http://soap.m4u.com.au',
'uri' => ''
);
// eof generate request veriables
//$client = new soap_client($wsdl, $options);// create soap client
$client = new nusoap_client($wsdl, 'wsdl');
$client->setCredentials($api_username, $api_password);// set crendencials
$opt = $client->call($action, $param, '', '', false, true);
print_r($opt);
?>