403 response when sending POST data via cURL - php

I'm trying to send some data to a custom built API that was built by a web development agency. The api is pretty straight forward, and all that I require to do is authorise myself with an authorisation basic header and submit a payload which is just JSON data.
The problem is that the API is responding with 403 every time I send a POST request using cURL. Is there any way that my code could be causing this? or is it an error that is caused by the API/API server? Regardless if the JSON payload is correctly formatted or not, it should still return with a 200 response.
The request is pretty straight forward -
<?php
//I've removed the actual username and password
$headers = array(
'Content-Type: application/json',
'Authorization: Basic '.base64_encode('username:password'));
$empty_array = array();
//Create empty json as an example
$json = json_encode($empty_array);
$url = "https://website.com/import";
//start cURL
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, false);
curl_setopt($ch, CURLOPT_POSTFIELDS,
$json);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
$output = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
If I take out the POST data, then it will return with a 404 response. This is the correct behaviour if there is no POST data submitted to the url. (I get a 404 if I navigate to the url via a browser). So I have a theory that the server must have some kind of restrictions on accepting POST data.
Does my code seem correct or am I missing something glaringly obvious? I wan't to rule out that it's a problem caused by my end due to not being able to access the code/server for the API as that is beyond my control.

Related

Oanda API v20 requests failing

