Skip to content

Commit

Permalink
Validate error code from response
Browse files Browse the repository at this point in the history
  • Loading branch information
parsilver committed Dec 2, 2023
1 parent 3b12a40 commit 7c95109
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 37 deletions.
2 changes: 1 addition & 1 deletion example/get-my-balance.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
// $response = $client->getOpenOrders('THB_BTC');
// $response = $client->getUserLimits();

dd($response->json());
dd($response->json('result'));
3 changes: 3 additions & 0 deletions src/Authorizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ class Authorizer
* The signature is in hex format.
* The user has to attach the signature via the Request Header You must get a new timestamp in millisecond from /api/v3/servertime.
* The old one is in second.
*
*
* @return string $signature
*/
public function generateSignature(string $secretKey, int $timestamp, string $method, string $path, string $query = '', string $payload = ''): string
{
Expand Down
84 changes: 50 additions & 34 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Farzai\Bitkub;

use Farzai\Bitkub\Responses\ResponseWithValidateErrorCode;
use Farzai\Transport\Contracts\ResponseInterface;
use Farzai\Transport\Request;
use Farzai\Transport\Response;
Expand All @@ -10,6 +11,7 @@
use GuzzleHttp\Psr7\Uri;
use Psr\Http\Client\ClientInterface as PsrClientInterface;
use Psr\Http\Message\RequestInterface as PsrRequestInterface;
use Psr\Http\Message\ResponseInterface as PsrResponseInterface;
use Psr\Log\LoggerInterface;

class Client
Expand Down Expand Up @@ -125,16 +127,57 @@ protected function get(string $path, array $options = []): ResponseInterface
{
$request = $this->createRequest('GET', $path, $options);

return new Response($request, $this->transport->sendRequest($request));
return $this->createResponse($request, $this->transport->sendRequest($request));
}

protected function post(string $path, array $options = []): ResponseInterface
{
$request = $this->createRequest('POST', $path, $options);

return new Response($request, $this->transport->sendRequest($request));
return $this->createResponse($request, $this->transport->sendRequest($request));
}

protected function ensureRequestAreSecure(PsrRequestInterface $request): PsrRequestInterface
{
if ($this->isSecureEndpoint($request->getMethod(), $request->getUri()->getPath())) {
$authorizer = new Authorizer();

$uri = $request->getUri();
$query = $uri->getQuery();
$body = $request->getBody()->getContents() ?: '';
$timestamp = (int) (microtime(true) * 1000);

$signature = $authorizer->generateSignature(
$this->config['secret'],
$timestamp,
$request->getMethod(),
$uri->getPath(),
$query,
$body
);

$request = $request
->withHeader('X-BTK-APIKEY', $this->config['api_key'])
->withHeader('X-BTK-SIGN', $signature)
->withHeader('X-BTK-TIMESTAMP', $timestamp);
}

return $request;
}

/**
* Determine if the given path is secure.
*/
protected function isSecureEndpoint(string $method, string $path): bool
{
$path = strtoupper($method).' '.$path;

return in_array($path, $this->secureEndpoints);
}

/**
* Create a new request instance.
*/
protected function createRequest(string $method, string $path, array $options = []): PsrRequestInterface
{
// Normalize path
Expand Down Expand Up @@ -171,42 +214,15 @@ protected function createRequest(string $method, string $path, array $options =
return $request;
}

protected function ensureRequestAreSecure(PsrRequestInterface $request): PsrRequestInterface
{
if ($this->isSecureEndpoint($request->getMethod(), $request->getUri()->getPath())) {
$authorizer = new Authorizer();

$uri = $request->getUri();
$query = $uri->getQuery();
$body = $request->getBody()->getContents() ?: '';
$timestamp = (int) (microtime(true) * 1000);

$signature = $authorizer->generateSignature(
$this->config['secret'],
$timestamp,
$request->getMethod(),
$uri->getPath(),
$query,
$body
);

$request = $request
->withHeader('X-BTK-APIKEY', $this->config['api_key'])
->withHeader('X-BTK-SIGN', $signature)
->withHeader('X-BTK-TIMESTAMP', $timestamp);
}

return $request;
}

/**
* Determine if the given path is secure.
* Create a new response instance.
*/
protected function isSecureEndpoint(string $method, string $path): bool
protected function createResponse(PsrRequestInterface $request, PsrResponseInterface $baseResponse): ResponseInterface
{
$path = strtoupper($method).' '.$path;
$response = new Response($request, $baseResponse);
$response = new ResponseWithValidateErrorCode($response);

return in_array($path, $this->secureEndpoints);
return $response;
}

private function ensureConfigIsValid(): void
Expand Down
8 changes: 7 additions & 1 deletion src/Constants/ErrorCodes.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ class ErrorCodes
self::SERVER_ERROR => 'Server error (please contact support)',
];

/**
* Get all error codes.
*/
public static function all(): array
{
$reflection = new \ReflectionClass(__CLASS__);
Expand All @@ -154,8 +157,11 @@ public static function all(): array
}, array_keys($reflection->getConstants()));
}

