From 6c947cdb63cac08c78acb1c5658764680ef90076 Mon Sep 17 00:00:00 2001 From: M Date: Mon, 20 Jun 2022 20:27:28 +0100 Subject: [PATCH] Add anypay. Add purschase gift card. --- lib/anypay/any_pay_payment.dart | 38 ++++++++++ lib/anypay/any_pay_payment_instruction.dart | 30 ++++++++ .../any_pay_payment_instruction_output.dart | 10 +++ lib/anypay/anypay_api.dart | 48 +++++++++++++ lib/di.dart | 5 +- lib/ionia/ionia_api.dart | 69 +++++++++++++++++++ lib/ionia/ionia_merchant_service.dart | 51 -------------- lib/ionia/ionia_order.dart | 23 +++++++ lib/ionia/{ionia.dart => ionia_service.dart} | 28 ++++++++ lib/view_model/ionia/ionia_view_model.dart | 2 +- 10 files changed, 248 insertions(+), 56 deletions(-) create mode 100644 lib/anypay/any_pay_payment.dart create mode 100644 lib/anypay/any_pay_payment_instruction.dart create mode 100644 lib/anypay/any_pay_payment_instruction_output.dart create mode 100644 lib/anypay/anypay_api.dart delete mode 100644 lib/ionia/ionia_merchant_service.dart create mode 100644 lib/ionia/ionia_order.dart rename lib/ionia/{ionia.dart => ionia_service.dart} (69%) diff --git a/lib/anypay/any_pay_payment.dart b/lib/anypay/any_pay_payment.dart new file mode 100644 index 000000000..4c8dff2d6 --- /dev/null +++ b/lib/anypay/any_pay_payment.dart @@ -0,0 +1,38 @@ +import 'package:flutter/foundation.dart'; +import 'package:cake_wallet/anypay/any_pay_payment_instruction.dart'; + +class AnyPayPayment { + AnyPayPayment({ + @required this.time, + @required this.expires, + @required this.memo, + @required this.paymentUrl, + @required this.paymentId, + @required this.chain, + @required this.network, + @required this.instructions}); + + factory AnyPayPayment.fromMap(Map obj) { + final instructions = (obj['instructions'] as List) + .map((dynamic instruction) => AnyPayPaymentInstruction.fromMap(instruction as Map)) + .toList(); + return AnyPayPayment( + time: DateTime.parse(obj['time'] as String), + expires: DateTime.parse(obj['expires'] as String), + memo: obj['memo'] as String, + paymentUrl: obj['paymentUrl'] as String, + paymentId: obj['paymentId'] as String, + chain: obj['chain'] as String, + network: obj['network'] as String, + instructions: instructions); + } + + final DateTime time; + final DateTime expires; + final String memo; + final String paymentUrl; + final String paymentId; + final String chain; + final String network; + final List instructions; +} \ No newline at end of file diff --git a/lib/anypay/any_pay_payment_instruction.dart b/lib/anypay/any_pay_payment_instruction.dart new file mode 100644 index 000000000..d6ed85527 --- /dev/null +++ b/lib/anypay/any_pay_payment_instruction.dart @@ -0,0 +1,30 @@ +import 'package:flutter/foundation.dart'; +import 'package:cake_wallet/anypay/any_pay_payment_instruction_output.dart'; + +class AnyPayPaymentInstruction { + AnyPayPaymentInstruction({ + @required this.type, + @required this.requiredFeeRate, + @required this.txKey, + @required this.txHash, + @required this.outputs}); + + factory AnyPayPaymentInstruction.fromMap(Map obj) { + final outputs = (obj['outputs'] as List) + .map((dynamic out) => + AnyPayPaymentInstructionOutput.fromMap(out as Map)) + .toList(); + return AnyPayPaymentInstruction( + type: obj['type'] as String, + requiredFeeRate: obj['requiredFeeRate'] as int, + txKey: obj['tx_key'] as bool, + txHash: obj['tx_hash'] as bool, + outputs: outputs); + } + + final String type; + final int requiredFeeRate; + final bool txKey; + final bool txHash; + final List outputs; +} \ No newline at end of file diff --git a/lib/anypay/any_pay_payment_instruction_output.dart b/lib/anypay/any_pay_payment_instruction_output.dart new file mode 100644 index 000000000..7fabea966 --- /dev/null +++ b/lib/anypay/any_pay_payment_instruction_output.dart @@ -0,0 +1,10 @@ +class AnyPayPaymentInstructionOutput { + const AnyPayPaymentInstructionOutput(this.address, this.amount); + + factory AnyPayPaymentInstructionOutput.fromMap(Map obj) { + return AnyPayPaymentInstructionOutput(obj['address'] as String, obj['amount'] as int); + } + + final String address; + final int amount; +} \ No newline at end of file diff --git a/lib/anypay/anypay_api.dart b/lib/anypay/anypay_api.dart new file mode 100644 index 000000000..e7583b083 --- /dev/null +++ b/lib/anypay/anypay_api.dart @@ -0,0 +1,48 @@ +import 'dart:convert'; +import 'package:http/http.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cake_wallet/anypay/any_pay_payment.dart'; + +class AnyPayApi { + static const contentTypePaymentRequest = 'application/payment-request'; + static const xPayproVersion = '2'; + + static String chainByScheme(String scheme) { + switch (scheme.toLowerCase()) { + case 'monero': + return CryptoCurrency.xmr.title; + default: + return ''; + } + } + + static CryptoCurrency currencyByScheme(String scheme) { + switch (scheme.toLowerCase()) { + case 'monero': + return CryptoCurrency.xmr; + default: + return null; + } + } + + Future pay(String uri) async { + final fragments = uri.split(':?r='); + final scheme = fragments.first; + final url = fragments[1]; + final headers = { + 'Content-Type': contentTypePaymentRequest, + 'X-Paypro-Version': xPayproVersion, + 'Accept': '*/*',}; + final body = { + 'chain': chainByScheme(scheme), + 'currency': currencyByScheme(scheme).title}; + final response = await post(url, headers: headers, body: utf8.encode(json.encode(body))); + + if (response.statusCode != 200) { + return null; + } + + final decodedBody = json.decode(response.body) as Map; + return AnyPayPayment.fromMap(decodedBody); + } +} \ No newline at end of file diff --git a/lib/di.dart b/lib/di.dart index f6afdeb3a..8805a0162 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/core/yat_service.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/entities/wake_lock.dart'; -import 'package:cake_wallet/ionia/ionia.dart'; +import 'package:cake_wallet/ionia/ionia_service.dart'; import 'package:cake_wallet/ionia/ionia_api.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/haven/haven.dart'; @@ -129,7 +129,6 @@ import 'package:cake_wallet/entities/template.dart'; import 'package:cake_wallet/exchange/exchange_template.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/src/screens/dashboard/widgets/address_page.dart'; -import 'package:cake_wallet/ionia/ionia_merchant_service.dart'; import 'package:cake_wallet/ionia/ionia_token_service.dart'; final getIt = GetIt.instance; @@ -225,8 +224,6 @@ Future setup( sharedPreferences: getIt.get())); getIt.registerFactory(() => IoniaTokenService(getIt.get())); - - getIt.registerFactory(() => IoniaMerchantService(getIt.get(), isDevEnv: true)); getIt.registerFactoryParam((type, _) => WalletNewVM(getIt.get(), diff --git a/lib/ionia/ionia_api.dart b/lib/ionia/ionia_api.dart index 10169c779..c1a90e6ca 100644 --- a/lib/ionia/ionia_api.dart +++ b/lib/ionia/ionia_api.dart @@ -1,4 +1,6 @@ import 'dart:convert'; +import 'package:cake_wallet/ionia/ionia_merchant.dart'; +import 'package:cake_wallet/ionia/ionia_order.dart'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart'; import 'package:cake_wallet/ionia/ionia_user_credentials.dart'; @@ -11,6 +13,8 @@ class IoniaApi { static final verifyEmailUri = Uri.https(baseUri, '/$pathPrefix/VerifyEmail'); static final createCardUri = Uri.https(baseUri, '/$pathPrefix/CreateCard'); static final getCardsUri = Uri.https(baseUri, '/$pathPrefix/GetCards'); + static final getMerchantsUrl = Uri.https(baseUri, '/$pathPrefix/GetMerchants'); + static final getPurchaseMerchantsUrl = Uri.https(baseUri, '/$pathPrefix/PurchaseGiftCard'); // Create user @@ -122,4 +126,69 @@ class IoniaApi { return IoniaVirtualCard.fromMap(data); } + + // Get Merchants + + Future> getMerchants({ + @required String username, + @required String password, + @required String clientId}) async { + final headers = { + 'clientId': clientId, + 'username': username, + 'password': password}; + final response = await post(getMerchantsUrl, headers: headers); + + if (response.statusCode != 200) { + return []; + } + + final decodedBody = json.decode(response.body) as Map; + final isSuccessful = decodedBody['Successful'] as bool ?? false; + + if (!isSuccessful) { + return []; + } + + final data = decodedBody['Data'] as List; + return data.map((dynamic e) { + final element = e as Map; + return IoniaMerchant.fromJsonMap(element); + }).toList(); + } + + // Purchase Gift Card + + Future purchaseGiftCard({ + @required String merchId, + @required double amount, + @required String currency, + @required String username, + @required String password, + @required String clientId}) async { + final headers = { + 'clientId': clientId, + 'username': username, + 'password': password, + 'Content-Type': 'application/json'}; + final body = { + 'Amount': amount, + 'Currency': currency, + 'MerchantId': merchId}; + final response = await post(getPurchaseMerchantsUrl, headers: headers, body: json.encode(body)); + + if (response.statusCode != 200) { + return null; + } + + final decodedBody = json.decode(response.body) as Map; + final isSuccessful = decodedBody['Successful'] as bool ?? false; + + if (!isSuccessful) { + return null; + } + + final data = decodedBody['Data'] as Map; + return IoniaOrder.fromMap(data); + } } \ No newline at end of file diff --git a/lib/ionia/ionia_merchant_service.dart b/lib/ionia/ionia_merchant_service.dart deleted file mode 100644 index 75bb4633d..000000000 --- a/lib/ionia/ionia_merchant_service.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'dart:convert'; -import 'package:http/http.dart'; -import 'package:flutter/foundation.dart'; -import 'package:cake_wallet/ionia/ionia_merchant.dart'; -import 'package:cake_wallet/ionia/ionia_token_service.dart'; - -class IoniaMerchantService { - IoniaMerchantService(this._tokenService, {@required this.isDevEnv}); - - static String devApiUrl = "https://apidev.dashdirect.org/partner"; - - final bool isDevEnv; - - final TokenService _tokenService; - - String get apiUrl => isDevEnv ? devApiUrl : ''; - - String get getMerchantsUrl => '$apiUrl/GetMerchants'; - - Future> getMerchants() async { - final token = await _tokenService.getToken(); - // FIX ME: remove hardcoded values - final headers = { - 'Authorization': token.toString(), - 'firstName': 'cake', - 'lastName': 'cake', - 'email': 'cake@test'}; - final response = await post(getMerchantsUrl, headers: headers); - - if (response.statusCode != 200) { - return []; - } - - final decodedBody = json.decode(response.body) as Map; - final isSuccessful = decodedBody['Successful'] as bool ?? false; - - if (!isSuccessful) { - return []; - } - - final data = decodedBody['Data'] as List; - return data.map((dynamic e) { - final element = e as Map; - return IoniaMerchant.fromJsonMap(element); - }).toList(); - } - - Future purchaseGiftCard() async { - - } -} \ No newline at end of file diff --git a/lib/ionia/ionia_order.dart b/lib/ionia/ionia_order.dart new file mode 100644 index 000000000..691327e43 --- /dev/null +++ b/lib/ionia/ionia_order.dart @@ -0,0 +1,23 @@ +import 'package:flutter/foundation.dart'; + +class IoniaOrder { + IoniaOrder({@required this.id, + @required this.uri, + @required this.currency, + @required this.amount, + @required this.paymentId}); + factory IoniaOrder.fromMap(Map obj) { + return IoniaOrder( + id: obj['order_id'] as String, + uri: obj['uri'] as String, + currency: obj['currency'] as String, + amount: obj['amount'] as double, + paymentId: obj['payment_id'] as String); + } + + final String id; + final String uri; + final String currency; + final double amount; + final String paymentId; +} \ No newline at end of file diff --git a/lib/ionia/ionia.dart b/lib/ionia/ionia_service.dart similarity index 69% rename from lib/ionia/ionia.dart rename to lib/ionia/ionia_service.dart index f55af0b17..7f84ff8c8 100644 --- a/lib/ionia/ionia.dart +++ b/lib/ionia/ionia_service.dart @@ -1,4 +1,7 @@ +import 'package:cake_wallet/ionia/ionia_merchant.dart'; +import 'package:cake_wallet/ionia/ionia_order.dart'; import 'package:cake_wallet/ionia/ionia_virtual_card.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/ionia/ionia_api.dart'; @@ -59,4 +62,29 @@ class IoniaService { final password = await secureStorage.read(key: ioniaPasswordStorageKey); return ioniaApi.getCards(username: username, password: password, clientId: clientId); } + + // Get Merchants + + Future> getMerchants() async { + final username = await secureStorage.read(key: ioniaUsernameStorageKey); + final password = await secureStorage.read(key: ioniaPasswordStorageKey); + return ioniaApi.getMerchants(username: username, password: password, clientId: clientId); + } + + // Purchase Gift Card + + Future purchaseGiftCard({ + @required String merchId, + @required double amount, + @required String currency}) async { + final username = await secureStorage.read(key: ioniaUsernameStorageKey); + final password = await secureStorage.read(key: ioniaPasswordStorageKey); + return ioniaApi.purchaseGiftCard( + merchId: merchId, + amount: amount, + currency: currency, + username: username, + password: password, + clientId: clientId); + } } \ No newline at end of file diff --git a/lib/view_model/ionia/ionia_view_model.dart b/lib/view_model/ionia/ionia_view_model.dart index 6aa324db3..b07c42440 100644 --- a/lib/view_model/ionia/ionia_view_model.dart +++ b/lib/view_model/ionia/ionia_view_model.dart @@ -1,4 +1,4 @@ -import 'package:cake_wallet/ionia/ionia.dart'; +import 'package:cake_wallet/ionia/ionia_service.dart'; import 'package:cake_wallet/ionia/ionia_create_state.dart'; import 'package:cake_wallet/ionia/ionia_virtual_card.dart'; import 'package:mobx/mobx.dart';