dev
dart 242 lines 7.49 KB
Raw
1 import 'dart:convert';
2
3 import 'package:cake_wallet/.secrets.g.dart' as secrets;
4 import 'package:cake_wallet/buy/buy_provider.dart';
5 import 'package:cake_wallet/buy/buy_quote.dart';
6 import 'package:cake_wallet/buy/pairs_utils.dart';
7 import 'package:cake_wallet/buy/payment_method.dart';
8 import 'package:cake_wallet/entities/fiat_currency.dart';
9 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
10 import 'package:cake_wallet/utils/show_pop_up.dart';
11 import 'package:cw_core/crypto_currency.dart';
12 import 'package:cw_core/utils/proxy_wrapper.dart';
13 import 'package:cw_core/wallet_base.dart';
14 import 'package:flutter/material.dart';
15 import 'dart:developer';
16 import 'package:url_launcher/url_launcher.dart';
17
18 class KryptonimBuyProvider extends BuyProvider {
19 KryptonimBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
20 : super(
21 wallet: wallet,
22 isTestEnvironment: isTestEnvironment,
23 hardwareWalletVM: null,
24 supportedCryptoList: supportedCryptoToFiatPairs(
25 notSupportedCrypto: _notSupportedCrypto, notSupportedFiat: _notSupportedFiat),
26 supportedFiatList: supportedFiatToCryptoPairs(
27 notSupportedFiat: _notSupportedFiat, notSupportedCrypto: _notSupportedCrypto));
28
29 static const _isProduction = true;
30
31 static const _baseUrl = _isProduction ? 'app.kryptonim.com' : 'intg-api.kryptonim.com';
32 static const _baseWidgetUrl = _isProduction ? 'buy.kryptonim.com' : 'intg.kryptonim.com';
33 static const _quotePath = '/v2/ramp/buy/quotes';
34 static const _merchantId = 'a70fe053';
35
36 static String get _kryptonimApiKey => secrets.kryptonimApiKey;
37
38 static const List<CryptoCurrency> _notSupportedCrypto = [];
39 static const List<FiatCurrency> _notSupportedFiat = [];
40
41 @override
42 String get title => 'Kryptonim';
43
44 @override
45 String get providerDescription => 'Kryptonim Buy Provider';
46
47 @override
48 String get lightIcon => 'assets/images/kryptonim_light.png';
49
50 @override
51 String get darkIcon => 'assets/images/kryptonim_dark.png';
52
53 @override
54 bool get isAggregator => false;
55
56 Future<Map<String, dynamic>> getExchangeRates(
57 {required CryptoCurrency cryptoCurrency,
58 required String fiatCurrency,
59 required double amount}) async {
60 final url = Uri.https(_baseUrl, _quotePath, {'m': _merchantId});
61
62 final headers = {
63 'accept': 'application/json',
64 'Content-Type': 'application/json',
65 'Authorization': _kryptonimApiKey,
66 };
67
68 try {
69 final body = jsonEncode({
70 'amount': amount,
71 'currency': fiatCurrency,
72 'converted_currency': cryptoCurrency.title,
73 'blockchain': _normalizeBlockChain(cryptoCurrency),
74 'quote_currency': fiatCurrency,
75 });
76
77 final response = await ProxyWrapper().post(
78 clearnetUri: url,
79 headers: headers,
80 body: body,
81 );
82
83 if (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 401) {
84 return jsonDecode(response.body) as Map<String, dynamic>;
85 } else {
86 return {};
87 }
88 } catch (e) {
89 return {};
90 }
91 }
92
93 @override
94 Future<List<PaymentMethod>> getAvailablePaymentTypes(
95 String fiatCurrency, CryptoCurrency cryptoCurrency, bool isBuyAction) async {
96 final data = await getExchangeRates(
97 cryptoCurrency: cryptoCurrency,
98 fiatCurrency: fiatCurrency,
99 amount: 100.0,
100 );
101
102 if (data.isEmpty || !data.containsKey('data')) return [];
103
104 final paymentMethods = (data['data'] as List<dynamic>)
105 .map((e) => PaymentMethod.fromKryptonimJson(e as Map<String, dynamic>))
106 .toList();
107
108 return paymentMethods;
109 }
110
111 @override
112 Future<List<Quote>?> fetchQuote({
113 required CryptoCurrency cryptoCurrency,
114 required FiatCurrency fiatCurrency,
115 required double amount,
116 required bool isBuyAction,
117 required String walletAddress,
118 PaymentType? paymentType,
119 String? customPaymentMethodType,
120 String? countryCode,
121 }) async {
122 log('Kryptonim: Fetching quote: ${isBuyAction ? cryptoCurrency : fiatCurrency} -> ${isBuyAction ? fiatCurrency : cryptoCurrency}, amount: $amount');
123
124 final data = await getExchangeRates(
125 cryptoCurrency: cryptoCurrency,
126 fiatCurrency: fiatCurrency.toString(),
127 amount: amount,
128 );
129
130 if (!data.containsKey('data') || (data['data'] as List).isEmpty) {
131 return null;
132 }
133
134 final quotesList = data['data'] as List<dynamic>;
135
136 Map<String, dynamic>? selectedPaymentMethod;
137
138 if (paymentType == PaymentType.all || paymentType == null) {
139 selectedPaymentMethod = quotesList.first as Map<String, dynamic>;
140 } else {
141 for (var quote in quotesList) {
142 final quotePaymentType = PaymentMethod.getPaymentTypeId(quote['payment_method'] as String?);
143 if (quotePaymentType == paymentType) {
144 selectedPaymentMethod = quote as Map<String, dynamic>;
145 break;
146 }
147 }
148 }
149
150 if (selectedPaymentMethod == null) {
151 return null;
152 }
153
154 final selectedPaymentType =
155 PaymentMethod.getPaymentTypeId(selectedPaymentMethod['payment_method'] as String?);
156 final quote = Quote.fromKryptonimJson(
157 selectedPaymentMethod, isBuyAction, selectedPaymentType ?? PaymentType.unknown);
158
159 quote.setFiatCurrency = fiatCurrency;
160 quote.setCryptoCurrency = cryptoCurrency;
161
162 return [quote];
163 }
164
165 @override
166 Future<void>? launchProvider(
167 {required BuildContext context,
168 required Quote quote,
169 required double amount,
170 required bool isBuyAction,
171 required String cryptoCurrencyAddress,
172 String? countryCode}) async {
173 final params = {
174 'amount': amount.toInt().toString(),
175 'currency': quote.fiatCurrency.name,
176 'convertedCurrency': quote.cryptoCurrency.title,
177 'blockchain': _normalizeBlockChain(quote.cryptoCurrency),
178 'address': cryptoCurrencyAddress,
179 'paymentMethod': normalizePaymentMethod(quote.paymentType),
180 };
181
182 final uri = Uri.https(_baseWidgetUrl, '/redirect-form', params);
183
184 try {
185 if (await canLaunchUrl(uri)) {
186 await launchUrl(uri, mode: LaunchMode.externalApplication);
187 } else {
188 throw Exception('Could not launch URL');
189 }
190 } catch (e) {
191 await showPopUp<void>(
192 context: context,
193 builder: (BuildContext context) {
194 return AlertWithOneAction(
195 alertTitle: "Kryptonim",
196 alertContent: "Payment provider is unavailable: $e",
197 buttonText: "OK",
198 buttonAction: () => Navigator.of(context).pop(),
199 );
200 },
201 );
202 }
203 }
204
205 String normalizePaymentMethod(PaymentType paymentType) {
206 switch (paymentType) {
207 case PaymentType.bankTransfer:
208 return 'bank';
209 case PaymentType.creditCard:
210 case PaymentType.debitCard:
211 return 'card';
212 default:
213 return paymentType.name.toLowerCase();
214 }
215 }
216
217 String _normalizeBlockChain(CryptoCurrency cur) {
218 String? blockchain = switch (cur.tag) {
219 'ETH' => 'Ethereum',
220 'BASE' => 'Base',
221 'ARB' => 'Arbitrum',
222 'POL' => 'Polygon',
223 'AVAXC' => 'Avalanche',
224 'SOL' => 'Solana',
225 _ => null,
226 };
227
228 if (blockchain == null) {
229 blockchain = switch (cur) {
230 CryptoCurrency.btc => 'Bitcoin',
231 CryptoCurrency.ltc => 'Litecoin',
232 CryptoCurrency.eth => 'Ethereum',
233 CryptoCurrency.baseEth => 'Base',
234 CryptoCurrency.arbEth => 'Arbitrum',
235 CryptoCurrency.maticpoly => 'Matic',
236 _ => null,
237 };
238 }
239
240 return blockchain ?? cur.fullName ?? '';
241 }
242 }