mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 20:39:51 +00:00
Cw 935 get exchange payment method recommendations from on ramper (#2235)
* fix: default to recommended payment method in Onramper * fix: support displaying unknown payment methods * feat: fetch recommended Onramper payment type
This commit is contained in:
parent
66efce4d70
commit
557e1c9839
10 changed files with 89 additions and 21 deletions
|
@ -64,6 +64,7 @@ abstract class BuyProvider {
|
|||
required bool isBuyAction,
|
||||
required String walletAddress,
|
||||
PaymentType? paymentType,
|
||||
String? customPaymentMethodType,
|
||||
String? countryCode}) async =>
|
||||
null;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ class Quote extends SelectableOption {
|
|||
this.rampName,
|
||||
this.rampIconPath,
|
||||
this.limits,
|
||||
this.customPaymentMethodType,
|
||||
}) : super(title: provider.isAggregator ? rampName ?? '' : provider.title);
|
||||
|
||||
final double rate;
|
||||
|
@ -68,6 +69,7 @@ class Quote extends SelectableOption {
|
|||
bool _isBestRate = false;
|
||||
bool isBuyAction;
|
||||
Limits? limits;
|
||||
String? customPaymentMethodType;
|
||||
|
||||
late FiatCurrency _fiatCurrency;
|
||||
late CryptoCurrency _cryptoCurrency;
|
||||
|
@ -130,7 +132,7 @@ class Quote extends SelectableOption {
|
|||
set setLimits(Limits limits) => this.limits = limits;
|
||||
|
||||
factory Quote.fromOnramperJson(Map<String, dynamic> json, bool isBuyAction,
|
||||
Map<String, dynamic> metaData, PaymentType paymentType) {
|
||||
Map<String, dynamic> metaData, PaymentType paymentType, String? customPaymentMethodType) {
|
||||
final rate = _toDouble(json['rate']) ?? 0.0;
|
||||
final networkFee = _toDouble(json['networkFee']) ?? 0.0;
|
||||
final transactionFee = _toDouble(json['transactionFee']) ?? 0.0;
|
||||
|
@ -183,6 +185,7 @@ class Quote extends SelectableOption {
|
|||
rampName: rampName,
|
||||
rampIconPath: rampIconPath,
|
||||
paymentType: paymentType,
|
||||
customPaymentMethodType: customPaymentMethodType,
|
||||
quoteId: json['quoteId'] as String? ?? '',
|
||||
recommendations: enumRecommendations,
|
||||
provider: ProvidersHelper.getProviderByType(ProviderType.onramper)!,
|
||||
|
|
|
@ -231,6 +231,7 @@ class DFXBuyProvider extends BuyProvider {
|
|||
required bool isBuyAction,
|
||||
required String walletAddress,
|
||||
PaymentType? paymentType,
|
||||
String? customPaymentMethodType,
|
||||
String? countryCode}) async {
|
||||
/// if buying with any currency other than eur or chf then DFX is not supported
|
||||
|
||||
|
@ -373,7 +374,7 @@ class DFXBuyProvider extends BuyProvider {
|
|||
case 'Instant':
|
||||
return PaymentType.sepa;
|
||||
default:
|
||||
return PaymentType.all;
|
||||
return PaymentType.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,6 +113,7 @@ class KryptonimBuyProvider extends BuyProvider {
|
|||
required bool isBuyAction,
|
||||
required String walletAddress,
|
||||
PaymentType? paymentType,
|
||||
String? customPaymentMethodType,
|
||||
String? countryCode,
|
||||
}) async {
|
||||
log('Kryptonim: Fetching quote: ${isBuyAction ? cryptoCurrency : fiatCurrency} -> ${isBuyAction ? fiatCurrency : cryptoCurrency}, amount: $amount');
|
||||
|
@ -149,7 +150,7 @@ class KryptonimBuyProvider extends BuyProvider {
|
|||
|
||||
final selectedPaymentType =
|
||||
PaymentMethod.getPaymentTypeId(selectedPaymentMethod['payment_method'] as String?);
|
||||
final quote = Quote.fromKryptonimJson(selectedPaymentMethod, isBuyAction, selectedPaymentType);
|
||||
final quote = Quote.fromKryptonimJson(selectedPaymentMethod, isBuyAction, selectedPaymentType ?? PaymentType.unknown);
|
||||
|
||||
quote.setFiatCurrency = fiatCurrency;
|
||||
quote.setCryptoCurrency = cryptoCurrency;
|
||||
|
|
|
@ -104,6 +104,7 @@ class MeldBuyProvider extends BuyProvider {
|
|||
required bool isBuyAction,
|
||||
required String walletAddress,
|
||||
PaymentType? paymentType,
|
||||
String? customPaymentMethodType,
|
||||
String? countryCode}) async {
|
||||
String? paymentMethod;
|
||||
if (paymentType != null && paymentType != PaymentType.all) {
|
||||
|
|
|
@ -162,6 +162,7 @@ class MoonPayProvider extends BuyProvider {
|
|||
required bool isBuyAction,
|
||||
required String walletAddress,
|
||||
PaymentType? paymentType,
|
||||
String? customPaymentMethodType,
|
||||
String? countryCode}) async {
|
||||
String? paymentMethod;
|
||||
|
||||
|
@ -410,7 +411,7 @@ class MoonPayProvider extends BuyProvider {
|
|||
case 'yellow_card_bank_transfer':
|
||||
return PaymentType.yellowCardBankTransfer;
|
||||
default:
|
||||
return PaymentType.all;
|
||||
return PaymentType.unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ class OnRamperBuyProvider extends BuyProvider {
|
|||
static const quotes = '/quotes';
|
||||
static const paymentTypes = '/payment-types';
|
||||
static const supported = '/supported';
|
||||
static const defaultsAll = '/defaults/all';
|
||||
|
||||
static const List<CryptoCurrency> _notSupportedCrypto = [];
|
||||
static const List<FiatCurrency> _notSupportedFiat = [];
|
||||
|
@ -40,6 +41,8 @@ class OnRamperBuyProvider extends BuyProvider {
|
|||
|
||||
final SettingsStore _settingsStore;
|
||||
|
||||
String? recommendedPaymentType;
|
||||
|
||||
String get _apiKey => secrets.onramperApiKey;
|
||||
|
||||
@override
|
||||
|
@ -57,6 +60,34 @@ class OnRamperBuyProvider extends BuyProvider {
|
|||
@override
|
||||
bool get isAggregator => true;
|
||||
|
||||
Future<String?> getRecommendedPaymentType(bool isBuyAction) async {
|
||||
|
||||
final params = {'type': isBuyAction ? 'buy' : 'sell'};
|
||||
|
||||
final url = Uri.https(_baseApiUrl, '$supported$defaultsAll', params);
|
||||
|
||||
try {
|
||||
final response =
|
||||
await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
final recommended = data['message']['recommended'] as Map<String, dynamic>;
|
||||
|
||||
final recommendedPaymentType = recommended['paymentMethod'] as String?;
|
||||
|
||||
return recommendedPaymentType ;
|
||||
} else {
|
||||
final responseBody =
|
||||
jsonDecode(response.body) as Map<String, dynamic>;
|
||||
printV('Failed to fetch available payment types: ${responseBody['message']}');
|
||||
}
|
||||
} catch (e) {
|
||||
printV('Failed to fetch available payment types: $e');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<List<PaymentMethod>> getAvailablePaymentTypes(
|
||||
String fiatCurrency, CryptoCurrency cryptoCurrency, bool isBuyAction) async {
|
||||
|
||||
|
@ -77,9 +108,14 @@ class OnRamperBuyProvider extends BuyProvider {
|
|||
if (response.statusCode == 200) {
|
||||
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
final List<dynamic> message = data['message'] as List<dynamic>;
|
||||
return message
|
||||
|
||||
final allAvailablePaymentMethods = message
|
||||
.map((item) => PaymentMethod.fromOnramperJson(item as Map<String, dynamic>))
|
||||
.toList();
|
||||
|
||||
recommendedPaymentType = await getRecommendedPaymentType(isBuyAction);
|
||||
|
||||
return allAvailablePaymentMethods;
|
||||
} else {
|
||||
final responseBody =
|
||||
jsonDecode(response.body) as Map<String, dynamic>;
|
||||
|
@ -131,13 +167,13 @@ class OnRamperBuyProvider extends BuyProvider {
|
|||
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;
|
||||
}
|
||||
if (paymentType == PaymentType.all && recommendedPaymentType != null) paymentMethod = recommendedPaymentType!;
|
||||
else if (paymentType == PaymentType.unknown) paymentMethod = customPaymentMethodType;
|
||||
else if (paymentType != null) paymentMethod = normalizePaymentMethod(paymentType);
|
||||
|
||||
final actionType = isBuyAction ? 'buy' : 'sell';
|
||||
|
||||
|
@ -182,7 +218,7 @@ class OnRamperBuyProvider extends BuyProvider {
|
|||
if (rampMetaData == null) continue;
|
||||
|
||||
final quote = Quote.fromOnramperJson(
|
||||
item, isBuyAction, _onrampMetadata, _getPaymentTypeByString(paymentMethod));
|
||||
item, isBuyAction, _onrampMetadata, _getPaymentTypeByString(paymentMethod), customPaymentMethodType);
|
||||
quote.setFiatCurrency = fiatCurrency;
|
||||
quote.setCryptoCurrency = cryptoCurrency;
|
||||
validQuotes.add(quote);
|
||||
|
@ -225,7 +261,7 @@ class OnRamperBuyProvider extends BuyProvider {
|
|||
final defaultCrypto =
|
||||
quote.cryptoCurrency.title + _getNormalizeNetwork(quote.cryptoCurrency).toLowerCase();
|
||||
|
||||
final paymentMethod = normalizePaymentMethod(quote.paymentType);
|
||||
final paymentMethod = quote.paymentType == PaymentType.unknown ? quote.customPaymentMethodType : normalizePaymentMethod(quote.paymentType);
|
||||
|
||||
final uri = Uri.https(_baseUrl, '', {
|
||||
'apiKey': _apiKey,
|
||||
|
@ -330,6 +366,8 @@ class OnRamperBuyProvider extends BuyProvider {
|
|||
return 'dana';
|
||||
case PaymentType.ideal:
|
||||
return 'ideal';
|
||||
case PaymentType.pixPay:
|
||||
return 'pix';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@ -379,8 +417,10 @@ class OnRamperBuyProvider extends BuyProvider {
|
|||
return PaymentType.dana;
|
||||
case 'ideal':
|
||||
return PaymentType.ideal;
|
||||
case 'pix':
|
||||
return PaymentType.pixPay;
|
||||
default:
|
||||
return PaymentType.all;
|
||||
return PaymentType.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ enum PaymentType {
|
|||
yellowCardBankTransfer,
|
||||
fiatBalance,
|
||||
bancontact,
|
||||
pixPay,
|
||||
unknown,
|
||||
}
|
||||
|
||||
extension PaymentTypeTitle on PaymentType {
|
||||
|
@ -101,6 +103,8 @@ extension PaymentTypeTitle on PaymentType {
|
|||
return 'Fiat Balance';
|
||||
case PaymentType.bancontact:
|
||||
return 'Bancontact';
|
||||
case PaymentType.pixPay:
|
||||
return 'PIX Pay';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@ -158,12 +162,14 @@ class PaymentMethod extends SelectableOption {
|
|||
required this.customTitle,
|
||||
required this.customIconPath,
|
||||
this.customDescription,
|
||||
this.customPaymentMethodType,
|
||||
}) : super(title: paymentMethodType.title ?? customTitle);
|
||||
|
||||
final PaymentType paymentMethodType;
|
||||
final String customTitle;
|
||||
final String customIconPath;
|
||||
final String? customDescription;
|
||||
final String? customPaymentMethodType;
|
||||
bool isSelected = false;
|
||||
|
||||
@override
|
||||
|
@ -188,7 +194,8 @@ class PaymentMethod extends SelectableOption {
|
|||
factory PaymentMethod.fromOnramperJson(Map<String, dynamic> json) {
|
||||
final type = PaymentMethod.getPaymentTypeId(json['paymentTypeId'] as String?);
|
||||
return PaymentMethod(
|
||||
paymentMethodType: type,
|
||||
paymentMethodType: type ?? PaymentType.unknown,
|
||||
customPaymentMethodType: json['paymentTypeId'] as String?,
|
||||
customTitle: json['name'] as String? ?? 'Unknown',
|
||||
customIconPath: json['icon'] as String? ?? 'assets/images/card.png',
|
||||
customDescription: json['description'] as String?);
|
||||
|
@ -212,7 +219,7 @@ class PaymentMethod extends SelectableOption {
|
|||
final type = PaymentMethod.getPaymentTypeId(json['paymentMethod'] as String?);
|
||||
final logos = json['logos'] as Map<String, dynamic>;
|
||||
return PaymentMethod(
|
||||
paymentMethodType: type,
|
||||
paymentMethodType: type ?? PaymentType.unknown,
|
||||
customTitle: json['name'] as String? ?? 'Unknown',
|
||||
customIconPath: logos['dark'] as String? ?? 'assets/images/card.png',
|
||||
customDescription: json['description'] as String?);
|
||||
|
@ -221,13 +228,13 @@ class PaymentMethod extends SelectableOption {
|
|||
factory PaymentMethod.fromKryptonimJson(Map<String, dynamic> json) {
|
||||
final type = PaymentMethod.getPaymentTypeId(json['payment_method'] as String?);
|
||||
return PaymentMethod(
|
||||
paymentMethodType: type,
|
||||
paymentMethodType: type ?? PaymentType.unknown,
|
||||
customTitle: json['payment_method'] as String? ?? 'Unknown',
|
||||
customIconPath: 'assets/images/card.png',
|
||||
);
|
||||
}
|
||||
|
||||
static PaymentType getPaymentTypeId(String? type) {
|
||||
static PaymentType? getPaymentTypeId(String? type) {
|
||||
switch (type?.toLowerCase()) {
|
||||
case 'banktransfer':
|
||||
case 'bank':
|
||||
|
@ -289,8 +296,10 @@ class PaymentMethod extends SelectableOption {
|
|||
return PaymentType.sepaOpenBankingPayment;
|
||||
case 'bancontact':
|
||||
return PaymentType.bancontact;
|
||||
case 'pix':
|
||||
return PaymentType.pixPay;
|
||||
default:
|
||||
return PaymentType.all;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -192,6 +192,7 @@ class RobinhoodBuyProvider extends BuyProvider {
|
|||
required bool isBuyAction,
|
||||
required String walletAddress,
|
||||
PaymentType? paymentType,
|
||||
String? customPaymentMethodType,
|
||||
String? countryCode}) async {
|
||||
String? paymentMethod;
|
||||
|
||||
|
@ -267,7 +268,7 @@ class RobinhoodBuyProvider extends BuyProvider {
|
|||
case 'bank_transfer':
|
||||
return PaymentType.bankTransfer;
|
||||
default:
|
||||
return PaymentType.all;
|
||||
return PaymentType.unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -359,14 +359,23 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
|
|||
onTimeout: () => [],
|
||||
)));
|
||||
|
||||
final Map<PaymentType, PaymentMethod> uniquePaymentMethods = {};
|
||||
final List<PaymentMethod> tempPaymentMethods = [];
|
||||
|
||||
for (var methods in result) {
|
||||
for (var method in methods) {
|
||||
uniquePaymentMethods[method.paymentMethodType] = method;
|
||||
final alreadyExists = tempPaymentMethods.any((m) {
|
||||
return m.paymentMethodType == method.paymentMethodType &&
|
||||
m.customTitle == method.customTitle;
|
||||
});
|
||||
|
||||
if (!alreadyExists) {
|
||||
tempPaymentMethods.add(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paymentMethods = ObservableList<PaymentMethod>.of(uniquePaymentMethods.values);
|
||||
paymentMethods = ObservableList<PaymentMethod>.of(tempPaymentMethods);
|
||||
|
||||
if (paymentMethods.isNotEmpty) {
|
||||
paymentMethods.insert(0, PaymentMethod.all());
|
||||
selectedPaymentMethod = paymentMethods.first;
|
||||
|
@ -404,6 +413,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
|
|||
paymentType: selectedPaymentMethod?.paymentMethodType,
|
||||
isBuyAction: isBuyAction,
|
||||
walletAddress: wallet.walletAddresses.address,
|
||||
customPaymentMethodType: selectedPaymentMethod?.customPaymentMethodType,
|
||||
)
|
||||
.timeout(
|
||||
Duration(seconds: 10),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue