From d51b096fdcdab083f434ebfb6ce5e3bab9ebdfa0 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Tue, 8 Mar 2022 13:40:19 +0100 Subject: [PATCH] add coinrabbit api --- lib/entities/preferences_key.dart | 1 + lib/loan/api/coinrabbit_api_client.dart | 180 ++++++++++++++++++++++++ lib/loan/models/captcha_response.dart | 20 +++ tool/utils/secret_key.dart | 1 + 4 files changed, 202 insertions(+) create mode 100644 lib/loan/api/coinrabbit_api_client.dart create mode 100644 lib/loan/models/captcha_response.dart diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 3c94e8322..c5b42fc99 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -21,4 +21,5 @@ class PreferencesKey { static const bitcoinTransactionPriority = 'current_fee_priority_bitcoin'; static const shouldShowReceiveWarning = 'should_show_receive_warning'; static const shouldShowYatPopup = 'should_show_yat_popup'; + static const isActive2fa = 'is_active_2fa'; } diff --git a/lib/loan/api/coinrabbit_api_client.dart b/lib/loan/api/coinrabbit_api_client.dart new file mode 100644 index 000000000..20bacb068 --- /dev/null +++ b/lib/loan/api/coinrabbit_api_client.dart @@ -0,0 +1,180 @@ +import 'dart:convert'; + +import 'package:cake_wallet/entities/preferences_key.dart'; +import 'package:cake_wallet/loan/models/captcha_response.dart'; +import 'package:http/http.dart' as http; +import 'package:shared_preferences/shared_preferences.dart'; + +/// Thrown if an exception occurs while making an `http` request. +class HttpException implements Exception {} + +/// Thrown if an `http` request returns a non-200 status code. +class HttpRequestFailure implements Exception { + const HttpRequestFailure(this.statusCode); + + /// The status code of the response. + final int statusCode; +} + +/// Thrown when an error occurs while decoding the response body. +class JsonDecodeException implements Exception {} + +/// Thrown when an error occurs while deserializing the response body. +class JsonDeserializationException implements Exception {} + +/// A Dart API Client for the CoinRabbit REST API. +class CoinRabbitApiClient { + CoinRabbitApiClient( + {http.Client httpClient, SharedPreferences sharedPreferences}) + : _httpClient = httpClient ?? http.Client(), + _sharedPreferences = sharedPreferences; + + /// The host URL used for all API requests. + static const authority = 'https://api-staging.coinrabbit.io'; + + final http.Client _httpClient; + + final SharedPreferences _sharedPreferences; + + static const monitorList = 'MONITOR_LIST'; + static const confirmLoanCreate = 'CONFIRM_LOAN_CREATE'; + static const confirmLoanRepayment = 'CONFIRM_LOAN_REPAYMENT'; + static const confirmEarnCreate = 'CONFIRM_EARN_CREATE'; + static const confirmEarnWithdraw = 'CONFIRM_EARN_WITHDRAW'; + static const secondCredential = 'SECOND_CREDENTIAL'; + + /// Get Captcha Challange for authentication. + /// + /// REST call: `GET /v2/utils/captcha` + Future getCaptchaChallenge() async { + final uri = Uri.https(authority, '/v2/utils/captcha'); + final responseBody = await _get(uri) as Map; + + try { + return CaptchaResponse.fromJson(responseBody); + } catch (_) { + throw JsonDeserializationException(); + } + } + + /// Send verification code for authentication. + /// Returns a token String + /// + /// REST call: `POST /v2/utils/verification-code/send` + Future sendVerificationCode(String channel, bool isEmail) async { + final uri = Uri.https(authority, '/v2/utils/verification-code/send'); + final captcha = await getCaptchaChallenge(); + + final body = { + 'type': monitorList, + 'geetest_challenge': captcha.challenge, + 'geetest_seccode': captcha.gt, + 'geetest_validate': captcha.gt + }; + if (isEmail) { + body.addAll({"email": channel}); + } else { + body.addAll({"phone": channel}); + } + + final responseBody = + await _post(uri: uri, body: body) as Map; + + try { + final isActive2fa = responseBody['is_active_2fa'] as bool; + await _sharedPreferences.setBool(PreferencesKey.isActive2fa, isActive2fa); + + return responseBody['tokens'][0] as String; + } catch (_) { + throw JsonDeserializationException(); + } + } + + /// Verify the verification code for authentication. + /// Returns a token String + /// + /// REST call: `POST /v2/utils/verification-code/verify` + Future verifyCode(String code, String verificationToken) async { + final uri = Uri.https(authority, '/v2/utils/verification-code/verify'); + final headers = { + 'x-api-key': '04b6e4b9-ba09-4d0b-9b6f-13a0bc7cb348', + 'Content-Type': 'application/json' + }; + final body = {'verification_token': verificationToken, 'code': code}; + + final responseBody = await _post(uri: uri, body: body, headers: headers) + as Map; + + try { + return responseBody['token'] as String; + } catch (_) { + throw JsonDeserializationException(); + } + } + + /// Validates crypto address network. + /// Returns a token String + /// + /// REST call: `POST /v2/utils/validate-address` + Future validateAddressByNetwork( + {String address, String network}) async { + final uri = Uri.https(authority, '/v2/utils/validate-address'); + final headers = { + 'x-api-key': '04b6e4b9-ba09-4d0b-9b6f-13a0bc7cb348', + 'Content-Type': 'application/json' + }; + final body = {'address': address, 'network': network}; + + final responseBody = await _httpClient.post(uri, + body: body, headers: headers) as Map; + + try { + return responseBody['message'] as String == 'OK'; + } catch (_) { + throw HttpException(); + } + } + + Future _get(Uri uri) async { + http.Response response; + + try { + response = await _httpClient.get(uri); + } catch (_) { + throw HttpException(); + } + + if (response.statusCode != 200) { + throw HttpRequestFailure(response.statusCode); + } + + try { + final res = json.decode(response.body) as Map; + return res['response']; + } catch (e) { + throw JsonDecodeException(); + } + } + + Future _post( + {Uri uri, dynamic body, Map headers}) async { + http.Response response; + + try { + response = await _httpClient.post(uri, body: body, headers: headers); + } catch (_) { + throw HttpException(); + } + + if (response.statusCode != 200) { + throw HttpRequestFailure(response.statusCode); + } + + try { + final res = json.decode(response.body) as Map; + return res['response']; + } catch (e) { + throw JsonDecodeException(); + } + } +} diff --git a/lib/loan/models/captcha_response.dart b/lib/loan/models/captcha_response.dart new file mode 100644 index 000000000..e38c8989c --- /dev/null +++ b/lib/loan/models/captcha_response.dart @@ -0,0 +1,20 @@ +class CaptchaResponse { + int success; + String challenge; + String gt; + bool newCaptcha; + + CaptchaResponse({ + this.success, + this.challenge, + this.gt, + this.newCaptcha, + }); + + CaptchaResponse.fromJson(Map json) { + success = json['success'] as int; + challenge = json['challenge'] as String; + gt = json['gt'] as String; + newCaptcha = json['new_captcha'] as bool; + } +} diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index e9bcfe253..f4ed11f65 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -23,6 +23,7 @@ class SecretKey { SecretKey('wyreAccountId', () => ''), SecretKey('moonPayApiKey', () => ''), SecretKey('moonPaySecretKey', () => ''), + SecretKey('coinRabbitApiKey', () => ''), ]; final String name;