Fix styling

This commit is contained in:
mahdimsr 2025-09-28 11:53:47 +00:00 committed by github-actions[bot]
parent 0996c5fc27
commit 49c1633f52
6 changed files with 58 additions and 84 deletions

View File

@ -2,12 +2,12 @@
/** /**
* Example usage of Change Leverage functionality * Example usage of Change Leverage functionality
* *
* This example demonstrates how to use the LaravelBitunixApi package * This example demonstrates how to use the LaravelBitunixApi package
* to change leverage for a trading pair on Bitunix exchange. * to change leverage for a trading pair on Bitunix exchange.
*/ */
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__.'/../vendor/autoload.php';
use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract; use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract;
@ -22,39 +22,39 @@ config([
try { try {
// Get the API instance // Get the API instance
$api = app(ChangeLeverageRequestContract::class); $api = app(ChangeLeverageRequestContract::class);
// Change leverage for BTCUSDT pair // Change leverage for BTCUSDT pair
$symbol = 'BTCUSDT'; $symbol = 'BTCUSDT';
$marginCoin = 'USDT'; $marginCoin = 'USDT';
$leverage = 12; $leverage = 12;
echo "Changing leverage for {$symbol} to {$leverage}x...\n"; echo "Changing leverage for {$symbol} to {$leverage}x...\n";
$response = $api->changeLeverage($symbol, $marginCoin, $leverage); $response = $api->changeLeverage($symbol, $marginCoin, $leverage);
// Check response status // Check response status
if ($response->getStatusCode() === 200) { if ($response->getStatusCode() === 200) {
$data = json_decode($response->getBody()->getContents(), true); $data = json_decode($response->getBody()->getContents(), true);
if ($data['code'] === 0) { if ($data['code'] === 0) {
echo "✅ Leverage changed successfully!\n"; echo "✅ Leverage changed successfully!\n";
echo "Symbol: " . $data['data'][0]['symbol'] . "\n"; echo 'Symbol: '.$data['data'][0]['symbol']."\n";
echo "Margin Coin: " . $data['data'][0]['marginCoin'] . "\n"; echo 'Margin Coin: '.$data['data'][0]['marginCoin']."\n";
echo "New Leverage: " . $data['data'][0]['leverage'] . "\n"; echo 'New Leverage: '.$data['data'][0]['leverage']."\n";
} else { } else {
echo "❌ API Error: " . $data['msg'] . "\n"; echo '❌ API Error: '.$data['msg']."\n";
} }
} else { } else {
echo "❌ HTTP Error: " . $response->getStatusCode() . "\n"; echo '❌ HTTP Error: '.$response->getStatusCode()."\n";
} }
} catch (Exception $e) { } catch (Exception $e) {
echo "❌ Exception: " . $e->getMessage() . "\n"; echo '❌ Exception: '.$e->getMessage()."\n";
} }
/** /**
* Environment Variables Required: * Environment Variables Required:
* *
* BITUNIX_API_KEY=your-api-key * BITUNIX_API_KEY=your-api-key
* BITUNIX_API_SECRET=your-api-secret * BITUNIX_API_SECRET=your-api-secret
* BITUNIX_LANGUAGE=en-US * BITUNIX_LANGUAGE=en-US

View File

@ -3,12 +3,12 @@
namespace Msr\LaravelBitunixApi; namespace Msr\LaravelBitunixApi;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract;
use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract; use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract;
use Msr\LaravelBitunixApi\Requests\FutureKLineRequestContract;
use Msr\LaravelBitunixApi\Requests\Header; use Msr\LaravelBitunixApi\Requests\Header;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
class LaravelBitunixApi implements FutureKLineRequestContract, ChangeLeverageRequestContract class LaravelBitunixApi implements ChangeLeverageRequestContract, FutureKLineRequestContract
{ {
private Client $publicFutureClient; private Client $publicFutureClient;

View File

@ -9,11 +9,9 @@ interface ChangeLeverageRequestContract
/** /**
* Change leverage for a trading pair * Change leverage for a trading pair
* *
* @param string $symbol Trading pair (e.g., 'BTCUSDT') * @param string $symbol Trading pair (e.g., 'BTCUSDT')
* @param string $marginCoin Margin coin (e.g., 'USDT') * @param string $marginCoin Margin coin (e.g., 'USDT')
* @param int $leverage Leverage value * @param int $leverage Leverage value
* @return ResponseInterface
*/ */
public function changeLeverage(string $symbol, string $marginCoin, int $leverage): ResponseInterface; public function changeLeverage(string $symbol, string $marginCoin, int $leverage): ResponseInterface;
} }

