Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?

Craft a Kraken API client with PHP8 (1/2)

At this time we gonna construct from scratch a easy however versatile shopper to make use of the Kraken API, which is at writing time the fourth biggest exchange in volume. The ultimate end result shall be a Composer package deal that you will can simply use as dependency in any PHP undertaking utilizing Composer as dependencies supervisor.

This shopper will assist any Kraken API endpoint, public or non-public, and likewise open WebSocket streams to carry out actual time actions or monitoring.

This primary half will describe the REST utilization of the Kraken API, then the second shall be about WebSocket.



Accessing public endpoints

Like most change APIs, Kraken permit us to retrieve market information with out authentication. As an instance we have to entry all change buying and selling pairs, we merely must make a GET request to this endpoint: https://api.kraken.com/0/public/AssetPairs then parse response, nothing fancy.



Accessing non-public endpoints

When coping with non-public endpoints, the Kraken API want each a selected header containing your API shopper key, but additionally require you to signal your request underneath a API-SIGN header constructed out of your payload, the endpoint URI, your API secret Base64 encoded and a nonce, which is a unsigned 64-bit integer.

Watch out with this nonce parameter, you can’t make API calls utilizing smaller nonce so do not use a random massive quantity or your shopper shall be trapped make sure you observe the present Unix timestamp as an illustration, extra information here on the Kraken API documentation.

The opposite level of consideration, is the truth that not like quite a few different change API, Kraken don’t supply a public take a look at setting, the place you protected to attach integration unit assessments as an illustration. With this API on some essential endpoint, such like work together with order guide, you will must cross an additional validate parameter.

Image description

Speak is reasonable, present me the code.

Linus Torvald, Linux creator

No must over engineering the whole lot, make some abstraction layers for the request or response, make tons of customized strategies for every endpoint. We’ll put the minimal quantity of code to cowl the utmost API use instances.

<?php
namespace NicolasBonniciPhpKrakenApiClient;

use NicolasBonniciPhpKrakenApiClientExceptionKrakenAPIException;

class KrakenAPIClient
{
    public const USER_AGENT = __NAMESPACE__;
    public const DEFAULT_API_ROOT = 'https://api.kraken.com';
    public const DEFAULT_API_VERSION = '0';
    public const AUTHENTICATED_ENDPOINTS_URI_PATH = 'non-public/';

    public perform __construct(
        non-public ?string $key = null,
        non-public ?string $secret = null,
        non-public string $apiRoot = self::DEFAULT_API_ROOT,
        non-public string $model = self::DEFAULT_API_VERSION,
        non-public bool $sslCheck = true,
        non-public bool|CurlHandle $curl = false,
    ) {
        if (!function_exists('curl_init')) {
            throw new KrakenAPIException('No curl extension out there.');
        }

        $this->loadClient();
    }

    public perform __destruct()
    {
        if (function_exists('curl_close') && $this->curl instanceof CurlHandle) {
            curl_close($this->curl);
        }
    }

    /**
     * @throws KrakenAPIException
     */
    public perform question(
        string $endpoint,
        array $request = [],
        array $headers = []
    ): array {
        $authenticated = str_starts_with($endpoint, self::AUTHENTICATED_ENDPOINTS_URI_PATH);

        $uri = sprintf('/%s/%s', $this->model, $endpoint);

        if (true === $authenticated) {
            $headers = $this->signRequest($uri, $request, $headers);
        } else {
            $this->buildRequest($request);
        }

        curl_setopt($this->curl, CURLOPT_URL, $this->apiRoot . $uri);
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, $headers);

        $response = curl_exec($this->curl);
        if ($response === false) {
            throw new KrakenAPIException(sprintf('CURL error: "%s"', curl_error($this->curl)));
        }

        $response = json_decode($response, true);
        if (!is_array($response)) {
            throw new KrakenAPIException(sprintf('JSON decode error: "%s"', json_last_error_msg()));
        }

        if (false === isset($response['result'])) {
            throw new KrakenAPIException(
                $response['error'] ? implode('. ', $response['error']) : 'Unknown error happen'
            );
        }

        return $response['result'];
    }

    non-public perform loadClient(): void
    {
        if ($this->curl === false) {
            $this->curl = curl_init();
        }

        curl_setopt_array($this->curl, [
            CURLOPT_SSL_VERIFYPEER => $this->sslCheck,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_USERAGENT => self::USER_AGENT,
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true
        ]);
    }

    non-public perform signRequest(string $uri, array $request, array $headers): array
    {
        if (!$this->key || !$this->secret) {
            throw new KrakenAPIException('No API credentials, please present each key and secret.');
        }

        if (!isset($request['nonce'])) {
            $request['nonce'] = $this->generateNonce();
        }

        $signal = hash_hmac(
            'sha512',
            $uri . hash('sha256', $request['nonce'] . $this->buildRequest($request), true),
            base64_decode($this->secret),
            true
        );

        return array_merge($headers, [
            'API-Key: ' . $this->key,
            'API-Sign: ' . base64_encode($sign)
        ]);
    }

    non-public perform buildRequest(array $request = []): string
    {
        $httpQuery = http_build_query($request, '', '&');
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $httpQuery);

        return $httpQuery;
    }

    non-public perform generateNonce(): string
    {
        // generate a 64 bit nonce utilizing a timestamp at microsecond decision
        $nonce = explode(' ', microtime());
        return $nonce[1] . str_pad(substr($nonce[0], 2, 6), 6, '0');
    }

    public perform setKey(?string $key): self
    {
        $this->key = $key;

        return $this;
    }

    public perform setSecret(?string $secret): self
    {
        $this->secret = $secret;

        return $this;
    }

    public perform setApiRoot(string $apiRoot): self
    {
        $this->apiRoot = $apiRoot;

        return $this;
    }

    public perform setVersion(string $model): self
    {
        $this->model = $model;

        return $this;
    }

    public perform setSslCheck(bool $sslCheck): self
    {
        $this->sslCheck = $sslCheck;

        return $this;
    }
}

