I try to access an API for a web app which will return the data in json format. The idea is to configure the header for the request to be read properly. An example request provided would be:
GET /links/{linkKey}/response HTTP/1.1
Access: *MY SECRET KEY*
Account: *MY ACCOUNT KEY*
Accept: application/json
Accept-Encoding: gzip
Host: localhost
User-Agent: YourApp/1.0
This example request should return this example response:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: gzip
Transfer-Encoding: chunked
Vary: Accept-Encoding
{
"headers": [
"column_a",
"column_b",
"column_c"
],
"rows": [
[
"text 1",
143.22,
true
],
[
"text 2",
98,
false
],
[
"text 3",
24.9,
false
],
[
"value 4",
242,
false
],
[
"value 5",
32,
true
]
],
"totalRows": 5
}
Based on this, I wrote the following code:
<?php
// Set header
header('Content-type: application/json');
// See https://app.example.com/#/api
$endpoint = "https://api.example.com/"; // The URL for API access
$api_key = "*MY API_KEY*";
$account_id = "*MY ACCOUNT KEY*";
$access_key = md5($account_id . $api_key);
$link_key = '*MY LINK KEY*'; //Edit your runs inside the app to get their ID
$curl_h = curl_init($endpoint);
curl_setopt($curl_h, CURLOPT_HTTPHEADER,
array(
"GET /links/" . $link_key . "/latest/result HTTP/1.1\r\n",
"Access:" . $access_key . "\r\n",
"Account:" . $account_id . "\r\n",
"Accept: application/json\r\n",
"Accept-Encoding: gzip\r\n",
"Host: localhost\r\n",
"User-Agent: CS-PHP-CLIENT/1.0\r\n",
"Content-Type: application/json\r\n"
)
);
// Store to variable
curl_setopt($curl_h, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl_h);
curl_close($curl_h);
var_dump($response);
This outputs a 400 Bad Request error in the browser:
string(298) "<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.7 (Ubuntu) Server at example.com Port 80</address>
</body></html>
"
As you can see from above, the regulation of the provider is that the access_key must be encrypted using md5(). Given that at least string(298) appears, I alter the code to var_dump($curl_h), instead of var_dump($response). This eventually outputs an unknown response:
resource(2) of type (Unknown)
I rewrote the code into something different:
<?php
// See https://app.example.com/#/api
$endpoint = "https://api.example.com/"; // The URL for API access
$api_key = "*MY API_KEY*";
$account_id = "*MY ACCOUNT KEY*";
$access_key = md5($account_id . $api_key);
$link_key = '*MY LINK KEY*'; //Edit your runs inside the app to get their ID
// Create a stream
$opts = array(
'http'=>array(
'method'=>"GET",
'header'=>"GET /links/" . $link_key . "/latest/result HTTP/1.1\r\n" .
"Access:" . $access_key . "\r\n" .
"Account:" . $account_id . "\r\n" .
"Accept: application/json\r\n" .
"Accept-Encoding: gzip\r\n" .
"Host: localhost\r\n" .
"User-Agent: CS-PHP-CLIENT/1.0\r\n" .
"Content-Type: application/json\r\n"
)
);
$context = stream_context_create($opts);
// Open the file using the HTTP headers set above
$file = file_get_contents($endpoint, false, $context);
var_dump($file);
The following response appears in the browser when I run this code:
bool(false)
I don't know why this doesn't work.
NB: I'm using a localhost on my pc which loads other php files and their respective code correctly.
API Endpoint: https://api.example.com/
Text Encoding: All requests must be encoded in UTF-8 - and all responses are UTF-8 encoded.
Related
I converted the following Python code from the official docs(https://docs.sandbox.gemini.com/rest-api/#private-api-invocation) to PHP but I always get InvalidSignature error:
url = "https://api.gemini.com/v1/mytrades"
gemini_api_key = "mykey"
gemini_api_secret = "1234abcd".encode()
t = datetime.datetime.now()
payload_nonce = time.time()
payload = {"request": "/v1/mytrades", "nonce": payload_nonce}
encoded_payload = json.dumps(payload).encode()
b64 = base64.b64encode(encoded_payload)
signature = hmac.new(gemini_api_secret, b64, hashlib.sha384).hexdigest()
request_headers = {
'Content-Type': "text/plain",
'Content-Length': "0",
'X-GEMINI-APIKEY': gemini_api_key,
'X-GEMINI-PAYLOAD': b64,
'X-GEMINI-SIGNATURE': signature,
'Cache-Control': "no-cache"
}
response = requests.post(url, headers=request_headers)
My PHP code is this and everything looks correct:
$b64 = base64_encode(utf8_encode(json_encode([ "request" => "/v1/balances", "nonce" => time() ])));
$header = [
'Content-Type: text/plain',
'Content-Length: 0',
'X-GEMINI-APIKEY: master-XXXXXXX',
'X-GEMINI-PAYLOAD: ' . $b64,
'X-GEMINI-SIGNATURE: ' . md5(hash_hmac('sha384', utf8_encode('XXXXXXXXXX'), $b64)),
'Cache-Control: no-cache'
];
$ch = curl_init('https://api.sandbox.gemini.com/v1/balances');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
echo curl_exec($ch);
Error:
{"result":"error","reason":"InvalidSignature","message":"InvalidSignature"}
I don't have any access to the API, but could you please try the following chagnes:
Remove the MD5 function from the signature calculation. The hash_hmac function in PHP already does this by default.
'X-GEMINI-SIGNATURE: ' . hash_hmac('sha384', utf8_encode('XXXXXXXXXX'), $b64),
Switch around the payload and the key in the same line:
'X-GEMINI-SIGNATURE: ' . hash_hmac('sha384', $b64, utf8_encode('XXXXXXXXXX')),
I'm trying to send a GET request to an api with my keys. My keys need to be in the header. I have saved my keys in an array $api and given them to Guzzle.
When I send the GET request I get a 403 Forbidden response, my best guess is that the headers aren't actually being set.
require "../vendor/autoload.php";
use GuzzleHttp\Client;
$api = array(
"X-Api-Id" => "id",
"X-Api-Secret" => "secret",
"X-Api-Key" => "key"
);
$client = new GuzzleHttp\Client([
"base_uri" => "example.com",
"timeout" => 5
]);
$response = $this->client->request("GET", "/configs/", ["verify" => false, "headers" => $api]);
var_dump($response);
The error
Fatal error: Uncaught GuzzleHttp\Exception\ClientException: Client error: GET https://api.onetap.com/configs/ resulted in a 403 Forbidden response: <!--[if IE 7]> <html class (truncated...) in C:\scaryconfigscc\vendor\guzzlehttp\guzzle\src\Exception\RequestException.php on line 113
The documentation doesn't show it much different, besides it also has default headers:
try {
$response = $client->request('GET', '/configs', [
'headers' => [
'User-Agent' => 'testing/1.0',
'Accept' => 'application/json',
'X-Api-Id' => 'id',
'X-Api-Secret' => 'secret',
'X-Api-Key' => 'key'
]
]);
} catch (ClientException $e) {
echo 'Caught exception: ' . $e->getMessage() . "\n";
}
If it doesn't work - either the credentials are wrong or it may possibly expect method POST.
You can try the following:
Use $request->getHeader() to retrieve all the headers that are being sent and check if the required headers are set.
Check $response->getHeader() and verify is the api is looking to set a cookie. In case a cookie is actually being set, you will need to set a cookiejar (https://docs.guzzlephp.org/en/stable/quickstart.html#cookies).
If both the above does not solve the problem then things get complicated. It is more of trial and error from here on. You can try one or all of the following:
Change user agent
it could be that the api is blocking your IP (so you need to try it on a different server or IP)
they could be detecting some of your use as an offending user and may be blocking the request via their firewall or at the app level. What triggers the rules is for you and them to figure out.
If it still does not work you will need to contact their support or share more details before anyone can help you.
I you receive a http response 403 Forbidden then you either not allowed to communicate with the url you request (check if you have all api keys etc generated) and/or your request is incomplete/wrong.
You may exclude Guzzle as a reason of the failure and narrow the possible causes by doing the same request with other tools. If you succeed then it means the problem is with the Guzzle, but if you don't then one of the problems described above is the real issue.
To do request with PHP's native funcion file_get_contents() use this code:
<?php
# display as a plain text instead of html, just for test
header('Content-Type: text/plain');
# full url including '/configs'
$url = 'https://www.example.com/configs/';
$headers = [
"X-Api-Id" => "id",
"X-Api-Secret" => "secret",
"X-Api-Key" => "key"
];
# optional query str
// $queryArr = [
// 'key' => 'value'
// ];
# ----------- prepare request -----------------
if (!empty($queryArr)) {
$url .= '?' . http_build_query($queryArr);
}
$opts = [];
$opts['http']['method'] = "GET";
if (!empty($headers)) {
$headersStr = '';
foreach ($headers as $header => $value) {
$headersStr .= $header . ': ' . $value . "\r\n";
}
$opts['http']['header'] = $headersStr;
$context = stream_context_create($opts);
}
# ----------- prepare request end ------------
if (isset($context)) {
$response = file_get_contents($url, false, $context);
} else {
$response = file_get_contents($url);
}
echo $response;
I checked that method with my own service and it works, all headers are send.
But if you don't believe me you can do the same by using cURL.
Type in the command line:
curl --insecure -L -H -S -v --connect-timeout 5 -H "X-Api-Id: id" -H "X-Api-Secret: secret" -H "X-Api-Key: key" "https://www.example.com/configs/"
you may need instead of curl at the beginning use the full path to the executable like C:\Downloads\curl.exe at Windows.
The options I used:
--insecure Allow insecure server connections when using SSL
-L, --location Follow redirects
-H, --header <header/#file> Pass custom header(s) to server
-S, --show-error Show error even when -s is used
-v A line starting with '>' means "header data" sent by curl, '<' means "header data"
received by curl that is hidden in normal cases, and a line starting with '*' means additional
info provided by curl.
--connect-timeout <seconds> Maximum time allowed for connection
You should firstly try the command line above without:
--insecure Allow insecure server connections when using SSL
but if you have a problem on Windows with checking certificates this is one of the workarounds (not recommended), it is better to fix that problem
for the command line curl certificate check this link, see section Certificate Verification point 4.Additionally how to fix certificate problem with php
If you run the command line curl example as above you should get something like that in the output:
* Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to www.example.com (xxx.xxx.xxx.xxx) port 80 (#0)
> GET /configs/ HTTP/1.1
> Host: www.example.com
> User-Agent: curl/7.58.0
> Accept: */*
> X-Api-Id: id
> X-Api-Secret: secret
> X-Api-Key: key
>
< HTTP/1.1 200 OK
< Connection: Keep-Alive
< Keep-Alive: timeout=5, max=100
(...)
where as previously mentioned:
-v A line starting with '>' means "header data" sent by curl, '<' means "header data"
So you can be sure and have a proof that the headers were send:
> X-Api-Id: id
> X-Api-Secret: secret
> X-Api-Key: key
There are some additional headers like:
> Accept: */*
And if you want to receive a response body as json you probably should also set this header too by adding to the command line:
-H "Accept: application/json"
but check that with the documentation of the api you request.
Once you confirm everything is working as it should by file_get_contents or cURL command line you can check everything with the Guzzle. Before you do that check if your php has curl by the command line:
php -m
you will see all the extensions in the php installed and there should be an extension named curl
Guzzle example to do a request with headers and optional query string.
I used guzzle installed by this command:
composer require "guzzlehttp/guzzle:^7.2"
Code using Guzzle:
<?php
require_once "../vendor/autoload.php";
# display as a plain text instead of html, just for test
header('Content-Type: text/plain');
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\RequestOptions;
try {
$headers = [
"X-Api-Id" => "id",
"X-Api-Secret" => "secret",
"X-Api-Key" => "key"
];
# optional query str
// $queryArr = [
// 'key' => 'value'
// ];
$method = 'GET';
$scheme = 'https';
$baseUrl = 'www.example.com';
$urlDir = '/configs/';
$baseUri = strtolower($scheme) . '://' . $baseUrl;
$clientConfigArr = [
'base_uri' => $baseUri,
RequestOptions::TIMEOUT => 5.0, // Timeout if a server does not return a response in 5 seconds.
RequestOptions::VERIFY => false, // Disable validation entirely (don't do this!).
RequestOptions::DEBUG => true,
];
# set method to default value if not set
if (empty(trim($method))) {
$method = 'GET';
}
# set location to / if not set
if (empty(trim($urlDir))) {
$urlDir = '/';
}
# add query string if query arr not empty
if (!empty($queryArr)) {
$urlDir .= '?' . http_build_query($queryArr);
}
# if headers are set
if (!empty($headers)) {
# create request with headers
$request = new Request(strtoupper(trim($method)), $urlDir, $headers);
} else {
# create request without headers
$request = new Request(strtoupper(trim($method)), $urlDir);
}
# create client and send a the requrest
$client = new Client($clientConfigArr);
$response = $client->sendRequest($request);
} catch (\GuzzleHttp\Exception\TransferException $e) {
$msg = ' ';
$msg .= \PHP_EOL;
$msg .= '[BAD] [Transfer ERR: ' . $e->getCode() . ']' . \PHP_EOL;
$msg .= 'at ' . $e->getFile() . ':' . $e->getLine() . \PHP_EOL;
if (!empty($response) && is_object($response) && method_exists($response, 'getBody')) {
$msg .= '[Response Body] ' . (string) $response->getBody() . \PHP_EOL;
}
echo "$msg";
exit(1);
} catch (\Throwable $e) {
$msg = '';
$msg .= \PHP_EOL;
$msg .= '[BAD] [ERROR Throwable]' . \PHP_EOL;
$msg .= '[Message] ' . $e->getMessage() . \PHP_EOL;
$msg .= 'at ' . $e->getFile() . ':' . $e->getLine() . \PHP_EOL;
echo "$msg";
exit(1);
}
echo '[OK] [SUCCESS]' . \PHP_EOL . \PHP_EOL;
echo $response->getBody() . \PHP_EOL;
Note that using:
RequestOptions::VERIFY => false
is the same as using --insecure for cURL command line and should be avoided, better fix the problem with the SSL certificates by following the instruction previously given. Having that problem fixed you may remove the RequestOptions::VERIFY => false from the $clientConfigArr
Guzzle usefull links:
Request
Request Options
How to check if a request or response has a specific header.
Client
Response
Exceptions
It is possible that you should not do the GET request but POST instead as Martin mentioned. If you want to make a request with different method then:
Request Methods
For the POST method you will need most likely send a body as well, you can create a POST Request and attach the body this way:
use GuzzleHttp\Psr7\Request;
$headers = ['X-Foo' => 'Bar'];
$body = 'hello!';
$request = new Request('POST', 'http://httpbin.org/post', $headers, $body);
# and send request (as in the Guzzle example)
$client = new Client($clientConfigArr);
$response = $client->sendRequest($request);
check with the api documentation if you need to pass some body content (perhaps your token) if you are going to do the POST request (or any other method)
A side note:
In the Guzzle Code example instead of require I used require_once because:
if the code from a file has already been included, it will not be included again.
source
Therefore you can use in may files as many times as you want:
require_once "../vendor/autoload.php";
and everything will work fine, in contrary to the require without once that will very often make you duplicate declaration errors.
For having windows like backslashes you may use DIRECTORY_SEPARATOR and DIR as a current dir. Using them both like this:
require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
Just in case you would like to know.
I am trying to GET targeting from an MMID but am returning the following error. I know my credentials are correct and have combed over the documentation several times. "appTesting" in the response below is the name of my App in YesMail
INFO: HTTP Response Code was: 400 INFO: Response Follows... HTTP/1.1 400 Bad Request Access-Control-Allow-Origin: * Api-User: appTesting Content-Type: application/json Date: Wed, 29 Jul 2015 21:38:54 GMT Tracking-Id: f257bd75-47bb-40e7-a12c-5a45a042cbac X-Content-Type-Options: nosniff transfer-encoding: chunked Connection: keep-alive {"status":400,"message":"Could not open Hibernate Session for transaction; nested exception is java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [appTesting]"}
I kind of stumped. What am I doing wrong?
<?php
$mmid = 'mmid';
$api_user = "appTesting";
$api_key = "api key";
$ym_api_url = 'https://api.yesmail.com/v2/masters/';
$curl_hdl = curl_init();
$curl_options = array(
CURLOPT_VERBOSE => 1,
CURLOPT_HEADER => TRUE,
CURLOPT_HTTPHEADER => array("Api-User: $api_user", "Api-Key: $api_key", "Accept: application/json",),
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => $ym_api_url.$mmid.'/target',
);
curl_setopt_array($curl_hdl, $curl_options);
$response = curl_exec($curl_hdl);
$http_response_code = curl_getinfo($curl_hdl, CURLINFO_HTTP_CODE);
curl_close($curl_hdl);
echo PHP_EOL . "INFO: HTTP Response Code was: " . $http_response_code . PHP_EOL;
if ( $response === false )
{
echo PHP_EOL . "ERROR: curl_exec() has failed." . PHP_EOL;
}
else
{
echo PHP_EOL . "INFO: Response Follows..." . PHP_EOL;
echo PHP_EOL . $response;
}
echo "<br/><br/>API USER: ".$api_user."<br/><br/>API KEY: ".$api_key;
?>
I'm trying to send a file through rest using HTTP request to BonitaBPM but the file im trying to send comes out empty when i use file get contents and the HTTP request doesn't work obviously due to that
$file_contents = file_get_contents("C:/inetpub/wwwroot/upload/Director.png");
$data1 = array(
"caseId"=> $case_id[1],
"file"=>"C:/inetpub/wwwroot/upload/Director.png",
"name"=>"doc_invoice",
"fileName"=> $_FILES['file_attach']['name'],
"description"=> "Invoice"
);
//Structure of process data to start case
$options1 = array(
"http" => array(
"method" => "POST",
"header"=> "POST /bonita/API/bpm/caseDocument HTTP/1.1\r\n".
"Host: bonita.libertypr.com\r\n".
"Cookie: ". $display[1]."\r\n".
"Content-Type: application/json\r\n" .
"Accept: application/json\r\n".
"Cache-Control: no-cache\r\n".
"Pragma: no-cache\r\n".
"Connection: Keep-Alive\r\n\r\n",
"content" => json_encode($data1)
)
);
//decode process data and adds document to case
$url1 = "http://bonita.libertypr.com:8081/bonita/API/bpm/caseDocument";
$context1 = stream_context_create($options1);
$result1 = file_get_contents($url1, false, $context1);
$response1 = json_decode($result1);
Please make sure that you are authenticated on the Bonita side before calling this API call.
See this link for more details:
http://documentation.bonitasoft.com/rest-api-overview#authentication
If you are not, the Bonita API calls will be rejected.
To analyze a bit further what is causing the issue, you should get your hands on the HTTP request and response sent between your code and Bonita.
To do so, you can capture the HTTP traffic with a tool such as Wireshark
Cheers,
i am using the rest api for docusign.
i am sending a document to another user for online signing. What is happening in the code is that the document is being uploaded and the contents of the file is being read using the file_get_contents function and then using curl this content is sent to the other user through docusign.
Here is my code:
$data = "{
\"emailBlurb\":\"\",
\"emailSubject\":\"DocuSign API - Please Sign This Document...\",
\"documents\":[
{
\"documentId\":\"1\",
\"name\":\"agreement.pdf\"
}
],
\"recipients\":{
\"signers\":[
{
\"email\":\"$email\",
\"name\":\"$name\",
\"recipientId\":\"1\",
\"tabs\":{
\"signHereTabs\":[
{
\"xPosition\":\"100\",
\"yPosition\":\"100\",
\"documentId\":\"1\",
\"pageNumber\":\"1\"
}
]
}
}
]
},
\"status\":\"sent\"
}";
$file_contents = file_get_contents("uploads/envelopes/" . $file_name);
$requestBody = "\r\n"
."\r\n"
."--myboundary\r\n"
."Content-Type: application/json\r\n"
."Content-Disposition: form-data\r\n"
."\r\n"
."$data\r\n"
."--myboundary\r\n"
."Content-Type:application/pdf\r\n"
."Content-Disposition: file; filename=\”document.pdf\"; documentid=1 \r\n"
."\r\n"
."$file_contents\r\n"
."--myboundary--\r\n"
."\r\n";
// *** append "/envelopes" to baseUrl and as signature request endpoint
$curl = curl_init($baseUrl . "/envelopes" );
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $requestBody);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Content-Type: multipart/form-data;boundary=myboundary',
'Content-Length: ' . strlen($requestBody),
"X-DocuSign-Authentication: $header" )
);
$json_response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ( $status != 201 ) {
$msg = 'Error. Please try again';
}
The above code is working fine for pdf files but not for docx files.
For docx files, i have changed the code like this (changed the content type):
$requestBody = "\r\n"
."\r\n"
."--myboundary\r\n"
."Content-Type: application/json\r\n"
."Content-Disposition: form-data\r\n"
."\r\n"
."$data\r\n"
."--myboundary\r\n"
."Content-Type:application/vnd.openxmlformats-officedocument.wordprocessingml.document\r\n"
."Content-Disposition: file; filename=\”document.docx\"; documentid=1 \r\n"
."\r\n"
."$file_contents\r\n"
."--myboundary--\r\n"
."\r\n";
Thanks
DocuSign does support sending .docx files, so file format is not causing your issue. Are you receiving a specific error message when you attempt to send a .docx file? If so, then what's the error?
FWIW, I'm able to send a .docx file via the DocuSign REST API -- I've included my full request below. If possible, I'd suggest that you use Fiddler (or any similar utility) to produce a trace of the full request that you're sending to the server, and compare its contents/structure with the (successful) example request that I've included below. Doing so would hopefully allow you to identify (and resolve) the problem with your request.
POST https://demo.docusign.net/restapi/v2/accounts/ACCOUNT_NUMBER/envelopes HTTP/1.1
X-DocuSign-Authentication: {"Username":"USERNAME","Password":"PASSWORD","IntegratorKey":"INTEGRATOR_KEY"}
Content-Type: multipart/form-data; boundary=MY_BOUNDARY
Accept: application/json
Host: demo.docusign.net
Content-Length: 15384
--MY_BOUNDARY
Content-Type: application/json
Content-Disposition: form-data
{
"status" : "sent",
"emailBlurb":"Test Email Body",
"emailSubject": "-- Test Email Subject --",
"documents": [
{
"name": "CustomerAgreement.docx",
"documentId": 1
}],
"recipients": {
"signers" : [{
"email": "bobsemail#outlook.com",
"name": "Bob Adamson",
"recipientId": "1",
"routingOrder": "1",
"tabs": {
"signHereTabs": [
{
"recipientId": "1",
"tabLabel": "Customer_Signature",
"documentId": "1",
"pageNumber": "1",
"xPosition": "99",
"yPosition": "424"
}]
}
}]
}
}
--MY_BOUNDARY
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
Content-Disposition: file; filename="CustomerAgreement.docx"; documentid="1"
<document bytes removed>
--MY_BOUNDARY--