CAKE-306 | fixed moonpay_buy_provider.dart; added onEnabled() method to moonpay_buy_provider.dart; checked is MoonPayBuyProvider enabled; added isMoonPayEnabled to settings_store.dart

This commit is contained in:
OleksandrSobol 2021-06-01 21:03:35 +03:00
parent bb92fb3288
commit 9e1c151f85
6 changed files with 135 additions and 30 deletions

View file

@ -1,8 +1,12 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
class BuyAmount { class BuyAmount {
BuyAmount({@required this.sourceAmount, @required this.destAmount}); BuyAmount({
@required this.sourceAmount,
@required this.destAmount,
this.minAmount = 0});
final double sourceAmount; final double sourceAmount;
final double destAmount; final double destAmount;
final int minAmount;
} }

View file

@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:cake_wallet/buy/buy_exception.dart'; import 'package:cake_wallet/buy/buy_exception.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:cake_wallet/buy/buy_amount.dart'; import 'package:cake_wallet/buy/buy_amount.dart';
@ -13,17 +14,18 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets;
class MoonPayBuyProvider extends BuyProvider { class MoonPayBuyProvider extends BuyProvider {
MoonPayBuyProvider({WalletBase wallet, bool isTestEnvironment = false}) MoonPayBuyProvider({WalletBase wallet, bool isTestEnvironment = false})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment) { : super(wallet: wallet, isTestEnvironment: isTestEnvironment) {
baseApiUrl = isTestEnvironment baseUrl = isTestEnvironment ? _baseTestUrl : _baseProductUrl;
? _baseTestApiUrl
: _baseProductApiUrl;
} }
static const _baseTestApiUrl = 'https://buy-staging.moonpay.com'; static const _baseTestUrl = 'https://buy-staging.moonpay.com';
static const _baseProductApiUrl = 'https://api.moonpay.com'; static const _baseProductUrl = 'https://buy.moonpay.com';
static const _apiUrl = 'https://api.moonpay.com';
static const _currenciesSuffix = '/v3/currencies'; static const _currenciesSuffix = '/v3/currencies';
static const _quoteSuffix = '/buy_quote'; static const _quoteSuffix = '/buy_quote';
static const _transactionsSuffix = '/v1/transactions'; static const _transactionsSuffix = '/v1/transactions';
static const _countrySuffix = '/v3/countries';
static const _apiKey = secrets.moonPayApiKey; static const _apiKey = secrets.moonPayApiKey;
static const _secretKey = secrets.moonPaySecretKey;
@override @override
String get title => 'MoonPay'; String get title => 'MoonPay';
@ -35,9 +37,9 @@ class MoonPayBuyProvider extends BuyProvider {
walletTypeToCryptoCurrency(walletType).title.toLowerCase(); walletTypeToCryptoCurrency(walletType).title.toLowerCase();
@override @override
String get trackUrl => baseApiUrl + '/transaction_receipt?transactionId='; String get trackUrl => baseUrl + '/transaction_receipt?transactionId=';
String baseApiUrl; String baseUrl;
@override @override
Future<String> requestUrl(String amount, String sourceCurrency) async { Future<String> requestUrl(String amount, String sourceCurrency) async {
@ -45,22 +47,32 @@ class MoonPayBuyProvider extends BuyProvider {
'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay' 'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay'
'%2Csepa_bank_transfer%2Cgbp_bank_transfer%2Cgbp_open_banking_payment'; '%2Csepa_bank_transfer%2Cgbp_bank_transfer%2Cgbp_open_banking_payment';
final originalUrl = baseApiUrl + '?apiKey=' + _apiKey + '&currencyCode=' + final suffix = '?apiKey=' + _apiKey + '&currencyCode=' +
currencyCode + '&enabledPaymentMethods=' + enabledPaymentMethods + currencyCode + '&enabledPaymentMethods=' + enabledPaymentMethods +
'&walletAddress=' + walletAddress + '&walletAddress=' + walletAddress +
'&baseCurrencyCode=' + sourceCurrency.toLowerCase() + '&baseCurrencyCode=' + sourceCurrency.toLowerCase() +
'&baseCurrencyAmount=' + amount + '&lockAmount=true' + '&baseCurrencyAmount=' + amount + '&lockAmount=true' +
'&showAllCurrencies=false' + '&showWalletAddressForm=false'; '&showAllCurrencies=false' + '&showWalletAddressForm=false';
return originalUrl; final originalUrl = baseUrl + suffix;
final messageBytes = utf8.encode(suffix);
final key = utf8.encode(_secretKey);
final hmac = Hmac(sha256, key);
final digest = hmac.convert(messageBytes);
final signature = base64.encode(digest.bytes);
final urlWithSignature = originalUrl +
'&signature=${Uri.encodeComponent(signature)}';
return isTestEnvironment ? originalUrl : urlWithSignature;
} }
@override @override
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) async { Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) async {
final url = _baseProductApiUrl + _currenciesSuffix + '/$currencyCode' + final url = _apiUrl + _currenciesSuffix + '/$currencyCode' +
_quoteSuffix + '/?apiKey=' + _apiKey + _quoteSuffix + '/?apiKey=' + _apiKey +
'&baseCurrencyAmount=' + amount + '&baseCurrencyAmount=' + amount +
'&baseCurrencyCode' + sourceCurrency.toLowerCase(); '&baseCurrencyCode=' + sourceCurrency.toLowerCase();
final response = await get(url); final response = await get(url);
@ -73,13 +85,17 @@ class MoonPayBuyProvider extends BuyProvider {
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final sourceAmount = responseJSON['totalAmount'] as double; final sourceAmount = responseJSON['totalAmount'] as double;
final destAmount = responseJSON['quoteCurrencyAmount'] as double; final destAmount = responseJSON['quoteCurrencyAmount'] as double;
final minSourceAmount = responseJSON['baseCurrency']['minAmount'] as int;
return BuyAmount(sourceAmount: sourceAmount, destAmount: destAmount); return BuyAmount(
sourceAmount: sourceAmount,
destAmount: destAmount,
minAmount: minSourceAmount);
} }
@override @override
Future<Order> findOrderById(String id) async { Future<Order> findOrderById(String id) async {
final url = _baseProductApiUrl + _transactionsSuffix + '/$id' + final url = _apiUrl + _transactionsSuffix + '/$id' +
'?apiKey=' + _apiKey; '?apiKey=' + _apiKey;
final response = await get(url); final response = await get(url);
@ -108,4 +124,29 @@ class MoonPayBuyProvider extends BuyProvider {
walletId: walletId walletId: walletId
); );
} }
static Future<bool> onEnabled(String deviceCountryCode) async {
final url = _apiUrl + _countrySuffix;
var isBuyEnable = false;
final response = await get(url);
try {
final responseJSON = json.decode(response.body) as List<dynamic>;
for (var element in responseJSON) {
final countryMap = element as Map<String, dynamic>;
final countryCode = countryMap['alpha2'] as String;
if (countryCode == deviceCountryCode) {
isBuyEnable = countryMap['isBuyAllowed'] as bool;
break;
}
}
} catch (e) {
isBuyEnable = false;
print(e.toString());
}
return isBuyEnable;
}
} }

