Инструкция по созданию JWT

Инструкция по созданию JWT токена:

Rapira использует стандартную схему JWT авторизации (документация: https://jwt.io/ и https://tools.ietf.org/html/rfc7519), то есть для работы с методами API вначале нужно запросить у сервера JWT токен, который уже будет использоваться в Bearer аутентификации.

Полученный токен нужно сохранить и использовать его для запросов. Не нужно создавать новый токен на каждый запрос.

Время жизни JWT токена ограничено, и после его истечения токен становится недействительным.

Время жизни JWT определяется параметрами API ключа, и может быть изменено в настройках для каждого API ключа. Минимальное время жизни JWT 30 минут, максимальное 24 часа. По умолчанию значение TTL 24 часа.

Параметры JWT запроса:

  • exp - опциональный, время жизни JWT запроса. Не устанавливайте его слишком большим и не оставляйте пустым, если вы не планируете переиспользовать запрос многократно.
  • jti - обязательный, уникальный идентификатор запроса.
  • алгоритм подписи JWT - RS256
  • все остальные опциональные параметры не используются и устанавливать их не обязательно.

Для декодирования и проверки токенов можно использовать онлайн отладчик на сайте https://jwt.io//

Снизу приведен пример на php.

# requirements:
# pip3 install 'pyjwt[crypto]'


import base64
import time
import datetime
import random
import requests
import jwt

private_key = 'приватный_ключ'
uid = 'uid_апи_ключа'
host = 'api.rapira.net'

key = base64.b64decode(private_key)
iat = int(time.mktime(datetime.datetime.now().timetuple()))

claims = {
    "exp": iat + 1*60*60, # JWT Request TTL in seconds since epoch
    "jti": hex(random.getrandbits(12)).upper()
}

jwt_token = jwt.encode(claims, key, algorithm="RS256")

print("JWT request token: %s\n" % jwt_token)

ret = requests.post('https://' + host + '/open/generate_jwt',
                    json={'kid': uid, 'jwt_token': jwt_token})

print("JWT response code: %d" % ret.status_code)
print("JWY response text: %s\n" % ret.text)

token = ret.json().get('token')

print("JWT token: %s\n" % token)

ret = requests.get('https://' + host + '/open/wallet/balance',
                   headers={'Authorization': 'Bearer ' + token})

print("Test request response code: %d" % ret.status_code)
print("Test request response text: %s" % ret.text)

response = ret.json()

<?php
if(!class_exists('JWT')){
 class JWT
 {
  function __construct(){
   
  } 
  
  function encode($payload, $key)
  {
   $algo = 'RS256';
   $header = array('typ' => 'JWT', 'alg' => $algo);

   $segments = array();
   $segments[] = $this->urlsafeB64Encode($this->jsonEncode($header));
   $segments[] = $this->urlsafeB64Encode($this->jsonEncode($payload));
   $signing_input = implode('.', $segments);

   $signature = $this->sign($signing_input, $key);
   $segments[] = $this->urlsafeB64Encode($signature);

   return implode('.', $segments);
  }

  function sign($payload, $key)
  {
   $passphrase = '';
   
   $algo = OPENSSL_ALGO_SHA256;
   $key_type = OPENSSL_KEYTYPE_RSA;
   
   $privateKey = openssl_pkey_get_private($key, $passphrase);
   
   if (is_bool($privateKey)) {
    $error = openssl_error_string();
    throw new Exception($error);
   }

   $details = openssl_pkey_get_details($privateKey);
   
   if (!array_key_exists('key', $details) || $details['type'] !== $key_type) {
    throw new Exception("Invalid key provided");
   }
   
   $signature = '';

   if (!openssl_sign($payload, $signature, $privateKey, $algo)) {
    $error = openssl_error_string();
    throw new Exception($error);
   }

   return $signature;
  }

  function jsonDecode($input)
  {
   $obj = json_decode($input);
   if (function_exists('json_last_error') && $errno = json_last_error()) {
    $this->_handleJsonError($errno);
   } else if ($obj === null && $input !== 'null') {
    throw new Exception('Null result with non-null input');
   }
   return $obj;
  }

  function jsonEncode($input)
  {
   $json = json_encode($input);
   if (function_exists('json_last_error') && $errno = json_last_error()) {
    $this->_handleJsonError($errno);
   } else if ($json === 'null' && $input !== null) {
    throw new Exception('Null result with non-null input');
   }
   return $json;
  }

  function urlsafeB64Decode($input)
  {
   $remainder = strlen($input) % 4;
   if ($remainder) {
    $padlen = 4 - $remainder;
    $input .= str_repeat('=', $padlen);
   }
   return base64_decode(strtr($input, '-_', '+/'));
  }

  function urlsafeB64Encode($input)
  {
   return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
  }

  function _handleJsonError($errno)
  {
   $messages = array(
    JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
    JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
    JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON'
   );
   throw new Exception(
    isset($messages[$errno])
    ? $messages[$errno]
    : 'Unknown JSON error: ' . $errno
   );
  }
 }
}

$private_key = "{здесь Ваш privateKey}";
$uid= "{Здесь Ваш uid}";
$request = array('exp' => time() + 3600, 'jti' => bin2hex(random_bytes(12)));
    $class = new JWT();
    $payload = $class->encode($request, base64_decode($private_key, true));
    $post_data = array('kid' => $uid, 'jwt_token' => $payload);
    
    print_r($post_data);
?>
<?php

class JWT
{
	function __construct(){
		
	}	
	
	function encode($payload, $key)
	{
		$algo = 'RS256';
		$header = array('typ' => 'JWT', 'alg' => $algo);

		$segments = array();
		$segments[] = $this->urlsafeB64Encode($this->jsonEncode($header));
		$segments[] = $this->urlsafeB64Encode($this->jsonEncode($payload));
		$signing_input = implode('.', $segments);

		$signature = $this->sign($signing_input, $key);
		$segments[] = $this->urlsafeB64Encode($signature);

		return implode('.', $segments);
	}

	function sign($payload, $key)
	{
		$passphrase = '';
		
		$algo = OPENSSL_ALGO_SHA256;
		$key_type = OPENSSL_KEYTYPE_RSA;
		
		$privateKey = openssl_pkey_get_private($key, $passphrase);
		
		if (is_bool($privateKey)) {
			$error = openssl_error_string();
			throw new Exception($error);
		}

		$details = openssl_pkey_get_details($privateKey);
		
		if (!array_key_exists('key', $details) || $details['type'] !== $key_type) {
			throw new Exception("Invalid key provided");
		}
		
		$signature = '';

		if (!openssl_sign($payload, $signature, $privateKey, $algo)) {
			$error = openssl_error_string();
			throw new Exception($error);
		}

		return $signature;
	}

	function jsonDecode($input)
	{
		$obj = json_decode($input);
		if (function_exists('json_last_error') && $errno = json_last_error()) {
			$this->_handleJsonError($errno);
		} else if ($obj === null && $input !== 'null') {
			throw new Exception('Null result with non-null input');
		}
		return $obj;
	}

	function jsonEncode($input)
	{
		$json = json_encode($input);
		if (function_exists('json_last_error') && $errno = json_last_error()) {
			$this->_handleJsonError($errno);
		} else if ($json === 'null' && $input !== null) {
			throw new Exception('Null result with non-null input');
		}
		return $json;
	}

	function urlsafeB64Decode($input)
	{
		$remainder = strlen($input) % 4;
		if ($remainder) {
			$padlen = 4 - $remainder;
			$input .= str_repeat('=', $padlen);
		}
		return base64_decode(strtr($input, '-_', '+/'));
	}

	function urlsafeB64Encode($input)
	{
		return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
	}

	function _handleJsonError($errno)
	{
		$messages = array(
			JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
			JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
			JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON'
		);
		throw new Exception(
			isset($messages[$errno])
			? $messages[$errno]
			: 'Unknown JSON error: ' . $errno
		);
	}
}



class Rapira {
	private $token = "";
	public $private_key = '';
	public $uid = '';
	public $hosts = '';
	public $host_type = 0;
	
	function __construct($private_key='', $uid='', $host_type=0){
		$this->hosts = array(
			'0' => array(
				'api' => 'api.rapira.net',
			),
			'1' => array(
				'api' => 'api.rapira.org',
			),				
		);
		$this->host_type = intval($host_type);			
		$this->private_key = trim($private_key);
		$this->uid = trim($uid);
		$this->set_token();
	}
	
	function set_token(){
		$token = trim($this->token);
		if (!$token) {
			
			$request = array('exp' => time() + 3600, 'jti' => bin2hex(random_bytes(12)));
			$class = new JWT();
			$payload = $class->encode($request, base64_decode($this->private_key, true));
			$post_data = array('kid' => $this->uid, 'jwt_token' => $payload);
			$json_data = json_encode($post_data);
			$headers = array(
				'Content-Type: application/json',
			);
			
			$host = $this->hosts[$this->host_type]['api'];
			
			if($ch = curl_init()){
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
				curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0');
				curl_setopt($ch, CURLOPT_URL, "https://{$host}/open/generate_jwt");
				curl_setopt($ch, CURLOPT_POST, true);
				curl_setopt($ch, CURLOPT_POSTFIELDS, $json_data);
				curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
				curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
				curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
				curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
				curl_setopt($ch, CURLOPT_HEADER, false);
				curl_setopt($ch, CURLOPT_TIMEOUT, 20);
				curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
				$err  = curl_errno($ch);
				$result = curl_exec($ch);
				
				curl_close($ch);
				
				$res = @json_decode($result, true);
				
				if (isset($res['token'])) {
					$this->token = $res['token'];
				}	
			}				
		}
	}		
	
	function create_address($currency, $amount, $nonce){
		$currency = trim(strtolower($currency));
		
		$post = array(
			'currency' => $currency,
			'amount' => $amount,
			'nonce' => $nonce
		);
		$res = $this->request('/open/deposit_address?currency=' . $currency.'&amount='.$amount.'&nonce='.$nonce, $post);
		
		$data = array();
		
		if (isset($res['id'], $res['address']) and !$res['address']) {
			$id = intval($res['id']);
			for ($i = 1; $i <= 5; $i++) {
				sleep(2);
				
				$res2 = $this->request('/open/deposit_address/details?id=' . $id, array());
				
				if (isset($res2['address'])) {
					$data['address'] = $res2['address'];
					break;
				}
			} 
		} elseif (isset($res['id'], $res['address']) and $res['address']) {
			$data['address'] = $res['address'];
		}
		
		return $data;
	}

	function get_history_deposits($limit) {
		$limit = intval($limit);
		$post = array( "pageNo" => 0, "pageSize" => $limit);
		$res = $this->request('/open/deposit/records', $post, "multipart/form-data");
		$trans = array();
		if (is_array($res) and !isset($res['error']) and $res['code'] != 500 and isset($res['data']['content'])) {
			$trans = $res['data']['content'];
		}
		return $trans;
	}		
	
	function stop_use($address, $nonce, $status) {
		$post = array("address" => $address, "nonce" => $nonce, "status" => $status);
		$res = $this->request('/open/deposit_address/use/stop', $post, "multipart/form-data");
		if ($res['message'] == "Success unuse address" || $res['message'] == "Already unused" || $res['message'] == "Can't unuse address" || str_starts_with($res['message'], "Can't unuse address because start with other bid=")) {	// && $res['code'] != 400 || address is empty || unknown address
			return true;
		} 
		return false; // "Already close address by id {externalId}" 
	}	
	
	function start_use($address, $nonce, $status) {
		$post = array("address" => $address, "nonce" => $nonce, "status" => $status);
		$res = $this->request('/open/deposit_address/use/start', $post, "multipart/form-data");
		if ($res['message'] == "Success use address"|| $res['message'] == "Can't use address") {	// && $res['code'] != 400 || address is empty || unknown address
			return true;
		} 
		return false;
	}		
	
	function request($method, $post=array(), $content_type="application/json"){

		$json_data = '';
		$headers = array(
			"Content-Type: ".$content_type,
			"Authorization: Bearer {$this->token}"
		);			

		$host = $this->hosts[$this->host_type]['api'];	

		if($ch = curl_init()){
			
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
			curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0');
			curl_setopt($ch, CURLOPT_URL, 'https://' . $host . $method);
			if (is_array($post) and count($post) > 0) {
				if ($content_type=="application/json") { $post = json_encode($post); }
				curl_setopt($ch, CURLOPT_POST, true);
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
			}
			curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
			curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
			curl_setopt($ch, CURLOPT_HEADER, false);
			curl_setopt($ch, CURLOPT_TIMEOUT, 20);
			curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);

			$err  = curl_errno($ch);
			$result = curl_exec($ch);
			
			curl_close($ch);
			
			$res = @json_decode($result, true);
	
			return $res;				 
		}	

		return '';
	}		
}