/**
* Get the description of the error code.
*/
public static function getDescription(int $code): string
{
return $descriptions[$code] ?? 'Unknown error';
return self::DESCRIPTIONS[$code] ?? "Unknown error code: {$code}";
}
}
82 changes: 82 additions & 0 deletions src/Responses/ResponseDecorator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

namespace Farzai\Bitkub\Responses;

use Farzai\Transport\Contracts\ResponseInterface;
use Farzai\Transport\Traits\PsrResponseTrait;
use Psr\Http\Message\RequestInterface as PsrRequestInterface;

abstract class ResponseDecorator implements ResponseInterface
{
use PsrResponseTrait;

protected ResponseInterface $response;

/**
* Create a new response instance.
*/
public function __construct(ResponseInterface $response)
{
$this->response = $response;
}

/**
* Return the response status code.
*/
public function statusCode(): int
{
return $this->response->statusCode();
}

/**
* Return the response body.
*/
public function body(): string
{
return $this->response->body();
}

/**
* Return the response headers.
*/
public function headers(): array
{
return $this->response->headers();
}

/**
* Check if the response is successfull.
*/
public function isSuccessfull(): bool
{
return $this->response->isSuccessfull();
}

/**
* Return the json decoded response.
*/
public function json(string $key = null): mixed
{
return $this->response->json($key);
}

/**
* Throw an exception if the response is not successfull.
*
* @param callable<\Farzai\Transport\Contracts\ResponseInterface> $callback Custom callback to throw an exception.
*
* @throws \Psr\Http\Client\ClientExceptionInterface
*/
public function throw(callable $callback = null)
{
return $this->response->throw($callback);
}

/**
* Return the psr request.
*/
public function getPsrRequest(): PsrRequestInterface
{
return $this->response->getPsrRequest();
}
}
29 changes: 29 additions & 0 deletions src/Responses/ResponseWithValidateErrorCode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Farzai\Bitkub\Responses;

use Farzai\Bitkub\Constants\ErrorCodes;
use Farzai\Transport\Contracts\ResponseInterface;

class ResponseWithValidateErrorCode extends ResponseDecorator
{
/**
* Throw an exception if the response is not successfull.
*
* @param callable<\Farzai\Transport\Contracts\ResponseInterface> $callback Custom callback to throw an exception.
*
* @throws \Psr\Http\Client\ClientExceptionInterface
*/
public function throw(callable $callback = null)
{
$callback = $callback ?? function (ResponseInterface $response, ?\Exception $e) use ($callback) {
if ($this->json('error') !== 0) {
throw new \Exception(ErrorCodes::getDescription($this->json('error')));
}

return $callback ? $callback($response, $e) : $response;
};

return parent::throw($callback);
}
}
16 changes: 15 additions & 1 deletion tests/AuthorizerTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
<?php

use Farzai\Bitkub\Authorizer;

it('can generate signature success', function () {
//
$secret = 'test-secret';
$timestamp = 1630483200000;

$method = 'POST';
$path = '/api/v3/market/balances';
$query = '';
$payload = '';

$authorizer = new Authorizer();

$signature = $authorizer->generateSignature($secret, $timestamp, $method, $path, $query, $payload);

expect($signature)->toBe('b8403c345ce41b25b47885254fb8aeed9ad7ceb9e30425b86a9a151dd6ac2e35');
});

0 comments on commit 7c95109

Please sign in to comment.