View file

@ -1,5 +1,6 @@
import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
import 'package:cake_wallet/bitcoin/litecoin_wallet_service.dart'; import 'package:cake_wallet/bitcoin/litecoin_wallet_service.dart';
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
import 'package:cake_wallet/core/backup_service.dart'; import 'package:cake_wallet/core/backup_service.dart';
import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/core/wallet_service.dart';
import 'package:cake_wallet/entities/biometric_auth.dart'; import 'package:cake_wallet/entities/biometric_auth.dart';
@ -91,6 +92,7 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart'; import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
import 'package:devicelocale/devicelocale.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
@ -152,8 +154,14 @@ Future setup(
final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) && final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) &&
(secrets.wyreApiKey?.isNotEmpty ?? false) && (secrets.wyreApiKey?.isNotEmpty ?? false) &&
(secrets.wyreAccountId?.isNotEmpty ?? false); (secrets.wyreAccountId?.isNotEmpty ?? false);
final locale = await Devicelocale.currentLocale;
final deviceCountryCode = locale.split('_').last;
final isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(deviceCountryCode);
final settingsStore = await SettingsStoreBase.load( final settingsStore = await SettingsStoreBase.load(
nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled); nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled,
isMoonPayEnabled: isMoonPayEnabled);
if (_isSetupFinished) { if (_isSetupFinished) {
return; return;
@ -541,7 +549,8 @@ Future setup(
final wallet = getIt.get<AppStore>().wallet; final wallet = getIt.get<AppStore>().wallet;
return BuyViewModel(_ordersSource, getIt.get<OrdersStore>(), return BuyViewModel(_ordersSource, getIt.get<OrdersStore>(),
getIt.get<BuyAmountViewModel>(), wallet: wallet); getIt.get<SettingsStore>(), getIt.get<BuyAmountViewModel>(),
wallet: wallet);
}); });
getIt.registerFactory(() { getIt.registerFactory(() {

View file

@ -1,7 +1,11 @@
import 'dart:ui'; import 'dart:ui';
import 'package:cake_wallet/buy/buy_amount.dart'; import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
import 'package:cake_wallet/src/screens/buy/widgets/buy_list_item.dart'; import 'package:cake_wallet/src/screens/buy/widgets/buy_list_item.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/buy/buy_view_model.dart'; import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -172,13 +176,16 @@ class PreOrderPage extends BasePage {
builder: (context, AsyncSnapshot<BuyAmount> snapshot) { builder: (context, AsyncSnapshot<BuyAmount> snapshot) {
double sourceAmount; double sourceAmount;
double destAmount; double destAmount;
int minAmount;
if (snapshot.hasData) { if (snapshot.hasData) {
sourceAmount = snapshot.data.sourceAmount; sourceAmount = snapshot.data.sourceAmount;
destAmount = snapshot.data.destAmount; destAmount = snapshot.data.destAmount;
minAmount = snapshot.data.minAmount;
} else { } else {
sourceAmount = 0.0; sourceAmount = 0.0;
destAmount = 0.0; destAmount = 0.0;
minAmount = 0;
} }
return Padding( return Padding(
@ -192,14 +199,15 @@ class PreOrderPage extends BasePage {
sourceCurrency: buyViewModel.fiatCurrency, sourceCurrency: buyViewModel.fiatCurrency,
destAmount: destAmount, destAmount: destAmount,
destCurrency: buyViewModel.cryptoCurrency, destCurrency: buyViewModel.cryptoCurrency,
onTap: onTap: ((buyViewModel.buyAmountViewModel
buyViewModel.buyAmountViewModel .doubleAmount != 0.0) &&
.doubleAmount == 0.0 ? null : () { (snapshot.hasData)) ? () =>
buyViewModel.selectedProvider = item.provider; onSelectBuyProvider(
sourceAmount > 0 context: context,
? buyViewModel.isDisabled = false provider: item.provider,
: buyViewModel.isDisabled = true; sourceAmount: sourceAmount,
} minAmount: minAmount
) : null
); );
}) })
); );
@ -242,4 +250,26 @@ class PreOrderPage extends BasePage {
) )
); );
} }
void onSelectBuyProvider({BuildContext context, BuyProvider provider,
double sourceAmount, int minAmount}) {
if ((provider is MoonPayBuyProvider)&&
(buyViewModel.buyAmountViewModel.doubleAmount < minAmount)) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: 'MoonPay',
alertContent: 'Value of the amount must be more than $minAmount ${buyViewModel.fiatCurrency.toString()}',
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
return;
}
buyViewModel.selectedProvider = provider;
sourceAmount > 0
? buyViewModel.isDisabled = false
: buyViewModel.isDisabled = true;
}
} }

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_base.dart';
@ -18,6 +19,7 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/entities/node.dart';
import 'package:cake_wallet/entities/monero_transaction_priority.dart'; import 'package:cake_wallet/entities/monero_transaction_priority.dart';
import 'package:cake_wallet/entities/action_list_display_mode.dart'; import 'package:cake_wallet/entities/action_list_display_mode.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
part 'settings_store.g.dart'; part 'settings_store.g.dart';
@ -39,6 +41,7 @@ abstract class SettingsStoreBase with Store {
@required TransactionPriority initialBitcoinTransactionPriority, @required TransactionPriority initialBitcoinTransactionPriority,
@required TransactionPriority initialMoneroTransactionPriority, @required TransactionPriority initialMoneroTransactionPriority,
@required this.isBitcoinBuyEnabled, @required this.isBitcoinBuyEnabled,
@required this.isMoonPayEnabled,
this.actionlistDisplayMode}) { this.actionlistDisplayMode}) {
fiatCurrency = initialFiatCurrency; fiatCurrency = initialFiatCurrency;
balanceDisplayMode = initialBalanceDisplayMode; balanceDisplayMode = initialBalanceDisplayMode;
@ -147,9 +150,12 @@ abstract class SettingsStoreBase with Store {
bool isBitcoinBuyEnabled; bool isBitcoinBuyEnabled;
bool isMoonPayEnabled;
static Future<SettingsStore> load( static Future<SettingsStore> load(
{@required Box<Node> nodeSource, {@required Box<Node> nodeSource,
@required bool isBitcoinBuyEnabled, @required bool isBitcoinBuyEnabled,
@required bool isMoonPayEnabled,
FiatCurrency initialFiatCurrency = FiatCurrency.usd, FiatCurrency initialFiatCurrency = FiatCurrency.usd,
MoneroTransactionPriority initialMoneroTransactionPriority = MoneroTransactionPriority initialMoneroTransactionPriority =
MoneroTransactionPriority.slow, MoneroTransactionPriority.slow,
@ -221,6 +227,7 @@ abstract class SettingsStoreBase with Store {
}, },
appVersion: packageInfo.version, appVersion: packageInfo.version,
isBitcoinBuyEnabled: isBitcoinBuyEnabled, isBitcoinBuyEnabled: isBitcoinBuyEnabled,
isMoonPayEnabled: isMoonPayEnabled,
initialFiatCurrency: currentFiatCurrency, initialFiatCurrency: currentFiatCurrency,
initialBalanceDisplayMode: currentBalanceDisplayMode, initialBalanceDisplayMode: currentBalanceDisplayMode,
initialSaveRecipientAddress: shouldSaveRecipientAddress, initialSaveRecipientAddress: shouldSaveRecipientAddress,
@ -242,8 +249,18 @@ abstract class SettingsStoreBase with Store {
BitcoinTransactionPriority.medium, BitcoinTransactionPriority.medium,
BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode initialBalanceDisplayMode =
BalanceDisplayMode.availableBalance}) async { BalanceDisplayMode.availableBalance}) async {
final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) &&
(secrets.wyreApiKey?.isNotEmpty ?? false) &&
(secrets.wyreAccountId?.isNotEmpty ?? false);
final locale = await Devicelocale.currentLocale;
final deviceCountryCode = locale.split('_').last;
final isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(deviceCountryCode);
final settings = await SettingsStoreBase.load( final settings = await SettingsStoreBase.load(
nodeSource: nodeSource, nodeSource: nodeSource,
isBitcoinBuyEnabled: isBitcoinBuyEnabled,
isMoonPayEnabled: isMoonPayEnabled,
initialBalanceDisplayMode: initialBalanceDisplayMode, initialBalanceDisplayMode: initialBalanceDisplayMode,
initialFiatCurrency: initialFiatCurrency, initialFiatCurrency: initialFiatCurrency,
initialMoneroTransactionPriority: initialMoneroTransactionPriority, initialMoneroTransactionPriority: initialMoneroTransactionPriority,

View file

@ -4,6 +4,7 @@ import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/crypto_currency.dart';
import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/buy/buy_item.dart'; import 'package:cake_wallet/view_model/buy/buy_item.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
@ -18,12 +19,14 @@ part 'buy_view_model.g.dart';
class BuyViewModel = BuyViewModelBase with _$BuyViewModel; class BuyViewModel = BuyViewModelBase with _$BuyViewModel;
abstract class BuyViewModelBase with Store { abstract class BuyViewModelBase with Store {
BuyViewModelBase(this.ordersSource, this.ordersStore, this.buyAmountViewModel, BuyViewModelBase(this.ordersSource, this.ordersStore, this.settingsStore,
{@required this.wallet}) { this.buyAmountViewModel, {@required this.wallet}) {
providerList = [ providerList = [WyreBuyProvider(wallet: wallet)];
WyreBuyProvider(wallet: wallet),
MoonPayBuyProvider(wallet: wallet) if (settingsStore.isMoonPayEnabled ?? false) {
]; providerList.add(MoonPayBuyProvider(wallet: wallet));
}
items = providerList.map((provider) => items = providerList.map((provider) =>
BuyItem(provider: provider, buyAmountViewModel: buyAmountViewModel)) BuyItem(provider: provider, buyAmountViewModel: buyAmountViewModel))
.toList(); .toList();
@ -34,6 +37,7 @@ abstract class BuyViewModelBase with Store {
final Box<Order> ordersSource; final Box<Order> ordersSource;
final OrdersStore ordersStore; final OrdersStore ordersStore;
final SettingsStore settingsStore;
final BuyAmountViewModel buyAmountViewModel; final BuyAmountViewModel buyAmountViewModel;
final WalletBase wallet; final WalletBase wallet;