I have tried the following code and receive a message saying my particular request is not supported, I cannot find any solutions that are not python for the v20 api. any help would be appreciated on what to use or where im going wrong
My error message is:
{"errorMessage":"Requested HTTP method is not supported for supplied
endpoint."}
<?php
$ch = curl_init();
$vars = "price=B&granularity=M5&count=20";
curl_setopt($ch, CURLOPT_URL,"https://api-fxpractice.oanda.com/v3/instruments/EUR_USD/candles");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,$vars); //Post Fields
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$headers = [
'Content-Type: application/json',
'Authorization: Bearer access-token',
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$server_output = curl_exec ($ch);
curl_close ($ch);
print $server_output ;
?>
My goal here is to successfully connect and get the response from the endpoint containing data. I have no python experience which is why im trying curl or even a javascript solution would work. Am i just not understanding this API at all?
Two things that stand out in the code are:
that there is no actual authorisation code. I don’t know if you have removed it for security while posting, or if you don’t realise you need to create a practice account and get an authorisation code for it? Both are free.
Your code curl_setopt($ch, CURLOPT_POST, 1); suggests you are trying a POST, whereas a GET is required for the \instruments endpoint.
But the simplest thing I can suggest that might help is to look at the following Github bash script that has taken OANDA's V1 API bash cURL example, and updated it for the v2 rest API.
https://github.com/p-burke/oanda-REST-v2-API-bash-script

Created a response listening service with php and curl just like paypal IPN

I am new to php web services and i have asked this question before but could not get useful solutions so i have tried to rephrase it. i am trying to listen to a response from an API and update my database like what is done by the paypal IPN service. The script that i have so far works well when you run it in the browser and i am a geting my a json response back via POST, but the API later sends another response without the involvement of the browse/client and i want it to communicate to my php script in the server and update the database. The API has a notifyURL field that it requires for it to send these notifications and i have set it to the same file that sends the request. Below is the code that i have so far:
if($ch !== false){
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
//Attach our encoded JSON string to the POST fields.
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonDataEncoded);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//Set the content type to application/json
// curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json;odata=verbose',
'Content-length:' . strlen($jsonDataEncoded))
);
}
//Execute the request
$result = curl_exec($ch);
if ($result === false){
curl_close($ch);
echo "Error".curl_errno($ch). "\n";
}else {
curl_close($ch);
$decoded = json_decode($result, true);
// echo '<pre>';
// var_dump($decoded["paymentAmount"]["totalAmountCharged"]);
// die;
$data['unique_id'] = $decoded["clientCorrelator"];
$data['subscriber_number'] = $decoded["endUserId"];
$data['payment_status'] = $decoded["transactionOperationStatus"];
$data['payment_gross'] = $decoded["paymentAmount"]["totalAmountCharged"];
$data['txn_id'] = $decoded["ecocashReference"];
$this->payments->insertTransaction($data);
die;
What do i need to add to the script for me to achieve my goal using curl and php. The API returns the json response below and i am trying to listen for it and update certain fields into the database.
{"id":71109,"version":0,"clientCorrelator":"58ada3aec8615","endTime":null,"startTime":1487774641697,"notifyUrl":"http://website/ecocash_send/transaction","referenceCode":"17589","endUserId":"774705932","serverReferenceCode":"2202201716440169792864","transactionOperationStatus":"PENDING SUBSCRIBER VALIDATION","paymentAmount":{"id":71110,"version":0,"charginginformation":{"id":71112,"version":0,"amount":1,"currency":"USD","description":" Bulk Sms Online payment"},"chargeMetaData":{"id":71111,"version":0,"channel":"WEB","purchaseCategoryCode":"Online Payment","onBeHalfOf":"PAMONMEFT","serviceId":null},"totalAmountCharged":null},"ecocashReference":"MP170222.1644.A00059","merchantCode":"0000","merchantPin":"000","merchantNumber":"771999313","notificationFormat":null,"serviceId":null,"originalServerReferenceCode":null}"
There needs to be nothing special about the script that responds to the PayPal IPN like call.
All it need to do is be there waiting for the other API to launch it, know what parameters to expect, have a robust error reporting mechanism i.e. save the errors somewhere that you can review them later, or email them to an email account you will look at regularly.
Other than that the usual database access code is all that should be necessary.
Of course you cannot get it to throw anything at your browser as there is no browser in the process.

php curl and CURLOPT_FOLLOWLOCATION behavior

Here's the situation. On one side, we have a server, with a RESTful service. One possible query to it is a POST query to create an object. When that is done, the server returns a 'Location' header, to indicate where information on the newly created object can be found.
However, said server is anal about having the correct Content-Type for each request. For instance, POST requires 'application/json', and GET requires this to be unset (make sense, since GET doesn't have a body).
To sum up, we have:
www.example.com/articles/ ; one can send a POST request with 'Content-Type: application/json', and server will return 'Location: www.example.com/articles/123' if 123 is the id of the new object ;
www.example.com/articles/123 ; one can send a GET request with no 'Content-Type' and server will return a description of the new article object.
On client side, we use PHP with cURL. We use the CURLOPT_FOLLOWLOCATIONsetting so we can read the description of the newly created object. Obviously, we also set 'Content-Type: application/json' for our POST request:
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"name": "test"}');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_URL, "https://www.example.com/Articles/");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$result=curl_exec($ch);
var_dump($result);
curl_close ($ch);
?>
This is what we get:
string(101) "{ "errorNo": 415, "error": "Unsupported Media Type Content-Type should not be set in a GET request" }"
I looked at the log of the server, and indeed, 'Content-Type: application/json' is sent to GET www.example.com/articles/123.
Is this an expected behaviour?
If yes, what is then best approach:
remove the 'Content-Type' check on GET requests, server-side?
(sounds silly)
forget about CURLOPT_FOLLOWLOCATION, and make 2 clearly separated curl requests, so I have control over the headers? (but then what's the point of CURLOPT_FOLLOWLOCATION?)
something else?
For control and testing, I also use Postman, and I have no problem with it, it follows the location, doesn't send the 'Content-Type' on the GET part (apparently) after the redirection and so I don't have an error.
EDIT:
There seems to be nothing useful in the PHP doc. But I found something interesting in the command line man page:
https://curl.haxx.se/docs/manpage.html
It says:
"WARNING: headers set with this option will be set in all requests - even after redirects are followed, like when told with -L, --location."
So I guess it probably is the expected behaviour for PHP too. May someone suggest best practices then?
Have you tried using
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
to set the post type

Post JSON data to an API via PHP

I need to integrate JSON into PHP, more specifically I need to post some information from a php page on our site to initiate an API request which will then send me information back. The information I am sending revolves around login information and I am struggling to work out how I can send this information.
This is the information I need to post:
POST /user/?action=login HTTP/1.1
Host: api.interlinkexpress.com
Content-Type: application/json
Accept: application/json
Authorization: Basic RFNNSVRIOk1ZUEFTU1dE
GEOClient: account/123456
Content-Length: 0
Reading around the subject I have seen that php cURL may be useful however the example I have doesn't contain all the information I need to pass over. Also I am unsure on whether I need to pass it via a button as well:
$data = array("username" => "TESTAPI", "password" => "APITEST");
$data_string = json_encode($data);
$ch = curl_init('http://api.interlinkexpress.com');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string))
);
$result = curl_exec($ch);
Any help on this matter would be appreciated. I am new to JSON having only learnt about it fleetingly.
edit: I suppose the main thing I'm asking is how do I pass this information to the API in PHP?
POST /user/?action=login HTTP/1.1
Host: api.interlinkexpress.com
Content-Type: application/json
Accept: application/json
Authorization: Basic RFNNSVRIOk1ZUEFTU1dE (this is the login credentials)
GEOClient: account/123456
Content-Length: 0
If you want something more powerful you can use Guzzle that integrates cURL in a really better way.
I don't get exactly what do you mean with passing it via a button. Are you talking just about PHP or also HTML/Javasript?
If you want a button you could proceed with a button that performs a XMLHttpRequest through Javascript, calling the API (that should return an application/json Content-Type), and then process this result via Javascript.
Or you can use cURL to call the API, get the result, json_decode it and do what you want with that.
You should explain better what you want to accomplish.
[EDIT]
Ok, after the clarifications I can tell you that you have to divide the parameters for the API from the settings for the HTTP call to the API (as you already did ^^ ).
So the parameters that will make the API to perform something are
action = login
while all the others are all setting for the HTTP client (cURL or Guzzle) that will perform the calling to the API's url
method = POST
Host = api.interlinkexpress.com
path = /user/
Accept = application/json
Content-Type = application/json
Authorization = Basic
GEOClient = account/123456 (is this a specific header needed from you API?)
Content-Length = 0
So, this is not complete nor tested but should give you a path to follow for adding
$data = array("action" => "login");
$data_string = json_encode($data);
$ch = curl_init(sprintf('%s/%s', 'http://api.interlinkexpress.com', 'user/');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string)),
'GEOClient: account/123456',
);
// I would suggest you to add the timeout
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY); // or set it to CURLAUTH_BASIC
curl_setopt($ch, CURLOPT_USERPWD, sprintf('%s:%s', $data['username'],$data['password']));
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); //get status code
$result = curl_exec($ch);
I honestly don't get/know where you should send JSON encoded data.
This way you are sending the 'action' as JSON data, but I guess that this is wrong.
Maybe you want to pass the $data_string as the value of a specific field? (like in the example stated from PHP Develoer?)
Work on that, and understand exactly what data you have to pass as JSON and what NOT instead.
I hope to have helped you and gave good suggestions.
Your $data_string is not in format like (field=value).
Nothing to do just pass array into POST.
curl_setopt($ch, CURLOPT_POSTFIELDS, array('Data'=>$data_string));

