Development/Flutter (Dart)

[SOLVED][Flutter3.19] status code 404 & response body empty - api.bybit.com/v5/account/wallet-balance 오류 해결

Tradgineer 2024. 6. 4. 09:48

 

 

 [ 작업 환경 ]

 

Flutter 3.19

Dart 3.3

 

 

 

 

 

 [ 문제 현상 ]

 

올바른 api key와 secret key를 입력해주고, 정상적인 signature를 만들어 call했음에도 불구하고 404 response와 함께 비어있는 데이터가 return되는 문제 발생

 

// bybit_private_api_service.dart

import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:crypto/crypto.dart';
import '../models/bybit_wallet_balance_model.dart';

class BybitPrivateApiService {
  Future<BybitWalletBalanceModel> fetchWalletBalance(
      String apiKey, String apiSecret) async {
    const endpoint = 'https://api.bybit.com';
    const path = '/v5/account/wallet-balance';
    const accountType = 'UNIFIED';

    final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
    const recvWindow = '20000';
    const queryString = 'accountType=$accountType';

    final paramStr = '$timestamp$apiKey$recvWindow$queryString';
    log('paramStr : $paramStr');
    final signature = _generateSignature(paramStr, apiSecret);
    const url = '$endpoint$path?$queryString';

    final Map<String, String> headers = {
      'X-BAPI-API-KEY': apiKey,
      'X-BAPI-TIMESTAMP': timestamp,
      'X-BAPI-RECV-WINDOW': recvWindow,
      'X-BAPI-SIGN': signature,
    };

    log('Headers: $headers');
    log('URI: $url');

    final response = await http.get(
      Uri.parse(url),
      headers: headers,
    );

    log('Response status: ${response.statusCode}');
    log('Response body: ${response.body}');

    if (response.statusCode == 200) {
      final jsonResponse = jsonDecode(response.body);
      if (jsonResponse['retCode'] == 0) {
        return BybitWalletBalanceModel.fromJson(
            jsonResponse['result']['list'][0]);
      } else {
        throw Exception('API error: ${jsonResponse['retMsg']}');
      }
    } else {
      throw Exception('Failed to load wallet balance');
    }
  }

  String _generateSignature(String data, String secretKey) {
    final key = utf8.encode(secretKey);
    final bytes = utf8.encode(data);

    final hmacSha256 = Hmac(sha256, key);
    final digest = hmacSha256.convert(bytes);

    return digest.toString();
  }
}
// bybit_wallet_balance_model.dart

import 'coin.dart';

class BybitWalletBalanceModel {
  final String totalEquity;
  final String accountType;
  final String totalAvailableBalance;
  final List<Coin> coins;

  BybitWalletBalanceModel({
    required this.totalEquity,
    required this.accountType,
    required this.totalAvailableBalance,
    required this.coins,
  });

  factory BybitWalletBalanceModel.fromJson(Map<String, dynamic> json) {
    var coinsList = json['coin'] as List;
    List<Coin> coinItems = coinsList.map((i) => Coin.fromJson(i)).toList();
    return BybitWalletBalanceModel(
      totalEquity: json['totalEquity'],
      accountType: json['accountType'],
      totalAvailableBalance: json['totalAvailableBalance'],
      coins: coinItems,
    );
  }
}
// coin.dart

class Coin {
  final String coin;
  final String walletBalance;
  final String availableToWithdraw;

  Coin({
    required this.coin,
    required this.walletBalance,
    required this.availableToWithdraw,
  });

  factory Coin.fromJson(Map<String, dynamic> json) {
    return Coin(
      coin: json['coin'],
      walletBalance: json['walletBalance'],
      availableToWithdraw: json['availableToWithdraw'],
    );
  }
}

 

 

 

 

 

 [ 문제 원인 ]

 

 accountType으로 UNIFIED를 넣어줬는데, UNIFIED account가 존재하지 않는 아이디에 대해 해당 api call을 할 경우 404 response가 나타납니다.

 

 

 

 

 

 [ 해결 방법 ]

 

그래서 accountType을 SPOT으로 변경해주어 해결합니다. 

class BybitPrivateApiService {
  Future<BybitWalletBalanceModel> fetchWalletBalance(
      String apiKey, String apiSecret) async {
    const endpoint = 'https://api.bybit.com';
    const path = '/v5/account/wallet-balance';
    const accountType = 'SPOT';

    final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
    const recvWindow = '20000';
    const queryString = 'accountType=$accountType';

    final paramStr = '$timestamp$apiKey$recvWindow$queryString';
    log('paramStr : $paramStr');
    final signature = _generateSignature(paramStr, apiSecret);
    const url = '$endpoint$path?$queryString';

    final Map<String, String> headers = {
      'X-BAPI-API-KEY': apiKey,
      'X-BAPI-TIMESTAMP': timestamp,
      'X-BAPI-RECV-WINDOW': recvWindow,
      'X-BAPI-SIGN': signature,
    };

    log('Headers: $headers');
    log('URI: $url');

    final response = await http.get(
      Uri.parse(url),
      headers: headers,
    );

    log('Response status: ${response.statusCode}');
    log('Response body: ${response.body}');

    if (response.statusCode == 200) {
      final jsonResponse = jsonDecode(response.body);
      if (jsonResponse['retCode'] == 0) {
        return BybitWalletBalanceModel.fromJson(
            jsonResponse['result']['list'][0]);
      } else {
        throw Exception('API error: ${jsonResponse['retMsg']}');
      }
    } else {
      throw Exception('Failed to load wallet balance');
    }
  }

  String _generateSignature(String data, String secretKey) {
    final key = utf8.encode(secretKey);
    final bytes = utf8.encode(data);

    final hmacSha256 = Hmac(sha256, key);
    final digest = hmacSha256.convert(bytes);

    return digest.toString();
  }
}

 

 

 

 

 

 [ 정상 해결 완료 확인 ]