mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-28 06:28:41 +01:00
Cw 1611 fetch moon pay payment methods from their api (#3565)
* add payment method api * add payment method limits * Update lib/buy/moonpay/moonpay_provider.dart * fix MoonPay payment method handling * Handle custom MoonPay payment methods * fix limits when max is infinite [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
@@ -200,7 +200,8 @@ class Quote extends SelectableOption {
|
||||
}
|
||||
|
||||
factory Quote.fromMoonPayJson(
|
||||
Map<String, dynamic> json, bool isBuyAction, PaymentType paymentType) {
|
||||
Map<String, dynamic> json, bool isBuyAction, PaymentType paymentType,
|
||||
{String? customPaymentMethodType}) {
|
||||
final rate = isBuyAction
|
||||
? json['quoteCurrencyPrice'] as double? ?? 0.0
|
||||
: json['baseCurrencyPrice'] as double? ?? 0.0;
|
||||
@@ -226,6 +227,7 @@ class Quote extends SelectableOption {
|
||||
transactionFee: transactionFee,
|
||||
payout: _toDouble(json['quoteCurrencyAmount']) ?? 0.0,
|
||||
paymentType: paymentType,
|
||||
customPaymentMethodType: customPaymentMethodType,
|
||||
recommendations: [],
|
||||
quoteId: json['signature'] as String? ?? '',
|
||||
provider: ProvidersHelper.getProviderByType(ProviderType.moonpay),
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
class MoonPayPaymentMethod {
|
||||
MoonPayPaymentMethod({
|
||||
required this.type,
|
||||
required this.displayName,
|
||||
required this.iconUrl,
|
||||
required this.active,
|
||||
this.limitAmount,
|
||||
this.limitCurrencyCode,
|
||||
});
|
||||
|
||||
factory MoonPayPaymentMethod.fromJson(Map<String, dynamic> json) {
|
||||
final availability = json["availability"] as Map<String, dynamic>? ?? {};
|
||||
final limits = json["limits"] as Map<String, dynamic>?;
|
||||
final perTransaction = limits?["perTransaction"] as Map<String, dynamic>?;
|
||||
|
||||
return MoonPayPaymentMethod(
|
||||
type: json["type"] as String? ?? "",
|
||||
displayName: json["displayName"] as String? ?? "",
|
||||
iconUrl: json["iconUrl"] as String? ?? "",
|
||||
active: availability["active"] as bool? ?? false,
|
||||
limitAmount: (perTransaction?["limit"] as num?)?.toDouble(),
|
||||
limitCurrencyCode: perTransaction?["currencyCode"] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
final String type;
|
||||
final String displayName;
|
||||
final String iconUrl;
|
||||
final bool active;
|
||||
final double? limitAmount;
|
||||
final String? limitCurrencyCode;
|
||||
}
|
||||
@@ -1,41 +1,38 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import "dart:convert";
|
||||
import "dart:developer";
|
||||
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/buy/buy_exception.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/buy_quote.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/order/order_source_description.dart';
|
||||
import 'package:cake_wallet/buy/pairs_utils.dart';
|
||||
import 'package:cake_wallet/buy/payment_method.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import "package:cake_wallet/.secrets.g.dart" as secrets;
|
||||
import "package:cake_wallet/buy/buy_exception.dart";
|
||||
import "package:cake_wallet/buy/buy_provider.dart";
|
||||
import "package:cake_wallet/buy/buy_provider_description.dart";
|
||||
import "package:cake_wallet/buy/buy_quote.dart";
|
||||
import "package:cake_wallet/buy/moonpay/moonpay_payment_methods.dart";
|
||||
import "package:cake_wallet/buy/pairs_utils.dart";
|
||||
import "package:cake_wallet/buy/payment_method.dart";
|
||||
import "package:cake_wallet/entities/fiat_currency.dart";
|
||||
import "package:cake_wallet/exchange/trade_state.dart";
|
||||
import "package:cake_wallet/generated/i18n.dart";
|
||||
import "package:cake_wallet/order/order.dart";
|
||||
import "package:cake_wallet/order/order_source_description.dart";
|
||||
import "package:cake_wallet/palette.dart";
|
||||
import "package:cake_wallet/src/widgets/alert_with_one_action.dart";
|
||||
import "package:cake_wallet/store/app_store.dart";
|
||||
import "package:cw_core/crypto_currency.dart";
|
||||
import "package:cw_core/currency_for_wallet_type.dart";
|
||||
import "package:cw_core/utils/print_verbose.dart";
|
||||
import "package:cw_core/utils/proxy_wrapper.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:url_launcher/url_launcher.dart";
|
||||
|
||||
class MoonPayProvider extends BuyProvider {
|
||||
MoonPayProvider({
|
||||
required AppStore appStore,
|
||||
required WalletBase wallet,
|
||||
bool isTestEnvironment = false,
|
||||
required super.wallet,
|
||||
super.isTestEnvironment = false,
|
||||
}) : baseSellUrl = isTestEnvironment ? _baseSellTestUrl : _baseSellProductUrl,
|
||||
baseBuyUrl = isTestEnvironment ? _baseBuyTestUrl : _baseBuyProductUrl,
|
||||
this._appStore = appStore,
|
||||
_appStore = appStore,
|
||||
super(
|
||||
wallet: wallet,
|
||||
isTestEnvironment: isTestEnvironment,
|
||||
hardwareWalletVM: null,
|
||||
supportedCryptoList: supportedCryptoToFiatPairs(
|
||||
notSupportedCrypto: _notSupportedCrypto, notSupportedFiat: _notSupportedFiat),
|
||||
@@ -44,37 +41,70 @@ class MoonPayProvider extends BuyProvider {
|
||||
|
||||
final AppStore _appStore;
|
||||
|
||||
static const _baseSellTestUrl = 'sell-sandbox.moonpay.com';
|
||||
static const _baseSellProductUrl = 'sell.moonpay.com';
|
||||
static const _baseBuyTestUrl = 'buy-staging.moonpay.com';
|
||||
static const _baseBuyProductUrl = 'buy.moonpay.com';
|
||||
static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
|
||||
static const _apiUrl = 'https://api.moonpay.com';
|
||||
static const _baseUrl = 'api.moonpay.com';
|
||||
static const _currenciesPath = '/v3/currencies';
|
||||
static const _buyQuote = '/buy_quote';
|
||||
static const _sellQuote = '/sell_quote';
|
||||
static const _baseSellTestUrl = "sell-sandbox.moonpay.com";
|
||||
static const _baseSellProductUrl = "sell.moonpay.com";
|
||||
static const _baseBuyTestUrl = "buy-staging.moonpay.com";
|
||||
static const _baseBuyProductUrl = "buy.moonpay.com";
|
||||
static const _cIdBaseUrl = "exchange-helper.cakewallet.com";
|
||||
static const _apiUrl = "https://api.moonpay.com";
|
||||
static const _baseUrl = "api.moonpay.com";
|
||||
static const _currenciesPath = "/v3/currencies";
|
||||
static const _buyQuote = "/buy_quote";
|
||||
static const _sellQuote = "/sell_quote";
|
||||
static const _paymentMethodsPath = "/api/moonpay/payment-methods";
|
||||
|
||||
static const _transactionsSuffix = '/v1/transactions';
|
||||
static const _transactionsSuffix = "/v1/transactions";
|
||||
|
||||
static const List<CryptoCurrency> _notSupportedCrypto = [];
|
||||
static const List<FiatCurrency> _notSupportedFiat = [];
|
||||
|
||||
static String get _exchangeHelperApiKey => secrets.exchangeHelperApiKey;
|
||||
|
||||
static const Map<PaymentType, String> _moonPayPaymentMethodStrings = {
|
||||
PaymentType.creditCard: 'credit_debit_card',
|
||||
PaymentType.debitCard: 'credit_debit_card',
|
||||
PaymentType.ach: 'ach_bank_transfer',
|
||||
PaymentType.applePay: 'apple_pay',
|
||||
PaymentType.googlePay: 'google_pay',
|
||||
PaymentType.sepa: 'sepa_bank_transfer',
|
||||
PaymentType.paypal: 'paypal',
|
||||
PaymentType.sepaOpenBankingPayment: 'sepa_open_banking_payment',
|
||||
PaymentType.gbpOpenBankingPayment: 'gbp_open_banking_payment',
|
||||
PaymentType.lowCostAch: 'low_cost_ach',
|
||||
PaymentType.mobileWallet: 'mobile_wallet',
|
||||
PaymentType.pixInstantPayment: 'pix_instant_payment',
|
||||
PaymentType.yellowCardBankTransfer: 'yellow_card_bank_transfer',
|
||||
PaymentType.fiatBalance: 'fiat_balance',
|
||||
PaymentType.revolutPay: 'revolut_pay',
|
||||
PaymentType.moonpayCashApp: 'cash_app',
|
||||
PaymentType.moonpayBalance: 'moonpay_balance',
|
||||
};
|
||||
|
||||
static final Map<String, PaymentType> _paymentTypeByMoonPayString = {
|
||||
for (final entry in _moonPayPaymentMethodStrings.entries) entry.value: entry.key,
|
||||
};
|
||||
|
||||
String? normalizePaymentMethod(PaymentType paymentMethod) =>
|
||||
_moonPayPaymentMethodStrings[paymentMethod];
|
||||
|
||||
PaymentType _getPaymentTypeByString(String? paymentMethod) =>
|
||||
_paymentTypeByMoonPayString[paymentMethod] ?? PaymentType.unknown;
|
||||
|
||||
final String baseBuyUrl;
|
||||
final String baseSellUrl;
|
||||
|
||||
@override
|
||||
String get providerDescription =>
|
||||
'MoonPay offers a fast and simple way to buy and sell cryptocurrencies';
|
||||
"MoonPay offers a fast and simple way to buy and sell cryptocurrencies";
|
||||
|
||||
@override
|
||||
String get title => 'MoonPay';
|
||||
String get title => "MoonPay";
|
||||
|
||||
@override
|
||||
String get lightIcon => 'assets/images/moonpay_light.png';
|
||||
String get lightIcon => "assets/images/moonpay_light.png";
|
||||
|
||||
@override
|
||||
String get darkIcon => 'assets/images/moonpay_dark.png';
|
||||
String get darkIcon => "assets/images/moonpay_dark.png";
|
||||
|
||||
@override
|
||||
bool get isAggregator => false;
|
||||
@@ -84,18 +114,7 @@ class MoonPayProvider extends BuyProvider {
|
||||
String get currencyCode =>
|
||||
walletTypeToCryptoCurrency(wallet.type, chainId: wallet.chainId).title.toLowerCase();
|
||||
|
||||
String get trackUrl => baseBuyUrl + '/transaction_receipt?transactionId=';
|
||||
|
||||
static String get _exchangeHelperApiKey => secrets.exchangeHelperApiKey;
|
||||
|
||||
static String themeToMoonPayTheme(MaterialThemeBase theme) {
|
||||
switch (theme.type) {
|
||||
case ThemeType.light:
|
||||
return 'light';
|
||||
case ThemeType.dark:
|
||||
return 'dark';
|
||||
}
|
||||
}
|
||||
String get trackUrl => "$baseBuyUrl/transaction_receipt?transactionId=";
|
||||
|
||||
Future<String> getMoonpaySignedQuery(String query) async {
|
||||
final uri =
|
||||
@@ -103,102 +122,126 @@ class MoonPayProvider extends BuyProvider {
|
||||
|
||||
final response = await ProxyWrapper().post(
|
||||
clearnetUri: uri,
|
||||
headers: {'Content-Type': 'application/json', 'x-api-key': _exchangeHelperApiKey},
|
||||
body: json.encode({'query': query}),
|
||||
headers: {"Content-Type": "application/json", "x-api-key": _exchangeHelperApiKey},
|
||||
body: json.encode({"query": query}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
printV((jsonDecode(response.body) as Map<String, dynamic>));
|
||||
return (jsonDecode(response.body) as Map<String, dynamic>)['query'] as String;
|
||||
printV(jsonDecode(response.body) as Map<String, dynamic>);
|
||||
return (jsonDecode(response.body) as Map<String, dynamic>)["query"] as String;
|
||||
} else {
|
||||
throw Exception(
|
||||
'Provider currently unavailable. Status: ${response.statusCode} ${response.body}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> fetchFiatCredentials(
|
||||
String fiatCurrency, String cryptocurrency, String? paymentMethod) async {
|
||||
final params = {'baseCurrencyCode': fiatCurrency.toLowerCase(), 'apiKey': _apiKey};
|
||||
|
||||
if (paymentMethod != null) params['paymentMethod'] = paymentMethod;
|
||||
|
||||
final path = '$_currenciesPath/${cryptocurrency.toLowerCase()}/limits';
|
||||
final url = Uri.https(_baseUrl, path, params);
|
||||
|
||||
try {
|
||||
final response = await ProxyWrapper().get(
|
||||
clearnetUri: url,
|
||||
headers: {'accept': 'application/json'},
|
||||
"Provider currently unavailable. Status: ${response.statusCode} ${response.body}",
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return jsonDecode(response.body) as Map<String, dynamic>;
|
||||
} else {
|
||||
printV('MoonPay does not support fiat: $fiatCurrency');
|
||||
return {};
|
||||
}
|
||||
} catch (e) {
|
||||
printV('MoonPay Error fetching fiat currencies: $e');
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<MoonPayPaymentMethod>> fetchMoonPayPaymentMethods({
|
||||
required String currencyCode,
|
||||
String transactionType = "buy",
|
||||
}) async {
|
||||
final String? countryCode = FiatCurrency.tryDeserialize(raw: currencyCode)?.apiCountryCode;
|
||||
|
||||
if (countryCode == null) {
|
||||
throw Exception("failed to determine country code for currency: $currencyCode");
|
||||
}
|
||||
|
||||
final params = <String, String>{
|
||||
"currencyCode": currencyCode,
|
||||
"countryCode": countryCode,
|
||||
"transactionType": transactionType,
|
||||
if (countryCode == "USA") "stateOfResidence": "NY",
|
||||
};
|
||||
|
||||
final uri = Uri.https(_cIdBaseUrl, _paymentMethodsPath, params);
|
||||
|
||||
final response = await ProxyWrapper().get(
|
||||
clearnetUri: uri,
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"x-api-key": _exchangeHelperApiKey,
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode == 500) {
|
||||
throw Exception("failed to fetch payment methods. Status: 500 Internal Server Error");
|
||||
}
|
||||
throw Exception(
|
||||
"failed to fetch payment methods. Status: ${response.statusCode} ${response.body}");
|
||||
}
|
||||
|
||||
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
final list = decoded["payment-methods"] as List? ?? [];
|
||||
|
||||
return list.map((e) => MoonPayPaymentMethod.fromJson(e as Map<String, dynamic>)).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<PaymentMethod>> getAvailablePaymentTypes(
|
||||
String fiatCurrency, CryptoCurrency cryptoCurrency, bool isBuyAction) async {
|
||||
final List<PaymentMethod> paymentMethods = [];
|
||||
|
||||
if (isBuyAction) {
|
||||
final fiatBuyCredentials =
|
||||
await fetchFiatCredentials(fiatCurrency, cryptoCurrency.title, null);
|
||||
if (fiatBuyCredentials.isNotEmpty) {
|
||||
final paymentMethod = fiatBuyCredentials['paymentMethod'] as String?;
|
||||
try {
|
||||
final moonPayMethods = await fetchMoonPayPaymentMethods(
|
||||
currencyCode: fiatCurrency,
|
||||
transactionType: isBuyAction ? "buy" : "sell",
|
||||
);
|
||||
|
||||
for (final method in moonPayMethods) {
|
||||
if (!method.active) {
|
||||
continue;
|
||||
}
|
||||
final paymentType = _getPaymentTypeByString(method.type);
|
||||
|
||||
paymentMethods.add(PaymentMethod.fromMoonPayJson(
|
||||
fiatBuyCredentials, _getPaymentTypeByString(paymentMethod)));
|
||||
return paymentMethods;
|
||||
method,
|
||||
paymentType,
|
||||
),);
|
||||
}
|
||||
} catch (e) {
|
||||
printV("Error fetching payment methods: $e");
|
||||
}
|
||||
|
||||
return paymentMethods;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Quote>?> fetchQuote(
|
||||
{required CryptoCurrency cryptoCurrency,
|
||||
required FiatCurrency fiatCurrency,
|
||||
required double amount,
|
||||
required bool isBuyAction,
|
||||
required String walletAddress,
|
||||
PaymentType? paymentType,
|
||||
String? customPaymentMethodType,
|
||||
String? countryCode}) async {
|
||||
Future<List<Quote>?> fetchQuote({
|
||||
required CryptoCurrency cryptoCurrency,
|
||||
required FiatCurrency fiatCurrency,
|
||||
required double amount,
|
||||
required bool isBuyAction,
|
||||
required String walletAddress,
|
||||
PaymentType? paymentType,
|
||||
String? customPaymentMethodType,
|
||||
String? countryCode,
|
||||
}) async {
|
||||
String? paymentMethod;
|
||||
|
||||
if (paymentType != null && paymentType != PaymentType.all) {
|
||||
paymentMethod = normalizePaymentMethod(paymentType);
|
||||
if (paymentMethod == null) paymentMethod = paymentType.name;
|
||||
} else {
|
||||
paymentMethod = 'credit_debit_card';
|
||||
paymentMethod = normalizePaymentMethod(paymentType) ?? customPaymentMethodType;
|
||||
}
|
||||
|
||||
final action = isBuyAction ? 'buy' : 'sell';
|
||||
final action = isBuyAction ? "buy" : "sell";
|
||||
|
||||
final formattedCryptoCurrency = _normalizeCurrency(cryptoCurrency);
|
||||
final baseCurrencyCode =
|
||||
isBuyAction ? fiatCurrency.name.toLowerCase() : cryptoCurrency.title.toLowerCase();
|
||||
|
||||
final params = {
|
||||
'baseCurrencyCode': baseCurrencyCode,
|
||||
'baseCurrencyAmount': amount.toStringAsFixed(2),
|
||||
'paymentMethod': paymentMethod,
|
||||
'areFeesIncluded': 'false',
|
||||
"baseCurrencyCode": baseCurrencyCode,
|
||||
"baseCurrencyAmount": amount.toStringAsFixed(2),
|
||||
if (paymentMethod != null) "paymentMethod": paymentMethod,
|
||||
"areFeesIncluded": "false",
|
||||
'apiKey': _apiKey
|
||||
};
|
||||
|
||||
log('MoonPay: Fetching $action quote: ${isBuyAction ? formattedCryptoCurrency : fiatCurrency.name.toLowerCase()} -> ${isBuyAction ? baseCurrencyCode : formattedCryptoCurrency}, amount: $amount, paymentMethod: $paymentMethod');
|
||||
log("MoonPay: Fetching $action quote: ${isBuyAction ? formattedCryptoCurrency : fiatCurrency.name.toLowerCase()} -> ${isBuyAction ? baseCurrencyCode : formattedCryptoCurrency}, amount: $amount, paymentMethod: $paymentMethod");
|
||||
|
||||
final quotePath = isBuyAction ? _buyQuote : _sellQuote;
|
||||
|
||||
final path = '$_currenciesPath/$formattedCryptoCurrency$quotePath';
|
||||
final path = "$_currenciesPath/$formattedCryptoCurrency$quotePath";
|
||||
final url = Uri.https(_baseUrl, path, params);
|
||||
try {
|
||||
final response = await ProxyWrapper().get(clearnetUri: url);
|
||||
@@ -218,19 +261,30 @@ class MoonPayProvider extends BuyProvider {
|
||||
}
|
||||
|
||||
final paymentMethods = data['paymentMethod'] as String?;
|
||||
final quote =
|
||||
Quote.fromMoonPayJson(data, isBuyAction, _getPaymentTypeByString(paymentMethods));
|
||||
|
||||
final quote = Quote.fromMoonPayJson(
|
||||
data,
|
||||
isBuyAction,
|
||||
_getPaymentTypeByString(paymentMethods ?? paymentMethod),
|
||||
customPaymentMethodType: paymentMethod,
|
||||
);
|
||||
|
||||
quote.setFiatCurrency = fiatCurrency;
|
||||
quote.setCryptoCurrency = cryptoCurrency;
|
||||
|
||||
return [quote];
|
||||
} else {
|
||||
printV('Moon Pay: Error fetching buy quote: ');
|
||||
}
|
||||
try {
|
||||
final errorData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
final errorMessage = errorData["message"] as String?;
|
||||
printV("Error fetching buy quote: $errorMessage");
|
||||
return null;
|
||||
} catch (e) {
|
||||
printV("Error parsing error response: $e");
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
printV('Moon Pay: Error fetching buy quote: $e');
|
||||
printV("Exception while fetching buy quote: $e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -244,20 +298,21 @@ class MoonPayProvider extends BuyProvider {
|
||||
required String cryptoCurrencyAddress,
|
||||
String? countryCode}) async {
|
||||
final Map<String, String> params = {
|
||||
'theme': themeToMoonPayTheme(_appStore.themeStore.currentTheme),
|
||||
'language': _appStore.settingsStore.languageCode,
|
||||
'colorCode': _appStore.themeStore.currentTheme.isDark
|
||||
"theme": _appStore.themeStore.currentTheme.type.name,
|
||||
"language": _appStore.settingsStore.languageCode,
|
||||
"colorCode": _appStore.themeStore.currentTheme.isDark
|
||||
? '#${Palette.blueCraiola.value.toRadixString(16).substring(2, 8)}'
|
||||
: '#${Palette.moderateSlateBlue.value.toRadixString(16).substring(2, 8)}',
|
||||
'baseCurrencyCode': isBuyAction ? quote.fiatCurrency.name : quote.cryptoCurrency.name,
|
||||
'baseCurrencyAmount': amount.toStringAsFixed(2),
|
||||
'walletAddress': cryptoCurrencyAddress,
|
||||
'lockAmount': 'false',
|
||||
'showAllCurrencies': 'false',
|
||||
'showWalletAddressForm': 'false',
|
||||
"baseCurrencyCode": isBuyAction ? quote.fiatCurrency.name : quote.cryptoCurrency.name,
|
||||
"baseCurrencyAmount": amount.toStringAsFixed(2),
|
||||
"walletAddress": cryptoCurrencyAddress,
|
||||
"lockAmount": "false",
|
||||
"showAllCurrencies": "false",
|
||||
"showWalletAddressForm": "false",
|
||||
if (isBuyAction)
|
||||
'enabledPaymentMethods': normalizePaymentMethod(quote.paymentType) ??
|
||||
'credit_debit_card,apple_pay,google_pay,samsung_pay,sepa_bank_transfer,gbp_bank_transfer,gbp_open_banking_payment',
|
||||
"enabledPaymentMethods": normalizePaymentMethod(quote.paymentType) ??
|
||||
quote.customPaymentMethodType ??
|
||||
"credit_debit_card,apple_pay,google_pay,samsung_pay,sepa_bank_transfer,gbp_bank_transfer,gbp_open_banking_payment",
|
||||
if (!isBuyAction) 'refundWalletAddress': cryptoCurrencyAddress
|
||||
};
|
||||
|
||||
@@ -274,20 +329,18 @@ class MoonPayProvider extends BuyProvider {
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
} else {
|
||||
throw Exception('Could not launch URL');
|
||||
throw Exception("Could not launch URL");
|
||||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: 'MoonPay',
|
||||
alertContent: 'The MoonPay service is currently unavailable: $e',
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
},
|
||||
builder: (BuildContext context) => AlertWithOneAction(
|
||||
alertTitle: 'MoonPay',
|
||||
alertContent: 'The MoonPay service is currently unavailable: $e',
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -347,76 +400,4 @@ class MoonPayProvider extends BuyProvider {
|
||||
|
||||
return currency.toString().toLowerCase();
|
||||
}
|
||||
|
||||
String? normalizePaymentMethod(PaymentType paymentMethod) {
|
||||
switch (paymentMethod) {
|
||||
case PaymentType.creditCard:
|
||||
return 'credit_debit_card';
|
||||
case PaymentType.debitCard:
|
||||
return 'credit_debit_card';
|
||||
case PaymentType.ach:
|
||||
return 'ach_bank_transfer';
|
||||
case PaymentType.applePay:
|
||||
return 'apple_pay';
|
||||
case PaymentType.googlePay:
|
||||
return 'google_pay';
|
||||
case PaymentType.sepa:
|
||||
return 'sepa_bank_transfer';
|
||||
case PaymentType.paypal:
|
||||
return 'paypal';
|
||||
case PaymentType.sepaOpenBankingPayment:
|
||||
return 'sepa_open_banking_payment';
|
||||
case PaymentType.gbpOpenBankingPayment:
|
||||
return 'gbp_open_banking_payment';
|
||||
case PaymentType.lowCostAch:
|
||||
return 'low_cost_ach';
|
||||
case PaymentType.mobileWallet:
|
||||
return 'mobile_wallet';
|
||||
case PaymentType.pixInstantPayment:
|
||||
return 'pix_instant_payment';
|
||||
case PaymentType.yellowCardBankTransfer:
|
||||
return 'yellow_card_bank_transfer';
|
||||
case PaymentType.fiatBalance:
|
||||
return 'fiat_balance';
|
||||
case PaymentType.revolutPay:
|
||||
return 'revolut_pay';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
PaymentType _getPaymentTypeByString(String? paymentMethod) {
|
||||
switch (paymentMethod) {
|
||||
case 'ach_bank_transfer':
|
||||
return PaymentType.ach;
|
||||
case 'apple_pay':
|
||||
return PaymentType.applePay;
|
||||
case 'credit_debit_card':
|
||||
return PaymentType.creditCard;
|
||||
case 'fiat_balance':
|
||||
return PaymentType.fiatBalance;
|
||||
case 'gbp_open_banking_payment':
|
||||
return PaymentType.gbpOpenBankingPayment;
|
||||
case 'google_pay':
|
||||
return PaymentType.googlePay;
|
||||
case 'low_cost_ach':
|
||||
return PaymentType.lowCostAch;
|
||||
case 'mobile_wallet':
|
||||
return PaymentType.mobileWallet;
|
||||
case 'paypal':
|
||||
return PaymentType.paypal;
|
||||
case 'pix_instant_payment':
|
||||
return PaymentType.pixInstantPayment;
|
||||
case 'sepa_bank_transfer':
|
||||
return PaymentType.sepa;
|
||||
case 'sepa_open_banking_payment':
|
||||
return PaymentType.sepaOpenBankingPayment;
|
||||
case 'yellow_card_bank_transfer':
|
||||
return PaymentType.yellowCardBankTransfer;
|
||||
case 'revolut_pay':
|
||||
return PaymentType.revolutPay;
|
||||
default:
|
||||
return PaymentType.unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import "package:cake_wallet/buy/moonpay/moonpay_payment_methods.dart";
|
||||
import 'package:cake_wallet/core/selectable_option.dart';
|
||||
import "package:cake_wallet/entities/calculate_fiat_amount.dart";
|
||||
|
||||
enum PaymentType {
|
||||
all,
|
||||
@@ -33,6 +35,8 @@ enum PaymentType {
|
||||
fiatBalance,
|
||||
bancontact,
|
||||
pixPay,
|
||||
moonpayCashApp,
|
||||
moonpayBalance,
|
||||
unknown,
|
||||
}
|
||||
|
||||
@@ -103,6 +107,10 @@ extension PaymentTypeTitle on PaymentType {
|
||||
return 'Bancontact';
|
||||
case PaymentType.pixPay:
|
||||
return 'PIX Pay';
|
||||
case PaymentType.moonpayCashApp:
|
||||
return 'Cash App';
|
||||
case PaymentType.moonpayBalance:
|
||||
return 'MoonPay Balance';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -175,6 +183,8 @@ class PaymentMethod extends SelectableOption {
|
||||
required this.customIconPath,
|
||||
this.customDescription,
|
||||
this.customPaymentMethodType,
|
||||
this.customBadges = const [],
|
||||
this.customBottomLeftSubTitle,
|
||||
}) : super(title: paymentMethodType.title ?? customTitle);
|
||||
|
||||
final PaymentType paymentMethodType;
|
||||
@@ -182,8 +192,16 @@ class PaymentMethod extends SelectableOption {
|
||||
final String customIconPath;
|
||||
final String? customDescription;
|
||||
final String? customPaymentMethodType;
|
||||
final List<String> customBadges;
|
||||
final String? customBottomLeftSubTitle;
|
||||
bool isSelected = false;
|
||||
|
||||
@override
|
||||
List<String> get badges => customBadges;
|
||||
|
||||
@override
|
||||
String? get bottomLeftSubTitle => customBottomLeftSubTitle;
|
||||
|
||||
@override
|
||||
String? get description => paymentMethodType.description ?? customDescription;
|
||||
|
||||
@@ -220,11 +238,25 @@ class PaymentMethod extends SelectableOption {
|
||||
customIconPath: 'assets/new-ui/buy_payment_methods/debit_card.svg');
|
||||
}
|
||||
|
||||
factory PaymentMethod.fromMoonPayJson(Map<String, dynamic> json, PaymentType paymentType) {
|
||||
factory PaymentMethod.fromMoonPayJson(MoonPayPaymentMethod method, PaymentType paymentType) {
|
||||
final List<String> badges = [];
|
||||
String? limitSubtitle;
|
||||
if (paymentType == PaymentType.moonpayCashApp) {
|
||||
badges.add('New');
|
||||
}
|
||||
if (method.limitAmount != null && method.limitCurrencyCode != null) {
|
||||
limitSubtitle =
|
||||
'max: ${formatWithCommas(method.limitAmount.toString())} ${method.limitCurrencyCode!.toUpperCase()}';
|
||||
}
|
||||
|
||||
return PaymentMethod(
|
||||
paymentMethodType: paymentType,
|
||||
customTitle: json['paymentMethod'] as String,
|
||||
customIconPath: 'assets/new-ui/buy_payment_methods/debit_card.svg');
|
||||
paymentMethodType: paymentType,
|
||||
customTitle: method.displayName,
|
||||
customIconPath: method.iconUrl.isNotEmpty ? method.iconUrl : 'assets/images/card.png',
|
||||
customPaymentMethodType: method.type,
|
||||
customBadges: badges,
|
||||
customBottomLeftSubTitle: limitSubtitle,
|
||||
);
|
||||
}
|
||||
|
||||
factory PaymentMethod.fromMeldJson(Map<String, dynamic> json) {
|
||||
|
||||
@@ -9,9 +9,11 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
||||
required this.fullName,
|
||||
required this.emoji,
|
||||
this.decimals = 2,
|
||||
this.isoCountryCode,
|
||||
}) : super(title: symbol, raw: symbol);
|
||||
|
||||
final String countryCode;
|
||||
final String? isoCountryCode;
|
||||
|
||||
@override
|
||||
final String fullName;
|
||||
@@ -25,6 +27,8 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
||||
@override
|
||||
final String symbol;
|
||||
|
||||
String get apiCountryCode => isoCountryCode ?? countryCode.toUpperCase();
|
||||
|
||||
final String emoji;
|
||||
|
||||
static List<FiatCurrency> get all => _all.values.toList();
|
||||
@@ -86,8 +90,13 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
||||
FiatCurrency(symbol: "BGN", countryCode: "bgr", fullName: "Bulgarian Lev", emoji: "🇧🇬");
|
||||
static const brl =
|
||||
FiatCurrency(symbol: "BRL", countryCode: "bra", fullName: "Brazilian Real", emoji: "🇧🇷");
|
||||
static const cad =
|
||||
FiatCurrency(symbol: "CAD", countryCode: "cad", fullName: "Canadian Dollar", emoji: "🇨🇦");
|
||||
static const cad = FiatCurrency(
|
||||
symbol: "CAD",
|
||||
countryCode: "cad",
|
||||
fullName: "Canadian Dollar",
|
||||
emoji: "🇨🇦",
|
||||
isoCountryCode: "CAN",
|
||||
);
|
||||
static const chf =
|
||||
FiatCurrency(symbol: "CHF", countryCode: "che", fullName: "Swiss Franc", emoji: "🇨🇭");
|
||||
static const clp =
|
||||
@@ -96,14 +105,24 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
||||
FiatCurrency(symbol: "CNY", countryCode: "chn", fullName: "Chinese Yuan", emoji: "🇨🇳");
|
||||
static const cop =
|
||||
FiatCurrency(symbol: "COP", countryCode: "col", fullName: "Colombian Peso", emoji: "🇨🇴");
|
||||
static const czk =
|
||||
FiatCurrency(symbol: "CZK", countryCode: "czk", fullName: "Czech Koruna", emoji: "🇨🇿");
|
||||
static const czk = FiatCurrency(
|
||||
symbol: "CZK",
|
||||
countryCode: "czk",
|
||||
fullName: "Czech Koruna",
|
||||
emoji: "🇨🇿",
|
||||
isoCountryCode: "CZE",
|
||||
);
|
||||
static const dkk =
|
||||
FiatCurrency(symbol: "DKK", countryCode: "dnk", fullName: "Danish Krone", emoji: "🇩🇰");
|
||||
static const egp =
|
||||
FiatCurrency(symbol: "EGP", countryCode: "egy", fullName: "Egyptian Pound", emoji: "🇪🇬");
|
||||
static const eur =
|
||||
FiatCurrency(symbol: "EUR", countryCode: "eur", fullName: "Euro", emoji: "🇪🇺");
|
||||
static const eur = FiatCurrency(
|
||||
symbol: "EUR",
|
||||
countryCode: "eur",
|
||||
fullName: "Euro",
|
||||
emoji: "🇪🇺",
|
||||
isoCountryCode: "DEU",
|
||||
);
|
||||
static const gbp =
|
||||
FiatCurrency(symbol: "GBP", countryCode: "gbr", fullName: "Pound Sterling", emoji: "🇬🇧");
|
||||
static const ghs =
|
||||
@@ -184,8 +203,8 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
||||
fullName: "Thai Baht",
|
||||
emoji: "🇹🇭",
|
||||
);
|
||||
static const twd =
|
||||
FiatCurrency(symbol: "TWD", countryCode: "twn", fullName: "New Thaiwan Dollar", emoji: "🇹🇼");
|
||||
static const twd = FiatCurrency(
|
||||
symbol: "TWD", countryCode: "twn", fullName: "New Thaiwan Dollar", emoji: "🇹🇼");
|
||||
static const uah =
|
||||
FiatCurrency(symbol: "UAH", countryCode: "ukr", fullName: "Ukrainian Hryvnia", emoji: "🇺🇦");
|
||||
static const usd = FiatCurrency(
|
||||
@@ -211,6 +230,7 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
||||
countryCode: "saf",
|
||||
fullName: "South African Rand",
|
||||
emoji: "🇿🇦",
|
||||
isoCountryCode: "ZAF",
|
||||
);
|
||||
static const tur =
|
||||
FiatCurrency(symbol: "TRY", countryCode: "tur", fullName: "Turkish Lira", emoji: "🇹🇷");
|
||||
@@ -273,6 +293,8 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
||||
|
||||
static FiatCurrency deserialize({required String raw}) => _all[raw] ?? FiatCurrency.usd;
|
||||
|
||||
static FiatCurrency? tryDeserialize({required String raw}) => _all[raw];
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => other is FiatCurrency && other.raw == raw;
|
||||
|
||||
|
||||
@@ -91,13 +91,15 @@ class BuySellConfirmationPage extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (buySellViewModel.selectedQuote!.paymentType.title?.isNotEmpty ??
|
||||
false)
|
||||
if (buySellViewModel.selectedQuote!.paymentType.title?.isNotEmpty
|
||||
?? buySellViewModel.selectedQuote!.customPaymentMethodType?.isNotEmpty
|
||||
?? false)
|
||||
ListItemRegularRow(
|
||||
showArrow: false,
|
||||
keyValue: "payment method",
|
||||
label: S.of(context).payment_method,
|
||||
trailingText: buySellViewModel.selectedQuote!.paymentType.title,
|
||||
trailingText: buySellViewModel.selectedQuote!.paymentType.title
|
||||
?? buySellViewModel.selectedQuote!.customPaymentMethodType,
|
||||
),
|
||||
ListItemRegularRow(
|
||||
showArrow: false,
|
||||
@@ -105,6 +107,13 @@ class BuySellConfirmationPage extends StatelessWidget {
|
||||
label: S.of(context).rate,
|
||||
trailingText: buySellViewModel.selectedQuote!.topLeftSubTitle,
|
||||
),
|
||||
if (_limitsText != null)
|
||||
ListItemRegularRow(
|
||||
showArrow: false,
|
||||
keyValue: "limits",
|
||||
label: "Limits",
|
||||
trailingText: _limitsText,
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
@@ -127,6 +136,14 @@ class BuySellConfirmationPage extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
|
||||
String? get _limitsText {
|
||||
final limits = buySellViewModel.selectedQuote?.limits;
|
||||
final max = limits?.max;
|
||||
if (limits == null || max == null || max == double.infinity) return null;
|
||||
|
||||
return "${limits.min ?? 0} - $max";
|
||||
}
|
||||
|
||||
String get _pageTitle =>
|
||||
"${buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell} ${buySellViewModel.cryptoCurrency.fullName ?? ""}";
|
||||
|
||||
|
||||
@@ -41,8 +41,7 @@ abstract class SelectOptionsPage extends BasePage {
|
||||
String get title => pageTitle;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return ScrollableWithBottomSection(
|
||||
Widget body(BuildContext context) => ScrollableWithBottomSection(
|
||||
content: BodySelectOptionsPage(
|
||||
currentTheme: currentTheme,
|
||||
items: items,
|
||||
@@ -81,7 +80,6 @@ abstract class SelectOptionsPage extends BasePage {
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BodySelectOptionsPage extends StatefulWidget {
|
||||
@@ -131,8 +129,7 @@ class _BodySelectOptionsPageState extends State<BodySelectOptionsPage> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
Widget build(BuildContext context) => Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 350),
|
||||
child: Column(
|
||||
@@ -191,5 +188,4 @@ class _BodySelectOptionsPageState extends State<BodySelectOptionsPage> {
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,7 +443,9 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
|
||||
for (var methods in result) {
|
||||
for (var method in methods) {
|
||||
final alreadyExists = tempPaymentMethods.any((m) {
|
||||
return m.paymentMethodType == method.paymentMethodType;
|
||||
if (m.paymentMethodType != method.paymentMethodType) return false;
|
||||
if (method.paymentMethodType != PaymentType.unknown) return true;
|
||||
return m.customTitle == method.customTitle;
|
||||
});
|
||||
|
||||
if (!alreadyExists) {
|
||||
|
||||
Reference in New Issue
Block a user