I try to get a single contact from my Dynamics Nav Web Service (Dynamics Nav 2016). I do this with a SOAP-request in PHP.
The web service is a codeunit which contains two functions:
fGetContact(iContactNumber : Text[20]) oContact : Text[250]
IF rContact.GET(iContactNumber) THEN BEGIN
oContact := '';
oContact := rContact."No." + ';' +
rContact."Company Name" + ';' +
rContact."First Name" + ';' +
rContact.Surname + ';' +
rContact."E-Mail";
END;
EXIT(oContact);
fGetContacts() oContacts : Text[250]
IF rContact.GET('KT100190') THEN BEGIN
oContacts := '';
oContacts := rContact."No." + ';' +
rContact."Company Name" + ';' +
rContact."First Name" + ';' +
rContact.Surname + ';' +
rContact."E-Mail";
END;
EXIT(oContacts);
The second function, fGetContacts, works fine.
But when I call fGetContact with a contact number as parameter, it returns the following error:
Parameter iContactNumber in method FGetContact in service MyService is null!
I use the NTLMSoapClient like the following:
<?php
ini_set('soap.wsdl_cache_enabled', '0');
require_once 'ntlmstream.php';
require_once 'ntlmsoapclient.php';
$url = 'http://localhost:7047/DynamicsNAV90/WS/CRONUS/Codeunit/MyService';
$options = array(
'uri' => $url,
'location' => $url,
'trace' => true,
'login' => 'my_user',
'password' => 'my_password'
);
// we unregister the current HTTP wrapper
stream_wrapper_unregister('http');
// we register the new HTTP wrapper
stream_wrapper_register('http', 'MyServiceProviderNTLMStream') or die("Failed to register protocol");
// so now all request to a http page will be done by MyServiceProviderNTLMStream.
// ok now, let's request the wsdl file
// if everything works fine, you should see the content of the wsdl file
$client = new MyServiceNTLMSoapClient(null, $options);
// should display your reply
try {
$params = array('iContactNumber' => 'KT100190');
echo '<pre>';
echo $client->FGetContacts(); // works
echo $client->FGetContact($params); // doesn't work
echo '</pre>';
} catch (SoapFault $e) {
echo '<pre>';
var_dump($e);
echo '</pre>';
}
// restore the original http protocole
stream_wrapper_restore('http');
I also tried to call the function like this:
echo $client->FGetContact('KT100190');
The return error is the same as before.
I tested my function with SoapUI and the return value is exactly what it shuold be.
Request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:new="urn:microsoft-dynamics-schemas/codeunit/MyService">
<soapenv:Header/>
<soapenv:Body>
<new:FGetContact>
<new:iContactNumber>KT100190</new:iContactNumber>
</new:FGetContact>
</soapenv:Body>
</soapenv:Envelope>
Response:
<Soap:Envelope xmlns:Soap="http://schemas.xmlsoap.org/soap/envelope/">
<Soap:Body>
<FGetContact_Result xmlns="urn:microsoft-dynamics-schemas/codeunit/MyService">
<return_value>KT100190;Add-ON Marketing;Chris;McGurk;chris.mcgurk#cronuscorp.net</return_value>
</FGetContact_Result>
</Soap:Body>
</Soap:Envelope>
So what am I doing wrong that this error appears and how can I fix it?
For what it's worth, I had this issue and solved it by adding "cache_wsdl" => WSDL_CACHE_NONE to the soap client's options.
Some fields were missing because of a cache issue after updating the WSDL.
I made a workaround and it works for me now.
I changed the $request variable in the class NTLMSoapClient, because the soap envelope that php sent to my service was absolutely useless.
So basically I just did this before the curl actions:
$request = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:new="urn:microsoft-dynamics-schemas/codeunit/MyService">
<soapenv:Header/>
<soapenv:Body>
<new:FGetContact>
<new:iContactNumber>'.$this->iContactNumber.'</new:iContactNumber>
</new:FGetContact>
</soapenv:Body>
</soapenv:Envelope>';
(If someone have the same problem, try var_dump($request) and view source in browser. You will see what a mess PHP did there...)
Related
I try to use PayPal Reference Transactin. I have already made a billing agreemet, got the Billing Agreement ID set up before any transaction is done.
I make a simple transaction request, but got:
PPConnectionException in PPHttpConnection.php line 108:
Got Http response code 400 when accessing
https://api-3t.sandbox.paypal.com/2.0.
My code (writte in Laravel 5.3 with merchant-sdk-php package):
$user=Auth::user();
$currencyCode = 'GBP';
$price=config('constants.offer_submit_price');
$reference_id=$user->userExt()->where('field_name', '=', 'billing_agreement')->firstOrFail()->field_value;
$amount = new BasicAmountType($currencyCode, $price);
$paymentDetails = new PaymentDetailsType();
$paymentDetails->OrderTotal = $amount;
$RTRequestDetails = new DoReferenceTransactionRequestDetailsType();
$RTRequestDetails->PaymentDetails = $paymentDetails;
$RTRequestDetails->ReferenceID = $reference_id;
$RTRequestDetails->PaymentAction ='sale';
$RTRequest = new DoReferenceTransactionRequestType();
$RTRequest->DoReferenceTransactionRequestDetails = $RTRequestDetails;
$RTReq = new DoReferenceTransactionReq();
$RTReq->DoReferenceTransactionRequest = $RTRequest;
$paypalService = new PayPalAPIInterfaceServiceService($this->config);
try {
$setRTResponse = $paypalService->DoReferenceTransaction($RTReq);
} catch (Exception $ex) {
dd($ex);
}
Thats the SAOP envelopr produced by script:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="urn:ebay:api:PayPalAPI" xmlns:ebl="urn:ebay:apis:eBLBaseComponents" xmlns:cc="urn:ebay:apis:CoreComponentTypes" xmlns:ed="urn:ebay:apis:EnhancedDataTypes" >
<soapenv:Header>
<ns:RequesterCredentials>
<ebl:Credentials>
<ebl:Username>username
</ebl:Username>
<ebl:Password>password
</ebl:Password>
<ebl:Signature>signature
</ebl:Signature>
</ebl:Credentials>
</ns:RequesterCredentials>
</soapenv:Header>
<soapenv:Body>
<ns:DoReferenceTransactionReq>
<ns:DoReferenceTransactionRequest>
<ebl:DoReferenceTransactionRequestDetails>
<ebl:ReferenceID>B-reference_id
</ebl:ReferenceID>
<ebl:PaymentAction>sale
</ebl:PaymentAction>
<ebl:PaymentDetails>
<ebl:OrderTotal currencyID="GBP">10
</ebl:OrderTotal>
<ebl:ButtonSource>PayPal_SDK
</ebl:ButtonSource>
</ebl:PaymentDetails>
</ebl:DoReferenceTransactionRequestDetails>
<ebl:Version>106.0
</ebl:Version>
</ns:DoReferenceTransactionRequest>
</ns:DoReferenceTransactionReq>
</soapenv:Body>
</soapenv:Envelope>'
With help of my friend I have figured out what was wrong.
$RTRequestDetails->PaymentAction ='Sale';
instead of
$RTRequestDetails->PaymentAction ='sale';
PayPal SOAP API need to have everything in right case sensitive.
Regret, they don't send any info what's wrong.
I am not familiar with SOAP webservices and I need to send a request to one.
I wrote the next snippet:
# WSDL http://webservices.sathomologa.sef.sc.gov.br/wsDfeSiv/Recepcao.asmx?WSDL
$this->client = new SoapClient(static::SERVICE_WSDL, ['exceptions' => 0]);
# Raw XML data
$data = $this->xml->saveXML();
# URL http://webservices.sathomologa.sef.sc.gov.br/wsDfeSiv/Recepcao.asmx
$location = static:SERVICE_URL;
$action = static::SERVICE_URL . '?op=Enviar';
$v = SOAP_1_1;
$response = $this->client->__doRequest($data, $location, $action, $v);
And I get the next test response:
soap:ClientServer did not recognize the value of HTTP Header
SOAPAction:
http://webservices.sathomologa.sef.sc.gov.br/wsDfeSiv/Recepcao.asmx?op=Enviar.
Any ideas?
Reading the specification of Enviar method, i saw the next header:
SOAPAction: "http://tempuri.org/Enviar"
So, i changed $action = static::SERVICE_URL . '?op=Enviar'; to $action = http://tempuri.org/Enviar; and works for me.
I want to use the Quick License Manager SOAP webservice to connect an e-commerce wordpress plugin to enable the user to buy a license and show it after a successful purchase.So for now, I'm trying to call the function GetProductInfo to get the info of an existing product I created with the QLM Management Console.I use the SoapClient php class but when I try to make a request I get this response :
Error validating request: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. . Caller: GetProductInfo
I don't understand what's wrong with my request, so I need help...
Here is the code I use :
<?php
$url = "https://quicklicensemanager.com/****/qlm/qlmservice.asmx";
$wsdl = $url . "?WSDL";
$soapClient = new SoapClient($wsdl, array(
"encoding" => "utf8",
"trace" => TRUE,
"version" => SOAP_1_1
));
$req = sprintf(
'<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<QlmSoapHeader xmlns="http://www.interactive-studios.net/qlmweb">
<CultureName>%s</CultureName>
<UtcOffset>%d</UtcOffset>
</QlmSoapHeader>
</soap:Header>
<soap:Body>
<GetProductInfo xmlns="http://www.interactive-studios.net/qlmweb">
<eProductName>%s</eProductName>
<productID>%d</productID>
<major>%d</major>
<minor>%d</minor>
</GetProductInfo>
</soap:Body>
</soap:Envelope>', "en_US", 0, "The IO plug-in: Student License", 4, 1, 0);
echo '<pre>';
print_r(htmlentities($req));
echo '</pre>';
$action = "http://www.interactive-studios.net/qlmweb/GetProductInfo";
$res = $soapClient->__doRequest($req, $url, $action, 1);
echo '<pre>';
print_r($res);
echo '</pre>';
echo "=======================================================";
echo '<pre>';
print_r(htmlentities($soapClient->__getLastRequest()));
echo '</pre>';
echo "=======================================================";
echo '<pre>';
print_r($soapClient->__getLastRequestHeaders());
echo '</pre>';
Thanks in advance for your help.
EDIT
Now I tried to change the field productID and it appears that it is the one that was wrong. So it needs to be a base64 string, so I updated my code :
</soap:Envelope>', "en_US", 0, "The IO plug-in: Student License", 4, 1, 0); becomes </soap:Envelope>', "en_US", 0, base64_encode("The IO plug-in: Student License"), 4, 1, 0);
The new error message is :
Error validating request: Length of the data to decrypt is invalid.. Caller: GetProductInfo
After hours of searching, I found this in the documentation, at page 121 :
Note that all the methods exposed by the web service cannot be called with a URL except GetActivationKey
and ActivateKey. All other web methods implement a secure authentication mechanism that only accepts
requests from the QLM Console.
So, as I was trying to call GetProductInfo, it couldn't work outside the QLM Console.
This is my first run into the SOAP forest, so I have no idea what I'm doing and worse, the company has zero documentation or code examples. They at least provide an example call.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:GetRatesIPXML>
<tem:ipXML>
<![CDATA[
<XML>
<RateInput>
<GUID>12345</GUID>
<RepID>abc</RepID>
<ZipCode>55343</ZipCode>
<EffectiveDate>1/15/2014</EffectiveDate>
<DateOfBirth >12/15/1980</DateOfBirth >
<FilterPlanCode></FilterPlanCode>
</RateInput>
</XML>]]>
</tem:ipXML>
</tem:GetRatesIPXML>
</soapenv:Body>
</soapenv:Envelope>
I've used both SoapClient and NuSoap. I've tried everything from nested arrays to objects, strings, simpleXML. I can't seem to figure this out and after two days of googling I've reached my end.
Here's my current implementation.
require('lib/nusoap.php');
class Carrier
{
const WSDL = 'http://getrates_staging.test.com/getrates.svc?wsdl';
public function get()
{
$soapClient = new nusoap_client( self::WSDL , true);
$soapClient->soap_defencoding = 'UTF-8';
$string = ""
. "<XML>"
. "<RateInput>";
$string .= "<GUID>12345</GUID>";
$string .= "</RateInput></XML>";
$response = $soapClient->call('GetRatesIPXML' , array('ipXML'=> $string) , '' , '', false, true);
var_dump($soapClient->request);
var_dump($soapClient->getError());
var_dump($response);
}
}
$foo = new Carrier();
$foo->get();
It results in something close but all the < get escaped to < so that doesn't work. Any help is appreciated.
edit
This is about as close as I get to the desired result
class Carrier
{
const WSDL = 'http://getrates_staging.test.com/getrates.svc?wsdl';
public function get()
{
$soapClient = new SoapClient( self::WSDL , array('trace' => true));
//$soapClient->soap_defencoding = 'UTF-8';
$string = ""
. "<![CDATA[ <XML>"
. "<RateInput>";
$string .= "<GUID>12345</GUID>";
$string .= "</RateInput></XML> ]]>";
$param = new SoapVar($string, XSD_ANYXML);
$ipXML = new stdClass();
$ipXML->ipXML = $param;
try
{
$response = $soapClient->GetRatesIPXML($ipXML);
}
catch(Exception $e)
{
var_dump($e);
}
var_dump($soapClient->__getLastRequest());
var_dump($response);
}
}
$foo = new Carrier();
$foo->get();
I end up with
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/"><SOAP-ENV:Body><ns1:GetRatesIPXML><![CDATA[ <XML><RateInput><GUID>12345</GUID></RateInput></XML> ]]></ns1:GetRatesIPXML></SOAP-ENV:Body></SOAP-ENV:Envelope>
I don't get why it's dropping the surrounding <ns1:ipXML>
edit 2
At the end of the day this worked.
class Carrier
{
const WSDL = 'http://getrates_staging.test.com/getrates.svc?wsdl';
public function get()
{
$soapClient = new SoapClient( self::WSDL , array('trace' => true));
//$soapClient->soap_defencoding = 'UTF-8';
$string = ""
. "<ns1:ipXML><![CDATA[ <XML>"
. "<RateInput>";
$string .= "<GUID>1234</GUID>
<RepID>1234</RepID>
<ZipCode>55343</ZipCode>
<EffectiveDate>1/15/2016</EffectiveDate>
<DateOfBirth >07/01/1983</DateOfBirth >
<FilterPlanCode></FilterPlanCode>";
$string .= "</RateInput></XML> ]]></ns1:ipXML>";
$param = new SoapVar($string, XSD_ANYXML);
$ipXML = new stdClass();
$ipXML->ipXML = $param;
try
{
$response = $soapClient->GetRatesIPXML($ipXML);
}
catch(Exception $e)
{
var_dump($e);
}
var_dump($soapClient->__getLastRequest());
var_dump($response);
}
}
$foo = new Carrier();
$foo->get();
But it seems so hacky. If anyone has a better suggestion I'm open.
Ordinarily, XML documents are constructed (and parsed) using PHP "helper libraries" such as SimpleXML (http://php.net/manual/en/book.simplexml.php), or techniques such as the ones described here (http://www.phpeveryday.com/articles/PHP-XML-Tutorial-P848.html).
The XML document is constructed as an in-memory data structure (arrays, etc.), and then converted, in one swoop, into the XML that is to be sent.
In fact, since what you are doing is SOAP, you can go one level of abstraction above that, e.g. (http://php.net/manual/en/book.soap.php). Off-the-shelf libraries exist which will handle both the task of constructing the XML payload, and sending it, and getting server responses and decoding them. That's where you should start.
"Actum Ne Agas: Do Not Do A Thing Already Done."
I'm attempting to get a shipping quote from an SOAP service. I've been able to successfully create authentication headers and query the SOAP service with basic requests that require no body parameters.
I'm able to create the proper structure for the request but the namespace values are not showing up in the request output.
Example code:
$client = new SoapClient("http://demo.smc3.com/AdminManager/services/RateWareXL?wsdl",
array('trace' => TRUE));
$headerParams = array('ns1:licenseKey' => $key,
'ns1:password' => $pass,
'ns1:username' => $user);
$soapStruct = new SoapVar($headerParams, SOAP_ENC_OBJECT);
$header = new SoapHeader($ns, 'AuthenticationToken', $soapStruct, false);
$client->__setSoapHeaders($header);
// Check if shipping is ready - base call
$ready_to_ship = $client->isReady();
The above works just fine and returns true if the shipping service is available.
So I use the following code to build the request body (only filling required fields):
I've also tried putting everything into an array and converting that to a SoapVar, I've tried including ns1: and ns2: in the body request creation but that hasn't worked either. I believe something needs to be adjusted in the request creation... not sure of the best approach..
$rate_request = $client->LTLRateShipment;
$rate_request->LTLRateShipmentRequest->destinationCountry = $destination_country;
$rate_request->LTLRateShipmentRequest->destinationPostalCode = $destination_postal_code;
$rate_request->LTLRateShipmentRequest->destinationPostalCode = $destination_postal_code;
$rate_request->LTLRateShipmentRequest->details->LTLRequestDetail->nmfcClass = $ship_class;
$rate_request->LTLRateShipmentRequest->details->LTLRequestDetail->weight = $ship_weight;
$rate_request->LTLRateShipmentRequest->originCountry = $origin_country;
$rate_request->LTLRateShipmentRequest->originPostalCode = $origin_postal_code;
$rate_request->LTLRateShipmentRequest->shipmentDateCCYYMMDD = $ship_date;
$rate_request->LTLRateShipmentRequest->tariffName = $tariff;
And it produces the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://webservices.smc.com">
<SOAP-ENV:Header>
<ns1:AuthenticationToken>
<ns1:licenseKey>xxxxxxxx</ns1:licenseKey>
<ns1:password>xxxxxxxx</ns1:password>
<ns1:username>xxxxxxxxm</ns1:username>
</ns1:AuthenticationToken>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:LTLRateShipment>
<LTLRateShipmentRequest>
<destinationCountry>USA</destinationCountry>
<destinationPostalCode>10001</destinationPostalCode>
<details>
<LTLRequestDetail>
<nmfcClass>60</nmfcClass>
<weight>300</weight>
</LTLRequestDetail>
</details>
<originCountry>USA</originCountry>
<originPostalCode>90210</originPostalCode>
<shipmentDateCCYYMMDD>20110516</shipmentDateCCYYMMDD>
<tariffName>DEMOLTLA</tariffName>
</LTLRateShipmentRequest>
</ns1:LTLRateShipment>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
But the output should include the namespaces (web: and web1: where appropriate). The above request returns an error code of missing tariffName.
Here's an example of what the xml request should look like:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:web="http://webservices.smc.com" xmlns:web1="http://web.ltl.smc.com">
<soapenv:Header>
<web:AuthenticationToken>
<web:licenseKey> string </web:licenseKey>
<web:password> string </web:password>
<web:username> string </web:username>
</web:AuthenticationToken>
</soapenv:Header>
<soapenv:Body>
<web:LTLRateShipment>
<web:LTLRateShipmentRequest>
<web1:LTL_Surcharge> string </web1:LTL_Surcharge>
<web1:TL_Surcharge> string </web1:TL_Surcharge>
<web1:destinationCity> string </web1:destinationCity>
<web1:destinationCountry> string </web1:destinationCountry>
<web1:destinationPostalCode> string </web1:destinationPostalCode>
<web1:destinationState> string </web1:destinationState>
<web1:details>
<!--Zero or more repetitions:-->
<web1:LTLRequestDetail>
<web1:nmfcClass> string </web1:nmfcClass>
<web1:weight> string </web1:weight>
</web1:LTLRequestDetail>
</web1:details>
<web1:discountApplication> string </web1:discountApplication>
<web1:mcDiscount> string </web1:mcDiscount>
<web1:orgDestToGateWayPointFlag> string </web1:orgDestToGateWayPointFlag>
<web1:originCity> string </web1:originCity>
<web1:originCountry> string </web1:originCountry>
<web1:originPostalCode> string </web1:originPostalCode>
<web1:originState> string </web1:originState>
<web1:rateAdjustmentFactor> string </web1:rateAdjustmentFactor>
<web1:shipmentDateCCYYMMDD> string </web1:shipmentDateCCYYMMDD>
<web1:shipmentID> string </web1:shipmentID>
<web1:stopAlternationWeight> string </web1:stopAlternationWeight>
<web1:surchargeApplication> string </web1:surchargeApplication>
<web1:tariffName> string </web1:tariffName>
<web1:weightBreak_Discount_1> string </web1:weightBreak_Discount_1>
</web:LTLRateShipmentRequest>
</web:LTLRateShipment>
</soapenv:Body>
</soapenv:Envelope>
Any suggestions / direction appreciated!
Ok... After too many hours of testing I finally have a solution..
I recreated the Authorization Token as a class and built the Soap Request without having to deal with any namespaces, SoapVars etc. it's surprisingly easy.
/* Object for holding authentication info
this could probably be accomplished using stdClass too */
class AuthHeader {
var $licenseKey;
var $password;
var $username;
function __construct($loginInfo) {
$this->licenseKey = $loginInfo['licenseKey'];
$this->password = $loginInfo['password'];
$this->username = $loginInfo['username'];
}
}
// set current soap header with login info
$client = new SoapClient("http://demo.smc3.com/AdminManager/services/RateWareXL?wsdl",
array('trace' => TRUE
));
// create header params array
$headerParams = array('licenseKey' => $key,
'password' => $pass,
'username' => $user);
// create AuthHeader object
$auth = new AuthHeader($headerParams);
// Turn auth header into a SOAP Header
$header = new SoapHeader($ns, 'AuthenticationToken', $auth, false);
// set the header
$client->__setSoapHeaders($header);
// Check if shipping is ready - base call
$ready_to_ship = $client->isReady();
// $last_request = $client->__getLastRequest();
$last_response = $client->__getLastResponse();
//print $last_request;
if ($last_response == true) {
print "Ready to ship\n";
// Create the shipping request
$d = new stdClass;
$d->nmfcClass = $ship_class;
$d->weight = $ship_weight;
$p = new stdClass;
$p->LTLRateShipmentRequest->destinationCountry = $destination_country;
$p->LTLRateShipmentRequest->destinationPostalCode = $destination_postal_code;
$p->LTLRateShipmentRequest->details = array($d);
$p->LTLRateShipmentRequest->originCountry = $origin_country;
$p->LTLRateShipmentRequest->originPostalCode = $origin_postal_code;
$p->LTLRateShipmentRequest->shipmentDateCCYYMMDD = $ship_date;
$p->LTLRateShipmentRequest->tariffName = $tariff;
$quote = $client->LTLRateShipment($p);
$last_request = $client->__getLastRequest();
$last_response = $client->__getLastResponse();
print "Request: " . $last_request;
print "\nResponse: " . $last_response;
}