How to add curl HTTP Headers for authentication in PHP?

I want to add the HTTP headers for authenticating Udemy API access.Can someone tell me as to how to add the headers.I already have the client id and secret key.I want to access the API from a PHP page.
https://developers.udemy.com/
Here is the code i tried using:
$ch = curl_init($request);
curl_setopt($ch, CURLOPT_URL, $request);
curl_setopt($ch,CURLOPT_HTTPHEADER,array('X-Udemy-Client-Id:MY_ID','X-Udemy-Client-Secret:Secret'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$results= curl_exec($ch);
echo $results;
Output:
Blank Page
Can someone point out what the problem might be?
As #drmarvelous wrote, you perform two requests (1st - by CURL, and 2nd - by file_get_contents) which does the same. Wherein the result of CURL request is not actually used in your script. It use the result of file_get_contents request which is performed without authentication parameters. Because of this you getting Unauthorized error.
So you have to use the result of CURL request:
...
$json = json_decode($results, true);
print_r($json);
Update:
You have to ensure you use valid URL for API request, i.e. value of $request in your code should be valid URL. Also, ensure you pass valid authentication parameters (Client-Id and Client-Secret) by HTTP headers.
Furthermore, since API is secured, you have to disable SSL peer verification by setting CURLOPT_SSL_VERIFYPEER option to false.
So the code should look like this:
$ch = curl_init($request);
curl_setopt($ch, CURLOPT_URL, $request);
curl_setopt($ch,CURLOPT_HTTPHEADER,array('X-Udemy-Client-Id: {YourID}','X-Udemy-Client-Secret: {YourSecret}'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$results= curl_exec($ch);
echo $results;

Categories