I am trying to integrate our app with Exact Online website using OAuth2,or more specifically i am trying to create hour registration which should include "Employee","Project","Hours","Hours type".
function registerTime($access_token_for_data) {
//$dataToFilterAccounts = array('$filter' => 'IsSales eq true');
$dataToRetrieveFromEmployees = array('$select' => 'ID');
// $queryToFilterAccounts = http_build_query($dataToFilterAccounts);
$queryToRetrieveFromEmployees = http_build_query($dataToRetrieveFromEmployees);
// $dataToFilterItems = array('$filter' =>'IsSalesItem eq true');
$dataToRetrieveProjects = array('$select' => 'ID');
//$queryToFilterItems = http_build_query($dataToFilterItems);
$queryToRetrieveProjects = http_build_query($dataToRetrieveProjects);
$urlProjects = 'https://start.exactonline.nl/api/v1/638842/project/Projects';
$urlEmployees = 'https://start.exactonline.nl/api/v1/638842/payroll/Employees';
$curlProjects = curl_init($urlProjects);
$curlEmployees = curl_init($urlEmployees);
$headers = array(
'Authorization: Bearer ' . $access_token_for_data,
'Accept: application/json',
'Content-type: application/json'
);
curl_setopt($curlProjects, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curlProjects, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curlProjects, CURLOPT_URL, $urlProjects . '?' . $queryToRetrieveProjects);
curl_setopt($curlProjects, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($curlEmployees, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curlEmployees, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curlEmployees, CURLOPT_URL, $urlEmployees . '?' . $queryToRetrieveFromEmployees);
curl_setopt($curlEmployees, CURLOPT_CUSTOMREQUEST, 'GET');
$resultProjects = curl_exec($curlProjects);
$resultEmployees = curl_exec($curlEmployees);
$projectsData = json_decode($resultProjects, true);
$projectID = $projectsData["d"]["results"]["0"]["ID"];
$employeesData = json_decode($resultEmployees, true);
$employeeID = $employeesData["d"]["results"]["0"]["ID"];
curl_close($curlProjects);
curl_close($curlEmployees);
$urlTimeTransaction = 'https://start.exactonline.nl/api/v1/638842/project/TimeTransactions';
$curlTimeTransacation = curl_init($urlTimeTransaction);
$content = json_encode(array("Project" => $projectID, "Employee" => $employeeI));
curl_setopt($curlTimeTransacation, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curlTimeTransacation, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curlTimeTransacation, CURLOPT_POST, true);
curl_setopt($curlTimeTransacation, CURLOPT_POSTFIELDS, $content);
$createdTimeTransaction = curl_exec($curlTimeTransacation);
$status = curl_getinfo($curlTimeTransacation, CURLINFO_HTTP_CODE);
if ($status != 201) {
die("Error: call to URL $curlTimeTransacation failed with status $status, response $createdTimeTransaction, curl_error " . curl_error($curlTimeTransacation) . ", curl_errno " . curl_errno($curlTimeTransacation));
}
echo "HTTP status $status creating time registartion<br/><br/>";
curl_close($curlTimeTransacation);
}
And this is the error i get
Error: call to URL Resource id #56 failed with status 500, response { "error": { "code": "", "message": { "lang": "", "value": "Mandatory: Employee\r\nMandatory: Hours\r\nMandatory: Hour type" } } }, curl_error , curl_errno 0
But when i try to include any of these mandatroy fileds i get:
Error: call to URL Resource id #56 failed with status 400, response { "error": { "code": "", "message": { "lang": "", "value": "Error processing request stream. The property name 'Hours' specified for type 'Exact.Web.Api.Models.TimeTransaction' is not valid." } } }, curl_error , curl_errno 0
please note that /api/v1/638842 contains your division ID. You might want to change that into a variable.
Regarding your problem: please note that the error messages contain text to be consumed by humans. The actual technical names can be different. I always do it the other way around: I query the existing data and look at all fields, and then I know what to send. You can use the Query Tool for Exact Online in the app center of Exact to do the query on REST api of Exact Online (but I am biased because involved).
As Guido pointed out correctly, there is no field named Hours in the TimeTransaction on projects, there is a field named Hours on the manufacturing TimeTransaction. This is a little confusing, especially since the error message isn't very clear.
You need to set Quantity on TimeTransactions in order to specify the hours on a project's time transaction.
Related
The issue:
I'm working with PHP, cURL and a public API to fetch json strings.
These json strings are formatted like this (simplified, average size is around 50-60 kB):
{
"data": {},
"previous": "url",
"next": "url"
}
What am trying to do is fetch all the json strings starting from the first one by checking for the "next" attribute. So I have a while loop and as long as there's a "next" attribute, I fetch the next URL.
The problem is sometimes, randomly the loop stops before the end and I cannot figure out why after many tests.
I say randomly because sometimes the loop goes through to the end and no problem occurs. Sometimes it crashes after N loops.
And so far I couldn't extract any information to help me debug it.
I'm using PHP 7.3.0 and launching my code from the CLI.
What I tried so far:
Check the headers:
No headers are returned. Nothing at all.
Use curl_errno() and curl_error():
I tried the following code right after executing the request (curl_exec($ch)) but it never triggers.
if(curl_errno($ch)) {
echo 'curl error ' . curl_error($ch) . PHP_EOL;
echo 'response received from curl error :' . PHP_EOL;
var_dump($response); // the json string I should get from the server.
}
Check if the response came back null:
if(is_null($response))
or if my json string has an error:
if(!json_last_error() == JSON_ERROR_NONE)
Though I think it's useless because it will never be valid if the cURL response is null or empty. When this code triggers, the json error code is 3 (JSON_ERROR_CTRL_CHAR)
The problematic code:
function apiCall($url) {
...
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
}
$inc = 0;
$url = 'https://api.example.com/' . $id;
$jsonString = apiCall($url);
if(!is_null($jsonString)) {
file_put_contents('pathToDirectory/' . $id + $inc, $jsonString);
$nextUrl = getNextUrl($jsonString);
while ($nextUrl) {
$jsonString = apiCall($url . '?page=' . $nextUrl);
if(!is_null($jsonString)) {
$inc++;
file_put_contents('pathToDirectory/' . $id + $inc, $jsonString);
$nextUrl = getNextUrl($jsonString);
}
}
}
What I'm expecting my code to do:
Not stop randomly, or at least give me a clear error code.
The problem is that your API could be returning an empty response, a malformed JSON, or even a status code different of 200 and you would stop execution imediately.
Since you do not control the API responses, you know that it can fail randomly, and you do not have access to the API server logs (because you don't, do you?); you need to build some kind of resilience in your consumer.
Something very simple (you'd need to tune it up) could be
function apiCall( $url, $attempts = 3 ) {
// ..., including setting "$headers"
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
for ( $i = 0; $i < $attempts; $i ++ ) {
$response = curl_exec( $ch );
$curl_info = curl_getinfo( $ch );
if ( curl_errno( $ch ) ) {
// log your error & try again
continue;
}
// I'm only accepting 200 as a status code. Check with your API if there could be other posssible "good" responses
if ( $curl_info['http_code'] != 200 ) {
// log your error & try again
continue;
}
// everything seems fine, but the response is empty? not good.
if ( empty( $response ) ) {
// log your error & and try again
continue;
}
return $response;
}
return null;
}
This would allow you to do something like (pulled from your code):
do {
$jsonString = apiCall($url . '?page=' . $nextUrl);
$nextUrl = false;
if(!is_null($jsonString)) {
$inc++;
file_put_contents('pathToDirectory/' . $id + $inc, $jsonString);
$nextUrl = getNextUrl($jsonString);
}
}
while ($nextUrl);
I'm not checking if the return from the API is non-empty, not a connection error, a status different from '200' and yet an invalid JSON.
You may want to check for these things as well, depending on how brittle the API you are consuming is.
I recently work with kraken.io API and I'm trying to integrate this API wuth my PHP CodeIgniter framework. So I followed the documentation but I got stuck when I used curl
This is my source code below ..
require_once(APPPATH.'libraries/kraken-php-master/Kraken.php');
$kraken = new Kraken("SOME_KEY", "SOME_SECRET");
$params = array(
"file" => base_url()."include/".$dataIn['logo'],
"wait" => true
);
$dataj='{"auth":{"api_key": "SOME_KEY", "api_secret": "SOME_SECRET"},"file":'.base_url()."include/".$dataIn['logo'].',wait":true}';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.kraken.io/v1/upload");
curl_setopt($ch, CURLOPT_HTTPHEADER,array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_POSTFIELDS, $dataj);
$response = curl_exec($ch);
curl_close($ch);
$data = $kraken->upload($params);
print_r($response);exit();
And I got this result
"{"success":false,"message":"Incoming request body does not contain a valid JSON object"}1"
So can anyone please help me,
And thanks in advance,
DONT POST YOUR API_KEY AND API_SECRET
The error message is quite clear, your json object is not valid. For instance this would be a valid JSON object for your request:
{
"auth": {
"api_key": "SOME",
"api_secret": "SECRET"
},
"file": "somefile.txt",
"wait": true
}
In your php code you are setting up a $params array but then you don't use it. Try this:
$dataj='{"auth":{"api_key": "SOME_KEY", "api_secret": "SOME_SECRET"},"file":"' . $params["file"]. '", "wait":true}';
You can validate your JSON HERE
You should use json_encode function to generate your JSON data
$dataj = json_encode([
"auth" => [
"api_key" => "API_KEY",
"api_secret" => "API_SECRET"
],
"file" => base_url() . "include/" . $dataIn['logo'],
"wait" => true
]);
EDIT:
Here is an example from https://kraken.io/docs/upload-url so you don't need to use curl
require_once("Kraken.php");
$kraken = new Kraken("your-api-key", "your-api-secret");
$params = array(
"file" => "/path/to/image/file.jpg",
"wait" => true
);
$data = $kraken->upload($params);
if ($data["success"]) {
echo "Success. Optimized image URL: " . $data["kraked_url"];
} else {
echo "Fail. Error message: " . $data["message"];
}
This is my code:
$contact_data = json_encode(array(
"name" => "Jimmy Jimmy",
"email" => "jimmy#example.com",
"phone" => "555-555-555",
"mobile" => "312-312-213"
));
$url = $domain."api/v2/contacts";
$ch = curl_init($url);
$header[] = "Content-type: application/json";
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_USERPWD, "$apiKey");
curl_setopt($ch, CURLOPT_POSTFIELDS, $contact_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
$info = curl_getinfo($ch);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($server_output, 0, $header_size);
$response = substr($server_output, $header_size);
if($info['http_code'] == 201) {
echo "Contact created successfully, the response is given below \n";
echo "Response Headers are \n";
echo $headers."\n";
echo "Response Body \n";
echo "$response \n";
} else {
if($info['http_code'] == 404) {
echo "Error, Please check the end point \n";
} else {
echo "Error, HTTP Status Code : " . $info['http_code'] . "\n";
echo "Headers are ".$headers."\n";
echo "Response is ".$response;
}
}
curl_close($ch);
When I executed this piece of code , I received this errors message:
Response is {"description":"Validation failed","errors":[{"field":"last_name","message":"It should be a/an String","code":"missing_field"},{"field":"life_cycle_status","message":"It should be a/an String","code":"missing_field"}]}
The documentation nothing mentioned about these fields : last_name & life_cycle_status to create a new contact in freshdesk. Any idea what am i doing wrong ? thx
[UPDATE]
$contact_data = json_encode(array(
"name" => "Jimmy Jimmy",
"email" => "jimmy#example.com",
"phone" => "555-555-555",
"mobile" => "312-312-213"
"life_cycle_status" => "asdasdsa",
"last_name" =>"dasdasdad"
));
With these new items, I got this message error:
Response is {"description":"Validation failed","errors":[{"field":"life_cycle_status","message":"Unexpected/invalid field in request","code":"invalid_field"},{"field":"last_name","message":"Unexpected/invalid field in request","code":"invalid_field"}]}
These fields are not default for the Freshdesk Contact entity but are, probably, defined as required in the backend (Check Admin > Customer fields in the backend of Freshdesk)
This means we have to define them as custom_fields as indicated in the Freshdesk API documentation here.
This means your POST array would look something like this
$contact_data = json_encode(array(
'name' => 'Jimmy Jimmy',
'email' => 'jimmy#example.com',
'custom_fields' => [
// put all your custom fields here
'last_name' => 'Jimmy',
'life_cycle_status' => 'value'
]
));
Well you already have the answer - you did read that error message yes?
Response:
{
"description": "Validation failed",
"errors":[
{
"field":"last_name",
"message":"It should be a/an String",
"code":"missing_field"
},
{
"field":"life_cycle_status",
"message":"It should be a/an String",
"code":"missing_field"
}
]
}
Meaning:
last_name and life_cycle_status both need to be a String and cannot be empty.
I am working on an payment gateway API to process refunds.
On successful operation, the API returns a json array like this
{
"currencyCode" : "GBP",
"amount" : 100,
"originalMerchantRefNum" : "MERCHANTREF12346",
"mode" : "live",
"confirmationNumber" : 1997160616609792,
"authType" : "refund",
"id" : "25TWPTLHRR81AIG1LF"
}
On error the array returned is
{
"error": {
"code": "400",
"message": "Amount exceeds refundable amount"
}
}
I need to decode the json output and then show it to the user. But since the structure of the json array is different in both cases, how do I go arnd parsing the json array, so as to give relevant readable data to the end user.
My code which, does all the talking and fetching data from the gateway processor is given below
<?php
include('lock.php');
$flag=0;
$oid=$_POST['oid'];
if(isset($_POST['amount']))
{
$amount=$_POST['amount'];
$amount = $amount*100;
$flag=1;
}
// generate random number
$merchantref=mt_rand(10,9999999999);
//API Url
$url = 'https://api.netbanx.com/hosted/v1/orders/'.$oid.'/refund';
//Initiate cURL.
$ch = curl_init($url);
//The JSON data.
if($flag==1)
{
$jsonData = array(
"amount" => $amount,
'merchantRefNum' => $merchantref
);
}
else
{
$jsonData = array(
'merchantRefNum' => $merchantref
);
}
//Encode the array into JSON.
$jsonDataEncoded = json_encode($jsonData);
//Tell cURL that we want to send a POST request.
curl_setopt($ch, CURLOPT_POST, 1);
//Attach our encoded JSON string to the POST fields.
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonDataEncoded);
//Set the content type to application/json and HTTP Authorization code
$headers = array(
'Content-Type:application/json',
'Authorization: Basic '. base64_encode("..") //Base 64 encoding and appending Authorization: Basic
);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//Execute the request
$result = curl_exec($ch);
$jdata=$result;
//decode the json output and store it in a variable
$jfo = json_decode($jdata);
//Handle decision making based on json output
?>
Basically something as simple as:
$response = json_decode(..., true);
if (isset($response['error'])) {
echo 'Sorry, ', $response['error']['message'];
} else {
echo 'Yay!';
}
What exactly you need to check for depends on the possible values the API may return. Most APIs specify something along the lines of "status will be set to 'success' or 'error'", or maybe "if the error key is present, this indicates an error, otherwise a success".
I'm working with the Urban Airship (v3) API to push out messages to Android/iPhone/Blackberry and hopefully soon Windows phones. I'm not responsible for that; instead, I'm setting up the backend to allow users to send out a broadcast.
Another guy built the original backend, but I chose to rebuilt it from the bottom up to add in some additional functionality. Everything works in it, except the whole pushing of the broadcast part. Well, it sort of works; let me explain:
When a form is submitted, the data goes into the database via MYSQL and then with mysql_fetch_id() I get the new id and toss that id into a PHP function called sentBroadcast. It looks like the following:
function sentBroadcast($id){
$alertinfo = getAlertInfo($id);//this just gets all the data matching the id
$alert = mysql_fetch_assoc($alertinfo);
//these just get extra values
$organization = getOrganizationById($alert['broadcast_organization_id']);
$cityinfo = getCityInfo($organization['organization_city_id']);
$city = mysql_fetch_assoc($cityinfo);
// Create Airship object
$airship = new Airship(APP_KEY, APP_MASTER_SECRET);
$apiurl = "https://go.urbanairship.com/api/location/?q=".str_replace(" ","",strtolower($city['city_name']));
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $apiurl);
curl_setopt($ch, CURLOPT_USERPWD, APP_KEY.":".APP_MASTER_SECRET);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$output = curl_exec($ch);
curl_close ($ch);
$json = json_decode($output);
$locationid = "all";
if(count($json->features) > 0){
$locationid = $json->features[0]->id;
}
//send the message
$broadcasttype = "";
if($alert['broadcast_broadcasttypeother'] != ""){
$broadcasttype = $alert['broadcast_broadcasttypeother'];
}
else {
//this just gets data, nothing to see here
$broadcasttype = getCategoryInfo($alert['broadcast_broadcasttype_id'],'broadcasttype_name');
}
$message = html_entity_decode($broadcasttype)."\r\n".html_entity_decode($organization['organization_name'])."\r\n". html_entity_decode($alert['broadcast_subject']);
$blackberry_message = html_entity_decode($organization['organization_name'])."\r\n". html_entity_decode($alert['broadcast_subject']);
//calc as UTC
$timestamp = strtotime($alert['broadcast_sentdatetime']) + strtotime("+1 minute"); //add an hour
$offset = new DateTime(date("Y-m-d H:i:s T",$timestamp));
$offset->setTimezone(new DateTimeZone('UTC'));
$minutes_to_add = 10;
$time = new DateTime($alert['broadcast_sentdatetime']);
$time->add(new DateInterval('PT' . $minutes_to_add . 'S'));
$stamp = $time->format('Y-m-d H:i:s');
//echo $stamp;
$broadcast_message = array(
'schedule' => array("scheduled_time" => $stamp),
'push' => array("audience" => "all",
"notification" => array("alert" => $message),
"device_types" => array()
),
);
$device_types = array();
$device_types[] = "ios";
$device_types[] = "android";
$device_types[] = "blackberry";
$broadcast_message["push"]["device_types"] = $device_types;
if(in_array("ios", $device_types)){
$broadcast_message["push"]["notification"]["ios"] = array("sound" => "police.mp3", "extra" => array("id"=>$alert['broadcast_id']), "badge" => "+1");
}
if(in_array("android", $device_types)){
$broadcast_message["push"]["notification"]["android"] = array("extra"=>array("id"=>$alert['broadcast_id']));
}
if(in_array("blackberry", $device_types)){
$broadcast_message["push"]["notification"]["blackberry"] = array("content-type"=>"text/plain","body"=> json_encode(array("id"=>$alert['broadcast_id'], "body"=>$blackberry_message, "type"=>$broadcasttype)));
}
$data_string = json_encode($broadcast_message);
$apiurl = "https://go.urbanairship.com/api/schedules/";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $apiurl);
curl_setopt($ch, CURLOPT_USERPWD, APP_KEY.":".APP_MASTER_SECRET);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-type: application/json',
'Accept: application/vnd.urbanairship+json; version=3;',
'Content-Length: ' . strlen($data_string)
));
$json = curl_exec($ch);
$output = json_decode($json, true);
if(!is_array($output) || empty($output["ok"])){
echo "<h1>ERROR: (".(isset($output["error"]) ? $output["error"] : "An unknown error has occurred while trying to send your message.").")</h1>";
echo $data_string;
print_r($output);
$error = true;
}
curl_close ($ch);
$debug = false;
if($debug || $error){
if($error) echo "<!--";
var_dump($broadcast_message);
echo "<hr><br>";
echo $json."<hr><br>";
echo "<pre>";
print_r($output);
echo "</pre>";
if(!empty($output['ok'])){
//maybe we should save the status, or the json in the db.
echo 'yay it sent';
}
if($error) echo "-->";
}
if($error){
exit;
}
}//end sendBroadcast
When I do this query, I get hit by an error "Could not parse body request body". That wasn't very helpful, so I printed the response (look under "if(!is_array($output) || empty($output["ok"])){"). I get the following error message:
Array ( [ok] => [error] => Could not parse request body. [error_code] => 40700 [details] => Array ( [error] => Cannot schedule for the past 2013-10-12T06:46:00.000Z ) [operation_id] => 6fde4fa0-4b64-11e3-8903-90e2ba0253a0 )
The error I'm getting is "Cannot schedule for the past", however at the time of submitting this, it was the future. I began doing some research and read that I had to set it to UTC time. That being said, whatever my time is now, it will always be 6 hours into the past in UTC, so I have to convert it up to UTC.
So, I did that and the message went out and the phones received it and all went well. Except when we went to read the message: we then got an error that said the message was deleted.
We didn't delete it, so I think maybe (it hasn't been 6 hours yet) the users phone will get the new broadcast in the future, but they got informed of the alert now. That alert isn't visible yet, so it throws an error. At least that's what I think; it hasn't been 6 hours yet so I can't prove that.
My problem is this: How do I tell Urban Airship I want an immediate post to go out, without having to add 6 hours to the current time to make it in the "present", as well as actually having the phones get it at the correct time?
I contacted UA but they said to "expect a week delay in responding to you" (No rush, eh?) and I googled the error code (40700) and came up with nothing. I then emailed the guy who built the original and all he said was the "UTC was very important". Thank you for that.
If anybody can help me out I would be very thankful.
Thank you.
Oh, and if anybody is wondering, the json I'm submitting looks like the following:
{"schedule":{"scheduled_time":"2013-10-12 06:46:00"},"push":{"audience":"all","notification":{"alert":"Message\r\nKenton Industries\r\nKenton Test","ios":{"sound":"police.mp3","extra":{"id":"406"},"badge":"+1"},"android":{"extra":{"id":"406"}},"blackberry":{"content-type":"text\/plain","body":"{\"id\":\"406\",\"body\":\"Kenton Industries\\r\\nKenton Test\",\"type\":\"Message\"}"}},"device_types":["ios","android","blackberry"]}}
Thanks :)
I'm not sure I understand the second part of your question of, "having the phones get it at the correct time". In regards to the question of, "How do I tell Urban Airship I want an immediate post to go out, without having to add 6 hours to the current time to make it in the "present"":
If you want your users to receive the message as soon as possible then you shouldn't be scheduling your message. Via their curl examples an immediate push message curl request should look as thus:
curl -v -X POST -u "<AppKey>:<MasterSecret>" -H "Content-type: application/json" -H "Accept: application/vnd.urbanairship+json; version=3;" --data '{"audience" : "all", "device_types" : "all", "notification" : {"alert": "This is a broadcast"} }' https://go.urbanairship.com/api/push/