Skip to content

Instantly share code, notes, and snippets.

@Samuell1
Last active October 25, 2019 06:56
Show Gist options
  • Save Samuell1/4b7873e4941283edaa563719196985ea to your computer and use it in GitHub Desktop.
Save Samuell1/4b7873e4941283edaa563719196985ea to your computer and use it in GitHub Desktop.
<?php namespace Classes\Api;
use DateTime;
use Exception;
use \GuzzleHttp\Client as GuzzleHttp;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ServerException;
class PortosHmacApi
{
private $publicKey;
private $privateKey;
private $apiUrl;
private $timestamp;
private $data;
private $params;
private $apiName;
private $method = 'GET';
private $result = null;
private $error = null;
public function __construct(string $apiUrl, string $publicKey, string $privateKey)
{
$this->apiUrl = $apiUrl;
$this->publicKey = $publicKey;
$this->privateKey = $privateKey;
$this->timestamp = date('Y-m-d\TH:i:s', $this->date()->getTimestamp()) . substr((string) microtime(), 1, 8) . 'Z'; // generate date format: "yyyy-MM-ddTHH:mm:ss.fffffffK"
}
private function date(): DateTime
{
return new DateTime;
}
private function hashedPayload(): string
{
if ($this->data) {
return hash('sha256', json_encode($this->data));
}
return hash('sha256', '');
}
private function getHost(): string
{
$url = parse_url($this->apiUrl);
return $url['host'] . (isset($url['port']) ? ':' . $url['port'] : '');
}
private function requestHeaders(): array
{
$headers = [
'host' => $this->getHost() ?? 'localhost:3000',
'x-nd-content-sha256' => $this->hashedPayload(),
'x-nd-date' => $this->timestamp,
];
ksort($headers);
return $headers;
}
private function canonicalHeaders(): string
{
$canonicalHeaders = [];
foreach ($this->requestHeaders() as $key => $value) {
$canonicalHeaders[] = strtolower($key) . ":" . $value;
}
return implode("\n", $canonicalHeaders);
}
private function signedHeaders(): string
{
$signedHeaders = [];
foreach ($this->requestHeaders() as $key => $value) {
$signedHeaders[] = strtolower($key);
}
return implode(";", $signedHeaders);
}
private function canonicalUri(): string
{
return '/'.ltrim($this->apiName, '/');
}
private function canonicalQuery(): string
{
if (empty($this->params)) {
return '';
}
ksort($this->params);
return '?' . http_build_query($this->params);
}
private function canonicalRequest(): string
{
$array = [
$this->method, // HTTPMethod
$this->canonicalUri(), // CanonicalURI
$this->canonicalQuery(), // CanonicalQueryString
$this->canonicalHeaders(), // CanonicalHeaders
'', // add new line character
$this->signedHeaders(), // SignedHeaders
$this->hashedPayload() // HashedPayload
];
return implode("\n", $array);
}
private function stringToSign(): string
{
$array = [
'NWS4-HMAC-SHA256',
$this->timestamp,
$this->publicKey,
hash('sha256', $this->canonicalRequest()),
];
return implode("\n", $array);
}
private function signature(): string
{
$date = $this->date()->format('Y-m-d');
$signingKey = hash_hmac('SHA256', $date, 'NWS4' . $this->privateKey, true);
return hash_hmac('SHA256', $this->stringToSign(), $signingKey);
}
private function authorization(): string
{
$authorization = [
'Credential=' . $this->publicKey,
'SignedHeaders=' . $this->signedHeaders(),
'Timestamp=' . $this->timestamp,
'Signature=' . $this->signature()
];
return urlencode(implode(',', $authorization));
}
private function headers(): array
{
return array_merge([
'Authorization' => 'NWS4-HMAC-SHA256 ' . $this->authorization(),
], $this->requestHeaders());
}
private function requestApi()
{
return new GuzzleHttp([
'base_uri' => $this->apiUrl,
'headers' => $this->headers(),
]);
}
public function get(string $apiName, array $params = [])
{
$this->apiName = $apiName;
$this->params = $params;
$this->method = 'GET';
$response = $this->requestApi()->get($this->apiName, ['query' => $this->params]);
$this->result = json_decode($response->getBody());
return $this;
}
public function post(string $apiName, array $data)
{
$this->apiName = $apiName;
$this->data = $data;
$this->method = 'POST';
try {
$response = $this->requestApi()->post($this->apiName, ['json' => $data]);
$this->result = json_decode($response->getBody());
} catch (Exception $ex) {
report($ex);
$this->result = null;
$this->error = $ex;
} catch (RequestException | ClientException | ServerException | BadResponseException $ex) {
// report($ex);
$this->result = null;
$this->error = json_decode($ex->getResponse()->getBody()->getContents());
}
return $this;
}
public function put(string $apiName, array $data, array $params = [])
{
$this->apiName = $apiName;
$this->data = $data;
$this->params = $params;
$this->method = 'PUT';
try {
$response = $this->requestApi()->put($this->apiName, ['query' => $this->params, 'json' => $data]);
$this->result = json_decode($response->getBody());
} catch (Exception $ex) {
report($ex);
$this->result = null;
$this->error = $ex;
} catch (RequestException | ClientException | ServerException | BadResponseException $ex) {
// report($ex);
$this->result = null;
$this->error = json_decode($ex->getResponse()->getBody()->getContents());
}
return $this;
}
public function delete(string $apiName)
{
$this->apiName = $apiName;
$this->method = 'DELETE';
try {
$response = $this->requestApi()->delete($this->apiName);
$this->result = json_decode($response->getBody());
} catch (Exception $ex) {
report($ex);
$this->result = null;
$this->error = $ex;
} catch (RequestException | ClientException | ServerException | BadResponseException $ex) {
// report($ex);
$this->result = null;
$this->error = json_decode($ex->getResponse()->getBody()->getContents());
}
return $this;
}
public function hasError()
{
return $this->error;
}
public function result()
{
return $this->result;
}
}
@tompetko
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment