Process SOAP Headers in PHP - php

I am building (in PHP) a SOAP server that is configured by its WSDL to accept messages that look like this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="https://my.awesome.namespace/">
<SOAP-ENV:Header>
<ns1:Header>
<ns1:APIKey>E4C5BDE0-48DC-543C-1CA3-8E55C63F8E60</ns1:APIKey>
<ns1:SiteID>111</ns1:SiteID>
</ns1:Header>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:Heartbeat>
<ns1:Message>Hello world.</ns1:Message>
</ns1:Heartbeat>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I had no problem getting my SOAPServer to process Heartbeat messages - $server->addFunction("Heartbeat"); works fine. I want, however, to be able to process the contents of the <ns1:Header> enclosure - so I can validate the API key and Site ID to make sure they are what they should be.
I looked here, (and of course elsewhere) but the responder seems to have missed the point of the question. Does anyone know how I can access the header element to validate? Do I add a function for Header the way I would a method in the body? ($server->addFunction("Header");?)
Thanks very much in advance.

Found it! Here's what you do.
Create your SOAP server:
$server = new SoapServer($your_wsdl,$any_options);
$server->setClass($name_of_your_class);
$server->handle($location_of_request_data);
The class named in $name_of_your_class, in addition to containing functions for each service defined in $your_wsdl, should also contain a function named for whatever you have in your <SOAP-ENV:Header> tag enclosure. I have <ns1:Header>, so my function is named Header. Put whatever logic you need in there. For me, I wanted to validate the API key and site ID, so I created a private variable, and if the API key and site ID are correct, the variable is set to true. All of the other functions in the class check to see if that variable is true before proceeding, and if not, a SOAPFault is thrown.
Hope this helps anyone who comes across this question on Google.
P.S.: For the functions in the class defined in $server->setClass(), don't forget that they must accept arguments in the order defined in the WSDL. That tripped me up.
Good luck to all other PHP/SOAP developers - seems like we all need it.
Edit/P.P.S.: For whatever reason, simply calling $server->addFunction("Header") did not work for me - I tried that first before I tried the setClass approach above. Just a heads-up to folks.

Related

XML tags match but namespaces differ

I am trying to setup a Soap connection between a server in php and a client in C. My server is using a working wsdl file and a class to add these methods. I can confirm with Wireshark that my client request is well received and correctly processed.
My issue is that the values of the XML element sent by the server cannot be read because the namespaces differs. By adding debug log in my client I have found that the error is :
Tags 'state' and 'ns2:state' match but namespaces differ
Issue :
The issue seems to be that the server response does not contain any default namespace :
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://www.w3.org/2005/08/adressing"
xmlns:ns2="http://www.namespace1/">
<env:Body>
<ns2:HelloResponse>
<state>OK</state>
<intElement>123</intElement>
</ns2:HelloResponse>
</env:Body>
</env:Envelope>
It looks like <state> and <intElement> are not in any namespace, so it can't match one of the client. In my Wsdl file, these element belongs to xmlns:s="http://www.w3.org/2001/XMLSchema"
What I tried :
Obvious solution is to add an namespace to these element, but I can't find a way to do it.
In my php server, I can modify any request that comes in but can't affect any response that comes out (or at least i didn't find how to do it).
2nd solution : Adding the namespace that describe these element to the Namespace struct in my client and then use the set_namespace() function.
But I couldn't manage to put them to work, please keep in mind that I am still new to the XML/Soap world, any help is appreciated.
As said before, the solution was to add the corresponding namespace to these element.
I managed to do it using ob_get_contents() and adding the namespace to <ns2:HelloResponse>. Using Wireshark was really useful for this kind of stuff.

Authorize.net: "createTransactionRequest" has invalid child element "clientId"