View File

@ -6,9 +6,6 @@ class Header
{ {
/** /**
* Sort query parameters in ascending ASCII order by Key * Sort query parameters in ascending ASCII order by Key
*
* @param array $parameters
* @return array
*/ */
public static function sortQueryParameters(array $parameters): array public static function sortQueryParameters(array $parameters): array
{ {
@ -17,15 +14,13 @@ class Header
} }
ksort($parameters, SORT_STRING); ksort($parameters, SORT_STRING);
return $parameters; return $parameters;
} }
/** /**
* Convert sorted parameters to string format * Convert sorted parameters to string format
* Example: ["id" => "1", "uid" => "200"] becomes "id1uid200" * Example: ["id" => "1", "uid" => "200"] becomes "id1uid200"
*
* @param array $parameters
* @return string
*/ */
public static function digestQueryParameters(array $parameters): string public static function digestQueryParameters(array $parameters): string
{ {
@ -35,18 +30,16 @@ class Header
$sortedParameters = self::sortQueryParameters($parameters); $sortedParameters = self::sortQueryParameters($parameters);
$result = ''; $result = '';
foreach ($sortedParameters as $key => $value) { foreach ($sortedParameters as $key => $value) {
$result .= $key . $value; $result .= $key.$value;
} }
return $result; return $result;
} }
/** /**
* Generate a random 32-bit nonce string * Generate a random 32-bit nonce string
*
* @return string
*/ */
public static function generateNonce(): string public static function generateNonce(): string
{ {
@ -55,8 +48,6 @@ class Header
/** /**
* Generate current timestamp in milliseconds * Generate current timestamp in milliseconds
*
* @return string
*/ */
public static function generateTimestamp(): string public static function generateTimestamp(): string
{ {
@ -65,58 +56,48 @@ class Header
/** /**
* Generate signature according to Bitunix API documentation * Generate signature according to Bitunix API documentation
* *
* Steps: * Steps:
* 1. Sort all queryParams in ascending ASCII order by Key * 1. Sort all queryParams in ascending ASCII order by Key
* 2. Remove all spaces from body string * 2. Remove all spaces from body string
* 3. Create digest: SHA256(nonce + timestamp + api-key + queryParams + body) * 3. Create digest: SHA256(nonce + timestamp + api-key + queryParams + body)
* 4. Create sign: SHA256(digest + secretKey) * 4. Create sign: SHA256(digest + secretKey)
*
* @param array $queryParams
* @param string $body
* @param string $nonce
* @param string $timestamp
* @return string
*/ */
public static function generateSignValue(array $queryParams = [], string $body = '', string $nonce = '', string $timestamp = ''): string public static function generateSignValue(array $queryParams = [], string $body = '', string $nonce = '', string $timestamp = ''): string
{ {
$apiKey = config('bitunix-api.api_key'); $apiKey = config('bitunix-api.api_key');
$apiSecret = config('bitunix-api.api_secret'); $apiSecret = config('bitunix-api.api_secret');
if (empty($apiKey) || empty($apiSecret)) { if (empty($apiKey) || empty($apiSecret)) {
throw new \InvalidArgumentException('API key and secret must be configured'); throw new \InvalidArgumentException('API key and secret must be configured');
} }
// Step 1: Sort query parameters in ascending ASCII order // Step 1: Sort query parameters in ascending ASCII order
$queryParamsString = self::digestQueryParameters($queryParams); $queryParamsString = self::digestQueryParameters($queryParams);
// Step 2: Remove all spaces from body (already done if JSON encoded properly) // Step 2: Remove all spaces from body (already done if JSON encoded properly)
$bodyString = trim($body); $bodyString = trim($body);
// Step 3: Create digest: SHA256(nonce + timestamp + api-key + queryParams + body) // Step 3: Create digest: SHA256(nonce + timestamp + api-key + queryParams + body)
$digestInput = $nonce . $timestamp . $apiKey . $queryParamsString . $bodyString; $digestInput = $nonce.$timestamp.$apiKey.$queryParamsString.$bodyString;
$digest = hash('sha256', $digestInput); $digest = hash('sha256', $digestInput);
// Step 4: Create sign: SHA256(digest + secretKey) // Step 4: Create sign: SHA256(digest + secretKey)
$signInput = $digest . $apiSecret; $signInput = $digest.$apiSecret;
$sign = hash('sha256', $signInput); $sign = hash('sha256', $signInput);
return $sign; return $sign;
} }
/** /**
* Generate complete headers for authenticated requests * Generate complete headers for authenticated requests
*
* @param array $queryParams
* @param string $body
* @return array
*/ */
public static function generateHeaders(array $queryParams = [], string $body = ''): array public static function generateHeaders(array $queryParams = [], string $body = ''): array
{ {
$nonce = self::generateNonce(); $nonce = self::generateNonce();
$timestamp = self::generateTimestamp(); $timestamp = self::generateTimestamp();
$sign = self::generateSignValue($queryParams, $body, $nonce, $timestamp); $sign = self::generateSignValue($queryParams, $body, $nonce, $timestamp);
return [ return [
'api-key' => config('bitunix-api.api_key'), 'api-key' => config('bitunix-api.api_key'),
'sign' => $sign, 'sign' => $sign,

View File

@ -1,25 +1,20 @@
<?php <?php
use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract; use Msr\LaravelBitunixApi\Requests\ChangeLeverageRequestContract;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
it('can change leverage successfully', function () { it('can change leverage successfully', function () {
$api = app(ChangeLeverageRequestContract::class); $api = app(ChangeLeverageRequestContract::class);
// This test will make a real API call if credentials are valid // This test will make a real API call if credentials are valid
// For testing purposes, we'll just verify the method exists and can be called // For testing purposes, we'll just verify the method exists and can be called
expect(fn() => $api->changeLeverage('BTCUSDT', 'USDT', 12)) expect(fn () => $api->changeLeverage('BTCUSDT', 'USDT', 12))
->not->toThrow(Exception::class); ->not->toThrow(Exception::class);
}); });
it('validates required parameters for change leverage', function () { it('validates required parameters for change leverage', function () {
$api = app(ChangeLeverageRequestContract::class); $api = app(ChangeLeverageRequestContract::class);
expect(fn() => $api->changeLeverage('BTCUSDT', 'USDT', 10)) expect(fn () => $api->changeLeverage('BTCUSDT', 'USDT', 10))
->not->toThrow(Exception::class) ->not->toThrow(Exception::class)
->and(fn() => $api->changeLeverage('BTCUSDT', 'USDT', 0)) ->and(fn () => $api->changeLeverage('BTCUSDT', 'USDT', 0))
->not->toThrow(Exception::class); ->not->toThrow(Exception::class);
}); });

View File

@ -14,33 +14,33 @@ it('can sort query parameters in ascending ASCII order', function () {
$parameters = [ $parameters = [
'uid' => '200', 'uid' => '200',
'id' => '1', 'id' => '1',
'name' => 'test' 'name' => 'test',
]; ];
$sorted = Header::sortQueryParameters($parameters); $sorted = Header::sortQueryParameters($parameters);
expect(array_keys($sorted))->toBe(['id', 'name', 'uid']); expect(array_keys($sorted))->toBe(['id', 'name', 'uid']);
expect($sorted)->toBe([ expect($sorted)->toBe([
'id' => '1', 'id' => '1',
'name' => 'test', 'name' => 'test',
'uid' => '200' 'uid' => '200',
]); ]);
}); });
it('can digest query parameters to string format', function () { it('can digest query parameters to string format', function () {
$parameters = [ $parameters = [
'id' => '1', 'id' => '1',
'uid' => '200' 'uid' => '200',
]; ];
$result = Header::digestQueryParameters($parameters); $result = Header::digestQueryParameters($parameters);
expect($result)->toBe('id1uid200'); expect($result)->toBe('id1uid200');
}); });
it('can generate a valid nonce', function () { it('can generate a valid nonce', function () {
$nonce = Header::generateNonce(); $nonce = Header::generateNonce();
expect($nonce) expect($nonce)
->toBeString() ->toBeString()
->toHaveLength(32) ->toHaveLength(32)
@ -49,7 +49,7 @@ it('can generate a valid nonce', function () {
it('can generate timestamp in milliseconds', function () { it('can generate timestamp in milliseconds', function () {
$timestamp = Header::generateTimestamp(); $timestamp = Header::generateTimestamp();
expect($timestamp) expect($timestamp)
->toBeString() ->toBeString()
->toMatch('/^\d{13}$/'); // 13 digits for milliseconds ->toMatch('/^\d{13}$/'); // 13 digits for milliseconds
@ -60,9 +60,9 @@ it('can generate signature according to Bitunix documentation', function () {
$timestamp = '20241120123045'; $timestamp = '20241120123045';
$queryParams = ['id' => '1', 'uid' => '200']; $queryParams = ['id' => '1', 'uid' => '200'];
$body = '{"uid":"2899","arr":[{"id":1,"name":"maple"},{"id":2,"name":"lily"}]}'; $body = '{"uid":"2899","arr":[{"id":1,"name":"maple"},{"id":2,"name":"lily"}]}';
$sign = Header::generateSignValue($queryParams, $body, $nonce, $timestamp); $sign = Header::generateSignValue($queryParams, $body, $nonce, $timestamp);
expect($sign) expect($sign)
->toBeString() ->toBeString()
->toHaveLength(64) // SHA256 hex length ->toHaveLength(64) // SHA256 hex length
@ -72,9 +72,9 @@ it('can generate signature according to Bitunix documentation', function () {
it('can generate complete headers for authenticated requests', function () { it('can generate complete headers for authenticated requests', function () {
$queryParams = ['symbol' => 'BTCUSDT']; $queryParams = ['symbol' => 'BTCUSDT'];
$body = '{"symbol":"BTCUSDT","marginCoin":"USDT","leverage":12}'; $body = '{"symbol":"BTCUSDT","marginCoin":"USDT","leverage":12}';
$headers = Header::generateHeaders($queryParams, $body); $headers = Header::generateHeaders($queryParams, $body);
expect($headers) expect($headers)
->toHaveKeys(['api-key', 'sign', 'nonce', 'timestamp', 'language', 'Content-Type']) ->toHaveKeys(['api-key', 'sign', 'nonce', 'timestamp', 'language', 'Content-Type'])
->and($headers['api-key'])->toBe('test-api-key') ->and($headers['api-key'])->toBe('test-api-key')
@ -90,23 +90,23 @@ it('throws exception when API credentials are missing', function () {
'bitunix-api.api_key' => '', 'bitunix-api.api_key' => '',
'bitunix-api.api_secret' => '', 'bitunix-api.api_secret' => '',
]); ]);
expect(fn() => Header::generateSignValue([], '', 'nonce', 'timestamp')) expect(fn () => Header::generateSignValue([], '', 'nonce', 'timestamp'))
->toThrow(InvalidArgumentException::class, 'API key and secret must be configured'); ->toThrow(InvalidArgumentException::class, 'API key and secret must be configured');
}); });
it('handles empty query parameters correctly', function () { it('handles empty query parameters correctly', function () {
$result = Header::digestQueryParameters([]); $result = Header::digestQueryParameters([]);
expect($result)->toBe(''); expect($result)->toBe('');
}); });
it('handles empty body correctly', function () { it('handles empty body correctly', function () {
$nonce = '123456'; $nonce = '123456';
$timestamp = '20241120123045'; $timestamp = '20241120123045';
$sign = Header::generateSignValue([], '', $nonce, $timestamp); $sign = Header::generateSignValue([], '', $nonce, $timestamp);
expect($sign) expect($sign)
->toBeString() ->toBeString()
->toHaveLength(64); ->toHaveLength(64);
@ -117,19 +117,19 @@ it('generates consistent signature for same inputs', function () {
$timestamp = '20241120123045'; $timestamp = '20241120123045';
$queryParams = ['id' => '1']; $queryParams = ['id' => '1'];
$body = '{"test":"value"}'; $body = '{"test":"value"}';
$sign1 = Header::generateSignValue($queryParams, $body, $nonce, $timestamp); $sign1 = Header::generateSignValue($queryParams, $body, $nonce, $timestamp);
$sign2 = Header::generateSignValue($queryParams, $body, $nonce, $timestamp); $sign2 = Header::generateSignValue($queryParams, $body, $nonce, $timestamp);
expect($sign1)->toBe($sign2); expect($sign1)->toBe($sign2);
}); });
it('generates different signatures for different inputs', function () { it('generates different signatures for different inputs', function () {
$nonce = '123456'; $nonce = '123456';
$timestamp = '20241120123045'; $timestamp = '20241120123045';
$sign1 = Header::generateSignValue(['id' => '1'], '{"test":"value1"}', $nonce, $timestamp); $sign1 = Header::generateSignValue(['id' => '1'], '{"test":"value1"}', $nonce, $timestamp);
$sign2 = Header::generateSignValue(['id' => '2'], '{"test":"value2"}', $nonce, $timestamp); $sign2 = Header::generateSignValue(['id' => '2'], '{"test":"value2"}', $nonce, $timestamp);
expect($sign1)->not->toBe($sign2); expect($sign1)->not->toBe($sign2);
}); });