Enter fullscreen mode

Exit fullscreen mode

Virtually all API use instances are coated with this easy class and his question methodology. The Curl extension is the one dependency used, with roughly 152 strains of code at this degree.

Thank to the Kraken API structure, it’s totally simple for the question methodology to detect if we have to authenticate and signal request for a personal endpoint.



Let’s break this code in chunks



Constructor

The __construct methodology permit us to instantly retrieve a freshly instantiated API shopper, all classe attributes are elective to request information from a public Kraken API endpoint.

For personal calls that must be authenticated, simply cross your API shopper key and secret. As an instance you already had a selected curl occasion in your undertaking together with his personal customized configuration, you may override the one created from this shopper.



The question methodology

Signature is self explaining right here, notice that the primary $endpoint parameter is relative to the API root, the worth you cross is similar you will discover in API documentation, with out the primary trailing slash, instance public/AssetPairs.

Image description

No must signal your calls, configure fancy headers and many others this methodology will do all of the magic for you when wanted. All you want to do is cross the wanted $endpoint first parameter and if wanted, information onto the second $request parameter.

Underneath the hood, the question methodology parse the requested endpoint to find out if authentication is required. In that the request shall be signed with API shopper credentials, all of the wanted header and parameters shall be added. In any other case we simply parse and construct the POST request, if any.

We may use and implement an abstraction layer on prime of curl for the request and likewise the response, however for this undertaking let’s hold issues easy silly as urged within the KISS precept.



Request signature

The generateNonce() methodology, somewhat than merely utilizing a Unix timestamp in seconds return an integer. This interger is casted as a string variable however don’t fret the HTTP request shall be despatched lastly as a string by Curl later anyway.

Whereas the signRequest() methodology will set the required nonce parameter if lacking, the API-KEY and the API-SIGN headers containing respectively the API shopper key and a signature constructed with a SHA 512 hash from the requested endpoint, nonce and request information as a SHA 256 hash.



Accessors (solely setters)

To permit extra flexibility at utilization, including setters like setKey() or setSecret() strategies will be very helpful when coping with many API shoppers on behalf your customers to tweak the shopper settings between calls if wanted.



Set up

First set up the nicolasbonnici/php-kraken-api-client Composer package deal on variations round 2.1 department onto your undertaking.

composer require nicolasbonnici/php-kraken-api-client:2.1
Enter fullscreen mode

Exit fullscreen mode



Public endpoints

Now retrieve all out there pairs from public/AssetPairs public endpoint.

$shopper = new KrakenAPIClient();
$pairs = $shopper->question('public/AssetPairs', ['pair' => 'BTCUSDT']);
var_dump($pairs);

Enter fullscreen mode

Exit fullscreen mode



Output

array(467) {
  ["1INCHEUR"]=>
  array(18) {
    ["altname"]=>
    string(8) "1INCHEUR"
    ["wsname"]=>
    string(9) "1INCH/EUR"
    ["aclass_base"]=>
    string(8) "foreign money"
    ["base"]=>
    string(5) "1INCH"
    ["aclass_quote"]=>
    string(8) "foreign money"
    ["quote"]=>
    string(4) "ZEUR"
    ["lot"]=>
    string(4) "unit"
    ["pair_decimals"]=>
    int(3)
    ["lot_decimals"]=>
    int(8)
    ["lot_multiplier"]=>
    int(1)
    ["leverage_buy"]=>
    array(0) {
    }
    ["leverage_sell"]=>
    array(0) {
    }
    ["fees"]=>
    array(9) {
      [0]=>
      array(2) {
        [0]=>
        int(0)
        [1]=>
        float(0)
      }
      ...
    }
    ["fee_volume_currency"]=>
    string(4) "ZUSD"
    ["margin_call"]=>
    int(80)
    ["margin_stop"]=>
    int(40)
    ["ordermin"]=>
    string(1) "1"
  }
...
}
Enter fullscreen mode

Exit fullscreen mode



Non-public endpoints

Retrieve consumer’s account steadiness from non-public endpoint.

$shopper = new KrakenAPIClient('YOUR API KEY', 'YOUR API SECRET');
$balances = $shopper->question('non-public/Steadiness');
var_dump($balances);
Enter fullscreen mode

Exit fullscreen mode



Output

array(467) {
  ["ZEUR"]=>string(7) "47913.8"
  ...
}
Enter fullscreen mode

Exit fullscreen mode

In line with official API documentation, this endpoint “Retrieve all money balances, internet of pending withdrawals”, notice that this endpoint will return an empty response should you obtained no asset in your portfolio in Kraken change.

Within the subsequent a part of this text, we’ll find out about how one can work together in actual time with the Kraken API utilizing WebSocket, then bundle the ultimate code as a Composer package deal.

Be happy to contribute on the undertaking positioned here on Gitlab and likewise feedback and/or reactions are nonetheless welcome.

Thanks for studying, and make sure you subscribe for the subsequent half should you take pleasure in.

Add a Comment

Your email address will not be published. Required fields are marked *

Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?