I am trying to make a test transaction using my Laravel 7 app and Authorize.net.
After submitting the sample data, I'm getting:
The element 'createTransactionRequest' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd' has invalid child element 'clientId' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'. List of possible elements expected: 'merchantAuthentication' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'.
Anyone know what's causing this error or how to fix it?
Well, I'll answer my own question since it might help others. The problem is the error message in the Authorize.net response is really vague.
Kurt Friars' comment was helpful, since it pointed me in the right direction. As for Mansour Hamcherif's suggestion, the merchantAuthentication object was set in my app, it just didn't have the right values, so it wasn't that.
The solution for me was setting the proper values to setName() and setTransactionKey(). The previous developer who had worked on this project had left values and the credentials had expired. I did a Linux text search for "setTransactionKey", which lead me to the correct PHP file where I need to set:
$merchantAuthentication = new AnetAPI\MerchantAuthenticationType();
$merchantAuthentication->setName('EnterYourLoginNameHere');
$merchantAuthentication->setTransactionKey('EnterYourTransactionKey');
After that, I cleared all of my Laravel app's caches as well as my browser's caches, did a hard reload, tried a transaction again and it worked! I got:
This transaction has been approved., Transaction ID: **********.
You may want to check the log for the raw request, it's likely the merchantAuthentication object has not been set, if you are using the PHP SDK I recommend checking the SimpleCodeConstants.php file and make sure your merchant credentials constants are set.
For example, if I set my merchant credentials to NULL, I get the same E00003 error as a result of sending the following raw request:
{"createTransactionRequest":{"merchantAuthentication":[],"clientId":"sdk-php-2.0.0-ALPHA", ...}

Error calling Pho.to API

I am trying to call the Pho.to API to edit photos, and every time I try to POST I get the same error. I've double and triple checked my app_id and my key, and I can't figure out what I'm doing wrong. I'm currently using the ARC Chrome Extension to call this api, so I haven't even started coding that part yet, I'm just trying to get a real response back from the api to make sure it even works.
I followed the instructions in their docs as well as I could. Here's the link for reference: http://developers.pho.to/documentation/sending-requests
Here is my api call:
http://opeapi.ws.pho.to/addtask?APP_ID=<my-app-id>&KEY=<my-key>&SIGN_DATA=910ceb5bdb238b9248a34cce8b29ba64d5f239df
And here is the response I get back (don't be deceived by the 200):
Status: 200 OK
<?xml version="1.0" ?>
<image_process_response>
<status>SecurityError</status>
<err_code>614</err_code>
<description>Error in POST parameters: one or more parameters (DATA , SIGN_DATA or APP_ID) are empty</description>
</image_process_response>
Here's the PHP code I use to create the SHA1 for SIGN_DATA:
<?php
echo hash_hmac('SHA1', '<image_process_call><lang>en</lang><image_url order="1">http://www.w3schools.com/html/pic_mountain.jpg</image_url><methods_list><method order="1"><name>desaturation</name></method><method order="2"><name>cartoon</name><params>fill_solid_color=1;target_color=(255,255,255);border_strength=20;border_width=3</params></method></methods_list><result_format>png</result_format><result_size>600</result_size></image_process_call>','<my-key>');
?>
Here is the xml from above, formatted for readability:
<image_process_call>
<lang>en</lang>
<image_url order="1">http://www.w3schools.com/html/pic_mountain.jpg</image_url>
<methods_list>
<method order="1">
<name>desaturation</name>
</method>
<method order="2">
<name>cartoon</name>
<params>fill_solid_color=1;target_color=(255,255,255);border_strength=20;border_width=3</params>
</method>
</methods_list>
<result_format>png</result_format>
<result_size>600</result_size>
</image_process_call>
Any help would be appreciated. Thanks in advance!
So I figured out what was wrong. Here is my detailed solution for anyone else that may run into similar problems with this api (regardless of platform):
Part of the problem (as #u_mulder pointed out) is that DATA needs to be sent up along with SIGNED_DATA so that the SHA1 can be decoded on the other end.
The other piece that fixed my problem was removing <lang>en</lang>. For whatever reason, that was returning Error 613: Invalid SIGN_DATA parameter. English is the default language anyway, so it was unnecessary.
So after fixing those things, here's my final url:
http://opeapi.ws.pho.to/addtask/?app_id=<my-app-id>&key=<my-key>9&sign_data=e456c393d11797c1a2945a85dd49ba2208cc66de&data=%3Cimage_process_call%3E%3Cimage_url+order%3D%221%22%3Ehttp%3A%2F%2Fwww.heroesandheartbreakers.com%2Fimages%2Fstories%2Fblogarticles%2F2016%2FJanuary2016%2FTV-Recap-Arrow-4x11-Olicity-is-home-470.jpg%3C%2Fimage_url%3E%3Cmethods_list%3E%3Cmethod+order%3D%221%22%3E%3Cname%3Ecartoon%3C%2Fname%3E%3Cparams%3Efill_solid_color%3D1%3Btarget_color%3D%28255%2C255%2C255%29%3Bborder_strength%3D20%3Bborder_width%3D1%3C%2Fparams%3E%3C%2Fmethod%3E%3C%2Fmethods_list%3E%3Cresult_format%3Epng%3C%2Fresult_format%3E%3Cresult_size%3E1500%3C%2Fresult_size%3E%3C%2Fimage_process_call%3E
Notice that the url is encoded. This may or may not be necessary, I just encoded it to be safe.
This returns:
<?xml version="1.0" ?>
<image_process_response>
<request_id>010afc13-6bba-44dd-b278-4f3bd1e41946</request_id>
<status>OK</status>
<description />
<err_code>0</err_code>
</image_process_response>
And I can now use request_id to get the url of the edited image:
http://opeapi.ws.pho.to/getresult?request_id=010afc13-6bba-44dd-b278-4f3bd1e41946
Which returns the following xml:
<image_process_response>
<request_id>010afc13-6bba-44dd-b278-4f3bd1e41946</request_id>
<status>OK</status>
<result_url>http://worker-images.ws.pho.to/i1/3BCB160A-691A-458B-9161-67AFA8A9EAA0.png</result_url>
<result_url_alt>http://worker-images.ws.pho.to.s3.amazonaws.com/i1/3BCB160A-691A-458B-9161-67AFA8A9EAA0.png</result_url_alt>
<nowm_image_url>http://worker-images.ws.pho.to/i1/3BCB160A-691A-458B-9161-67AFA8A9EAA0.png</nowm_image_url>
</image_process_response>
So the url of the final edited image is http://worker-images.ws.pho.to/i1/3BCB160A-691A-458B-9161-67AFA8A9EAA0.png (I believe links expire after 24 hours)
And we're done!
If you'd like to check out how I implemented this api in a simple Android app, here's the github link: https://github.com/youravgjoe/ColoringPageGenerator
Before:
After:
For me the error was fixed by (A) making sure to use SHA1 rather than SHA256 (my own fault) and (B) for some reason the sign_data value has to be lowercase.
Remove all new lines(Enter) in XML and try again.
<image_process_call>
<lang>en</lang>
<image_url order="1">http://www.w3schools.com/html/pic_mountain.jpg</image_url>
<methods_list>
<method order="1">
<name>desaturation</name>
</method>
<method order="2">
<name>cartoon</name>
<params>fill_solid_color=1;target_color=(255,255,255);border_strength=20;border_width=3</params>
</method>
</methods_list>
<result_format>png</result_format>
<result_size>600</result_size>
</image_process_call>
To
<image_process_call><lang>en</lang><image_url order="1">http://www.w3schools.com/html/pic_mountain.jpg</image_url><methods_list><method order="1"><name>desaturation</name></method><method order="2"><name>cartoon</name><params>fill_solid_color=1;target_color=(255,255,255);border_strength=20;border_width=3</params></method></methods_list><result_format>png</result_format><result_size>600</result_size></image_process_call>

Two Easy PHP SOAP setHeaders Questions

Here's the code I'm using to generate the request headers:
$headers = array(
new SOAPHEADER($this->_ns,'username',$this->_username,false, $this->_actor),
new SOAPHEADER($this->_ns,'password',$this->_password,false, $this->_actor));
$this->_client->__setSOAPHeaders($headers);
This generates:
<SOAP-ENV:Header>
<ns2:username SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">test</ns2:username>
<ns2:password SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">test</ns2:password>
</SOAP-ENV:Header>
That's all fine and dandy.
Here are my two questions:
The API doc requires that username be ns1:username and password be ns2:password. Mine are both ns2. First of all, what is the significance of the ns1|2? How can I fix this?
Second question is just is there a way to generate the same result by only calling SOAPHEADER() once?
Not easy to say whether the namespace makes a difference without seeing the declaration & knowing the service, but usually the receiving service looks only for the tags in that particular namespace. As you add $this->_ns to both of them, of course they'll be the same. You'll have to provide the proper namespace yourself (prefix doesn't matter, uri does).
Why would you want only 1 call?

How do I achieve this custom Soap request using PHP's Soap class?

I need to create a Soap request using PHP's Soap extension. I have searched and read both SO questions and the PHP.net manual, however, I have not been able to find a plain English explanation regarding how to implement a custom header with multiple namespaces, a special type, etc. Basically, I just find all the current documentation very difficult to understand, and need someone to offer a more down-to-earth explanation of how to accomplish the request below:
<OAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.some-url1.com" xmlns:ns2="http://www.some-url2.com" xmlns:ns3="http://www.some-url3.com">
<OAP-ENV:Header>
<s2:Security SOAP-ENV:mustUnderstand="1">
<s2:UsernameToken>
<s2:Username>someusername</ns2:Username>
<s2:Password Type="http://www.some-url4.com">somepassword</ns2:Password></ns2:UsernameToken></ns2:Security></SOAP-ENV:Header>
<OAP-ENV:Body>
<s1:invokeSomeService>
<s3:someRequest ns3:someReferenceNumber="912" ns3:locale="en_US" ns3:someAliasName="SOME_NAME" ns3:someTrackingID="THE-ID-123">
<s3:inputCriteria>
<s3:name>
<s3:firstName>John</ns3:firstName>
<s3:lastName>BBITTERSWEET</ns3:lastName></ns3:name>
<s3:address ns3:someExtraInfo="EXTRA_INFO_HERE">
<s3:addressLine1>1 Main St</ns3:addressLine1>
<s3:city>Anywhere</ns3:city>
<s3:stateCode>NY</ns3:stateCode>
<s3:postalCode>12345</ns3:postalCode>
<s3:countryCode>US</ns3:countryCode></ns3:address></ns3:inputCriteria></ns3:someRequest></ns1:invokeSomeService></SOAP-ENV:Body></SOAP-ENV:Envelope>

Categories