$private_key = "";
$uid= "";
$class = new Rapira($private_key, $uid);

$result = $class->create_address('usdttrc20', 1000, 'exchanger.ru_123456');
print_r($result);

const axios = require("axios"); // npm i axios
const jwt = require("jsonwebtoken"); // npm i jsonwebtoken
const crypto = require("crypto");

// Константы
const host = "api.rapira.net"; 
const privateKey = ""; // приватный ключ, полученный на этапе создания API ключей
const uid = ""; // UID, полученный на этапе создания API ключей

// Получаем токен
const getToken = async () => {
  try {
    let { data } = await axios.post(
      "https://" + host + "/open/generate_jwt",
      {
        kid: uid,
        jwt_token: jwt.sign(
          {
            exp: Math.round(Date.now() / 1000) + 30 * 60, // JWT Request TTL: 30 minutes
            jti: crypto.randomBytes(12).toString("hex"),
          },
          new Buffer.from(privateKey, "base64").toString("ascii"),
          { algorithm: "RS256" }
        ),
      }
    );
    console.log("Your JWT token is:", data.token)
    return data.token;
  } catch (e) {
    console.error(e);
    return false;
  }
};

// Тест работоспособности
(async () => {
  try {
    let { data } = await axios.post(
      "https://" + host + "/open/deposit_address?currency=usdt-trc20",
      null,
      {
        headers: {
          Authorization: `Bearer ${await getToken()}`,
        },
      }
    );
    console.log(data);
  } catch (e) {
    console.error(e.message, e.response.data.error);
  }
})();