I'm working with a PHP website where I want to integrate to some 3rd party API.
In particular I am looking at using CURL to interact with their server but this is something I am far from an expert in so hoping the community can help me gain a better understanding of what I am doing.
I am unclear what options such as -X and -d do, also I am unclear how I script this command on my PHP page? (Unfortunately it's tricky searching google for "-d" as this isn't considered part of the search string)
My particular example I am stuck on is requesting an access token, the API documentation provided to me is;
curl -X POST \
-d \ "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&scope=REQUESTED_SCOPES" \
'https://api.example.com/token'
grant_type- client_credentials
client_id- Generated during setup
client_secret - Web apps only, generated during setup
scope Optional - List of comma separated values, see supported scopes
If the request is successful, an access token will be returned in the response:
{
"access_token":"ACCESS_TOKEN",
"token_type":"Bearer",
"expires_in":3600
"scope":"REQUEST_SCOPES"
}
That is the above guidance, I have completed the pre-requisites so can confirm the client id, secret and required scope are correct.
I have tried both of the following in vein in my PHP script
$tk = curl_init();
curl_setopt($tk, CURLOPT_URL, "https://api.example.com/token");
curl_setopt($tk, CURLOPT_POST, 1);
curl_setopt($tk, CURL_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($tk, CURLOPT_POSTFIELDS, array( 'grant_type=client_credentials&client_id=myownid&client_secret=xyz123&scope=instrument'));
// grab URL and pass it to the browser
$result=curl_exec($tk);
// close cURL resource, and free up system resources
curl_close($tk);
And
$tk = curl_init();
curl_setopt($tk, CURLOPT_URL, "https://api.example.com/token?grant_type=client_credentials&client_id=myownid&client_secret=xyz123&scope=instrument");
curl_setopt($tk, CURLOPT_POST, 1);
curl_setopt($tk, CURL_HTTPHEADER, array('Content-Type: application/json'));
// grab URL and pass it to the browser
$result=curl_exec($tk);
// close cURL resource, and free up system resources
curl_close($tk);
Both of these examples produce the following error;
{"error_description":"grant_type parameter is missing","error":"invalid_request"}
Any help on this particular issue or even to just understand how I am going wrong to give me some ideas of the correct syntax will be much appreciated!
Thank you all in advanced for your time.
Check out below sample code of cURL call in php. You need to change your domain name instead of example.com also put values for POSTFIELDS.
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.example.com/oauth/token",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{\"grant_type\":\"client_credentials\",\"client_id\": \"YOUR_CLIENT_ID\",\"client_secret\": \"YOUR_CLIENT_SECRET\",\"scope\": \"instrument\"}",
CURLOPT_HTTPHEADER => array(
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
If you'd like to do it OO instead of using cURL, you might like this better. First up require in Guzzle:
composer require guzzlehttp/guzzle
Create an ApiCredentials Object:
<?php
namespace Some\Company;
class ApiCredentials
{
private $clientKey;
private $clientSecret;
private $proxy;
private $baseUrl;
public function __construct(string $clientKey, string $clientSecret, string $proxy = '', string $baseUrl = 'https://api.somewhere.com')
{
$this->clientKey = $clientKey;
$this->clientSecret = $clientSecret;
$this->proxy = $proxy;
$this->baseUrl = $baseUrl;
}
public function getClientKey(): string
{
return $this->clientKey;
}
public function getClientSecret(): string
{
return $this->clientSecret;
}
public function getProxy(): string
{
return $this->proxy;
}
public function getBaseUrl(): string
{
return $this->baseUrl;
}
}
Now create an ApiService class:
<?php
namespace Some\Company;
use DateTime;
use GuzzleHttp\Client;
class ApiService
{
const API_TOKEN_ENDPOINT = '/token';
private $baseUrl;
private $client;
private $accessToken;
private $credentials;
public function __construct(ApiCredentials $credentials)
{
$this->baseUrl = $credentials->getBaseUrl();
$options = $this->initOptions($credentials);
$this->client = new Client($options);
$this->credentials = $credentials;
}
private function initOptions(ApiCredentials $credentials) : array
{
$options = [
'base_uri' => $this->baseUrl,
'verify' => false,
];
if ($credentials->getProxy() !== '') {
$options = \array_merge($options, ['proxy' => [
'https' => $credentials->getProxy(),
]]);
}
return $options;
}
private function hasAccessToken() : bool
{
return $this->accessToken instanceof AccessToken && $this->accessToken->getExpires() > new DateTime();
}
private function getAccessToken() : AccessToken
{
return $this->accessToken;
}
private function getCredentials(): ApiCredentials
{
return $this->credentials;
}
private function refreshAccessToken()
{
$client = $this->getClient();
$response = $client->post(
$this->baseUrl . self::API_TOKEN_ENDPOINT, [
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
],
'form_params' => [
'grant_type' => 'client_credentials',
'client_id' => $this->getCredentials()->getClientKey(),
'client_secret' => $this->getCredentials()->getClientSecret(),
'scope' => 'put your scopes in here',
],
]);
$json = $response->getBody()->getContents();
$this->accessToken = new AccessToken($json);
}
private function getClient() : Client
{
return $this->client;
}
private function validateToken()
{
if (!$this->hasAccessToken()) {
$this->refreshAccessToken();
}
}
public function getSomeEndpointData(string $someParam = 'whatever') : string
{
$this->validateToken();
$response = $this->getClient()->get(
$this->baseUrl . '/get/some/data/' . $someParam, [
'headers' => [
'Authorization' => 'Bearer ' . $this->getAccessToken()->getToken(),
],
'query' => [
'additional' => 'this creates stuff like ?additional=whatever',
],
]);
$json = $response->getBody()->getContents();
return $json;
}
}
And an access token class:
<?php
namespace Some\Company;
use DateTime;
class AccessToken
{
private $token;
private $scope;
private $type;
private $expires;
public function __construct(string $tokenJson)
{
$token = \json_decode($tokenJson, true);
$keys = [
'access_token', 'scope', 'token_type', 'expires_in',
];
$this->token = $token['access_token'];
$this->scope = $token['scope'];
$this->type = $token['token_type'];
$date = new DateTime('+' .$token['expires_in'] . ' seconds');
$this->expires = $date;
}
public function getToken(): string
{
return $this->token;
}
public function getScope(): string
{
return $this->scope;
}
public function getType(): string
{
return $this->type;
}
public function getExpires(): DateTime
{
return $this->expires;
}
}
Now, to use this stuff:
<?php
use Some\Company\ApiCredentials;
use Some\Company\ApiService;
$clientKey = 'client key key here';
$clientSecret = 'client secret here';
$proxy = 'tcp://199.199.132.132:80'; // optional
$creds = new ApiCredentials($clientKey, $clientSecret, $proxy);
$apiService = new ApiService($creds);
$results = $apiService->getSomeEndpointData('whatever'); // returns json
It will handle refreshing access tokens etc too.