From 949f839a13bb59b4cddd1e9732d1d5a37e147f2f Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 25 Dec 2024 09:59:58 +0200 Subject: [PATCH 01/24] WIP: support kyc for each wallet --- app/lib/helpers/kyc_helpers.dart | 11 +++++--- app/lib/models/wallet.dart | 2 ++ .../screens/identity_verification_screen.dart | 7 +++-- app/lib/screens/recover_screen.dart | 8 ++++-- app/lib/screens/wallets/wallet_screen.dart | 1 + .../services/shared_preference_service.dart | 5 +++- app/lib/services/wallet_service.dart | 6 +++++ app/lib/widgets/wallets/wallet_card.dart | 26 +++++++++++++++---- 8 files changed, 53 insertions(+), 13 deletions(-) diff --git a/app/lib/helpers/kyc_helpers.dart b/app/lib/helpers/kyc_helpers.dart index 2d578971..1fd3b533 100644 --- a/app/lib/helpers/kyc_helpers.dart +++ b/app/lib/helpers/kyc_helpers.dart @@ -2,12 +2,14 @@ import 'dart:convert'; import 'package:flutter_pkid/flutter_pkid.dart'; import 'package:threebotlogin/models/idenfy.dart'; +import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/services/idenfy_service.dart'; import 'package:threebotlogin/services/migration_service.dart'; import 'package:threebotlogin/services/pkid_service.dart'; import 'package:threebotlogin/services/tfchain_service.dart'; import 'package:threebotlogin/services/tools_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; +import 'package:threebotlogin/services/wallet_service.dart'; import 'globals.dart'; @@ -15,6 +17,9 @@ Future fetchPKidData() async { FlutterPkid client = await getPkidClient(); List keyWords = ['email', 'phone']; + final wallets = (await getPkidWallets()) + .where((w) => w.type == WalletType.NATIVE) + .toList(); var futures = keyWords.map((keyword) async { var pKidResult = await client.getPKidDoc(keyword); @@ -26,11 +31,11 @@ Future fetchPKidData() async { var pKidResult = await Future.wait(futures); Map dataMap = pKidResult.asMap(); - await handleKYCData(dataMap[0], dataMap[1]); + await handleKYCData(dataMap[0], dataMap[1], wallets.first.seed); } Future handleKYCData( - Map emailData, Map phoneData) async { + Map emailData, Map phoneData, String walletSecret) async { final address = await getMyAddress(); final identityVerificationStatus = await getVerificationStatus(address: address); @@ -71,7 +76,7 @@ Future handleKYCData( final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); final lastName = utf8.decode(latin1.encode(data.orgLastName!)); await saveIdentity('$lastName $firstName', data.docIssuingCountry, - data.docDob, data.docSex, data.idenfyRef); + data.docDob, data.docSex, data.idenfyRef, walletSecret); } } diff --git a/app/lib/models/wallet.dart b/app/lib/models/wallet.dart index f55a1915..098ec135 100644 --- a/app/lib/models/wallet.dart +++ b/app/lib/models/wallet.dart @@ -14,6 +14,7 @@ class Wallet { required this.tfchainAddress, required this.tfchainBalance, required this.type, + required this.verificationStatus, }); String name; final String stellarSecret; @@ -23,6 +24,7 @@ class Wallet { String stellarBalance; String tfchainBalance; final WalletType type; + final String verificationStatus; } class PkidWallet { diff --git a/app/lib/screens/identity_verification_screen.dart b/app/lib/screens/identity_verification_screen.dart index 7c5054a9..aa95553f 100644 --- a/app/lib/screens/identity_verification_screen.dart +++ b/app/lib/screens/identity_verification_screen.dart @@ -71,7 +71,7 @@ class _IdentityVerificationScreenState int emailCountdown = 60; Timer? emailTimer; ValueNotifier countdownNotifier = ValueNotifier(-1); - + void startOrResumeEmailCountdown({bool startNew = false}) { int currentTime = DateTime.now().millisecondsSinceEpoch; int lockedUntil = @@ -687,8 +687,11 @@ class _IdentityVerificationScreenState final data = await getVerificationData(); final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); final lastName = utf8.decode(latin1.encode(data.orgLastName!)); + final wallets = (await getPkidWallets()) + .where((w) => w.type == WalletType.NATIVE) + .toList(); await saveIdentity('$lastName $firstName', data.docIssuingCountry, - data.docDob, data.docSex, data.idenfyRef); + data.docDob, data.docSex, data.idenfyRef, wallets.first.seed ); Events().emit(IdentityCallbackEvent(type: 'success')); } on BadRequest catch (e) { setState(() { diff --git a/app/lib/screens/recover_screen.dart b/app/lib/screens/recover_screen.dart index 220c1ffe..c24a96ef 100644 --- a/app/lib/screens/recover_screen.dart +++ b/app/lib/screens/recover_screen.dart @@ -7,11 +7,13 @@ import 'package:sodium_libs/sodium_libs.dart'; import 'package:http/http.dart'; import 'package:threebotlogin/helpers/kyc_helpers.dart'; import 'package:threebotlogin/helpers/logger.dart'; +import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/services/3bot_service.dart'; import 'package:threebotlogin/services/crypto_service.dart'; import 'package:threebotlogin/services/migration_service.dart'; import 'package:threebotlogin/services/pkid_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; +import 'package:threebotlogin/services/wallet_service.dart'; class RecoverScreen extends StatefulWidget { const RecoverScreen({super.key, this.recoverScreen}); @@ -92,8 +94,10 @@ class _RecoverScreenState extends State { await savePhrase(seedPhrase); await saveFingerprint(false); await saveDoubleName(doubleName); - - await handleKYCData(dataMap[0], dataMap[1]); + final wallets = (await getPkidWallets()) + .where((w) => w.type == WalletType.NATIVE) + .toList(); + await handleKYCData(dataMap[0], dataMap[1], wallets.first.seed); await fixPkidMigration(); } catch (e) { diff --git a/app/lib/screens/wallets/wallet_screen.dart b/app/lib/screens/wallets/wallet_screen.dart index 49bfbf73..1a130a0a 100644 --- a/app/lib/screens/wallets/wallet_screen.dart +++ b/app/lib/screens/wallets/wallet_screen.dart @@ -118,6 +118,7 @@ class _WalletScreenState extends ConsumerState { loading = true; }); try { + print("HELLO!"); await walletRef.list(); await Future.delayed(const Duration(milliseconds: 100)); if (wallets.isEmpty) { diff --git a/app/lib/services/shared_preference_service.dart b/app/lib/services/shared_preference_service.dart index 702886a5..d15f9c91 100644 --- a/app/lib/services/shared_preference_service.dart +++ b/app/lib/services/shared_preference_service.dart @@ -253,21 +253,24 @@ Future> getIdentity() async { 'identityCountry': prefs.getString('identityCountry'), 'identityDOB': prefs.getString('identityDOB'), 'identityGender': prefs.getString('identityGender'), + 'walletSecret': prefs.getString('walletSecret'), }; } Future saveIdentity(String? identityName, String? identityCountry, - String? identityDOB, String? identityGender, String? referenceId) async { + String? identityDOB, String? identityGender, String? referenceId, String? walletSecret) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.remove('identityName'); prefs.remove('identityCountry'); prefs.remove('identityDOB'); prefs.remove('identityGender'); + prefs.remove('walletSecret'); prefs.setString('identityName', identityName!); prefs.setString('identityCountry', identityCountry!); prefs.setString('identityDOB', identityDOB!); prefs.setString('identityGender', identityGender!); + prefs.setString('walletSecret', walletSecret!); updateUserData('identity_reference', referenceId!); Globals().identityVerified.value = true; diff --git a/app/lib/services/wallet_service.dart b/app/lib/services/wallet_service.dart index de1863d6..744fab54 100644 --- a/app/lib/services/wallet_service.dart +++ b/app/lib/services/wallet_service.dart @@ -4,7 +4,9 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_pkid/flutter_pkid.dart'; import 'package:threebotlogin/apps/wallet/wallet_config.dart'; import 'package:threebotlogin/helpers/globals.dart'; +import 'package:threebotlogin/models/idenfy.dart'; import 'package:threebotlogin/models/wallet.dart'; +import 'package:threebotlogin/services/idenfy_service.dart'; import 'package:threebotlogin/services/pkid_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; import 'package:stellar_client/stellar_client.dart' as Stellar; @@ -98,6 +100,9 @@ Future loadWallet(String walletName, String walletSeed, await loadWalletClients(walletName, walletSeed, walletType, chainUrl); final stellarBalance = await StellarService.getBalanceByClient(stellarClient); final tfchainBalance = await TFChainService.getBalanceByClient(tfchainClient); + VerificationStatus kycResult = await getVerificationStatus( + address: walletSeed); + // print('KYC RESULT: ${kycResult.status}'); final wallet = Wallet( name: walletName, stellarSecret: stellarClient.secretSeed, @@ -108,6 +113,7 @@ Future loadWallet(String walletName, String walletSeed, tfchainBalance: tfchainBalance.toString() == '0.0' ? '0' : tfchainBalance.toString(), type: walletType, + verificationStatus: kycResult.status.name, ); return wallet; } diff --git a/app/lib/widgets/wallets/wallet_card.dart b/app/lib/widgets/wallets/wallet_card.dart index e54d938e..7bc00dfe 100644 --- a/app/lib/widgets/wallets/wallet_card.dart +++ b/app/lib/widgets/wallets/wallet_card.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:threebotlogin/helpers/globals.dart'; import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/helpers/transaction_helpers.dart'; +import 'package:threebotlogin/models/idenfy.dart'; import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/screens/wallets/wallet_details.dart'; import 'package:threebotlogin/services/stellar_service.dart' as StellarService; @@ -169,11 +170,26 @@ class _WalletCardWidgetState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - widget.wallet.name, - style: Theme.of(context).textTheme.titleLarge!.copyWith( - color: Theme.of(context).colorScheme.onSecondaryContainer, - ), + Row( + children: [ + Text( + widget.wallet.name, + style: Theme.of(context).textTheme.titleLarge!.copyWith( + color: Theme.of(context) + .colorScheme + .onSecondaryContainer, + ), + ), + const Spacer(), + // Text( + // widget.wallet.verificationStatus, + // style: Theme.of(context).textTheme.titleLarge!.copyWith( + // color: Theme.of(context) + // .colorScheme + // .onSecondaryContainer, + // ), + // ), + ], ), const SizedBox(height: 10), ...cardContent, From c77aeceba4bf0b8bcdf0f8e7dd145244787c6ee1 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 25 Dec 2024 16:09:40 +0200 Subject: [PATCH 02/24] WIP: move kyc verification steps to separate component --- app/ios/Podfile.lock | 24 ++++- app/lib/screens/wallets/wallet_info.dart | 29 +++++- app/lib/services/idenfy_service.dart | 2 +- app/lib/services/wallet_service.dart | 6 +- app/lib/widgets/kyc_widget.dart | 114 +++++++++++++++++++++++ app/lib/widgets/wallets/wallet_card.dart | 23 +++-- 6 files changed, 181 insertions(+), 17 deletions(-) create mode 100644 app/lib/widgets/kyc_widget.dart diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index afdcec54..f48b8b8d 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -1,6 +1,12 @@ PODS: - audio_session (0.0.1): - Flutter + - Crisp (2.6.0): + - Crisp/Crisp (= 2.6.0) + - Crisp/Crisp (2.6.0) + - crisp_chat (2.0.3): + - Crisp + - Flutter - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) @@ -11,6 +17,8 @@ PODS: - flutter_inappwebview/Core (0.0.1): - Flutter - OrderedSet (~> 5.0) + - flutter_keyboard_visibility (0.0.1): + - Flutter - flutter_pkid (0.0.1): - Flutter - idenfy_sdk_flutter (2.5.3): @@ -63,9 +71,11 @@ PODS: DEPENDENCIES: - audio_session (from `.symlinks/plugins/audio_session/ios`) + - crisp_chat (from `.symlinks/plugins/crisp_chat/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`) + - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) - flutter_pkid (from `.symlinks/plugins/flutter_pkid/ios`) - idenfy_sdk_flutter (from `.symlinks/plugins/idenfy_sdk_flutter/ios`) - just_audio (from `.symlinks/plugins/just_audio/ios`) @@ -88,6 +98,7 @@ DEPENDENCIES: SPEC REPOS: trunk: + - Crisp - iDenfySDK - lottie-ios - MTBBarcodeScanner @@ -96,12 +107,16 @@ SPEC REPOS: EXTERNAL SOURCES: audio_session: :path: ".symlinks/plugins/audio_session/ios" + crisp_chat: + :path: ".symlinks/plugins/crisp_chat/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: :path: Flutter flutter_inappwebview: :path: ".symlinks/plugins/flutter_inappwebview/ios" + flutter_keyboard_visibility: + :path: ".symlinks/plugins/flutter_keyboard_visibility/ios" flutter_pkid: :path: ".symlinks/plugins/flutter_pkid/ios" idenfy_sdk_flutter: @@ -143,14 +158,17 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207 + Crisp: 047b4e8b77f28cae5089f037cb327a9a03c9bb4d + crisp_chat: 2a726a86ce1c796388db27c80f0f49c423b7c260 device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_inappwebview: 3d32228f1304635e7c028b0d4252937730bbc6cf + flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 flutter_pkid: 7808bd0baee7580afc1cebebf2a6f3b290e190c5 idenfy_sdk_flutter: 3f53cc19cd757e9bb9f01d83386a6db84192dae9 iDenfySDK: 4bba551e7e824daf7db2b3bac166a9fe339c4269 just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa - local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c + local_auth: 25938960984c3a7f6e3253e3f8d962fdd16852bd lottie-ios: fcb5e73e17ba4c983140b7d21095c834b3087418 MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4 @@ -170,6 +188,6 @@ SPEC CHECKSUMS: wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 webview_flutter_wkwebview: 2a23822e9039b7b1bc52e5add778e5d89ad488d1 -PODFILE CHECKSUM: f8ab9e5577c96127975d4ff88c95faaa16cd71bb +PODFILE CHECKSUM: 050ff199c8e97450c391a88d64db90da96da9995 -COCOAPODS: 1.14.3 \ No newline at end of file +COCOAPODS: 1.14.3 diff --git a/app/lib/screens/wallets/wallet_info.dart b/app/lib/screens/wallets/wallet_info.dart index 89496a41..e4b57266 100644 --- a/app/lib/screens/wallets/wallet_info.dart +++ b/app/lib/screens/wallets/wallet_info.dart @@ -266,7 +266,34 @@ class _WalletDetailsWidgetState extends State { ), ), ), - ) + ), + Text( + 'KYC Verification', + style: Theme.of(context).textTheme.titleLarge!.copyWith( + color: Theme.of(context).colorScheme.onSurface, + ), + ), + if (widget.wallet.verificationStatus == 'UNVERIFIED') + Center( + child: SizedBox( + width: MediaQuery.of(context).size.width - 40, + child: ElevatedButton( + onPressed: () async { + // await verifyIdentityProcess(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.errorContainer), + child: Text( + 'Verify your Identity', + style: Theme.of(context).textTheme.bodyLarge!.copyWith( + color: + Theme.of(context).colorScheme.onErrorContainer, + ), + ), + ), + ), + ), ], ), ), diff --git a/app/lib/services/idenfy_service.dart b/app/lib/services/idenfy_service.dart index 47beeb1c..ab46ed1f 100644 --- a/app/lib/services/idenfy_service.dart +++ b/app/lib/services/idenfy_service.dart @@ -54,7 +54,7 @@ Future getVerificationStatus( final idenfyServiceUrl = Globals().idenfyServiceUrl; final response = await http.get( - Uri.https(idenfyServiceUrl, '/api/v1/status', {'client_id': address}), + Uri.https('kyc.grid.tf', '/api/v1/status', {'client_id': address}), ); if (response.statusCode == HttpStatus.ok) { return VerificationStatus.fromJson(jsonDecode(response.body)['result']); diff --git a/app/lib/services/wallet_service.dart b/app/lib/services/wallet_service.dart index 744fab54..053503ac 100644 --- a/app/lib/services/wallet_service.dart +++ b/app/lib/services/wallet_service.dart @@ -100,8 +100,8 @@ Future loadWallet(String walletName, String walletSeed, await loadWalletClients(walletName, walletSeed, walletType, chainUrl); final stellarBalance = await StellarService.getBalanceByClient(stellarClient); final tfchainBalance = await TFChainService.getBalanceByClient(tfchainClient); - VerificationStatus kycResult = await getVerificationStatus( - address: walletSeed); + final kycVerified = + await getVerificationStatus(address: tfchainClient.keypair!.address); // print('KYC RESULT: ${kycResult.status}'); final wallet = Wallet( name: walletName, @@ -113,7 +113,7 @@ Future loadWallet(String walletName, String walletSeed, tfchainBalance: tfchainBalance.toString() == '0.0' ? '0' : tfchainBalance.toString(), type: walletType, - verificationStatus: kycResult.status.name, + verificationStatus: kycVerified.status.name, ); return wallet; } diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart new file mode 100644 index 00000000..e9acb4db --- /dev/null +++ b/app/lib/widgets/kyc_widget.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; +import 'package:threebotlogin/models/idenfy.dart'; +import 'package:threebotlogin/services/idenfy_service.dart'; +import 'package:threebotlogin/services/wallet_service.dart'; +import 'package:threebotlogin/models/wallet.dart'; + +class IdentityVerificationService { + final BuildContext context; + final Future Function(String authToken) initIdenfySdk; + final Future Function() handleIdenfyResponse; + final Logger logger; + final Globals globals; + + IdentityVerificationService({ + required this.context, + required this.initIdenfySdk, + required this.handleIdenfyResponse, + required this.logger, + required this.globals, + }); + + Future verifyIdentityProcess({ + required ValueNotifier isLoadingNotifier, + required ValueNotifier isInIdentityProcessNotifier, + }) async { + isLoadingNotifier.value = true; + + try { + Token token = await getToken(); + isLoadingNotifier.value = false; + isInIdentityProcessNotifier.value = true; + await initIdenfySdk(token.authToken); + } on BadRequest catch (e) { + _handleError( + title: 'Bad Request', + description: '$e \nIf this issue persists, please contact support.', + ); + } on Unauthorized catch (e) { + _handleError( + title: 'Unauthorized', + description: '$e \nIf this issue persists, please contact support.', + ); + } on TooManyRequests catch (_) { + _handleError( + title: 'Maximum Requests Reached', + description: + 'You already had ${globals.maximumKYCRetries} requests in the last 24 hours.\nPlease try again in 24 hours.', + ); + } on NotEnoughBalance catch (_) { + final wallets = (await getPkidWallets()) + .where((w) => w.type == WalletType.NATIVE) + .toList(); + final minimumBalance = globals.minimumTFChainBalanceForKYC; + _handleError( + title: 'Not enough balance', + description: wallets.isEmpty + ? 'Please initialize a wallet and fund it with at least $minimumBalance TFTs.' + : 'Please fund your ${wallets.first.name} TFChain wallet with at least $minimumBalance TFTs.', + ); + } on NoTwinId catch (_) { + _handleError( + title: "Account doesn't exist", + description: + 'Your account is not activated.\nPlease go to the wallet section and initialize your wallet.', + ); + } on AlreadyVerified catch (_) { + isLoadingNotifier.value = false; + await handleIdenfyResponse(); + } catch (e) { + logger.e(e); + _handleError( + title: 'Failed to setup process', + description: + 'Something went wrong. \nIf this issue persists, please contact support.', + ); + } + } + + void _handleError({required String title, required String description}) { + isLoadingNotifier.value = false; + showDialog( + context: context, + builder: (BuildContext context) => CustomDialog( + type: DialogType.Warning, + image: Icons.warning, + title: title, + description: description, + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ), + ); + } +} + +// Example usage: +// final service = IdentityVerificationService( +// context: context, +// getToken: getToken, +// initIdenfySdk: initIdenfySdk, +// getPkidWallets: getPkidWallets, +// handleIdenfyResponse: handleIdenfyResponse, +// logger: logger, +// globals: globals, +// ); +// service.verifyIdentityProcess( +// isLoadingNotifier: ValueNotifier(false), +// isInIdentityProcessNotifier: ValueNotifier(false), +// ); diff --git a/app/lib/widgets/wallets/wallet_card.dart b/app/lib/widgets/wallets/wallet_card.dart index 7bc00dfe..7a89cd20 100644 --- a/app/lib/widgets/wallets/wallet_card.dart +++ b/app/lib/widgets/wallets/wallet_card.dart @@ -180,15 +180,20 @@ class _WalletCardWidgetState extends State { .onSecondaryContainer, ), ), - const Spacer(), - // Text( - // widget.wallet.verificationStatus, - // style: Theme.of(context).textTheme.titleLarge!.copyWith( - // color: Theme.of(context) - // .colorScheme - // .onSecondaryContainer, - // ), - // ), + const Spacer(), + Text( + widget.wallet.verificationStatus, + style: widget.wallet.verificationStatus == 'VERIFIED' + ? Theme.of(context).textTheme.bodySmall!.copyWith( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + ) + : TextStyle( + color: Theme.of(context).colorScheme.error, + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), ], ), const SizedBox(height: 10), From 5e1d4c00b48d662a9d1f345f7a68984f53080273 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 25 Dec 2024 16:10:50 +0200 Subject: [PATCH 03/24] WIP: move kyc to separate componnet --- app/lib/widgets/kyc_widget.dart | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index e9acb4db..82c7545e 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -6,17 +6,9 @@ import 'package:threebotlogin/models/wallet.dart'; class IdentityVerificationService { final BuildContext context; - final Future Function(String authToken) initIdenfySdk; - final Future Function() handleIdenfyResponse; - final Logger logger; - final Globals globals; IdentityVerificationService({ required this.context, - required this.initIdenfySdk, - required this.handleIdenfyResponse, - required this.logger, - required this.globals, }); Future verifyIdentityProcess({ @@ -98,17 +90,3 @@ class IdentityVerificationService { } } -// Example usage: -// final service = IdentityVerificationService( -// context: context, -// getToken: getToken, -// initIdenfySdk: initIdenfySdk, -// getPkidWallets: getPkidWallets, -// handleIdenfyResponse: handleIdenfyResponse, -// logger: logger, -// globals: globals, -// ); -// service.verifyIdentityProcess( -// isLoadingNotifier: ValueNotifier(false), -// isInIdentityProcessNotifier: ValueNotifier(false), -// ); From 521495e6e0f84d91ebdccbfb25b21e48045b9eca Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 29 Dec 2024 10:40:07 +0200 Subject: [PATCH 04/24] WIP: add kyc fucntions in separate file --- .../screens/identity_verification_screen.dart | 652 +++++++++--------- app/lib/screens/wallets/wallet_info.dart | 3 +- app/lib/widgets/kyc_widget.dart | 288 ++++++-- 3 files changed, 552 insertions(+), 391 deletions(-) diff --git a/app/lib/screens/identity_verification_screen.dart b/app/lib/screens/identity_verification_screen.dart index aa95553f..05a148ba 100644 --- a/app/lib/screens/identity_verification_screen.dart +++ b/app/lib/screens/identity_verification_screen.dart @@ -6,29 +6,20 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_pkid/flutter_pkid.dart'; import 'package:http/http.dart'; -import 'package:idenfy_sdk_flutter/idenfy_sdk_flutter.dart'; -import 'package:idenfy_sdk_flutter/models/auto_identification_status.dart'; -import 'package:idenfy_sdk_flutter/models/idenfy_identification_status.dart'; -import 'package:threebotlogin/events/events.dart'; -import 'package:threebotlogin/events/identity_callback_event.dart'; import 'package:threebotlogin/helpers/globals.dart'; import 'package:threebotlogin/helpers/kyc_helpers.dart'; import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/main.dart'; -import 'package:threebotlogin/models/idenfy.dart'; -import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/screens/authentication_screen.dart'; import 'package:threebotlogin/screens/wizard/web_view.dart'; import 'package:threebotlogin/services/gridproxy_service.dart'; -import 'package:threebotlogin/services/idenfy_service.dart'; import 'package:threebotlogin/services/identity_service.dart'; import 'package:threebotlogin/services/open_kyc_service.dart'; import 'package:threebotlogin/services/pkid_service.dart'; -import 'package:threebotlogin/services/tfchain_service.dart'; import 'package:threebotlogin/services/tools_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; -import 'package:threebotlogin/services/wallet_service.dart'; import 'package:threebotlogin/widgets/custom_dialog.dart'; +import 'package:threebotlogin/widgets/kyc_widget.dart'; import 'package:threebotlogin/widgets/layout_drawer.dart'; import 'package:threebotlogin/widgets/phone_widget.dart'; @@ -71,7 +62,7 @@ class _IdentityVerificationScreenState int emailCountdown = 60; Timer? emailTimer; ValueNotifier countdownNotifier = ValueNotifier(-1); - + void startOrResumeEmailCountdown({bool startNew = false}) { int currentTime = DateTime.now().millisecondsSinceEpoch; int lockedUntil = @@ -324,7 +315,14 @@ class _IdentityVerificationScreenState identityVerified == true ? ElevatedButton( onPressed: () async { - await verifyIdentityProcess(); + await verifyIdentityProcess( + context: context, + setIdentityProcess: (value) => + setState(() => + isInIdentityProcess = + value), + setLoading: (value) => setState( + () => isLoading = value)); }, child: const Text( 'Redo identity verification')) @@ -513,7 +511,12 @@ class _IdentityVerificationScreenState onPressed: isAccepted ? () async { Navigator.pop(customContext); - await verifyIdentityProcess(); + await verifyIdentityProcess( + context: context, + setIdentityProcess: (value) => + setState(() => isInIdentityProcess = value), + setLoading: (value) => + setState(() => isLoading = value)); } : null, child: Text( @@ -615,158 +618,158 @@ class _IdentityVerificationScreenState } } - Future initIdenfySdk(String token) async { - IdenfyIdentificationResult? idenfySDKresult; - try { - idenfySDKresult = await IdenfySdkFlutter.start(token); - } catch (e) { - logger.e(e); - if (context.mounted) { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext dialogContext) => CustomDialog( - type: DialogType.Error, - image: Icons.close, - title: 'Error', - description: - 'Something went wrong. Please contact support if this issue persists.', - actions: [ - TextButton( - onPressed: () { - Navigator.pop(dialogContext); - }, - child: const Text('Close')) - ], - ), - ); - } - } - await Future.delayed(const Duration(seconds: 5)); - if (idenfySDKresult != null && - idenfySDKresult.autoIdentificationStatus != - AutoIdentificationStatus.UNVERIFIED) { - await handleIdenfyResponse(); - } - } - - Future handleIdenfyResponse() async { - VerificationStatus verificationStatus; - try { - final address = await getMyAddress(); - verificationStatus = await getVerificationStatus(address: address); - } catch (e) { - setState(() { - isLoading = false; - }); - logger.e(e); - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Error, - image: Icons.error, - title: 'Error', - description: - 'Failed to get the verification status. \nIf this issue persist, please contact support.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ), - ); - } - if (verificationStatus.status == VerificationState.VERIFIED) { - identityVerified = true; - setIsIdentityVerified(true); - Globals().identityVerified.value = true; - try { - final data = await getVerificationData(); - final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); - final lastName = utf8.decode(latin1.encode(data.orgLastName!)); - final wallets = (await getPkidWallets()) - .where((w) => w.type == WalletType.NATIVE) - .toList(); - await saveIdentity('$lastName $firstName', data.docIssuingCountry, - data.docDob, data.docSex, data.idenfyRef, wallets.first.seed ); - Events().emit(IdentityCallbackEvent(type: 'success')); - } on BadRequest catch (e) { - setState(() { - isLoading = false; - }); - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Warning, - image: Icons.warning, - title: 'Bad Request', - description: - '$e \nIf this issue persist, please contact support.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - )); - } on Unauthorized catch (e) { - setState(() { - isLoading = false; - }); - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Warning, - image: Icons.warning, - title: 'Unauthorized', - description: - '$e \nIf this issue persist, please contact support.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - )); - } catch (e) { - setState(() { - isLoading = false; - }); - logger.e(e); - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Error, - image: Icons.error, - title: 'Error', - description: - 'Failed to process the verification details. \nIf this issue persist, please contact support.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ), - ); - } - } else { - identityVerified = false; - setIsIdentityVerified(false); - Globals().identityVerified.value = false; - Events().emit(IdentityCallbackEvent(type: 'failed')); - } - setState(() {}); - } + // Future initIdenfySdk(String token) async { + // IdenfyIdentificationResult? idenfySDKresult; + // try { + // idenfySDKresult = await IdenfySdkFlutter.start(token); + // } catch (e) { + // logger.e(e); + // if (context.mounted) { + // showDialog( + // context: context, + // barrierDismissible: false, + // builder: (BuildContext dialogContext) => CustomDialog( + // type: DialogType.Error, + // image: Icons.close, + // title: 'Error', + // description: + // 'Something went wrong. Please contact support if this issue persists.', + // actions: [ + // TextButton( + // onPressed: () { + // Navigator.pop(dialogContext); + // }, + // child: const Text('Close')) + // ], + // ), + // ); + // } + // } + // await Future.delayed(const Duration(seconds: 5)); + // if (idenfySDKresult != null && + // idenfySDKresult.autoIdentificationStatus != + // AutoIdentificationStatus.UNVERIFIED) { + // await handleIdenfyResponse(); + // } + // } + + // Future handleIdenfyResponse() async { + // VerificationStatus verificationStatus; + // try { + // final address = await getMyAddress(); + // verificationStatus = await getVerificationStatus(address: address); + // } catch (e) { + // setState(() { + // isLoading = false; + // }); + // logger.e(e); + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Error, + // image: Icons.error, + // title: 'Error', + // description: + // 'Failed to get the verification status. \nIf this issue persist, please contact support.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // ), + // ); + // } + // if (verificationStatus.status == VerificationState.VERIFIED) { + // identityVerified = true; + // setIsIdentityVerified(true); + // Globals().identityVerified.value = true; + // try { + // final data = await getVerificationData(); + // final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); + // final lastName = utf8.decode(latin1.encode(data.orgLastName!)); + // final wallets = (await getPkidWallets()) + // .where((w) => w.type == WalletType.NATIVE) + // .toList(); + // await saveIdentity('$lastName $firstName', data.docIssuingCountry, + // data.docDob, data.docSex, data.idenfyRef, wallets.first.seed ); + // Events().emit(IdentityCallbackEvent(type: 'success')); + // } on BadRequest catch (e) { + // setState(() { + // isLoading = false; + // }); + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Warning, + // image: Icons.warning, + // title: 'Bad Request', + // description: + // '$e \nIf this issue persist, please contact support.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // )); + // } on Unauthorized catch (e) { + // setState(() { + // isLoading = false; + // }); + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Warning, + // image: Icons.warning, + // title: 'Unauthorized', + // description: + // '$e \nIf this issue persist, please contact support.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // )); + // } catch (e) { + // setState(() { + // isLoading = false; + // }); + // logger.e(e); + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Error, + // image: Icons.error, + // title: 'Error', + // description: + // 'Failed to process the verification details. \nIf this issue persist, please contact support.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // ), + // ); + // } + // } else { + // identityVerified = false; + // setIsIdentityVerified(false); + // Globals().identityVerified.value = false; + // Events().emit(IdentityCallbackEvent(type: 'failed')); + // } + // setState(() {}); + // } Widget _pleaseWait() { return Dialog( @@ -1044,7 +1047,14 @@ class _IdentityVerificationScreenState // Verify identity case 3: { - await verifyIdentityProcess(); + await verifyIdentityProcess( + context: context, + setIdentityProcess: (value) => + setState(() => + isInIdentityProcess = + value), + setLoading: (value) => setState( + () => isLoading = value)); } break; default: @@ -1166,161 +1176,161 @@ class _IdentityVerificationScreenState ])); } - Future verifyIdentityProcess() async { - setState(() { - isLoading = true; - }); - - Token token; - try { - token = await getToken(); - - setState(() { - isLoading = false; - isInIdentityProcess = true; - }); - } on BadRequest catch (e) { - setState(() { - isLoading = false; - }); - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Warning, - image: Icons.warning, - title: 'Bad Request', - description: - '$e \nIf this issue persist, please contact support.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - )); - } on Unauthorized catch (e) { - setState(() { - isLoading = false; - }); - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Warning, - image: Icons.warning, - title: 'Unauthorized', - description: - '$e \nIf this issue persist, please contact support.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - )); - } on TooManyRequests catch (_) { - setState(() { - isLoading = false; - }); - final maxRetries = Globals().maximumKYCRetries; - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Warning, - image: Icons.warning, - title: 'Maximum Requests Reached', - description: - 'You already had $maxRetries requests in last 24 hours.\nPlease try again in 24 hours.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - )); - } on NotEnoughBalance catch (_) { - final wallets = (await getPkidWallets()) - .where((w) => w.type == WalletType.NATIVE) - .toList(); - setState(() { - isLoading = false; - }); - final minimumBalance = Globals().minimumTFChainBalanceForKYC; - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Warning, - image: Icons.warning, - title: 'Not enough balance', - description: wallets.isEmpty - ? 'Please initialize a wallet and fund it with at least $minimumBalance TFTs.' - : 'Please fund your ${wallets.first.name} TFChain wallet with at least $minimumBalance TFTs.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - )); - } on NoTwinId catch (_) { - setState(() { - isLoading = false; - }); - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Warning, - image: Icons.warning, - title: "Account doesn't exist", - description: - 'Your account is not activated.\nPlease go to wallet section and initialize your wallet.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - )); - } on AlreadyVerified catch (_) { - setState(() { - isLoading = false; - }); - return await handleIdenfyResponse(); - } catch (e) { - setState(() { - isLoading = false; - }); - logger.e(e); - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Error, - image: Icons.error, - title: 'Failed to setup process', - description: - 'Something went wrong. \nIf this issue persist, please contact support.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ), - ); - } - await initIdenfySdk(token.authToken); - } + // Future verifyIdentityProcess() async { + // setState(() { + // isLoading = true; + // }); + + // Token token; + // try { + // token = await getToken(); + + // setState(() { + // isLoading = false; + // isInIdentityProcess = true; + // }); + // } on BadRequest catch (e) { + // setState(() { + // isLoading = false; + // }); + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Warning, + // image: Icons.warning, + // title: 'Bad Request', + // description: + // '$e \nIf this issue persist, please contact support.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // )); + // } on Unauthorized catch (e) { + // setState(() { + // isLoading = false; + // }); + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Warning, + // image: Icons.warning, + // title: 'Unauthorized', + // description: + // '$e \nIf this issue persist, please contact support.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // )); + // } on TooManyRequests catch (_) { + // setState(() { + // isLoading = false; + // }); + // final maxRetries = Globals().maximumKYCRetries; + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Warning, + // image: Icons.warning, + // title: 'Maximum Requests Reached', + // description: + // 'You already had $maxRetries requests in last 24 hours.\nPlease try again in 24 hours.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // )); + // } on NotEnoughBalance catch (_) { + // final wallets = (await getPkidWallets()) + // .where((w) => w.type == WalletType.NATIVE) + // .toList(); + // setState(() { + // isLoading = false; + // }); + // final minimumBalance = Globals().minimumTFChainBalanceForKYC; + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Warning, + // image: Icons.warning, + // title: 'Not enough balance', + // description: wallets.isEmpty + // ? 'Please initialize a wallet and fund it with at least $minimumBalance TFTs.' + // : 'Please fund your ${wallets.first.name} TFChain wallet with at least $minimumBalance TFTs.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // )); + // } on NoTwinId catch (_) { + // setState(() { + // isLoading = false; + // }); + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Warning, + // image: Icons.warning, + // title: "Account doesn't exist", + // description: + // 'Your account is not activated.\nPlease go to wallet section and initialize your wallet.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // )); + // } on AlreadyVerified catch (_) { + // setState(() { + // isLoading = false; + // }); + // return await handleIdenfyResponse(); + // } catch (e) { + // setState(() { + // isLoading = false; + // }); + // logger.e(e); + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Error, + // image: Icons.error, + // title: 'Failed to setup process', + // description: + // 'Something went wrong. \nIf this issue persist, please contact support.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // ), + // ); + // } + // await initIdenfySdk(token.authToken); + // } Future showIdentityDetails() { return showDialog( diff --git a/app/lib/screens/wallets/wallet_info.dart b/app/lib/screens/wallets/wallet_info.dart index e4b57266..6d1ef425 100644 --- a/app/lib/screens/wallets/wallet_info.dart +++ b/app/lib/screens/wallets/wallet_info.dart @@ -279,7 +279,7 @@ class _WalletDetailsWidgetState extends State { width: MediaQuery.of(context).size.width - 40, child: ElevatedButton( onPressed: () async { - // await verifyIdentityProcess(); + // await verifyIdentityProcess(context: context); }, style: ElevatedButton.styleFrom( backgroundColor: @@ -294,6 +294,7 @@ class _WalletDetailsWidgetState extends State { ), ), ), + // else get verified data and show dialog ], ), ), diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index 82c7545e..58447110 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -1,92 +1,242 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:idenfy_sdk_flutter/idenfy_sdk_flutter.dart'; +import 'package:idenfy_sdk_flutter/models/idenfy_identification_status.dart'; +import 'package:threebotlogin/events/events.dart'; +import 'package:threebotlogin/events/identity_callback_event.dart'; import 'package:threebotlogin/models/idenfy.dart'; import 'package:threebotlogin/services/idenfy_service.dart'; +import 'package:threebotlogin/helpers/globals.dart'; +import 'package:threebotlogin/helpers/logger.dart'; +import 'package:threebotlogin/services/tfchain_service.dart'; import 'package:threebotlogin/services/wallet_service.dart'; +import 'package:threebotlogin/services/shared_preference_service.dart'; +import 'package:threebotlogin/widgets/custom_dialog.dart'; import 'package:threebotlogin/models/wallet.dart'; +import 'package:idenfy_sdk_flutter/models/auto_identification_status.dart'; -class IdentityVerificationService { - final BuildContext context; +Future verifyIdentityProcess({ + required BuildContext context, + required ValueChanged setLoading, + required ValueChanged setIdentityProcess, +}) async { + setLoading(true); - IdentityVerificationService({ - required this.context, - }); + Token? token; + try { + token = await getToken(); - Future verifyIdentityProcess({ - required ValueNotifier isLoadingNotifier, - required ValueNotifier isInIdentityProcessNotifier, - }) async { - isLoadingNotifier.value = true; + setLoading(false); + setIdentityProcess(true); - try { - Token token = await getToken(); - isLoadingNotifier.value = false; - isInIdentityProcessNotifier.value = true; - await initIdenfySdk(token.authToken); - } on BadRequest catch (e) { - _handleError( - title: 'Bad Request', - description: '$e \nIf this issue persists, please contact support.', - ); - } on Unauthorized catch (e) { - _handleError( - title: 'Unauthorized', - description: '$e \nIf this issue persists, please contact support.', - ); - } on TooManyRequests catch (_) { - _handleError( - title: 'Maximum Requests Reached', - description: - 'You already had ${globals.maximumKYCRetries} requests in the last 24 hours.\nPlease try again in 24 hours.', - ); - } on NotEnoughBalance catch (_) { - final wallets = (await getPkidWallets()) - .where((w) => w.type == WalletType.NATIVE) - .toList(); - final minimumBalance = globals.minimumTFChainBalanceForKYC; - _handleError( + // await initIdenfySdk(token.authToken); + } on BadRequest catch (e) { + setLoading(false); + await showWarningDialog( + context: context, + title: 'Bad Request', + description: '$e \nIf this issue persist, please contact support.', + ); + } on Unauthorized catch (e) { + setLoading(false); + await showWarningDialog( + context: context, + title: 'Unauthorized', + description: '$e \nIf this issue persist, please contact support.', + ); + } on TooManyRequests catch (_) { + setLoading(false); + final maxRetries = Globals().maximumKYCRetries; + await showWarningDialog( + context: context, + title: 'Maximum Requests Reached', + description: + 'You already had $maxRetries requests in last 24 hours.\nPlease try again in 24 hours.', + ); + } on NotEnoughBalance catch (_) { + final wallets = (await getPkidWallets()) + .where((w) => w.type == WalletType.NATIVE) + .toList(); + setLoading(false); + final minimumBalance = Globals().minimumTFChainBalanceForKYC; + await showWarningDialog( + context: context, title: 'Not enough balance', description: wallets.isEmpty ? 'Please initialize a wallet and fund it with at least $minimumBalance TFTs.' - : 'Please fund your ${wallets.first.name} TFChain wallet with at least $minimumBalance TFTs.', - ); - } on NoTwinId catch (_) { - _handleError( + : 'Please fund your ${wallets.first.name} TFChain wallet with at least $minimumBalance TFTs.'); + } on NoTwinId catch (_) { + setLoading(false); + await showWarningDialog( + context: context, title: "Account doesn't exist", description: - 'Your account is not activated.\nPlease go to the wallet section and initialize your wallet.', - ); - } on AlreadyVerified catch (_) { - isLoadingNotifier.value = false; - await handleIdenfyResponse(); - } catch (e) { - logger.e(e); - _handleError( + 'Your account is not activated.\nPlease go to wallet section and initialize your wallet.'); + } on AlreadyVerified catch (_) { + setLoading(false); + await showErrorDialog( + context: context, title: 'Failed to setup process', description: - 'Something went wrong. \nIf this issue persists, please contact support.', - ); - } + 'Something went wrong. \nIf this issue persist, please contact support.'); + await handleIdenfyResponse( + context: context, + setLoading: setLoading, + setIdentityVerified: setIdentityProcess); + } catch (e) { + setLoading(false); + logger.e(e); + await showErrorDialog( + context: context, + title: 'Failed to setup process', + description: + 'Something went wrong. \nIf this issue persist, please contact support.', + ); } - void _handleError({required String title, required String description}) { - isLoadingNotifier.value = false; - showDialog( + await initIdenfySdk( context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Warning, - image: Icons.warning, - title: title, - description: description, - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ), + token!.authToken, + setLoading: setLoading, + setIdentityVerified: setIdentityProcess); +} + +Future handleIdenfyResponse({ + required BuildContext context, + required ValueChanged setLoading, + required ValueChanged setIdentityVerified, +}) async { + VerificationStatus verificationStatus; + try { + final address = await getMyAddress(); + verificationStatus = await getVerificationStatus(address: address); + } catch (e) { + setLoading(false); + logger.e(e); + await showErrorDialog( + context: context, + title: 'Error', + description: + 'Failed to get the verification status. \nIf this issue persist, please contact support.', ); + return; + } + + if (verificationStatus.status == VerificationState.VERIFIED) { + setIdentityVerified(true); + Globals().identityVerified.value = true; + + try { + final data = await getVerificationData(); + final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); + final lastName = utf8.decode(latin1.encode(data.orgLastName!)); + final wallets = (await getPkidWallets()) + .where((w) => w.type == WalletType.NATIVE) + .toList(); + await saveIdentity('$lastName $firstName', data.docIssuingCountry, + data.docDob, data.docSex, data.idenfyRef, wallets.first.seed); + Events().emit(IdentityCallbackEvent(type: 'success')); + } on BadRequest catch (e) { + setLoading(false); + await showWarningDialog( + context: context, + title: 'Bad Request', + description: '$e \nIf this issue persist, please contact support.'); + } on Unauthorized catch (e) { + setLoading(false); + await showWarningDialog( + context: context, + title: 'Unauthorized', + description: '$e \nIf this issue persist, please contact support.'); + } catch (e) { + setLoading(false); + logger.e(e); + await showErrorDialog( + context: context, + title: 'Error', + description: 'Failed to process verification details', + ); + } + } else { + setIdentityVerified(false); + Globals().identityVerified.value = false; + Events().emit(IdentityCallbackEvent(type: 'failed')); + } +} + +Future initIdenfySdk(String token, + {required BuildContext context, + required ValueChanged setLoading, + required ValueChanged setIdentityVerified}) async { + IdenfyIdentificationResult? idenfySDKresult; + try { + idenfySDKresult = await IdenfySdkFlutter.start(token); + } catch (e) { + logger.e(e); + if (context.mounted) { + await showErrorDialog( + context: context, + title: 'Error', + description: + 'Something went wrong. Please contact support if this issue persists.'); + } + } + await Future.delayed(const Duration(seconds: 5)); + if (idenfySDKresult != null && + idenfySDKresult.autoIdentificationStatus != + AutoIdentificationStatus.UNVERIFIED) { + await handleIdenfyResponse( + context: context, + setLoading: setLoading, + setIdentityVerified: setIdentityVerified); } } +Future showWarningDialog({ + required BuildContext context, + required String title, + required String description, +}) async { + await showDialog( + context: context, + builder: (BuildContext context) => CustomDialog( + type: DialogType.Warning, + image: Icons.warning, + title: title, + description: description, + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ), + ); +} + +Future showErrorDialog({ + required BuildContext context, + required String title, + required String description, +}) async { + await showDialog( + context: context, + builder: (BuildContext context) => CustomDialog( + type: DialogType.Error, + image: Icons.error, + title: title, + description: description, + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ), + ); +} From 8b13b92d931d30ca4b2e3efc6e5d9de484f221e9 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 29 Dec 2024 12:41:09 +0200 Subject: [PATCH 05/24] WIP: trying to fix error while verifying kyc --- .../screens/identity_verification_screen.dart | 156 ------------------ app/lib/widgets/kyc_widget.dart | 11 +- 2 files changed, 2 insertions(+), 165 deletions(-) diff --git a/app/lib/screens/identity_verification_screen.dart b/app/lib/screens/identity_verification_screen.dart index 05a148ba..9d3199b4 100644 --- a/app/lib/screens/identity_verification_screen.dart +++ b/app/lib/screens/identity_verification_screen.dart @@ -1176,162 +1176,6 @@ class _IdentityVerificationScreenState ])); } - // Future verifyIdentityProcess() async { - // setState(() { - // isLoading = true; - // }); - - // Token token; - // try { - // token = await getToken(); - - // setState(() { - // isLoading = false; - // isInIdentityProcess = true; - // }); - // } on BadRequest catch (e) { - // setState(() { - // isLoading = false; - // }); - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Warning, - // image: Icons.warning, - // title: 'Bad Request', - // description: - // '$e \nIf this issue persist, please contact support.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // )); - // } on Unauthorized catch (e) { - // setState(() { - // isLoading = false; - // }); - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Warning, - // image: Icons.warning, - // title: 'Unauthorized', - // description: - // '$e \nIf this issue persist, please contact support.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // )); - // } on TooManyRequests catch (_) { - // setState(() { - // isLoading = false; - // }); - // final maxRetries = Globals().maximumKYCRetries; - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Warning, - // image: Icons.warning, - // title: 'Maximum Requests Reached', - // description: - // 'You already had $maxRetries requests in last 24 hours.\nPlease try again in 24 hours.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // )); - // } on NotEnoughBalance catch (_) { - // final wallets = (await getPkidWallets()) - // .where((w) => w.type == WalletType.NATIVE) - // .toList(); - // setState(() { - // isLoading = false; - // }); - // final minimumBalance = Globals().minimumTFChainBalanceForKYC; - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Warning, - // image: Icons.warning, - // title: 'Not enough balance', - // description: wallets.isEmpty - // ? 'Please initialize a wallet and fund it with at least $minimumBalance TFTs.' - // : 'Please fund your ${wallets.first.name} TFChain wallet with at least $minimumBalance TFTs.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // )); - // } on NoTwinId catch (_) { - // setState(() { - // isLoading = false; - // }); - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Warning, - // image: Icons.warning, - // title: "Account doesn't exist", - // description: - // 'Your account is not activated.\nPlease go to wallet section and initialize your wallet.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // )); - // } on AlreadyVerified catch (_) { - // setState(() { - // isLoading = false; - // }); - // return await handleIdenfyResponse(); - // } catch (e) { - // setState(() { - // isLoading = false; - // }); - // logger.e(e); - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Error, - // image: Icons.error, - // title: 'Failed to setup process', - // description: - // 'Something went wrong. \nIf this issue persist, please contact support.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // ), - // ); - // } - // await initIdenfySdk(token.authToken); - // } - Future showIdentityDetails() { return showDialog( context: context, diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index 58447110..e2e4ebe4 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -23,14 +23,12 @@ Future verifyIdentityProcess({ }) async { setLoading(true); - Token? token; + late Token token; try { token = await getToken(); setLoading(false); setIdentityProcess(true); - - // await initIdenfySdk(token.authToken); } on BadRequest catch (e) { setLoading(false); await showWarningDialog( @@ -75,11 +73,6 @@ Future verifyIdentityProcess({ 'Your account is not activated.\nPlease go to wallet section and initialize your wallet.'); } on AlreadyVerified catch (_) { setLoading(false); - await showErrorDialog( - context: context, - title: 'Failed to setup process', - description: - 'Something went wrong. \nIf this issue persist, please contact support.'); await handleIdenfyResponse( context: context, setLoading: setLoading, @@ -97,7 +90,7 @@ Future verifyIdentityProcess({ await initIdenfySdk( context: context, - token!.authToken, + token.authToken, setLoading: setLoading, setIdentityVerified: setIdentityProcess); } From f8f76a8a0af71f2c69876a74f469510124aec74b Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 29 Dec 2024 14:31:02 +0200 Subject: [PATCH 06/24] undo changes in podfile.lock --- app/ios/Podfile.lock | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index f48b8b8d..afdcec54 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -1,12 +1,6 @@ PODS: - audio_session (0.0.1): - Flutter - - Crisp (2.6.0): - - Crisp/Crisp (= 2.6.0) - - Crisp/Crisp (2.6.0) - - crisp_chat (2.0.3): - - Crisp - - Flutter - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) @@ -17,8 +11,6 @@ PODS: - flutter_inappwebview/Core (0.0.1): - Flutter - OrderedSet (~> 5.0) - - flutter_keyboard_visibility (0.0.1): - - Flutter - flutter_pkid (0.0.1): - Flutter - idenfy_sdk_flutter (2.5.3): @@ -71,11 +63,9 @@ PODS: DEPENDENCIES: - audio_session (from `.symlinks/plugins/audio_session/ios`) - - crisp_chat (from `.symlinks/plugins/crisp_chat/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`) - - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) - flutter_pkid (from `.symlinks/plugins/flutter_pkid/ios`) - idenfy_sdk_flutter (from `.symlinks/plugins/idenfy_sdk_flutter/ios`) - just_audio (from `.symlinks/plugins/just_audio/ios`) @@ -98,7 +88,6 @@ DEPENDENCIES: SPEC REPOS: trunk: - - Crisp - iDenfySDK - lottie-ios - MTBBarcodeScanner @@ -107,16 +96,12 @@ SPEC REPOS: EXTERNAL SOURCES: audio_session: :path: ".symlinks/plugins/audio_session/ios" - crisp_chat: - :path: ".symlinks/plugins/crisp_chat/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: :path: Flutter flutter_inappwebview: :path: ".symlinks/plugins/flutter_inappwebview/ios" - flutter_keyboard_visibility: - :path: ".symlinks/plugins/flutter_keyboard_visibility/ios" flutter_pkid: :path: ".symlinks/plugins/flutter_pkid/ios" idenfy_sdk_flutter: @@ -158,17 +143,14 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207 - Crisp: 047b4e8b77f28cae5089f037cb327a9a03c9bb4d - crisp_chat: 2a726a86ce1c796388db27c80f0f49c423b7c260 device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_inappwebview: 3d32228f1304635e7c028b0d4252937730bbc6cf - flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 flutter_pkid: 7808bd0baee7580afc1cebebf2a6f3b290e190c5 idenfy_sdk_flutter: 3f53cc19cd757e9bb9f01d83386a6db84192dae9 iDenfySDK: 4bba551e7e824daf7db2b3bac166a9fe339c4269 just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa - local_auth: 25938960984c3a7f6e3253e3f8d962fdd16852bd + local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c lottie-ios: fcb5e73e17ba4c983140b7d21095c834b3087418 MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4 @@ -188,6 +170,6 @@ SPEC CHECKSUMS: wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 webview_flutter_wkwebview: 2a23822e9039b7b1bc52e5add778e5d89ad488d1 -PODFILE CHECKSUM: 050ff199c8e97450c391a88d64db90da96da9995 +PODFILE CHECKSUM: f8ab9e5577c96127975d4ff88c95faaa16cd71bb -COCOAPODS: 1.14.3 +COCOAPODS: 1.14.3 \ No newline at end of file From 626389508be4f962405091168b04a23ee5afc463 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 29 Dec 2024 16:00:04 +0200 Subject: [PATCH 07/24] idenfy-service-url --- .../screens/identity_verification_screen.dart | 358 +++++++++--------- app/lib/screens/wallets/wallet_info.dart | 9 +- app/lib/services/idenfy_service.dart | 2 +- app/lib/widgets/kyc_widget.dart | 3 +- 4 files changed, 194 insertions(+), 178 deletions(-) diff --git a/app/lib/screens/identity_verification_screen.dart b/app/lib/screens/identity_verification_screen.dart index 9d3199b4..b145afec 100644 --- a/app/lib/screens/identity_verification_screen.dart +++ b/app/lib/screens/identity_verification_screen.dart @@ -6,18 +6,28 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_pkid/flutter_pkid.dart'; import 'package:http/http.dart'; +import 'package:idenfy_sdk_flutter/idenfy_sdk_flutter.dart'; +import 'package:idenfy_sdk_flutter/models/auto_identification_status.dart'; +import 'package:idenfy_sdk_flutter/models/idenfy_identification_status.dart'; +import 'package:threebotlogin/events/events.dart'; +import 'package:threebotlogin/events/identity_callback_event.dart'; import 'package:threebotlogin/helpers/globals.dart'; import 'package:threebotlogin/helpers/kyc_helpers.dart'; import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/main.dart'; +import 'package:threebotlogin/models/idenfy.dart'; +import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/screens/authentication_screen.dart'; import 'package:threebotlogin/screens/wizard/web_view.dart'; import 'package:threebotlogin/services/gridproxy_service.dart'; +import 'package:threebotlogin/services/idenfy_service.dart'; import 'package:threebotlogin/services/identity_service.dart'; import 'package:threebotlogin/services/open_kyc_service.dart'; import 'package:threebotlogin/services/pkid_service.dart'; +import 'package:threebotlogin/services/tfchain_service.dart'; import 'package:threebotlogin/services/tools_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; +import 'package:threebotlogin/services/wallet_service.dart'; import 'package:threebotlogin/widgets/custom_dialog.dart'; import 'package:threebotlogin/widgets/kyc_widget.dart'; import 'package:threebotlogin/widgets/layout_drawer.dart'; @@ -315,14 +325,14 @@ class _IdentityVerificationScreenState identityVerified == true ? ElevatedButton( onPressed: () async { - await verifyIdentityProcess( - context: context, - setIdentityProcess: (value) => - setState(() => - isInIdentityProcess = - value), - setLoading: (value) => setState( - () => isLoading = value)); + // await verifyIdentityProcess( + // context: context, + // setIdentityProcess: (value) => + // setState(() => + // isInIdentityProcess = + // value), + // setLoading: (value) => setState( + // () => isLoading = value)); }, child: const Text( 'Redo identity verification')) @@ -511,12 +521,12 @@ class _IdentityVerificationScreenState onPressed: isAccepted ? () async { Navigator.pop(customContext); - await verifyIdentityProcess( - context: context, - setIdentityProcess: (value) => - setState(() => isInIdentityProcess = value), - setLoading: (value) => - setState(() => isLoading = value)); + // await verifyIdentityProcess( + // context: context, + // setIdentityProcess: (value) => + // setState(() => isInIdentityProcess = value), + // setLoading: (value) => + // setState(() => isLoading = value)); } : null, child: Text( @@ -618,158 +628,158 @@ class _IdentityVerificationScreenState } } - // Future initIdenfySdk(String token) async { - // IdenfyIdentificationResult? idenfySDKresult; - // try { - // idenfySDKresult = await IdenfySdkFlutter.start(token); - // } catch (e) { - // logger.e(e); - // if (context.mounted) { - // showDialog( - // context: context, - // barrierDismissible: false, - // builder: (BuildContext dialogContext) => CustomDialog( - // type: DialogType.Error, - // image: Icons.close, - // title: 'Error', - // description: - // 'Something went wrong. Please contact support if this issue persists.', - // actions: [ - // TextButton( - // onPressed: () { - // Navigator.pop(dialogContext); - // }, - // child: const Text('Close')) - // ], - // ), - // ); - // } - // } - // await Future.delayed(const Duration(seconds: 5)); - // if (idenfySDKresult != null && - // idenfySDKresult.autoIdentificationStatus != - // AutoIdentificationStatus.UNVERIFIED) { - // await handleIdenfyResponse(); - // } - // } - - // Future handleIdenfyResponse() async { - // VerificationStatus verificationStatus; - // try { - // final address = await getMyAddress(); - // verificationStatus = await getVerificationStatus(address: address); - // } catch (e) { - // setState(() { - // isLoading = false; - // }); - // logger.e(e); - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Error, - // image: Icons.error, - // title: 'Error', - // description: - // 'Failed to get the verification status. \nIf this issue persist, please contact support.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // ), - // ); - // } - // if (verificationStatus.status == VerificationState.VERIFIED) { - // identityVerified = true; - // setIsIdentityVerified(true); - // Globals().identityVerified.value = true; - // try { - // final data = await getVerificationData(); - // final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); - // final lastName = utf8.decode(latin1.encode(data.orgLastName!)); - // final wallets = (await getPkidWallets()) - // .where((w) => w.type == WalletType.NATIVE) - // .toList(); - // await saveIdentity('$lastName $firstName', data.docIssuingCountry, - // data.docDob, data.docSex, data.idenfyRef, wallets.first.seed ); - // Events().emit(IdentityCallbackEvent(type: 'success')); - // } on BadRequest catch (e) { - // setState(() { - // isLoading = false; - // }); - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Warning, - // image: Icons.warning, - // title: 'Bad Request', - // description: - // '$e \nIf this issue persist, please contact support.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // )); - // } on Unauthorized catch (e) { - // setState(() { - // isLoading = false; - // }); - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Warning, - // image: Icons.warning, - // title: 'Unauthorized', - // description: - // '$e \nIf this issue persist, please contact support.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // )); - // } catch (e) { - // setState(() { - // isLoading = false; - // }); - // logger.e(e); - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Error, - // image: Icons.error, - // title: 'Error', - // description: - // 'Failed to process the verification details. \nIf this issue persist, please contact support.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // ), - // ); - // } - // } else { - // identityVerified = false; - // setIsIdentityVerified(false); - // Globals().identityVerified.value = false; - // Events().emit(IdentityCallbackEvent(type: 'failed')); - // } - // setState(() {}); - // } + Future initIdenfySdk(String token) async { + IdenfyIdentificationResult? idenfySDKresult; + try { + idenfySDKresult = await IdenfySdkFlutter.start(token); + } catch (e) { + logger.e(e); + if (context.mounted) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext dialogContext) => CustomDialog( + type: DialogType.Error, + image: Icons.close, + title: 'Error', + description: + 'Something went wrong. Please contact support if this issue persists.', + actions: [ + TextButton( + onPressed: () { + Navigator.pop(dialogContext); + }, + child: const Text('Close')) + ], + ), + ); + } + } + await Future.delayed(const Duration(seconds: 5)); + if (idenfySDKresult != null && + idenfySDKresult.autoIdentificationStatus != + AutoIdentificationStatus.UNVERIFIED) { + await handleIdenfyResponse(); + } + } + + Future handleIdenfyResponse() async { + VerificationStatus verificationStatus; + try { + final address = await getMyAddress(); + verificationStatus = await getVerificationStatus(address: address); + } catch (e) { + setState(() { + isLoading = false; + }); + logger.e(e); + return showDialog( + context: context, + builder: (BuildContext context) => CustomDialog( + type: DialogType.Error, + image: Icons.error, + title: 'Error', + description: + 'Failed to get the verification status. \nIf this issue persist, please contact support.', + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ), + ); + } + if (verificationStatus.status == VerificationState.VERIFIED) { + identityVerified = true; + setIsIdentityVerified(true); + Globals().identityVerified.value = true; + try { + final data = await getVerificationData(); + final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); + final lastName = utf8.decode(latin1.encode(data.orgLastName!)); + final wallets = (await getPkidWallets()) + .where((w) => w.type == WalletType.NATIVE) + .toList(); + await saveIdentity('$lastName $firstName', data.docIssuingCountry, + data.docDob, data.docSex, data.idenfyRef, wallets.first.seed ); + Events().emit(IdentityCallbackEvent(type: 'success')); + } on BadRequest catch (e) { + setState(() { + isLoading = false; + }); + return showDialog( + context: context, + builder: (BuildContext context) => CustomDialog( + type: DialogType.Warning, + image: Icons.warning, + title: 'Bad Request', + description: + '$e \nIf this issue persist, please contact support.', + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + )); + } on Unauthorized catch (e) { + setState(() { + isLoading = false; + }); + return showDialog( + context: context, + builder: (BuildContext context) => CustomDialog( + type: DialogType.Warning, + image: Icons.warning, + title: 'Unauthorized', + description: + '$e \nIf this issue persist, please contact support.', + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + )); + } catch (e) { + setState(() { + isLoading = false; + }); + logger.e(e); + return showDialog( + context: context, + builder: (BuildContext context) => CustomDialog( + type: DialogType.Error, + image: Icons.error, + title: 'Error', + description: + 'Failed to process the verification details. \nIf this issue persist, please contact support.', + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ), + ); + } + } else { + identityVerified = false; + setIsIdentityVerified(false); + Globals().identityVerified.value = false; + Events().emit(IdentityCallbackEvent(type: 'failed')); + } + setState(() {}); + } Widget _pleaseWait() { return Dialog( @@ -1047,14 +1057,14 @@ class _IdentityVerificationScreenState // Verify identity case 3: { - await verifyIdentityProcess( - context: context, - setIdentityProcess: (value) => - setState(() => - isInIdentityProcess = - value), - setLoading: (value) => setState( - () => isLoading = value)); + // await verifyIdentityProcess( + // context: context, + // setIdentityProcess: (value) => + // setState(() => + // isInIdentityProcess = + // value), + // setLoading: (value) => setState( + // () => isLoading = value)); } break; default: diff --git a/app/lib/screens/wallets/wallet_info.dart b/app/lib/screens/wallets/wallet_info.dart index 6d1ef425..2b5c90d0 100644 --- a/app/lib/screens/wallets/wallet_info.dart +++ b/app/lib/screens/wallets/wallet_info.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/services/wallet_service.dart'; +import 'package:threebotlogin/widgets/kyc_widget.dart'; import 'package:threebotlogin/widgets/wallets/warning_dialog.dart'; class WalletDetailsWidget extends StatefulWidget { @@ -279,7 +280,11 @@ class _WalletDetailsWidgetState extends State { width: MediaQuery.of(context).size.width - 40, child: ElevatedButton( onPressed: () async { - // await verifyIdentityProcess(context: context); + await verifyIdentityProcess( + context: context, + walletName: widget.wallet.name, + setIdentityProcess: (value) {}, + setLoading: (value) {}); }, style: ElevatedButton.styleFrom( backgroundColor: @@ -294,7 +299,7 @@ class _WalletDetailsWidgetState extends State { ), ), ), - // else get verified data and show dialog + // else get verified data and show dialog ], ), ), diff --git a/app/lib/services/idenfy_service.dart b/app/lib/services/idenfy_service.dart index ab46ed1f..47beeb1c 100644 --- a/app/lib/services/idenfy_service.dart +++ b/app/lib/services/idenfy_service.dart @@ -54,7 +54,7 @@ Future getVerificationStatus( final idenfyServiceUrl = Globals().idenfyServiceUrl; final response = await http.get( - Uri.https('kyc.grid.tf', '/api/v1/status', {'client_id': address}), + Uri.https(idenfyServiceUrl, '/api/v1/status', {'client_id': address}), ); if (response.statusCode == HttpStatus.ok) { return VerificationStatus.fromJson(jsonDecode(response.body)['result']); diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index e2e4ebe4..69e5fa6a 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -18,6 +18,7 @@ import 'package:idenfy_sdk_flutter/models/auto_identification_status.dart'; Future verifyIdentityProcess({ required BuildContext context, + required String walletName, required ValueChanged setLoading, required ValueChanged setIdentityProcess, }) async { @@ -54,7 +55,7 @@ Future verifyIdentityProcess({ ); } on NotEnoughBalance catch (_) { final wallets = (await getPkidWallets()) - .where((w) => w.type == WalletType.NATIVE) + .where((w) => w.name == walletName) .toList(); setLoading(false); final minimumBalance = Globals().minimumTFChainBalanceForKYC; From 1bce7c04c8623a38235fd7142fcd7d2cbe8236d4 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 29 Dec 2024 16:02:10 +0200 Subject: [PATCH 08/24] remove prints && commnted lines --- app/lib/screens/wallets/wallet_screen.dart | 1 - app/lib/services/wallet_service.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/app/lib/screens/wallets/wallet_screen.dart b/app/lib/screens/wallets/wallet_screen.dart index 1a130a0a..49bfbf73 100644 --- a/app/lib/screens/wallets/wallet_screen.dart +++ b/app/lib/screens/wallets/wallet_screen.dart @@ -118,7 +118,6 @@ class _WalletScreenState extends ConsumerState { loading = true; }); try { - print("HELLO!"); await walletRef.list(); await Future.delayed(const Duration(milliseconds: 100)); if (wallets.isEmpty) { diff --git a/app/lib/services/wallet_service.dart b/app/lib/services/wallet_service.dart index 053503ac..019cc3dc 100644 --- a/app/lib/services/wallet_service.dart +++ b/app/lib/services/wallet_service.dart @@ -102,7 +102,6 @@ Future loadWallet(String walletName, String walletSeed, final tfchainBalance = await TFChainService.getBalanceByClient(tfchainClient); final kycVerified = await getVerificationStatus(address: tfchainClient.keypair!.address); - // print('KYC RESULT: ${kycResult.status}'); final wallet = Wallet( name: walletName, stellarSecret: stellarClient.secretSeed, From 0406d980ad3bafe78df305c86697e706ca3070f3 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 29 Dec 2024 20:34:26 +0200 Subject: [PATCH 09/24] fix threads bug --- app/lib/helpers/kyc_helpers.dart | 3 ++- .../screens/identity_verification_screen.dart | 3 ++- app/lib/services/idenfy_service.dart | 3 +-- app/lib/services/wallet_service.dart | 7 ++++--- app/lib/widgets/add_farm.dart | 4 +++- app/lib/widgets/farm_item.dart | 4 +++- app/lib/widgets/kyc_widget.dart | 16 ++++++++-------- app/lib/widgets/wallets/add_wallet.dart | 3 ++- 8 files changed, 25 insertions(+), 18 deletions(-) diff --git a/app/lib/helpers/kyc_helpers.dart b/app/lib/helpers/kyc_helpers.dart index 1fd3b533..8c576313 100644 --- a/app/lib/helpers/kyc_helpers.dart +++ b/app/lib/helpers/kyc_helpers.dart @@ -37,8 +37,9 @@ Future fetchPKidData() async { Future handleKYCData( Map emailData, Map phoneData, String walletSecret) async { final address = await getMyAddress(); + final idenfyServiceUrl = Globals().idenfyServiceUrl; final identityVerificationStatus = - await getVerificationStatus(address: address); + await getVerificationStatus(address: address, idenfyServiceUrl: idenfyServiceUrl); await saveCorrectVerificationStates( emailData, phoneData, identityVerificationStatus); diff --git a/app/lib/screens/identity_verification_screen.dart b/app/lib/screens/identity_verification_screen.dart index b145afec..b1422f3b 100644 --- a/app/lib/screens/identity_verification_screen.dart +++ b/app/lib/screens/identity_verification_screen.dart @@ -667,7 +667,8 @@ class _IdentityVerificationScreenState VerificationStatus verificationStatus; try { final address = await getMyAddress(); - verificationStatus = await getVerificationStatus(address: address); + final idenfyServiceUrl = Globals().idenfyServiceUrl; + verificationStatus = await getVerificationStatus(address: address, idenfyServiceUrl: idenfyServiceUrl); } catch (e) { setState(() { isLoading = false; diff --git a/app/lib/services/idenfy_service.dart b/app/lib/services/idenfy_service.dart index 47beeb1c..2ac96327 100644 --- a/app/lib/services/idenfy_service.dart +++ b/app/lib/services/idenfy_service.dart @@ -50,8 +50,7 @@ Future getToken() async { } Future getVerificationStatus( - {required String address}) async { - final idenfyServiceUrl = Globals().idenfyServiceUrl; + {required String address, required String idenfyServiceUrl}) async { final response = await http.get( Uri.https(idenfyServiceUrl, '/api/v1/status', {'client_id': address}), diff --git a/app/lib/services/wallet_service.dart b/app/lib/services/wallet_service.dart index 019cc3dc..55fb5db9 100644 --- a/app/lib/services/wallet_service.dart +++ b/app/lib/services/wallet_service.dart @@ -45,10 +45,11 @@ Future> getPkidWallets() async { Future> listWallets() async { List pkidWallets = await getPkidWallets(); final String chainUrl = Globals().chainUrl; + final idenfyServiceUrl = Globals().idenfyServiceUrl; final List wallets = await compute((void _) async { final List> walletFutures = []; for (final w in pkidWallets) { - final walletFuture = loadWallet(w.name, w.seed, w.type, chainUrl); + final walletFuture = loadWallet(w.name, w.seed, w.type, chainUrl, idenfyServiceUrl); walletFutures.add(walletFuture); } return await Future.wait(walletFutures); @@ -95,13 +96,13 @@ Future<(Stellar.Client, TFChain.Client)> loadWalletClients(String walletName, } Future loadWallet(String walletName, String walletSeed, - WalletType walletType, String chainUrl) async { + WalletType walletType, String chainUrl, String idenfyServiceUrl) async { final (stellarClient, tfchainClient) = await loadWalletClients(walletName, walletSeed, walletType, chainUrl); final stellarBalance = await StellarService.getBalanceByClient(stellarClient); final tfchainBalance = await TFChainService.getBalanceByClient(tfchainClient); final kycVerified = - await getVerificationStatus(address: tfchainClient.keypair!.address); + await getVerificationStatus(address: tfchainClient.keypair!.address,idenfyServiceUrl: idenfyServiceUrl ); final wallet = Wallet( name: walletName, stellarSecret: stellarClient.secretSeed, diff --git a/app/lib/widgets/add_farm.dart b/app/lib/widgets/add_farm.dart index 88d43cff..6a630034 100644 --- a/app/lib/widgets/add_farm.dart +++ b/app/lib/widgets/add_farm.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:threebotlogin/helpers/globals.dart'; import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/models/farm.dart'; import 'package:threebotlogin/models/idenfy.dart'; @@ -74,8 +75,9 @@ class _NewFarmState extends State { _add(String farmName) async { Farm? farm; try { + final idenfyServiceUrl = Globals().idenfyServiceUrl; final kycVerified = - await getVerificationStatus(address: _selectedWallet!.tfchainAddress); + await getVerificationStatus(address: _selectedWallet!.tfchainAddress, idenfyServiceUrl: idenfyServiceUrl); if (kycVerified.status == VerificationState.VERIFIED) { final f = await createFarm(farmName, _selectedWallet!.tfchainSecret, _selectedWallet!.stellarAddress); diff --git a/app/lib/widgets/farm_item.dart b/app/lib/widgets/farm_item.dart index 22477bb1..d6d98161 100644 --- a/app/lib/widgets/farm_item.dart +++ b/app/lib/widgets/farm_item.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:threebotlogin/helpers/globals.dart'; import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/models/farm.dart'; import 'package:threebotlogin/models/idenfy.dart'; @@ -237,8 +238,9 @@ class _FarmItemWidgetState extends State { children: [ IconButton( onPressed: () async { + final idenfyServiceUrl = Globals().idenfyServiceUrl; final kycVerified = await getVerificationStatus( - address: tfchainAddress!); + address: tfchainAddress!, idenfyServiceUrl: idenfyServiceUrl); if (kycVerified.status != VerificationState.VERIFIED) { showDialog( diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index 69e5fa6a..2315b069 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -24,12 +24,17 @@ Future verifyIdentityProcess({ }) async { setLoading(true); - late Token token; + Token token; try { token = await getToken(); setLoading(false); setIdentityProcess(true); + await initIdenfySdk( + context: context, + token.authToken, + setLoading: setLoading, + setIdentityVerified: setIdentityProcess); } on BadRequest catch (e) { setLoading(false); await showWarningDialog( @@ -88,12 +93,6 @@ Future verifyIdentityProcess({ 'Something went wrong. \nIf this issue persist, please contact support.', ); } - - await initIdenfySdk( - context: context, - token.authToken, - setLoading: setLoading, - setIdentityVerified: setIdentityProcess); } Future handleIdenfyResponse({ @@ -104,7 +103,8 @@ Future handleIdenfyResponse({ VerificationStatus verificationStatus; try { final address = await getMyAddress(); - verificationStatus = await getVerificationStatus(address: address); + final idenfyServiceUrl = Globals().idenfyServiceUrl; + verificationStatus = await getVerificationStatus(address: address, idenfyServiceUrl: idenfyServiceUrl); } catch (e) { setLoading(false); logger.e(e); diff --git a/app/lib/widgets/wallets/add_wallet.dart b/app/lib/widgets/wallets/add_wallet.dart index fe8e675f..3410cc80 100644 --- a/app/lib/widgets/wallets/add_wallet.dart +++ b/app/lib/widgets/wallets/add_wallet.dart @@ -295,8 +295,9 @@ class _NewWalletState extends State { Future loadAddedWallet(String walletName, String walletSecret, {WalletType type = WalletType.IMPORTED}) async { final chainUrl = Globals().chainUrl; + final idenfyServiceUrl = Globals().idenfyServiceUrl; final Wallet wallet = await compute((void _) async { - final wallet = await loadWallet(walletName, walletSecret, type, chainUrl); + final wallet = await loadWallet(walletName, walletSecret, type, chainUrl, idenfyServiceUrl); return wallet; }, null); return wallet; From 9c150e9e4294195def56bc34e0dad3553b09d74b Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Mon, 30 Dec 2024 18:05:28 +0200 Subject: [PATCH 10/24] wip: fix verifying kyc --- app/lib/helpers/kyc_helpers.dart | 9 +- .../screens/identity_verification_screen.dart | 238 +++++++------- app/lib/screens/wallets/wallet_info.dart | 60 ++-- app/lib/services/idenfy_service.dart | 15 +- app/lib/services/tfchain_service.dart | 11 + app/lib/widgets/kyc_widget.dart | 309 ++++++++++++++++-- 6 files changed, 469 insertions(+), 173 deletions(-) diff --git a/app/lib/helpers/kyc_helpers.dart b/app/lib/helpers/kyc_helpers.dart index 8c576313..b2f1921a 100644 --- a/app/lib/helpers/kyc_helpers.dart +++ b/app/lib/helpers/kyc_helpers.dart @@ -35,11 +35,10 @@ Future fetchPKidData() async { } Future handleKYCData( - Map emailData, Map phoneData, String walletSecret) async { - final address = await getMyAddress(); + Map emailData, Map phoneData, String walletAddress) async { final idenfyServiceUrl = Globals().idenfyServiceUrl; final identityVerificationStatus = - await getVerificationStatus(address: address, idenfyServiceUrl: idenfyServiceUrl); + await getVerificationStatus(address: walletAddress, idenfyServiceUrl: idenfyServiceUrl); await saveCorrectVerificationStates( emailData, phoneData, identityVerificationStatus); @@ -73,11 +72,11 @@ Future handleKYCData( if (isIdentityVerified == true) { Globals().identityVerified.value = true; - final data = await getVerificationData(); + final data = await getVerificationData(walletAddress); final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); final lastName = utf8.decode(latin1.encode(data.orgLastName!)); await saveIdentity('$lastName $firstName', data.docIssuingCountry, - data.docDob, data.docSex, data.idenfyRef, walletSecret); + data.docDob, data.docSex, data.idenfyRef, walletAddress); } } diff --git a/app/lib/screens/identity_verification_screen.dart b/app/lib/screens/identity_verification_screen.dart index b1422f3b..e7947ffe 100644 --- a/app/lib/screens/identity_verification_screen.dart +++ b/app/lib/screens/identity_verification_screen.dart @@ -659,128 +659,128 @@ class _IdentityVerificationScreenState if (idenfySDKresult != null && idenfySDKresult.autoIdentificationStatus != AutoIdentificationStatus.UNVERIFIED) { - await handleIdenfyResponse(); + // await handleIdenfyResponse(); } } - Future handleIdenfyResponse() async { - VerificationStatus verificationStatus; - try { - final address = await getMyAddress(); - final idenfyServiceUrl = Globals().idenfyServiceUrl; - verificationStatus = await getVerificationStatus(address: address, idenfyServiceUrl: idenfyServiceUrl); - } catch (e) { - setState(() { - isLoading = false; - }); - logger.e(e); - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Error, - image: Icons.error, - title: 'Error', - description: - 'Failed to get the verification status. \nIf this issue persist, please contact support.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ), - ); - } - if (verificationStatus.status == VerificationState.VERIFIED) { - identityVerified = true; - setIsIdentityVerified(true); - Globals().identityVerified.value = true; - try { - final data = await getVerificationData(); - final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); - final lastName = utf8.decode(latin1.encode(data.orgLastName!)); - final wallets = (await getPkidWallets()) - .where((w) => w.type == WalletType.NATIVE) - .toList(); - await saveIdentity('$lastName $firstName', data.docIssuingCountry, - data.docDob, data.docSex, data.idenfyRef, wallets.first.seed ); - Events().emit(IdentityCallbackEvent(type: 'success')); - } on BadRequest catch (e) { - setState(() { - isLoading = false; - }); - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Warning, - image: Icons.warning, - title: 'Bad Request', - description: - '$e \nIf this issue persist, please contact support.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - )); - } on Unauthorized catch (e) { - setState(() { - isLoading = false; - }); - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Warning, - image: Icons.warning, - title: 'Unauthorized', - description: - '$e \nIf this issue persist, please contact support.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - )); - } catch (e) { - setState(() { - isLoading = false; - }); - logger.e(e); - return showDialog( - context: context, - builder: (BuildContext context) => CustomDialog( - type: DialogType.Error, - image: Icons.error, - title: 'Error', - description: - 'Failed to process the verification details. \nIf this issue persist, please contact support.', - actions: [ - TextButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ), - ); - } - } else { - identityVerified = false; - setIsIdentityVerified(false); - Globals().identityVerified.value = false; - Events().emit(IdentityCallbackEvent(type: 'failed')); - } - setState(() {}); - } + // Future handleIdenfyResponse() async { + // VerificationStatus verificationStatus; + // try { + // final address = await getMyAddress(); + // final idenfyServiceUrl = Globals().idenfyServiceUrl; + // verificationStatus = await getVerificationStatus(address: address, idenfyServiceUrl: idenfyServiceUrl); + // } catch (e) { + // setState(() { + // isLoading = false; + // }); + // logger.e(e); + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Error, + // image: Icons.error, + // title: 'Error', + // description: + // 'Failed to get the verification status. \nIf this issue persist, please contact support.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // ), + // ); + // } + // if (verificationStatus.status == VerificationState.VERIFIED) { + // identityVerified = true; + // setIsIdentityVerified(true); + // Globals().identityVerified.value = true; + // try { + // final data = await getVerificationData(address); + // final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); + // final lastName = utf8.decode(latin1.encode(data.orgLastName!)); + // final wallets = (await getPkidWallets()) + // .where((w) => w.type == WalletType.NATIVE) + // .toList(); + // await saveIdentity('$lastName $firstName', data.docIssuingCountry, + // data.docDob, data.docSex, data.idenfyRef, wallets.first.seed ); + // Events().emit(IdentityCallbackEvent(type: 'success')); + // } on BadRequest catch (e) { + // setState(() { + // isLoading = false; + // }); + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Warning, + // image: Icons.warning, + // title: 'Bad Request', + // description: + // '$e \nIf this issue persist, please contact support.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // )); + // } on Unauthorized catch (e) { + // setState(() { + // isLoading = false; + // }); + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Warning, + // image: Icons.warning, + // title: 'Unauthorized', + // description: + // '$e \nIf this issue persist, please contact support.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // )); + // } catch (e) { + // setState(() { + // isLoading = false; + // }); + // logger.e(e); + // return showDialog( + // context: context, + // builder: (BuildContext context) => CustomDialog( + // type: DialogType.Error, + // image: Icons.error, + // title: 'Error', + // description: + // 'Failed to process the verification details. \nIf this issue persist, please contact support.', + // actions: [ + // TextButton( + // child: const Text('Close'), + // onPressed: () { + // Navigator.pop(context); + // }, + // ), + // ], + // ), + // ); + // } + // } else { + // identityVerified = false; + // setIsIdentityVerified(false); + // Globals().identityVerified.value = false; + // Events().emit(IdentityCallbackEvent(type: 'failed')); + // } + // setState(() {}); + // } Widget _pleaseWait() { return Dialog( diff --git a/app/lib/screens/wallets/wallet_info.dart b/app/lib/screens/wallets/wallet_info.dart index 2b5c90d0..236d9aba 100644 --- a/app/lib/screens/wallets/wallet_info.dart +++ b/app/lib/screens/wallets/wallet_info.dart @@ -268,37 +268,55 @@ class _WalletDetailsWidgetState extends State { ), ), ), + const SizedBox(height: 40), Text( 'KYC Verification', style: Theme.of(context).textTheme.titleLarge!.copyWith( color: Theme.of(context).colorScheme.onSurface, ), ), - if (widget.wallet.verificationStatus == 'UNVERIFIED') - Center( - child: SizedBox( - width: MediaQuery.of(context).size.width - 40, - child: ElevatedButton( - onPressed: () async { + Center( + child: SizedBox( + width: MediaQuery.of(context).size.width - 40, + child: ElevatedButton( + onPressed: () async { + if (widget.wallet.verificationStatus != 'VERIFIED') { await verifyIdentityProcess( - context: context, - walletName: widget.wallet.name, - setIdentityProcess: (value) {}, - setLoading: (value) {}); - }, - style: ElevatedButton.styleFrom( - backgroundColor: - Theme.of(context).colorScheme.errorContainer), - child: Text( - 'Verify your Identity', - style: Theme.of(context).textTheme.bodyLarge!.copyWith( - color: - Theme.of(context).colorScheme.onErrorContainer, - ), - ), + context: context, + walletName: widget.wallet.name, + setIdentityProcess: (value) {}, + setLoading: (value) {}, + walletAddress: widget.wallet.tfchainSecret + ); + setState(() { + + }); + } else { + showIdentityDetails(context); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: + widget.wallet.verificationStatus != 'VERIFIED' + ? Theme.of(context).colorScheme.errorContainer + : Theme.of(context).colorScheme.primaryContainer, + ), + child: Text( + widget.wallet.verificationStatus != 'VERIFIED' + ? 'Verify your Identity' + : 'Show Verified Data', + style: Theme.of(context).textTheme.bodyLarge!.copyWith( + color: widget.wallet.verificationStatus != 'VERIFIED' + ? Theme.of(context).colorScheme.onErrorContainer + : Theme.of(context) + .colorScheme + .onPrimaryContainer, + ), ), ), ), + ) + // else get verified data and show dialog ], ), diff --git a/app/lib/services/idenfy_service.dart b/app/lib/services/idenfy_service.dart index 2ac96327..3d851e68 100644 --- a/app/lib/services/idenfy_service.dart +++ b/app/lib/services/idenfy_service.dart @@ -6,10 +6,9 @@ import 'package:threebotlogin/services/tfchain_service.dart'; import 'package:convert/convert.dart'; import 'package:threebotlogin/models/idenfy.dart'; -Future> _prepareRequestHeaders() async { +Future> _prepareRequestHeaders({required String walletSecretSeed} ) async { final idenfyServiceUrl = Globals().idenfyServiceUrl; - final signer = await getMySigner(); - final address = signer.keypair!.address; + final signer = await getSignerFromSeed(walletSecretSeed); final now = DateTime.now().millisecondsSinceEpoch; final content = '$idenfyServiceUrl:$now'; final contentHex = hex.encode(content.codeUnits); @@ -17,16 +16,16 @@ Future> _prepareRequestHeaders() async { final headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', - 'X-Client-ID': address, + 'X-Client-ID': signer.keypair!.address, 'X-Challenge': contentHex, 'X-Signature': signedContent, }; return headers; } -Future getToken() async { +Future getToken(String seed) async { final idenfyServiceUrl = Globals().idenfyServiceUrl; - final headers = await _prepareRequestHeaders(); + final headers = await _prepareRequestHeaders(walletSecretSeed: seed); final response = await http.post( Uri.https(idenfyServiceUrl, '/api/v1/token'), @@ -69,9 +68,9 @@ Future getVerificationStatus( } } -Future getVerificationData() async { +Future getVerificationData(String seed) async { final idenfyServiceUrl = Globals().idenfyServiceUrl; - final headers = await _prepareRequestHeaders(); + final headers = await _prepareRequestHeaders(walletSecretSeed: seed); final response = await http.get( Uri.https(idenfyServiceUrl, '/api/v1/data'), diff --git a/app/lib/services/tfchain_service.dart b/app/lib/services/tfchain_service.dart index d9a29dbb..970f98ad 100644 --- a/app/lib/services/tfchain_service.dart +++ b/app/lib/services/tfchain_service.dart @@ -36,6 +36,17 @@ Future getMySigner() async { return signer; } +Future getSignerFromSeed(String seed) async { + final signer = Signer(); + if (seed.startsWith('0x')) { + signer.fromHexSeed(seed, KPType.sr25519); + } else { + //TODO: test this with mnemonic + signer.fromMnemonic(seed, KPType.sr25519); + } + return signer; +} + Future getMyAddress() async { final signer = await getMySigner(); return signer.keypair!.address; diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index 2315b069..fc5952e6 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -9,7 +9,6 @@ import 'package:threebotlogin/models/idenfy.dart'; import 'package:threebotlogin/services/idenfy_service.dart'; import 'package:threebotlogin/helpers/globals.dart'; import 'package:threebotlogin/helpers/logger.dart'; -import 'package:threebotlogin/services/tfchain_service.dart'; import 'package:threebotlogin/services/wallet_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; import 'package:threebotlogin/widgets/custom_dialog.dart'; @@ -21,20 +20,16 @@ Future verifyIdentityProcess({ required String walletName, required ValueChanged setLoading, required ValueChanged setIdentityProcess, + required String walletAddress, }) async { setLoading(true); Token token; try { - token = await getToken(); + token = await getToken(walletAddress); setLoading(false); setIdentityProcess(true); - await initIdenfySdk( - context: context, - token.authToken, - setLoading: setLoading, - setIdentityVerified: setIdentityProcess); } on BadRequest catch (e) { setLoading(false); await showWarningDialog( @@ -42,6 +37,7 @@ Future verifyIdentityProcess({ title: 'Bad Request', description: '$e \nIf this issue persist, please contact support.', ); + return; } on Unauthorized catch (e) { setLoading(false); await showWarningDialog( @@ -49,6 +45,7 @@ Future verifyIdentityProcess({ title: 'Unauthorized', description: '$e \nIf this issue persist, please contact support.', ); + return; } on TooManyRequests catch (_) { setLoading(false); final maxRetries = Globals().maximumKYCRetries; @@ -58,10 +55,10 @@ Future verifyIdentityProcess({ description: 'You already had $maxRetries requests in last 24 hours.\nPlease try again in 24 hours.', ); + return; } on NotEnoughBalance catch (_) { - final wallets = (await getPkidWallets()) - .where((w) => w.name == walletName) - .toList(); + final wallets = + (await getPkidWallets()).where((w) => w.name == walletName).toList(); setLoading(false); final minimumBalance = Globals().minimumTFChainBalanceForKYC; await showWarningDialog( @@ -70,6 +67,7 @@ Future verifyIdentityProcess({ description: wallets.isEmpty ? 'Please initialize a wallet and fund it with at least $minimumBalance TFTs.' : 'Please fund your ${wallets.first.name} TFChain wallet with at least $minimumBalance TFTs.'); + return; } on NoTwinId catch (_) { setLoading(false); await showWarningDialog( @@ -77,12 +75,15 @@ Future verifyIdentityProcess({ title: "Account doesn't exist", description: 'Your account is not activated.\nPlease go to wallet section and initialize your wallet.'); + return; } on AlreadyVerified catch (_) { setLoading(false); - await handleIdenfyResponse( + return await handleIdenfyResponse( context: context, setLoading: setLoading, - setIdentityVerified: setIdentityProcess); + setIdentityVerified: setIdentityProcess, + walletName: walletName, + walletAddress: walletAddress); } catch (e) { setLoading(false); logger.e(e); @@ -92,19 +93,28 @@ Future verifyIdentityProcess({ description: 'Something went wrong. \nIf this issue persist, please contact support.', ); + return; } + await initIdenfySdk(token.authToken, + context: context, + setLoading: setLoading, + setIdentityVerified: setIdentityProcess, + walletName: walletName, + walletAddress: walletAddress); } Future handleIdenfyResponse({ required BuildContext context, required ValueChanged setLoading, required ValueChanged setIdentityVerified, + required String walletName, + required String walletAddress, }) async { VerificationStatus verificationStatus; try { - final address = await getMyAddress(); final idenfyServiceUrl = Globals().idenfyServiceUrl; - verificationStatus = await getVerificationStatus(address: address, idenfyServiceUrl: idenfyServiceUrl); + verificationStatus = await getVerificationStatus( + address: walletAddress, idenfyServiceUrl: idenfyServiceUrl); } catch (e) { setLoading(false); logger.e(e); @@ -122,12 +132,11 @@ Future handleIdenfyResponse({ Globals().identityVerified.value = true; try { - final data = await getVerificationData(); + final data = await getVerificationData(walletAddress); final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); final lastName = utf8.decode(latin1.encode(data.orgLastName!)); - final wallets = (await getPkidWallets()) - .where((w) => w.type == WalletType.NATIVE) - .toList(); + final wallets = + (await getPkidWallets()).where((w) => w.name == walletName).toList(); await saveIdentity('$lastName $firstName', data.docIssuingCountry, data.docDob, data.docSex, data.idenfyRef, wallets.first.seed); Events().emit(IdentityCallbackEvent(type: 'success')); @@ -162,7 +171,9 @@ Future handleIdenfyResponse({ Future initIdenfySdk(String token, {required BuildContext context, required ValueChanged setLoading, - required ValueChanged setIdentityVerified}) async { + required ValueChanged setIdentityVerified, + required String walletName, + required String walletAddress}) async { IdenfyIdentificationResult? idenfySDKresult; try { idenfySDKresult = await IdenfySdkFlutter.start(token); @@ -183,7 +194,9 @@ Future initIdenfySdk(String token, await handleIdenfyResponse( context: context, setLoading: setLoading, - setIdentityVerified: setIdentityVerified); + setIdentityVerified: setIdentityVerified, + walletName: walletName, + walletAddress: walletAddress); } } @@ -234,3 +247,259 @@ Future showErrorDialog({ ), ); } + +Widget pleaseWait(BuildContext context) { + return Dialog( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: 10, + ), + CircularProgressIndicator( + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox( + height: 10, + ), + Text( + 'One moment please', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Theme.of(context).colorScheme.onSurface), + ), + const SizedBox( + height: 10, + ), + ], + ), + ); +} + +Future showIdentityDetails(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext context) => Dialog( + child: FutureBuilder( + future: getIdentity(), + builder: (BuildContext customContext, + AsyncSnapshot snapshot) { + if (!snapshot.hasData) { + return pleaseWait(context); + } + String name = snapshot.data['identityName']; + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 10, + ), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 15, vertical: 10), + child: Column( + children: [ + Text( + 'ID CARD', + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith( + fontWeight: FontWeight.bold, + color: Theme.of(context) + .colorScheme + .onSecondaryContainer), + textAlign: TextAlign.center, + ), + const SizedBox(height: 5), + Row(children: [ + Text( + 'Your own personal KYC ID CARD', + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + color: Theme.of(context) + .colorScheme + .onSecondaryContainer), + ), + ]), + ], + )), + Container( + color: Theme.of(context).colorScheme.secondaryContainer, + padding: const EdgeInsets.symmetric( + horizontal: 15, vertical: 20), + child: Column( + children: [ + Row( + children: [ + Text( + 'Full name', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: + Theme.of(context).colorScheme.primary, + ), + ) + ], + ), + Row( + children: [ + Text( + name, + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: Theme.of(context) + .colorScheme + .onSecondaryContainer, + ), + ) + ], + ) + ], + ), + ), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 15, vertical: 20), + child: Column( + children: [ + Row( + children: [ + Text( + 'Birthday', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: + Theme.of(context).colorScheme.primary, + ), + ), + ], + ), + Row( + children: [ + Text( + snapshot.data['identityDOB'] != 'None' + ? snapshot.data['identityDOB'] + : 'Unknown', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: Theme.of(context) + .colorScheme + .onSecondaryContainer, + ), + ) + ], + ) + ], + ), + ), + Container( + color: Theme.of(context).colorScheme.secondaryContainer, + padding: const EdgeInsets.symmetric( + horizontal: 15, vertical: 20), + child: Column( + children: [ + Row( + children: [ + Text( + 'Country', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: + Theme.of(context).colorScheme.primary, + ), + ) + ], + ), + Row( + children: [ + Text( + snapshot.data['identityCountry'] != 'None' + ? snapshot.data['identityCountry'] + : 'Unknown', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: Theme.of(context) + .colorScheme + .onSecondaryContainer, + ), + ) + ], + ) + ], + ), + ), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 15, vertical: 20), + child: Column( + children: [ + Row( + children: [ + Text( + 'Gender', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: + Theme.of(context).colorScheme.primary, + ), + ) + ], + ), + Row( + children: [ + Text( + snapshot.data['identityGender'] != 'None' + ? snapshot.data['identityGender'] + : 'Unknown', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: Theme.of(context) + .colorScheme + .onSecondaryContainer, + ), + ) + ], + ) + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + onPressed: () { + Navigator.pop(customContext); + }, + child: const Text('OK')), + const SizedBox( + height: 10, + ), + ], + ) + ], + ); + }, + ), + )); +} From 2e157bfeb17449589456d6db4e72260989365a58 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Tue, 31 Dec 2024 19:04:52 +0200 Subject: [PATCH 11/24] fix kyc data dialog && remove kyc from identity screen --- .../screens/identity_verification_screen.dart | 768 +----------------- app/lib/screens/wallets/wallet_info.dart | 11 +- app/lib/services/identity_service.dart | 20 +- app/lib/services/tfchain_service.dart | 15 +- app/lib/widgets/kyc_widget.dart | 269 ++++-- 5 files changed, 238 insertions(+), 845 deletions(-) diff --git a/app/lib/screens/identity_verification_screen.dart b/app/lib/screens/identity_verification_screen.dart index e7947ffe..f928bfae 100644 --- a/app/lib/screens/identity_verification_screen.dart +++ b/app/lib/screens/identity_verification_screen.dart @@ -1,33 +1,21 @@ import 'dart:async'; import 'dart:convert'; import 'dart:core'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_pkid/flutter_pkid.dart'; import 'package:http/http.dart'; -import 'package:idenfy_sdk_flutter/idenfy_sdk_flutter.dart'; -import 'package:idenfy_sdk_flutter/models/auto_identification_status.dart'; -import 'package:idenfy_sdk_flutter/models/idenfy_identification_status.dart'; -import 'package:threebotlogin/events/events.dart'; -import 'package:threebotlogin/events/identity_callback_event.dart'; import 'package:threebotlogin/helpers/globals.dart'; import 'package:threebotlogin/helpers/kyc_helpers.dart'; import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/main.dart'; -import 'package:threebotlogin/models/idenfy.dart'; -import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/screens/authentication_screen.dart'; -import 'package:threebotlogin/screens/wizard/web_view.dart'; import 'package:threebotlogin/services/gridproxy_service.dart'; -import 'package:threebotlogin/services/idenfy_service.dart'; import 'package:threebotlogin/services/identity_service.dart'; import 'package:threebotlogin/services/open_kyc_service.dart'; import 'package:threebotlogin/services/pkid_service.dart'; -import 'package:threebotlogin/services/tfchain_service.dart'; import 'package:threebotlogin/services/tools_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; -import 'package:threebotlogin/services/wallet_service.dart'; import 'package:threebotlogin/widgets/custom_dialog.dart'; import 'package:threebotlogin/widgets/kyc_widget.dart'; import 'package:threebotlogin/widgets/layout_drawer.dart'; @@ -48,15 +36,11 @@ class _IdentityVerificationScreenState String email = ''; String phone = ''; - String kycLogs = ''; - String reference = ''; bool emailVerified = false; bool phoneVerified = false; - bool identityVerified = false; - bool isInIdentityProcess = false; bool isLoading = false; bool hidePhoneVerifyButton = false; @@ -129,14 +113,6 @@ class _IdentityVerificationScreenState } } - setIdentityVerified() { - if (mounted) { - setState(() { - identityVerified = Globals().identityVerified.value; - }); - } - } - setHidePhoneVerify() { if (mounted) { setState(() { @@ -151,7 +127,6 @@ class _IdentityVerificationScreenState Globals().emailVerified.addListener(setEmailVerified); Globals().phoneVerified.addListener(setPhoneVerified); - Globals().identityVerified.addListener(setIdentityVerified); Globals().hidePhoneButton.addListener(setHidePhoneVerify); checkPhoneStatus(); getUserValues(); @@ -202,17 +177,6 @@ class _IdentityVerificationScreenState } }); }); - getIdentity().then((verificationDate) { - setState(() { - if (verificationDate['identityName'] != null) { - identityVerified = true; - setIsIdentityVerified(true); - } else { - identityVerified = false; - setIsIdentityVerified(false); - } - }); - }); getSpending(); } @@ -239,7 +203,7 @@ class _IdentityVerificationScreenState builder: (ctx, snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (isLoading) { - return _pleaseWait(); + return pleaseWait(context); } return Column( @@ -253,7 +217,6 @@ class _IdentityVerificationScreenState animation: Listenable.merge([ Globals().emailVerified, Globals().phoneVerified, - Globals().identityVerified ]), builder: (BuildContext context, _) { return Column( @@ -290,7 +253,7 @@ class _IdentityVerificationScreenState // Step one: verify email _fillCard( getCorrectState(1, emailVerified, - phoneVerified, identityVerified), + phoneVerified), 1, email, Icons.email), @@ -302,64 +265,19 @@ class _IdentityVerificationScreenState spending > Globals().spendingLimit)) ? _fillCard( getCorrectState(2, emailVerified, - phoneVerified, identityVerified), + phoneVerified), 2, phone, Icons.phone) : Container(), customDivider(context: context), + const ListTile( + leading: Icon(Icons.info), + title: Text( + 'KYC Verification has been moved to wallet page.' + ), + ), - // Step three: verify identity - (Globals().isOpenKYCEnabled || - (Globals().spendingLimit > 0 && - spending > Globals().spendingLimit)) - ? _fillCard( - getCorrectState(3, emailVerified, - phoneVerified, identityVerified), - 3, - extract3Bot(doubleName), - Icons.perm_identity) - : Container(), - - Globals().redoIdentityVerification && - identityVerified == true - ? ElevatedButton( - onPressed: () async { - // await verifyIdentityProcess( - // context: context, - // setIdentityProcess: (value) => - // setState(() => - // isInIdentityProcess = - // value), - // setLoading: (value) => setState( - // () => isLoading = value)); - }, - child: const Text( - 'Redo identity verification')) - : Container(), - Globals().debugMode == true - ? ElevatedButton( - onPressed: () async { - bool? isEmailVerified = - await getIsEmailVerified(); - bool? isPhoneVerified = - await getIsPhoneVerified(); - bool? isIdentityVerified = - await getIsIdentityVerified(); - - kycLogs = ''; - kycLogs += - 'Email verified: $isEmailVerified\n'; - kycLogs += - 'Phone verified: $isPhoneVerified\n'; - kycLogs += - 'Identity verified: $isIdentityVerified\n'; - - setState(() {}); - }, - child: const Text('KYC Status')) - : Container(), - Text(kycLogs), ], ); }) @@ -369,219 +287,12 @@ class _IdentityVerificationScreenState ], ); } - return _pleaseWait(); + return pleaseWait(context); }, ), ); } - termsAndConditionsDialog() { - bool isAccepted = false; - - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext customContext) { - return StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - return CustomDialog( - title: 'Terms and Conditions', - type: DialogType.Info, - image: Icons.info, - widgetDescription: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox(height: MediaQuery.of(context).size.height * 0.01), - RichText( - text: TextSpan( - text: - "As part of the verification process, we utilize iDenfy to verify your identity. Please ensure you review iDenfy's ", - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: Theme.of(context).colorScheme.onSurface, - ), - children: [ - TextSpan( - text: 'Security and Compliance', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - fontWeight: FontWeight.bold, - color: - Theme.of(context).colorScheme.onSurface, - ), - ), - TextSpan( - text: ', which include their ', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: - Theme.of(context).colorScheme.onSurface, - ), - ), - TextSpan( - text: 'Terms & Conditions, Privacy Policy,', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - fontWeight: FontWeight.bold, - color: - Theme.of(context).colorScheme.onSurface, - ), - ), - TextSpan( - text: ' and other relevant documents.', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: - Theme.of(context).colorScheme.onSurface, - ), - ) - // - ], - ), - ), - Row( - children: [ - Checkbox( - value: isAccepted, - onChanged: (bool? value) { - setState(() { - isAccepted = value ?? false; - }); - }, - ), - Expanded( - child: RichText( - text: TextSpan( - text: 'I have read and agreed to ', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: - Theme.of(context).colorScheme.onSurface, - ), - children: [ - TextSpan( - text: 'iDenfy Terms and Conditions.', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .primary, - decoration: TextDecoration.underline, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const WebView( - url: - 'https://www.idenfy.com/security/', - title: - 'iDenfy Terms and Conditions', - ), - ), - ); - }, - ), - TextSpan( - text: '.', - style: Theme.of(context).textTheme.bodyMedium, - ), - ], - ), - ), - ), - ], - ) - ], - ), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(customContext); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: isAccepted - ? () async { - Navigator.pop(customContext); - // await verifyIdentityProcess( - // context: context, - // setIdentityProcess: (value) => - // setState(() => isInIdentityProcess = value), - // setLoading: (value) => - // setState(() => isLoading = value)); - } - : null, - child: Text( - 'Continue', - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: isAccepted - ? Theme.of(context).colorScheme.primary - : Theme.of(context).disabledColor, - ), - ), - ), - ], - ); - }, - ); - }, - ); - } - - showAreYouSureToExitDialog() { - return showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext customContext) => CustomDialog( - type: DialogType.Warning, - image: Icons.warning, - title: 'Are you sure', - description: 'Are you sure you want to exit the verification process', - actions: [ - TextButton( - child: const Text('No'), - onPressed: () async { - Navigator.pop(customContext); - }, - ), - TextButton( - child: Text( - 'Yes', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Theme.of(context).colorScheme.warning), - ), - onPressed: () async { - Navigator.pop(customContext); - setState(() { - isInIdentityProcess = false; - }); - }, - ), - ], - ), - ); - } - Future copySeedPhrase() async { Clipboard.setData(ClipboardData(text: (await getPhrase()).toString())); @@ -628,189 +339,6 @@ class _IdentityVerificationScreenState } } - Future initIdenfySdk(String token) async { - IdenfyIdentificationResult? idenfySDKresult; - try { - idenfySDKresult = await IdenfySdkFlutter.start(token); - } catch (e) { - logger.e(e); - if (context.mounted) { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext dialogContext) => CustomDialog( - type: DialogType.Error, - image: Icons.close, - title: 'Error', - description: - 'Something went wrong. Please contact support if this issue persists.', - actions: [ - TextButton( - onPressed: () { - Navigator.pop(dialogContext); - }, - child: const Text('Close')) - ], - ), - ); - } - } - await Future.delayed(const Duration(seconds: 5)); - if (idenfySDKresult != null && - idenfySDKresult.autoIdentificationStatus != - AutoIdentificationStatus.UNVERIFIED) { - // await handleIdenfyResponse(); - } - } - - // Future handleIdenfyResponse() async { - // VerificationStatus verificationStatus; - // try { - // final address = await getMyAddress(); - // final idenfyServiceUrl = Globals().idenfyServiceUrl; - // verificationStatus = await getVerificationStatus(address: address, idenfyServiceUrl: idenfyServiceUrl); - // } catch (e) { - // setState(() { - // isLoading = false; - // }); - // logger.e(e); - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Error, - // image: Icons.error, - // title: 'Error', - // description: - // 'Failed to get the verification status. \nIf this issue persist, please contact support.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // ), - // ); - // } - // if (verificationStatus.status == VerificationState.VERIFIED) { - // identityVerified = true; - // setIsIdentityVerified(true); - // Globals().identityVerified.value = true; - // try { - // final data = await getVerificationData(address); - // final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); - // final lastName = utf8.decode(latin1.encode(data.orgLastName!)); - // final wallets = (await getPkidWallets()) - // .where((w) => w.type == WalletType.NATIVE) - // .toList(); - // await saveIdentity('$lastName $firstName', data.docIssuingCountry, - // data.docDob, data.docSex, data.idenfyRef, wallets.first.seed ); - // Events().emit(IdentityCallbackEvent(type: 'success')); - // } on BadRequest catch (e) { - // setState(() { - // isLoading = false; - // }); - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Warning, - // image: Icons.warning, - // title: 'Bad Request', - // description: - // '$e \nIf this issue persist, please contact support.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // )); - // } on Unauthorized catch (e) { - // setState(() { - // isLoading = false; - // }); - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Warning, - // image: Icons.warning, - // title: 'Unauthorized', - // description: - // '$e \nIf this issue persist, please contact support.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // )); - // } catch (e) { - // setState(() { - // isLoading = false; - // }); - // logger.e(e); - // return showDialog( - // context: context, - // builder: (BuildContext context) => CustomDialog( - // type: DialogType.Error, - // image: Icons.error, - // title: 'Error', - // description: - // 'Failed to process the verification details. \nIf this issue persist, please contact support.', - // actions: [ - // TextButton( - // child: const Text('Close'), - // onPressed: () { - // Navigator.pop(context); - // }, - // ), - // ], - // ), - // ); - // } - // } else { - // identityVerified = false; - // setIsIdentityVerified(false); - // Globals().identityVerified.value = false; - // Events().emit(IdentityCallbackEvent(type: 'failed')); - // } - // setState(() {}); - // } - - Widget _pleaseWait() { - return Dialog( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox( - height: 10, - ), - CircularProgressIndicator( - color: Theme.of(context).colorScheme.primary, - ), - const SizedBox( - height: 10, - ), - Text( - 'One moment please', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Theme.of(context).colorScheme.onSurface), - ), - const SizedBox( - height: 10, - ), - ], - ), - ); - } - Future _loadingDialog() { return showDialog( barrierDismissible: false, @@ -992,19 +520,6 @@ class _IdentityVerificationScreenState child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Row( - children: [ - Expanded( - child: Text( - step == 3 - ? 'Identity' - : (text.isEmpty - ? 'Unknown' - : text), - ), - ) - ], - ), step == 2 && Globals().hidePhoneButton.value == true @@ -1054,20 +569,6 @@ class _IdentityVerificationScreenState await verifyPhone(); } break; - - // Verify identity - case 3: - { - // await verifyIdentityProcess( - // context: context, - // setIdentityProcess: (value) => - // setState(() => - // isInIdentityProcess = - // value), - // setLoading: (value) => setState( - // () => isLoading = value)); - } - break; default: {} break; @@ -1100,12 +601,6 @@ class _IdentityVerificationScreenState if (step == 1) { return _changeEmailDialog(false); } - // Only make this section clickable if it is Identity Verification + Current Phase - if (step != 3) { - return; - } - - return showIdentityDetails(); }, child: Column(children: [ Padding( @@ -1167,18 +662,6 @@ class _IdentityVerificationScreenState ), ]) : const Column(), - step == 3 - ? const Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: EdgeInsets.only(left: 15), - child: Icon( - Icons.chevron_right, - ), - ), - ]) - : const Column() ], ), ], @@ -1187,237 +670,6 @@ class _IdentityVerificationScreenState ])); } - Future showIdentityDetails() { - return showDialog( - context: context, - builder: (BuildContext context) => Dialog( - child: FutureBuilder( - future: getIdentity(), - builder: (BuildContext customContext, - AsyncSnapshot snapshot) { - if (!snapshot.hasData) { - return _pleaseWait(); - } - String name = snapshot.data['identityName']; - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox( - height: 10, - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 15, vertical: 10), - child: Column( - children: [ - Text( - 'ID CARD', - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith( - fontWeight: FontWeight.bold, - color: Theme.of(context) - .colorScheme - .onSecondaryContainer), - textAlign: TextAlign.center, - ), - const SizedBox(height: 5), - Row(children: [ - Text( - 'Your own personal KYC ID CARD', - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .onSecondaryContainer), - ), - ]), - ], - )), - Container( - color: Theme.of(context).colorScheme.secondaryContainer, - padding: const EdgeInsets.symmetric( - horizontal: 15, vertical: 20), - child: Column( - children: [ - Row( - children: [ - Text( - 'Full name', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .primary, - ), - ) - ], - ), - Row( - children: [ - Text( - name, - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .onSecondaryContainer, - ), - ) - ], - ) - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 15, vertical: 20), - child: Column( - children: [ - Row( - children: [ - Text( - 'Birthday', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .primary, - ), - ), - ], - ), - Row( - children: [ - Text( - snapshot.data['identityDOB'] != 'None' - ? snapshot.data['identityDOB'] - : 'Unknown', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .onSecondaryContainer, - ), - ) - ], - ) - ], - ), - ), - Container( - color: Theme.of(context).colorScheme.secondaryContainer, - padding: const EdgeInsets.symmetric( - horizontal: 15, vertical: 20), - child: Column( - children: [ - Row( - children: [ - Text( - 'Country', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .primary, - ), - ) - ], - ), - Row( - children: [ - Text( - snapshot.data['identityCountry'] != 'None' - ? snapshot.data['identityCountry'] - : 'Unknown', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .onSecondaryContainer, - ), - ) - ], - ) - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 15, vertical: 20), - child: Column( - children: [ - Row( - children: [ - Text( - 'Gender', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .primary, - ), - ) - ], - ), - Row( - children: [ - Text( - snapshot.data['identityGender'] != 'None' - ? snapshot.data['identityGender'] - : 'Unknown', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .onSecondaryContainer, - ), - ) - ], - ) - ], - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextButton( - onPressed: () { - Navigator.pop(customContext); - }, - child: const Text('OK')), - const SizedBox( - height: 10, - ), - ], - ) - ], - ); - }, - ), - )); - } - Future resendEmailDialog(context) { return showDialog( context: context, diff --git a/app/lib/screens/wallets/wallet_info.dart b/app/lib/screens/wallets/wallet_info.dart index 236d9aba..cad6044b 100644 --- a/app/lib/screens/wallets/wallet_info.dart +++ b/app/lib/screens/wallets/wallet_info.dart @@ -281,18 +281,13 @@ class _WalletDetailsWidgetState extends State { child: ElevatedButton( onPressed: () async { if (widget.wallet.verificationStatus != 'VERIFIED') { - await verifyIdentityProcess( + await termsAndConditionsDialog( context: context, walletName: widget.wallet.name, - setIdentityProcess: (value) {}, - setLoading: (value) {}, walletAddress: widget.wallet.tfchainSecret ); - setState(() { - - }); } else { - showIdentityDetails(context); + showIdentityDetails(context, widget.wallet.tfchainSecret); } }, style: ElevatedButton.styleFrom( @@ -316,8 +311,6 @@ class _WalletDetailsWidgetState extends State { ), ), ) - - // else get verified data and show dialog ], ), ), diff --git a/app/lib/services/identity_service.dart b/app/lib/services/identity_service.dart index 7c4a4b51..c3e2d093 100644 --- a/app/lib/services/identity_service.dart +++ b/app/lib/services/identity_service.dart @@ -8,7 +8,7 @@ String getFullNameOfObject(Map identityName) { } String getCorrectState( - int step, emailVerified, phoneVerified, identityVerified) { + int step, emailVerified, phoneVerified) { if (step == 1) { if (!emailVerified) { return 'CurrentPhase'; @@ -27,23 +27,5 @@ String getCorrectState( return 'Verified'; } - if (step == 3) { - if (identityVerified) { - return 'Verified'; - } - - if (!emailVerified) { - return 'Unverified'; - } - - if (!phoneVerified) { - return 'Unverified'; - } - - if (emailVerified && phoneVerified && !identityVerified) { - return 'CurrentPhase'; - } - } - return ''; } diff --git a/app/lib/services/tfchain_service.dart b/app/lib/services/tfchain_service.dart index 970f98ad..de42ecc3 100644 --- a/app/lib/services/tfchain_service.dart +++ b/app/lib/services/tfchain_service.dart @@ -2,6 +2,8 @@ import 'dart:convert'; +import 'package:bip39/bip39.dart'; +import 'package:convert/convert.dart'; import 'package:tfchain_client/generated/dev/types/tfchain_support/types/farm.dart'; import 'package:threebotlogin/apps/wallet/wallet_config.dart'; import 'package:threebotlogin/helpers/globals.dart'; @@ -13,6 +15,7 @@ import 'package:crypto/crypto.dart'; import 'package:http/http.dart' as http; import 'package:hashlib/hashlib.dart' as hashlib; import 'package:signer/signer.dart'; +import 'package:substrate_bip39/substrate_bip39.dart'; Future getMySeed() async { final derivedSeed = await getDerivedSeed(WalletConfig().appId()); @@ -36,13 +39,15 @@ Future getMySigner() async { return signer; } -Future getSignerFromSeed(String seed) async { +Future getSignerFromSeed(String walletSeed) async { final signer = Signer(); - if (seed.startsWith('0x')) { - signer.fromHexSeed(seed, KPType.sr25519); + if (walletSeed.startsWith('0x')) { + signer.fromHexSeed(walletSeed, KPType.sr25519); } else { - //TODO: test this with mnemonic - signer.fromMnemonic(seed, KPType.sr25519); + final entropy = mnemonicToEntropy(walletSeed); + final seed = await CryptoScheme.seedFromEntropy(hex.decode(entropy)); + final hexSeed = '0x${hex.encode(seed).substring(0, 64)}'; + signer.fromHexSeed(hexSeed, KPType.sr25519); } return signer; } diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index fc5952e6..8ab0c90f 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -1,37 +1,196 @@ import 'dart:convert'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:idenfy_sdk_flutter/idenfy_sdk_flutter.dart'; import 'package:idenfy_sdk_flutter/models/idenfy_identification_status.dart'; import 'package:threebotlogin/events/events.dart'; import 'package:threebotlogin/events/identity_callback_event.dart'; import 'package:threebotlogin/models/idenfy.dart'; +import 'package:threebotlogin/screens/wizard/web_view.dart'; import 'package:threebotlogin/services/idenfy_service.dart'; import 'package:threebotlogin/helpers/globals.dart'; import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/services/wallet_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; import 'package:threebotlogin/widgets/custom_dialog.dart'; -import 'package:threebotlogin/models/wallet.dart'; import 'package:idenfy_sdk_flutter/models/auto_identification_status.dart'; +termsAndConditionsDialog( + {required BuildContext context, + required String walletName, + required String walletAddress}) { + bool isAccepted = false; + + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext customContext) { + return StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return CustomDialog( + title: 'Terms and Conditions', + type: DialogType.Info, + image: Icons.info, + widgetDescription: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(height: MediaQuery.of(context).size.height * 0.01), + RichText( + text: TextSpan( + text: + "As part of the verification process, we utilize iDenfy to verify your identity. Please ensure you review iDenfy's ", + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.onSurface, + ), + children: [ + TextSpan( + text: 'Security and Compliance', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + TextSpan( + text: ', which include their ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: Theme.of(context).colorScheme.onSurface, + ), + ), + TextSpan( + text: 'Terms & Conditions, Privacy Policy,', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + TextSpan( + text: ' and other relevant documents.', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: Theme.of(context).colorScheme.onSurface, + ), + ) + // + ], + ), + ), + Row( + children: [ + Checkbox( + value: isAccepted, + onChanged: (bool? value) { + setState(() { + isAccepted = value ?? false; + }); + }, + ), + Expanded( + child: RichText( + text: TextSpan( + text: 'I have read and agreed to ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: + Theme.of(context).colorScheme.onSurface, + ), + children: [ + TextSpan( + text: 'iDenfy Terms and Conditions.', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( + color: + Theme.of(context).colorScheme.primary, + decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const WebView( + url: + 'https://www.idenfy.com/security/', + title: 'iDenfy Terms and Conditions', + ), + ), + ); + }, + ), + TextSpan( + text: '.', + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + ), + ), + ], + ) + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(customContext); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: isAccepted + ? () async { + Navigator.pop(customContext); + await verifyIdentityProcess( + context: context, + walletName: walletName, + walletAddress: walletAddress); + } + : null, + child: Text( + 'Continue', + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: isAccepted + ? Theme.of(context).colorScheme.primary + : Theme.of(context).disabledColor, + ), + ), + ), + ], + ); + }, + ); + }, + ); +} + Future verifyIdentityProcess({ required BuildContext context, required String walletName, - required ValueChanged setLoading, - required ValueChanged setIdentityProcess, required String walletAddress, }) async { - setLoading(true); - Token token; try { token = await getToken(walletAddress); - - setLoading(false); - setIdentityProcess(true); } on BadRequest catch (e) { - setLoading(false); await showWarningDialog( context: context, title: 'Bad Request', @@ -39,7 +198,6 @@ Future verifyIdentityProcess({ ); return; } on Unauthorized catch (e) { - setLoading(false); await showWarningDialog( context: context, title: 'Unauthorized', @@ -47,7 +205,6 @@ Future verifyIdentityProcess({ ); return; } on TooManyRequests catch (_) { - setLoading(false); final maxRetries = Globals().maximumKYCRetries; await showWarningDialog( context: context, @@ -59,7 +216,6 @@ Future verifyIdentityProcess({ } on NotEnoughBalance catch (_) { final wallets = (await getPkidWallets()).where((w) => w.name == walletName).toList(); - setLoading(false); final minimumBalance = Globals().minimumTFChainBalanceForKYC; await showWarningDialog( context: context, @@ -69,7 +225,6 @@ Future verifyIdentityProcess({ : 'Please fund your ${wallets.first.name} TFChain wallet with at least $minimumBalance TFTs.'); return; } on NoTwinId catch (_) { - setLoading(false); await showWarningDialog( context: context, title: "Account doesn't exist", @@ -77,15 +232,9 @@ Future verifyIdentityProcess({ 'Your account is not activated.\nPlease go to wallet section and initialize your wallet.'); return; } on AlreadyVerified catch (_) { - setLoading(false); return await handleIdenfyResponse( - context: context, - setLoading: setLoading, - setIdentityVerified: setIdentityProcess, - walletName: walletName, - walletAddress: walletAddress); + context: context, walletName: walletName, walletAddress: walletAddress); } catch (e) { - setLoading(false); logger.e(e); await showErrorDialog( context: context, @@ -96,17 +245,11 @@ Future verifyIdentityProcess({ return; } await initIdenfySdk(token.authToken, - context: context, - setLoading: setLoading, - setIdentityVerified: setIdentityProcess, - walletName: walletName, - walletAddress: walletAddress); + context: context, walletName: walletName, walletAddress: walletAddress); } Future handleIdenfyResponse({ required BuildContext context, - required ValueChanged setLoading, - required ValueChanged setIdentityVerified, required String walletName, required String walletAddress, }) async { @@ -116,7 +259,6 @@ Future handleIdenfyResponse({ verificationStatus = await getVerificationStatus( address: walletAddress, idenfyServiceUrl: idenfyServiceUrl); } catch (e) { - setLoading(false); logger.e(e); await showErrorDialog( context: context, @@ -128,7 +270,6 @@ Future handleIdenfyResponse({ } if (verificationStatus.status == VerificationState.VERIFIED) { - setIdentityVerified(true); Globals().identityVerified.value = true; try { @@ -141,19 +282,16 @@ Future handleIdenfyResponse({ data.docDob, data.docSex, data.idenfyRef, wallets.first.seed); Events().emit(IdentityCallbackEvent(type: 'success')); } on BadRequest catch (e) { - setLoading(false); await showWarningDialog( context: context, title: 'Bad Request', description: '$e \nIf this issue persist, please contact support.'); } on Unauthorized catch (e) { - setLoading(false); await showWarningDialog( context: context, title: 'Unauthorized', description: '$e \nIf this issue persist, please contact support.'); } catch (e) { - setLoading(false); logger.e(e); await showErrorDialog( context: context, @@ -162,7 +300,6 @@ Future handleIdenfyResponse({ ); } } else { - setIdentityVerified(false); Globals().identityVerified.value = false; Events().emit(IdentityCallbackEvent(type: 'failed')); } @@ -170,8 +307,6 @@ Future handleIdenfyResponse({ Future initIdenfySdk(String token, {required BuildContext context, - required ValueChanged setLoading, - required ValueChanged setIdentityVerified, required String walletName, required String walletAddress}) async { IdenfyIdentificationResult? idenfySDKresult; @@ -187,16 +322,12 @@ Future initIdenfySdk(String token, 'Something went wrong. Please contact support if this issue persists.'); } } - await Future.delayed(const Duration(seconds: 5)); + await Future.delayed(const Duration(seconds: 7)); if (idenfySDKresult != null && idenfySDKresult.autoIdentificationStatus != AutoIdentificationStatus.UNVERIFIED) { await handleIdenfyResponse( - context: context, - setLoading: setLoading, - setIdentityVerified: setIdentityVerified, - walletName: walletName, - walletAddress: walletAddress); + context: context, walletName: walletName, walletAddress: walletAddress); } } @@ -277,18 +408,52 @@ Widget pleaseWait(BuildContext context) { ); } -Future showIdentityDetails(BuildContext context) { +Future showIdentityDetails( + BuildContext context, String walletAddress) { return showDialog( context: context, builder: (BuildContext context) => Dialog( child: FutureBuilder( - future: getIdentity(), + future: getVerificationData(walletAddress), builder: (BuildContext customContext, AsyncSnapshot snapshot) { - if (!snapshot.hasData) { + if (snapshot.connectionState == ConnectionState.waiting) { return pleaseWait(context); + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.data == null) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 10, + ), + Text( + 'No data available for the provided wallet address.', + style: Theme.of(context).textTheme.bodyMedium, + textAlign: TextAlign.center, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + onPressed: () { + Navigator.pop(customContext); + }, + child: const Text('OK')), + const SizedBox( + height: 10, + ), + ], + ) + ], + ); } - String name = snapshot.data['identityName']; + final data = snapshot.data; + final firstName = + utf8.decode(latin1.encode(data.orgFirstName!)); + final lastName = utf8.decode(latin1.encode(data.orgLastName!)); + final fullName = '$firstName $lastName'; return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -351,7 +516,7 @@ Future showIdentityDetails(BuildContext context) { Row( children: [ Text( - name, + fullName, style: Theme.of(context) .textTheme .bodyMedium! @@ -388,9 +553,7 @@ Future showIdentityDetails(BuildContext context) { Row( children: [ Text( - snapshot.data['identityDOB'] != 'None' - ? snapshot.data['identityDOB'] - : 'Unknown', + data.docDob != 'None' ? data.docDob : 'Unknown', style: Theme.of(context) .textTheme .bodyMedium! @@ -428,8 +591,8 @@ Future showIdentityDetails(BuildContext context) { Row( children: [ Text( - snapshot.data['identityCountry'] != 'None' - ? snapshot.data['identityCountry'] + data.docIssuingCountry != 'None' + ? data.docIssuingCountry : 'Unknown', style: Theme.of(context) .textTheme @@ -467,9 +630,7 @@ Future showIdentityDetails(BuildContext context) { Row( children: [ Text( - snapshot.data['identityGender'] != 'None' - ? snapshot.data['identityGender'] - : 'Unknown', + data.docSex != 'None' ? data.docSex : 'Unknown', style: Theme.of(context) .textTheme .bodyMedium! From 35e3511a205f9219138e5943d18443da25507078 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 1 Jan 2025 17:18:51 +0200 Subject: [PATCH 12/24] fix context bug --- app/lib/screens/wallets/wallet_info.dart | 4 +- app/lib/widgets/kyc_widget.dart | 117 ++++++++++------------- app/lib/widgets/wallets/wallet_card.dart | 1 - 3 files changed, 55 insertions(+), 67 deletions(-) diff --git a/app/lib/screens/wallets/wallet_info.dart b/app/lib/screens/wallets/wallet_info.dart index cad6044b..b18e8c5c 100644 --- a/app/lib/screens/wallets/wallet_info.dart +++ b/app/lib/screens/wallets/wallet_info.dart @@ -283,9 +283,9 @@ class _WalletDetailsWidgetState extends State { if (widget.wallet.verificationStatus != 'VERIFIED') { await termsAndConditionsDialog( context: context, - walletName: widget.wallet.name, - walletAddress: widget.wallet.tfchainSecret + walletSeed: widget.wallet.tfchainSecret ); + setState(() {}); } else { showIdentityDetails(context, widget.wallet.tfchainSecret); } diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index 8ab0c90f..25cd5a9d 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -17,9 +17,7 @@ import 'package:threebotlogin/widgets/custom_dialog.dart'; import 'package:idenfy_sdk_flutter/models/auto_identification_status.dart'; termsAndConditionsDialog( - {required BuildContext context, - required String walletName, - required String walletAddress}) { + {required BuildContext context, required String walletSeed}) { bool isAccepted = false; showDialog( @@ -27,7 +25,7 @@ termsAndConditionsDialog( barrierDismissible: false, builder: (BuildContext customContext) { return StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { + builder: (BuildContext childContext, StateSetter setState) { return CustomDialog( title: 'Terms and Conditions', type: DialogType.Info, @@ -38,51 +36,51 @@ termsAndConditionsDialog( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - SizedBox(height: MediaQuery.of(context).size.height * 0.01), + SizedBox(height: MediaQuery.of(childContext).size.height * 0.01), RichText( text: TextSpan( text: "As part of the verification process, we utilize iDenfy to verify your identity. Please ensure you review iDenfy's ", - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: Theme.of(context).colorScheme.onSurface, + style: Theme.of(childContext).textTheme.bodyMedium!.copyWith( + color: Theme.of(childContext).colorScheme.onSurface, ), children: [ TextSpan( text: 'Security and Compliance', - style: Theme.of(context) + style: Theme.of(childContext) .textTheme .bodyMedium! .copyWith( fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(childContext).colorScheme.onSurface, ), ), TextSpan( text: ', which include their ', - style: Theme.of(context) + style: Theme.of(childContext) .textTheme .bodyMedium! .copyWith( - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(childContext).colorScheme.onSurface, ), ), TextSpan( text: 'Terms & Conditions, Privacy Policy,', - style: Theme.of(context) + style: Theme.of(childContext) .textTheme .bodyMedium! .copyWith( fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(childContext).colorScheme.onSurface, ), ), TextSpan( text: ' and other relevant documents.', - style: Theme.of(context) + style: Theme.of(childContext) .textTheme .bodyMedium! .copyWith( - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(childContext).colorScheme.onSurface, ), ) // @@ -103,28 +101,28 @@ termsAndConditionsDialog( child: RichText( text: TextSpan( text: 'I have read and agreed to ', - style: Theme.of(context) + style: Theme.of(childContext) .textTheme .bodyMedium! .copyWith( color: - Theme.of(context).colorScheme.onSurface, + Theme.of(childContext).colorScheme.onSurface, ), children: [ TextSpan( text: 'iDenfy Terms and Conditions.', - style: Theme.of(context) + style: Theme.of(childContext) .textTheme .bodyMedium! .copyWith( color: - Theme.of(context).colorScheme.primary, + Theme.of(childContext).colorScheme.primary, decoration: TextDecoration.underline, ), recognizer: TapGestureRecognizer() ..onTap = () { Navigator.push( - context, + childContext, MaterialPageRoute( builder: (context) => const WebView( url: @@ -137,7 +135,7 @@ termsAndConditionsDialog( ), TextSpan( text: '.', - style: Theme.of(context).textTheme.bodyMedium, + style: Theme.of(childContext).textTheme.bodyMedium, ), ], ), @@ -160,17 +158,15 @@ termsAndConditionsDialog( ? () async { Navigator.pop(customContext); await verifyIdentityProcess( - context: context, - walletName: walletName, - walletAddress: walletAddress); + context: context, walletSeed: walletSeed); } : null, child: Text( 'Continue', - style: Theme.of(context).textTheme.bodyMedium!.copyWith( + style: Theme.of(childContext).textTheme.bodyMedium!.copyWith( color: isAccepted - ? Theme.of(context).colorScheme.primary - : Theme.of(context).disabledColor, + ? Theme.of(childContext).colorScheme.primary + : Theme.of(childContext).disabledColor, ), ), ), @@ -184,21 +180,20 @@ termsAndConditionsDialog( Future verifyIdentityProcess({ required BuildContext context, - required String walletName, - required String walletAddress, + required String walletSeed, }) async { Token token; try { - token = await getToken(walletAddress); + token = await getToken(walletSeed); } on BadRequest catch (e) { - await showWarningDialog( + showWarningDialog( context: context, title: 'Bad Request', description: '$e \nIf this issue persist, please contact support.', ); return; } on Unauthorized catch (e) { - await showWarningDialog( + showWarningDialog( context: context, title: 'Unauthorized', description: '$e \nIf this issue persist, please contact support.', @@ -206,7 +201,7 @@ Future verifyIdentityProcess({ return; } on TooManyRequests catch (_) { final maxRetries = Globals().maximumKYCRetries; - await showWarningDialog( + showWarningDialog( context: context, title: 'Maximum Requests Reached', description: @@ -215,9 +210,9 @@ Future verifyIdentityProcess({ return; } on NotEnoughBalance catch (_) { final wallets = - (await getPkidWallets()).where((w) => w.name == walletName).toList(); + (await getPkidWallets()).where((w) => w.seed == walletSeed).toList(); final minimumBalance = Globals().minimumTFChainBalanceForKYC; - await showWarningDialog( + showWarningDialog( context: context, title: 'Not enough balance', description: wallets.isEmpty @@ -225,18 +220,17 @@ Future verifyIdentityProcess({ : 'Please fund your ${wallets.first.name} TFChain wallet with at least $minimumBalance TFTs.'); return; } on NoTwinId catch (_) { - await showWarningDialog( + showWarningDialog( context: context, title: "Account doesn't exist", description: 'Your account is not activated.\nPlease go to wallet section and initialize your wallet.'); return; } on AlreadyVerified catch (_) { - return await handleIdenfyResponse( - context: context, walletName: walletName, walletAddress: walletAddress); + return await handleIdenfyResponse(context: context, walletSeed: walletSeed); } catch (e) { logger.e(e); - await showErrorDialog( + showErrorDialog( context: context, title: 'Failed to setup process', description: @@ -245,22 +239,21 @@ Future verifyIdentityProcess({ return; } await initIdenfySdk(token.authToken, - context: context, walletName: walletName, walletAddress: walletAddress); + context: context, walletSeed: walletSeed); } Future handleIdenfyResponse({ required BuildContext context, - required String walletName, - required String walletAddress, + required String walletSeed, }) async { VerificationStatus verificationStatus; try { final idenfyServiceUrl = Globals().idenfyServiceUrl; verificationStatus = await getVerificationStatus( - address: walletAddress, idenfyServiceUrl: idenfyServiceUrl); + address: walletSeed, idenfyServiceUrl: idenfyServiceUrl); } catch (e) { logger.e(e); - await showErrorDialog( + showErrorDialog( context: context, title: 'Error', description: @@ -273,27 +266,27 @@ Future handleIdenfyResponse({ Globals().identityVerified.value = true; try { - final data = await getVerificationData(walletAddress); + final data = await getVerificationData(walletSeed); final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); final lastName = utf8.decode(latin1.encode(data.orgLastName!)); final wallets = - (await getPkidWallets()).where((w) => w.name == walletName).toList(); + (await getPkidWallets()).where((w) => w.name == walletSeed).toList(); await saveIdentity('$lastName $firstName', data.docIssuingCountry, data.docDob, data.docSex, data.idenfyRef, wallets.first.seed); Events().emit(IdentityCallbackEvent(type: 'success')); } on BadRequest catch (e) { - await showWarningDialog( + showWarningDialog( context: context, title: 'Bad Request', description: '$e \nIf this issue persist, please contact support.'); } on Unauthorized catch (e) { - await showWarningDialog( + showWarningDialog( context: context, title: 'Unauthorized', description: '$e \nIf this issue persist, please contact support.'); } catch (e) { logger.e(e); - await showErrorDialog( + showErrorDialog( context: context, title: 'Error', description: 'Failed to process verification details', @@ -306,16 +299,14 @@ Future handleIdenfyResponse({ } Future initIdenfySdk(String token, - {required BuildContext context, - required String walletName, - required String walletAddress}) async { + {required BuildContext context, required String walletSeed}) async { IdenfyIdentificationResult? idenfySDKresult; try { idenfySDKresult = await IdenfySdkFlutter.start(token); } catch (e) { logger.e(e); if (context.mounted) { - await showErrorDialog( + showErrorDialog( context: context, title: 'Error', description: @@ -326,17 +317,16 @@ Future initIdenfySdk(String token, if (idenfySDKresult != null && idenfySDKresult.autoIdentificationStatus != AutoIdentificationStatus.UNVERIFIED) { - await handleIdenfyResponse( - context: context, walletName: walletName, walletAddress: walletAddress); + await handleIdenfyResponse(context: context, walletSeed: walletSeed); } } -Future showWarningDialog({ +void showWarningDialog({ required BuildContext context, required String title, required String description, -}) async { - await showDialog( +}) { + showDialog( context: context, builder: (BuildContext context) => CustomDialog( type: DialogType.Warning, @@ -355,12 +345,12 @@ Future showWarningDialog({ ); } -Future showErrorDialog({ +void showErrorDialog({ required BuildContext context, required String title, required String description, -}) async { - await showDialog( +}) { + showDialog( context: context, builder: (BuildContext context) => CustomDialog( type: DialogType.Error, @@ -408,13 +398,12 @@ Widget pleaseWait(BuildContext context) { ); } -Future showIdentityDetails( - BuildContext context, String walletAddress) { +Future showIdentityDetails(BuildContext context, String walletSeed) { return showDialog( context: context, builder: (BuildContext context) => Dialog( child: FutureBuilder( - future: getVerificationData(walletAddress), + future: getVerificationData(walletSeed), builder: (BuildContext customContext, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { diff --git a/app/lib/widgets/wallets/wallet_card.dart b/app/lib/widgets/wallets/wallet_card.dart index 7a89cd20..e3089d89 100644 --- a/app/lib/widgets/wallets/wallet_card.dart +++ b/app/lib/widgets/wallets/wallet_card.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:threebotlogin/helpers/globals.dart'; import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/helpers/transaction_helpers.dart'; -import 'package:threebotlogin/models/idenfy.dart'; import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/screens/wallets/wallet_details.dart'; import 'package:threebotlogin/services/stellar_service.dart' as StellarService; From 3b490f409d4c5c76e36fe4e3100abb5a4531ca55 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Thu, 2 Jan 2025 14:52:49 +0200 Subject: [PATCH 13/24] remove kyc stuff from identity preferences --- app/lib/helpers/kyc_helpers.dart | 45 +--- app/lib/helpers/login_helpers.dart | 74 ------ app/lib/main.dart | 4 +- .../screens/identity_verification_screen.dart | 7 +- app/lib/screens/recover_screen.dart | 5 +- .../services/shared_preference_service.dart | 29 --- app/lib/widgets/kyc_widget.dart | 77 +++--- app/lib/widgets/preference_dialog.dart | 226 ------------------ 8 files changed, 46 insertions(+), 421 deletions(-) diff --git a/app/lib/helpers/kyc_helpers.dart b/app/lib/helpers/kyc_helpers.dart index b2f1921a..7f2b0a52 100644 --- a/app/lib/helpers/kyc_helpers.dart +++ b/app/lib/helpers/kyc_helpers.dart @@ -1,15 +1,10 @@ import 'dart:convert'; import 'package:flutter_pkid/flutter_pkid.dart'; -import 'package:threebotlogin/models/idenfy.dart'; -import 'package:threebotlogin/models/wallet.dart'; -import 'package:threebotlogin/services/idenfy_service.dart'; import 'package:threebotlogin/services/migration_service.dart'; import 'package:threebotlogin/services/pkid_service.dart'; -import 'package:threebotlogin/services/tfchain_service.dart'; import 'package:threebotlogin/services/tools_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; -import 'package:threebotlogin/services/wallet_service.dart'; import 'globals.dart'; @@ -17,9 +12,6 @@ Future fetchPKidData() async { FlutterPkid client = await getPkidClient(); List keyWords = ['email', 'phone']; - final wallets = (await getPkidWallets()) - .where((w) => w.type == WalletType.NATIVE) - .toList(); var futures = keyWords.map((keyword) async { var pKidResult = await client.getPKidDoc(keyword); @@ -31,17 +23,11 @@ Future fetchPKidData() async { var pKidResult = await Future.wait(futures); Map dataMap = pKidResult.asMap(); - await handleKYCData(dataMap[0], dataMap[1], wallets.first.seed); + await handleKYCData(dataMap[0], dataMap[1]); } Future handleKYCData( - Map emailData, Map phoneData, String walletAddress) async { - final idenfyServiceUrl = Globals().idenfyServiceUrl; - final identityVerificationStatus = - await getVerificationStatus(address: walletAddress, idenfyServiceUrl: idenfyServiceUrl); - - await saveCorrectVerificationStates( - emailData, phoneData, identityVerificationStatus); + Map emailData, Map phoneData) async { bool? isEmailVerified = await getIsEmailVerified(); bool? isPhoneVerified = await getIsPhoneVerified(); @@ -72,36 +58,9 @@ Future handleKYCData( if (isIdentityVerified == true) { Globals().identityVerified.value = true; - final data = await getVerificationData(walletAddress); - final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); - final lastName = utf8.decode(latin1.encode(data.orgLastName!)); - await saveIdentity('$lastName $firstName', data.docIssuingCountry, - data.docDob, data.docSex, data.idenfyRef, walletAddress); } } -Future saveCorrectVerificationStates( - Map emailData, - Map phoneData, - VerificationStatus identityVerificationStatus) async { - if (identityVerificationStatus.status == VerificationState.VERIFIED) { - await setIsIdentityVerified(true); - } else { - await setIsIdentityVerified(false); - } - - if (phoneData.containsKey('spi')) { - await setIsPhoneVerified(true); - } else { - await setIsPhoneVerified(false); - } - - if (emailData.containsKey('sei')) { - await setIsEmailVerified(true); - } else { - await setIsEmailVerified(false); - } -} bool checkEmail(String email) { String? emailValue = diff --git a/app/lib/helpers/login_helpers.dart b/app/lib/helpers/login_helpers.dart index 5845fde0..3719f434 100644 --- a/app/lib/helpers/login_helpers.dart +++ b/app/lib/helpers/login_helpers.dart @@ -49,80 +49,6 @@ Future?> readScopeAsObject( scope['digitalTwin'] = 'OK'; } - if (scopePermissionsDecoded['identityName'] == true) { - Map identityDetails = await getIdentity(); - - String identityName = identityDetails['identityName']; - String sIdentityName = identityDetails['signedIdentityNameIdentifier']; - - scope['identityName'] = { - 'identityName': identityName, - 'signedIdentityNameIdentifier': sIdentityName - }; - } - - if (scopePermissionsDecoded['identityDOB'] == true) { - Map identityDetails = await getIdentity(); - - String identityDOB = identityDetails['identityDOB']; - String sIdentityDOB = identityDetails['signedIdentityDOBIdentifier']; - - scope['identityDOB'] = { - 'identityDOB': identityDOB, - 'signedIdentityDOB': sIdentityDOB - }; - } - - if (scopePermissionsDecoded['identityCountry'] == true) { - Map identityDetails = await getIdentity(); - - String identityCountry = identityDetails['identityCountry']; - String sIdentityCountryIdentifier = - identityDetails['signedIdentityCountryIdentifier']; - - scope['identityCountry'] = { - 'identityCountry': identityCountry, - 'signedIdentityCountryIdentifier': sIdentityCountryIdentifier - }; - } - - if (scopePermissionsDecoded['identityCountry'] == true) { - Map identityDetails = await getIdentity(); - - String identityCountry = identityDetails['identityCountry']; - String sIdentityCountryIdentifier = - identityDetails['signedIdentityCountryIdentifier']; - - scope['identityCountry'] = { - 'identityCountry': identityCountry, - 'signedIdentityCountryIdentifier': sIdentityCountryIdentifier - }; - } - - if (scopePermissionsDecoded['identityDocumentMeta'] == true) { - Map identityDetails = await getIdentity(); - - String identityDocumentMeta = identityDetails['identityDocumentMeta']; - String sIdentityDocumentMeta = - identityDetails['signedIdentityCountryIdentifier']; - - scope['identityDocumentMeta'] = { - 'identityDocumentMeta': identityDocumentMeta, - 'signedIdentityDocumentMeta': sIdentityDocumentMeta - }; - } - - if (scopePermissionsDecoded['identityGender'] == true) { - Map identityDetails = await getIdentity(); - - String identityGender = identityDetails['identityGender']; - String sIdentityGender = identityDetails['signedIdentityGender']; - - scope['identityGender'] = { - 'identityGender': identityGender, - 'signedIdentityGender': sIdentityGender - }; - } if (scopePermissionsDecoded['walletAddress'] == true) { scope['walletAddressData'] = { diff --git a/app/lib/main.dart b/app/lib/main.dart index 787ed6f2..89ba0ff6 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -49,12 +49,10 @@ Future main() async { Future setGlobalValues() async { Map email = await getEmail(); Map phone = await getPhone(); - Map identity = await getIdentity(); Globals().emailVerified.value = (email['sei'] != null); Globals().phoneVerified.value = (phone['spi'] != null); - Globals().identityVerified.value = - (identity['signedIdentityNameIdentifier'] != null); + } class MyApp extends ConsumerWidget { diff --git a/app/lib/screens/identity_verification_screen.dart b/app/lib/screens/identity_verification_screen.dart index f928bfae..0262e884 100644 --- a/app/lib/screens/identity_verification_screen.dart +++ b/app/lib/screens/identity_verification_screen.dart @@ -193,12 +193,13 @@ class _IdentityVerificationScreenState ), ); } - + @override Widget build(BuildContext context) { return LayoutDrawer( titleText: 'Identity', - content: FutureBuilder( + content: + FutureBuilder( future: getEmail(), builder: (ctx, snapshot) { if (snapshot.connectionState == ConnectionState.done) { @@ -402,6 +403,8 @@ class _IdentityVerificationScreenState } Widget unVerifiedWidget(step, text, icon) { + print("HELLO EMAIL"); + print(text); return GestureDetector( onTap: () async {}, child: Opacity( diff --git a/app/lib/screens/recover_screen.dart b/app/lib/screens/recover_screen.dart index c24a96ef..b937cadb 100644 --- a/app/lib/screens/recover_screen.dart +++ b/app/lib/screens/recover_screen.dart @@ -94,10 +94,7 @@ class _RecoverScreenState extends State { await savePhrase(seedPhrase); await saveFingerprint(false); await saveDoubleName(doubleName); - final wallets = (await getPkidWallets()) - .where((w) => w.type == WalletType.NATIVE) - .toList(); - await handleKYCData(dataMap[0], dataMap[1], wallets.first.seed); + await handleKYCData(dataMap[0], dataMap[1]); await fixPkidMigration(); } catch (e) { diff --git a/app/lib/services/shared_preference_service.dart b/app/lib/services/shared_preference_service.dart index d15f9c91..978001f5 100644 --- a/app/lib/services/shared_preference_service.dart +++ b/app/lib/services/shared_preference_service.dart @@ -246,35 +246,6 @@ Future getIsIdentityVerified() async { return prefs.getBool('isIdentityVerified'); } -Future> getIdentity() async { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - return { - 'identityName': prefs.getString('identityName'), - 'identityCountry': prefs.getString('identityCountry'), - 'identityDOB': prefs.getString('identityDOB'), - 'identityGender': prefs.getString('identityGender'), - 'walletSecret': prefs.getString('walletSecret'), - }; -} - -Future saveIdentity(String? identityName, String? identityCountry, - String? identityDOB, String? identityGender, String? referenceId, String? walletSecret) async { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - prefs.remove('identityName'); - prefs.remove('identityCountry'); - prefs.remove('identityDOB'); - prefs.remove('identityGender'); - prefs.remove('walletSecret'); - - prefs.setString('identityName', identityName!); - prefs.setString('identityCountry', identityCountry!); - prefs.setString('identityDOB', identityDOB!); - prefs.setString('identityGender', identityGender!); - prefs.setString('walletSecret', walletSecret!); - - updateUserData('identity_reference', referenceId!); - Globals().identityVerified.value = true; -} /// /// diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index 25cd5a9d..80495ab3 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -36,12 +36,16 @@ termsAndConditionsDialog( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - SizedBox(height: MediaQuery.of(childContext).size.height * 0.01), + SizedBox( + height: MediaQuery.of(childContext).size.height * 0.01), RichText( text: TextSpan( text: "As part of the verification process, we utilize iDenfy to verify your identity. Please ensure you review iDenfy's ", - style: Theme.of(childContext).textTheme.bodyMedium!.copyWith( + style: Theme.of(childContext) + .textTheme + .bodyMedium! + .copyWith( color: Theme.of(childContext).colorScheme.onSurface, ), children: [ @@ -52,7 +56,9 @@ termsAndConditionsDialog( .bodyMedium! .copyWith( fontWeight: FontWeight.bold, - color: Theme.of(childContext).colorScheme.onSurface, + color: Theme.of(childContext) + .colorScheme + .onSurface, ), ), TextSpan( @@ -61,7 +67,9 @@ termsAndConditionsDialog( .textTheme .bodyMedium! .copyWith( - color: Theme.of(childContext).colorScheme.onSurface, + color: Theme.of(childContext) + .colorScheme + .onSurface, ), ), TextSpan( @@ -71,7 +79,9 @@ termsAndConditionsDialog( .bodyMedium! .copyWith( fontWeight: FontWeight.bold, - color: Theme.of(childContext).colorScheme.onSurface, + color: Theme.of(childContext) + .colorScheme + .onSurface, ), ), TextSpan( @@ -80,7 +90,9 @@ termsAndConditionsDialog( .textTheme .bodyMedium! .copyWith( - color: Theme.of(childContext).colorScheme.onSurface, + color: Theme.of(childContext) + .colorScheme + .onSurface, ), ) // @@ -105,8 +117,9 @@ termsAndConditionsDialog( .textTheme .bodyMedium! .copyWith( - color: - Theme.of(childContext).colorScheme.onSurface, + color: Theme.of(childContext) + .colorScheme + .onSurface, ), children: [ TextSpan( @@ -115,8 +128,9 @@ termsAndConditionsDialog( .textTheme .bodyMedium! .copyWith( - color: - Theme.of(childContext).colorScheme.primary, + color: Theme.of(childContext) + .colorScheme + .primary, decoration: TextDecoration.underline, ), recognizer: TapGestureRecognizer() @@ -135,7 +149,8 @@ termsAndConditionsDialog( ), TextSpan( text: '.', - style: Theme.of(childContext).textTheme.bodyMedium, + style: + Theme.of(childContext).textTheme.bodyMedium, ), ], ), @@ -248,9 +263,11 @@ Future handleIdenfyResponse({ }) async { VerificationStatus verificationStatus; try { + logger.d('Fetching verification status from iDenfy service...'); final idenfyServiceUrl = Globals().idenfyServiceUrl; verificationStatus = await getVerificationStatus( address: walletSeed, idenfyServiceUrl: idenfyServiceUrl); + logger.d('Fetched verification status: ${verificationStatus.status}'); } catch (e) { logger.e(e); showErrorDialog( @@ -263,39 +280,18 @@ Future handleIdenfyResponse({ } if (verificationStatus.status == VerificationState.VERIFIED) { + logger.d('Verification status is VERIFIED. Updating global state.'); + Globals().identityVerified.value = true; + Events().emit(IdentityCallbackEvent(type: 'success')); - try { - final data = await getVerificationData(walletSeed); - final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); - final lastName = utf8.decode(latin1.encode(data.orgLastName!)); - final wallets = - (await getPkidWallets()).where((w) => w.name == walletSeed).toList(); - await saveIdentity('$lastName $firstName', data.docIssuingCountry, - data.docDob, data.docSex, data.idenfyRef, wallets.first.seed); - Events().emit(IdentityCallbackEvent(type: 'success')); - } on BadRequest catch (e) { - showWarningDialog( - context: context, - title: 'Bad Request', - description: '$e \nIf this issue persist, please contact support.'); - } on Unauthorized catch (e) { - showWarningDialog( - context: context, - title: 'Unauthorized', - description: '$e \nIf this issue persist, please contact support.'); - } catch (e) { - logger.e(e); - showErrorDialog( - context: context, - title: 'Error', - description: 'Failed to process verification details', - ); - } } else { + logger.d('Verification status is not VERIFIED. Marking as failed.'); + Globals().identityVerified.value = false; Events().emit(IdentityCallbackEvent(type: 'failed')); } + logger.d('Finished handleIdenfyResponse for walletSeed: $walletSeed'); } Future initIdenfySdk(String token, @@ -313,7 +309,7 @@ Future initIdenfySdk(String token, 'Something went wrong. Please contact support if this issue persists.'); } } - await Future.delayed(const Duration(seconds: 7)); + await Future.delayed(const Duration(seconds: 13)); if (idenfySDKresult != null && idenfySDKresult.autoIdentificationStatus != AutoIdentificationStatus.UNVERIFIED) { @@ -419,7 +415,8 @@ Future showIdentityDetails(BuildContext context, String walletSeed) { ), Text( 'No data available for the provided wallet address.', - style: Theme.of(context).textTheme.bodyMedium, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.onSurface), textAlign: TextAlign.center, ), Row( diff --git a/app/lib/widgets/preference_dialog.dart b/app/lib/widgets/preference_dialog.dart index 0988649d..87f68f7a 100644 --- a/app/lib/widgets/preference_dialog.dart +++ b/app/lib/widgets/preference_dialog.dart @@ -369,232 +369,6 @@ class _PreferenceDialogState extends State { } }, ); - - case 'identityName': - return FutureBuilder( - future: getIdentity(), - builder: - (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return Container( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: - Theme.of(context).colorScheme.onSurface, - width: 0.5, - )), - ), - child: CheckboxListTile( - value: - (previousSelectedScope[scopeItem] == null) - ? mandatory - : previousSelectedScope[scopeItem], - onChanged: ((mandatory == true) - ? null - : (value) { - toggleScope(scopeItem, value); - }), - title: Text( - 'NAME (IDENTITY)${(mandatory ? " *" : "")}', - style: Theme.of(context) - .textTheme - .bodyLarge! - .copyWith( - color: Theme.of(context) - .colorScheme - .onSurface, - fontWeight: FontWeight.bold, - ), - ), - ), - ); - } else { - return const SizedBox(width: 0, height: 0); - } - }, - ); - - case 'identityDOB': - return FutureBuilder( - future: getIdentity(), - builder: - (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return Container( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: - Theme.of(context).colorScheme.onSurface, - width: 0.5, - )), - ), - child: CheckboxListTile( - value: - (previousSelectedScope[scopeItem] == null) - ? mandatory - : previousSelectedScope[scopeItem], - onChanged: ((mandatory == true) - ? null - : (value) { - toggleScope(scopeItem, value); - }), - title: Text( - 'DATE OF BIRTH (IDENTITY)${(mandatory ? " *" : "")}', - style: Theme.of(context) - .textTheme - .bodyLarge! - .copyWith( - color: Theme.of(context) - .colorScheme - .onSurface, - fontWeight: FontWeight.bold, - ), - ), - ), - ); - } else { - return const SizedBox(width: 0, height: 0); - } - }, - ); - - case 'identityGender': - return FutureBuilder( - future: getIdentity(), - builder: - (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return Container( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: - Theme.of(context).colorScheme.onSurface, - width: 0.5, - )), - ), - child: CheckboxListTile( - value: - (previousSelectedScope[scopeItem] == null) - ? mandatory - : previousSelectedScope[scopeItem], - onChanged: ((mandatory == true) - ? null - : (value) { - toggleScope(scopeItem, value); - }), - title: Text( - 'GENDER (IDENTITY)${(mandatory ? " *" : "")}', - style: Theme.of(context) - .textTheme - .bodyLarge! - .copyWith( - color: Theme.of(context) - .colorScheme - .onSurface, - fontWeight: FontWeight.bold, - ), - ), - ), - ); - } else { - return const SizedBox(width: 0, height: 0); - } - }, - ); - - case 'identityDocumentMeta': - return FutureBuilder( - future: getIdentity(), - builder: - (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return Container( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: - Theme.of(context).colorScheme.onSurface, - width: 0.5, - )), - ), - child: CheckboxListTile( - value: - (previousSelectedScope[scopeItem] == null) - ? mandatory - : previousSelectedScope[scopeItem], - onChanged: ((mandatory == true) - ? null - : (value) { - toggleScope(scopeItem, value); - }), - title: Text( - 'DOCUMENT META DATA (IDENTITY)${(mandatory ? " *" : "")}', - style: Theme.of(context) - .textTheme - .bodyLarge! - .copyWith( - color: Theme.of(context) - .colorScheme - .onSurface, - fontWeight: FontWeight.bold, - ), - ), - ), - ); - } else { - return const SizedBox(width: 0, height: 0); - } - }, - ); - - case 'identityCountry': - return FutureBuilder( - future: getIdentity(), - builder: - (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return Container( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: - Theme.of(context).colorScheme.onSurface, - width: 0.5, - )), - ), - child: CheckboxListTile( - value: - (previousSelectedScope[scopeItem] == null) - ? mandatory - : previousSelectedScope[scopeItem], - onChanged: ((mandatory == true) - ? null - : (value) { - toggleScope(scopeItem, value); - }), - title: Text( - 'COUNTRY (IDENTITY)${(mandatory ? " *" : "")}', - style: Theme.of(context) - .textTheme - .bodyLarge! - .copyWith( - color: Theme.of(context) - .colorScheme - .onSurface, - fontWeight: FontWeight.bold, - ), - ), - ), - ); - } else { - return const SizedBox(width: 0, height: 0); - } - }, - ); - case 'walletAddress': return FutureBuilder( future: getWallets(), From b3d9d11b3476653bfcb0240b1df5541410718f20 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 5 Jan 2025 11:57:38 +0200 Subject: [PATCH 14/24] remove debug logs --- app/lib/screens/identity_verification_screen.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/lib/screens/identity_verification_screen.dart b/app/lib/screens/identity_verification_screen.dart index 0262e884..7664a266 100644 --- a/app/lib/screens/identity_verification_screen.dart +++ b/app/lib/screens/identity_verification_screen.dart @@ -403,8 +403,6 @@ class _IdentityVerificationScreenState } Widget unVerifiedWidget(step, text, icon) { - print("HELLO EMAIL"); - print(text); return GestureDetector( onTap: () async {}, child: Opacity( From 746a3d6a8b27d480a5e17a00ec2e3381fad7556d Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Sun, 5 Jan 2025 13:04:48 +0200 Subject: [PATCH 15/24] fix bug of not showing email --- app/lib/screens/identity_verification_screen.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/lib/screens/identity_verification_screen.dart b/app/lib/screens/identity_verification_screen.dart index 7664a266..2a6a7791 100644 --- a/app/lib/screens/identity_verification_screen.dart +++ b/app/lib/screens/identity_verification_screen.dart @@ -521,6 +521,17 @@ class _IdentityVerificationScreenState child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ + Row( + children: [ + Expanded( + child: Text( + (text.isEmpty + ? 'Unknown' + : text), + ), + ) + ], + ), step == 2 && Globals().hidePhoneButton.value == true From 655bd20fa80ab3be4e3c82a850d89f79fda7cfd6 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 8 Jan 2025 09:42:08 +0200 Subject: [PATCH 16/24] remove debug logs --- app/lib/widgets/kyc_widget.dart | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index 80495ab3..3f48e7a6 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -12,7 +12,6 @@ import 'package:threebotlogin/services/idenfy_service.dart'; import 'package:threebotlogin/helpers/globals.dart'; import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/services/wallet_service.dart'; -import 'package:threebotlogin/services/shared_preference_service.dart'; import 'package:threebotlogin/widgets/custom_dialog.dart'; import 'package:idenfy_sdk_flutter/models/auto_identification_status.dart'; @@ -263,11 +262,9 @@ Future handleIdenfyResponse({ }) async { VerificationStatus verificationStatus; try { - logger.d('Fetching verification status from iDenfy service...'); final idenfyServiceUrl = Globals().idenfyServiceUrl; verificationStatus = await getVerificationStatus( address: walletSeed, idenfyServiceUrl: idenfyServiceUrl); - logger.d('Fetched verification status: ${verificationStatus.status}'); } catch (e) { logger.e(e); showErrorDialog( @@ -280,18 +277,12 @@ Future handleIdenfyResponse({ } if (verificationStatus.status == VerificationState.VERIFIED) { - logger.d('Verification status is VERIFIED. Updating global state.'); - Globals().identityVerified.value = true; Events().emit(IdentityCallbackEvent(type: 'success')); - } else { - logger.d('Verification status is not VERIFIED. Marking as failed.'); - Globals().identityVerified.value = false; Events().emit(IdentityCallbackEvent(type: 'failed')); } - logger.d('Finished handleIdenfyResponse for walletSeed: $walletSeed'); } Future initIdenfySdk(String token, From eb5c85050a9096f9c35308d4c5584df21cb28ab7 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 8 Jan 2025 13:10:38 +0200 Subject: [PATCH 17/24] handle consistency between idenfy and getVerification --- app/lib/widgets/kyc_widget.dart | 50 +++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index 3f48e7a6..663f9495 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -259,12 +259,35 @@ Future verifyIdentityProcess({ Future handleIdenfyResponse({ required BuildContext context, required String walletSeed, + AutoIdentificationStatus? idenfyState, }) async { VerificationStatus verificationStatus; try { final idenfyServiceUrl = Globals().idenfyServiceUrl; verificationStatus = await getVerificationStatus( address: walletSeed, idenfyServiceUrl: idenfyServiceUrl); + bool statesMatch = + _areStatesMatching(idenfyState, verificationStatus.status); + + if (!statesMatch) { + logger.i('States do not match. Retrying after 5 seconds...'); + await Future.delayed(const Duration(seconds: 5)); + + verificationStatus = await getVerificationStatus( + address: walletSeed, idenfyServiceUrl: idenfyServiceUrl); + + statesMatch = _areStatesMatching(idenfyState, verificationStatus.status); + + if (!statesMatch) { + showErrorDialog( + context: context, + title: 'Error', + description: + 'Something went wrong. Please contact support if this issue persists.', + ); + return; + } + } } catch (e) { logger.e(e); showErrorDialog( @@ -300,11 +323,12 @@ Future initIdenfySdk(String token, 'Something went wrong. Please contact support if this issue persists.'); } } - await Future.delayed(const Duration(seconds: 13)); - if (idenfySDKresult != null && - idenfySDKresult.autoIdentificationStatus != - AutoIdentificationStatus.UNVERIFIED) { - await handleIdenfyResponse(context: context, walletSeed: walletSeed); + await Future.delayed(const Duration(seconds: 10)); + if (idenfySDKresult != null) { + await handleIdenfyResponse( + context: context, + walletSeed: walletSeed, + idenfyState: idenfySDKresult.autoIdentificationStatus); } } @@ -641,3 +665,19 @@ Future showIdentityDetails(BuildContext context, String walletSeed) { ), )); } + +bool _areStatesMatching(AutoIdentificationStatus? idenfyState, + VerificationState? verificationState) { + if (idenfyState == null || verificationState == null) { + return false; + } + + final stateMapping = { + 'APPROVED': VerificationState.VERIFIED, + 'FAILED': VerificationState.REJECTED, + 'UNVERIFIED': VerificationState.UNVERIFIED, + }; + + final mappedState = stateMapping[idenfyState.name]; + return mappedState == verificationState; +} From 559b137c8ede92fdb29a484667047c92031b62b6 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 8 Jan 2025 13:45:16 +0200 Subject: [PATCH 18/24] handle checking for status until it matches idenfy --- app/lib/widgets/kyc_widget.dart | 61 ++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index 663f9495..a00d5d03 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -262,33 +262,46 @@ Future handleIdenfyResponse({ AutoIdentificationStatus? idenfyState, }) async { VerificationStatus verificationStatus; - try { - final idenfyServiceUrl = Globals().idenfyServiceUrl; - verificationStatus = await getVerificationStatus( - address: walletSeed, idenfyServiceUrl: idenfyServiceUrl); - bool statesMatch = - _areStatesMatching(idenfyState, verificationStatus.status); - - if (!statesMatch) { - logger.i('States do not match. Retrying after 5 seconds...'); - await Future.delayed(const Duration(seconds: 5)); + const timeoutDuration = Duration(minutes: 2); + const retryInterval = Duration(seconds: 5); + final startTime = DateTime.now(); + try { + while (DateTime.now().difference(startTime) < timeoutDuration) { + final idenfyServiceUrl = Globals().idenfyServiceUrl; verificationStatus = await getVerificationStatus( - address: walletSeed, idenfyServiceUrl: idenfyServiceUrl); - - statesMatch = _areStatesMatching(idenfyState, verificationStatus.status); + address: walletSeed, + idenfyServiceUrl: idenfyServiceUrl, + ); - if (!statesMatch) { - showErrorDialog( - context: context, - title: 'Error', - description: - 'Something went wrong. Please contact support if this issue persists.', - ); + if (_areStatesMatching(idenfyState, verificationStatus.status)) { + if (verificationStatus.status == VerificationState.VERIFIED) { + Globals().identityVerified.value = true; + Events().emit(IdentityCallbackEvent(type: 'success')); + } else { + Globals().identityVerified.value = false; + Events().emit(IdentityCallbackEvent(type: 'failed')); + } return; } + + logger.i( + 'States do not match yet. Retrying in ${retryInterval.inSeconds} seconds...'); + await Future.delayed(retryInterval); } + + Globals().identityVerified.value = false; + Events().emit(IdentityCallbackEvent(type: 'failed')); + logger.e('Timeout reached. States still do not match.'); + showErrorDialog( + context: context, + title: 'Error', + description: + 'Something went wrong. Please contact support if this issue persists.', + ); } catch (e) { + Globals().identityVerified.value = false; + Events().emit(IdentityCallbackEvent(type: 'failed')); logger.e(e); showErrorDialog( context: context, @@ -298,14 +311,6 @@ Future handleIdenfyResponse({ ); return; } - - if (verificationStatus.status == VerificationState.VERIFIED) { - Globals().identityVerified.value = true; - Events().emit(IdentityCallbackEvent(type: 'success')); - } else { - Globals().identityVerified.value = false; - Events().emit(IdentityCallbackEvent(type: 'failed')); - } } Future initIdenfySdk(String token, From bd535d85dde27328dc998c5a88377d1bb2b38aac Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 8 Jan 2025 14:12:13 +0200 Subject: [PATCH 19/24] remove identity global var && shred prefrences functions --- app/lib/helpers/globals.dart | 1 - app/lib/helpers/kyc_helpers.dart | 5 ----- .../screens/identity_verification_screen.dart | 10 ---------- app/lib/services/shared_preference_service.dart | 16 ---------------- app/lib/widgets/kyc_widget.dart | 4 ---- 5 files changed, 36 deletions(-) diff --git a/app/lib/helpers/globals.dart b/app/lib/helpers/globals.dart index ffbc2256..bae45b37 100644 --- a/app/lib/helpers/globals.dart +++ b/app/lib/helpers/globals.dart @@ -24,7 +24,6 @@ class Globals { ValueNotifier emailVerified = ValueNotifier(false); ValueNotifier phoneVerified = ValueNotifier(false); - ValueNotifier identityVerified = ValueNotifier(false); final JRouter router = JRouter(); diff --git a/app/lib/helpers/kyc_helpers.dart b/app/lib/helpers/kyc_helpers.dart index 7f2b0a52..40d54cee 100644 --- a/app/lib/helpers/kyc_helpers.dart +++ b/app/lib/helpers/kyc_helpers.dart @@ -31,7 +31,6 @@ Future handleKYCData( bool? isEmailVerified = await getIsEmailVerified(); bool? isPhoneVerified = await getIsPhoneVerified(); - bool? isIdentityVerified = await getIsIdentityVerified(); // This method got refactored due my mistake in one little mapping in the migration from no pkid to pkid if (isEmailVerified == false) { @@ -55,10 +54,6 @@ Future handleKYCData( Globals().phoneVerified.value = true; await savePhoneInCorrectFormatPKid(phoneData); } - - if (isIdentityVerified == true) { - Globals().identityVerified.value = true; - } } diff --git a/app/lib/screens/identity_verification_screen.dart b/app/lib/screens/identity_verification_screen.dart index a4c93254..5da7254f 100644 --- a/app/lib/screens/identity_verification_screen.dart +++ b/app/lib/screens/identity_verification_screen.dart @@ -115,21 +115,12 @@ class _IdentityVerificationScreenState } } - setHidePhoneVerify() { - if (mounted) { - setState(() { - hidePhoneVerifyButton = Globals().identityVerified.value; - }); - } - } - @override void initState() { super.initState(); Globals().emailVerified.addListener(setEmailVerified); Globals().phoneVerified.addListener(setPhoneVerified); - Globals().hidePhoneButton.addListener(setHidePhoneVerify); checkPhoneStatus(); getUserValues(); startOrResumeEmailCountdown(); @@ -218,7 +209,6 @@ class _IdentityVerificationScreenState animation: Listenable.merge([ Globals().emailVerified, Globals().phoneVerified, - Globals().identityVerified ]), builder: (BuildContext context, _) { return Column( diff --git a/app/lib/services/shared_preference_service.dart b/app/lib/services/shared_preference_service.dart index 978001f5..3be150c2 100644 --- a/app/lib/services/shared_preference_service.dart +++ b/app/lib/services/shared_preference_service.dart @@ -230,22 +230,6 @@ Future savePhone(String phone, String? signedPhoneIdentifier) async { client.setPKidDoc('phone', json.encode({'phone': phone})); } -/// -/// -/// Identity methods in Shared Preferences -/// -/// - -Future setIsIdentityVerified(bool isIdentityVerified) async { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - prefs.setBool('isIdentityVerified', isIdentityVerified); -} - -Future getIsIdentityVerified() async { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - return prefs.getBool('isIdentityVerified'); -} - /// /// diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index a00d5d03..9128d382 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -276,10 +276,8 @@ Future handleIdenfyResponse({ if (_areStatesMatching(idenfyState, verificationStatus.status)) { if (verificationStatus.status == VerificationState.VERIFIED) { - Globals().identityVerified.value = true; Events().emit(IdentityCallbackEvent(type: 'success')); } else { - Globals().identityVerified.value = false; Events().emit(IdentityCallbackEvent(type: 'failed')); } return; @@ -290,7 +288,6 @@ Future handleIdenfyResponse({ await Future.delayed(retryInterval); } - Globals().identityVerified.value = false; Events().emit(IdentityCallbackEvent(type: 'failed')); logger.e('Timeout reached. States still do not match.'); showErrorDialog( @@ -300,7 +297,6 @@ Future handleIdenfyResponse({ 'Something went wrong. Please contact support if this issue persists.', ); } catch (e) { - Globals().identityVerified.value = false; Events().emit(IdentityCallbackEvent(type: 'failed')); logger.e(e); showErrorDialog( From a1b1ccec5195172ac269962668bf0bcac5bfcd41 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 8 Jan 2025 15:04:15 +0200 Subject: [PATCH 20/24] fix bug of failed verification --- app/lib/screens/wallets/wallet_info.dart | 2 +- app/lib/widgets/kyc_widget.dart | 66 +++++++++--------------- 2 files changed, 24 insertions(+), 44 deletions(-) diff --git a/app/lib/screens/wallets/wallet_info.dart b/app/lib/screens/wallets/wallet_info.dart index b18e8c5c..2e05f3bb 100644 --- a/app/lib/screens/wallets/wallet_info.dart +++ b/app/lib/screens/wallets/wallet_info.dart @@ -283,7 +283,7 @@ class _WalletDetailsWidgetState extends State { if (widget.wallet.verificationStatus != 'VERIFIED') { await termsAndConditionsDialog( context: context, - walletSeed: widget.wallet.tfchainSecret + wallet: widget.wallet ); setState(() {}); } else { diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index 9128d382..2f62e5ce 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -7,6 +7,7 @@ import 'package:idenfy_sdk_flutter/models/idenfy_identification_status.dart'; import 'package:threebotlogin/events/events.dart'; import 'package:threebotlogin/events/identity_callback_event.dart'; import 'package:threebotlogin/models/idenfy.dart'; +import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/screens/wizard/web_view.dart'; import 'package:threebotlogin/services/idenfy_service.dart'; import 'package:threebotlogin/helpers/globals.dart'; @@ -16,7 +17,7 @@ import 'package:threebotlogin/widgets/custom_dialog.dart'; import 'package:idenfy_sdk_flutter/models/auto_identification_status.dart'; termsAndConditionsDialog( - {required BuildContext context, required String walletSeed}) { + {required BuildContext context, required Wallet wallet}) { bool isAccepted = false; showDialog( @@ -172,7 +173,7 @@ termsAndConditionsDialog( ? () async { Navigator.pop(customContext); await verifyIdentityProcess( - context: context, walletSeed: walletSeed); + context: context, wallet: wallet); } : null, child: Text( @@ -194,11 +195,11 @@ termsAndConditionsDialog( Future verifyIdentityProcess({ required BuildContext context, - required String walletSeed, + required Wallet wallet, }) async { Token token; try { - token = await getToken(walletSeed); + token = await getToken(wallet.tfchainSecret); } on BadRequest catch (e) { showWarningDialog( context: context, @@ -223,8 +224,9 @@ Future verifyIdentityProcess({ ); return; } on NotEnoughBalance catch (_) { - final wallets = - (await getPkidWallets()).where((w) => w.seed == walletSeed).toList(); + final wallets = (await getPkidWallets()) + .where((w) => w.seed == wallet.tfchainSecret) + .toList(); final minimumBalance = Globals().minimumTFChainBalanceForKYC; showWarningDialog( context: context, @@ -241,7 +243,8 @@ Future verifyIdentityProcess({ 'Your account is not activated.\nPlease go to wallet section and initialize your wallet.'); return; } on AlreadyVerified catch (_) { - return await handleIdenfyResponse(context: context, walletSeed: walletSeed); + return await handleIdenfyResponse( + context: context, walletAddress: wallet.tfchainAddress); } catch (e) { logger.e(e); showErrorDialog( @@ -253,48 +256,20 @@ Future verifyIdentityProcess({ return; } await initIdenfySdk(token.authToken, - context: context, walletSeed: walletSeed); + context: context, walletAddress: wallet.tfchainAddress); } Future handleIdenfyResponse({ required BuildContext context, - required String walletSeed, + required String walletAddress, AutoIdentificationStatus? idenfyState, }) async { VerificationStatus verificationStatus; - - const timeoutDuration = Duration(minutes: 2); - const retryInterval = Duration(seconds: 5); - final startTime = DateTime.now(); try { - while (DateTime.now().difference(startTime) < timeoutDuration) { - final idenfyServiceUrl = Globals().idenfyServiceUrl; - verificationStatus = await getVerificationStatus( - address: walletSeed, - idenfyServiceUrl: idenfyServiceUrl, - ); - - if (_areStatesMatching(idenfyState, verificationStatus.status)) { - if (verificationStatus.status == VerificationState.VERIFIED) { - Events().emit(IdentityCallbackEvent(type: 'success')); - } else { - Events().emit(IdentityCallbackEvent(type: 'failed')); - } - return; - } - - logger.i( - 'States do not match yet. Retrying in ${retryInterval.inSeconds} seconds...'); - await Future.delayed(retryInterval); - } - - Events().emit(IdentityCallbackEvent(type: 'failed')); - logger.e('Timeout reached. States still do not match.'); - showErrorDialog( - context: context, - title: 'Error', - description: - 'Something went wrong. Please contact support if this issue persists.', + final idenfyServiceUrl = Globals().idenfyServiceUrl; + verificationStatus = await getVerificationStatus( + address: walletAddress, + idenfyServiceUrl: idenfyServiceUrl, ); } catch (e) { Events().emit(IdentityCallbackEvent(type: 'failed')); @@ -307,10 +282,15 @@ Future handleIdenfyResponse({ ); return; } + if (verificationStatus.status == VerificationState.VERIFIED) { + Events().emit(IdentityCallbackEvent(type: 'success')); + } else { + Events().emit(IdentityCallbackEvent(type: 'failed')); + } } Future initIdenfySdk(String token, - {required BuildContext context, required String walletSeed}) async { + {required BuildContext context, required String walletAddress}) async { IdenfyIdentificationResult? idenfySDKresult; try { idenfySDKresult = await IdenfySdkFlutter.start(token); @@ -328,7 +308,7 @@ Future initIdenfySdk(String token, if (idenfySDKresult != null) { await handleIdenfyResponse( context: context, - walletSeed: walletSeed, + walletAddress: walletAddress, idenfyState: idenfySDKresult.autoIdentificationStatus); } } From c6ac685837b6701a41840fd08929d29edf6b299f Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 15 Jan 2025 10:17:04 +0200 Subject: [PATCH 21/24] fix warnings in workflow --- app/lib/screens/recover_screen.dart | 2 -- app/lib/services/wallet_service.dart | 1 - app/lib/widgets/kyc_widget.dart | 16 ---------------- 3 files changed, 19 deletions(-) diff --git a/app/lib/screens/recover_screen.dart b/app/lib/screens/recover_screen.dart index a451dfdb..0fcb4e54 100644 --- a/app/lib/screens/recover_screen.dart +++ b/app/lib/screens/recover_screen.dart @@ -7,13 +7,11 @@ import 'package:sodium_libs/sodium_libs.dart'; import 'package:http/http.dart'; import 'package:threebotlogin/helpers/kyc_helpers.dart'; import 'package:threebotlogin/helpers/logger.dart'; -import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/services/3bot_service.dart'; import 'package:threebotlogin/services/crypto_service.dart'; import 'package:threebotlogin/services/migration_service.dart'; import 'package:threebotlogin/services/pkid_service.dart'; import 'package:threebotlogin/services/shared_preference_service.dart'; -import 'package:threebotlogin/services/wallet_service.dart'; class RecoverScreen extends StatefulWidget { const RecoverScreen({super.key, this.recoverScreen}); diff --git a/app/lib/services/wallet_service.dart b/app/lib/services/wallet_service.dart index 2d986a29..7ee8745d 100644 --- a/app/lib/services/wallet_service.dart +++ b/app/lib/services/wallet_service.dart @@ -5,7 +5,6 @@ import 'package:flutter_pkid/flutter_pkid.dart'; import 'package:gridproxy_client/models/farms.dart'; import 'package:threebotlogin/apps/wallet/wallet_config.dart'; import 'package:threebotlogin/helpers/globals.dart'; -import 'package:threebotlogin/models/idenfy.dart'; import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/services/idenfy_service.dart'; import 'package:threebotlogin/services/gridproxy_service.dart'; diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index 2f62e5ce..49a8afb0 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -646,19 +646,3 @@ Future showIdentityDetails(BuildContext context, String walletSeed) { ), )); } - -bool _areStatesMatching(AutoIdentificationStatus? idenfyState, - VerificationState? verificationState) { - if (idenfyState == null || verificationState == null) { - return false; - } - - final stateMapping = { - 'APPROVED': VerificationState.VERIFIED, - 'FAILED': VerificationState.REJECTED, - 'UNVERIFIED': VerificationState.UNVERIFIED, - }; - - final mappedState = stateMapping[idenfyState.name]; - return mappedState == verificationState; -} From 0262ccefd0ba3b80eadfbb0196c86bf6da91255b Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Wed, 15 Jan 2025 14:09:48 +0200 Subject: [PATCH 22/24] WIP: handle refresh wallet after verification --- app/lib/models/wallet.dart | 2 +- app/lib/providers/wallets_provider.dart | 15 ++++++++++++++ app/lib/screens/wallets/wallet_info.dart | 25 ++++++++++++------------ 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/app/lib/models/wallet.dart b/app/lib/models/wallet.dart index 098ec135..1d11dabf 100644 --- a/app/lib/models/wallet.dart +++ b/app/lib/models/wallet.dart @@ -24,7 +24,7 @@ class Wallet { String stellarBalance; String tfchainBalance; final WalletType type; - final String verificationStatus; + late String verificationStatus; } class PkidWallet { diff --git a/app/lib/providers/wallets_provider.dart b/app/lib/providers/wallets_provider.dart index 0ca1a2c3..14823300 100644 --- a/app/lib/providers/wallets_provider.dart +++ b/app/lib/providers/wallets_provider.dart @@ -2,6 +2,7 @@ import 'package:mutex/mutex.dart'; import 'package:threebotlogin/helpers/globals.dart'; import 'package:threebotlogin/models/wallet.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:threebotlogin/services/idenfy_service.dart'; import 'package:threebotlogin/services/wallet_service.dart'; import 'package:threebotlogin/services/stellar_service.dart' as StellarService; @@ -48,6 +49,20 @@ class WalletsNotifier extends StateNotifier> { }); } + Future verifyWallet(String walletName) async { + await _mutex.protect(() async { + final idenfyServiceUrl = Globals().idenfyServiceUrl; + final wallet = state.where((w) => w.name == walletName).firstOrNull; + final updatedVerificationStatus = await getVerificationStatus( + address: wallet!.tfchainAddress, idenfyServiceUrl: idenfyServiceUrl); + + if (wallet.verificationStatus != updatedVerificationStatus.status.name) { + wallet.verificationStatus = updatedVerificationStatus.status.name; + } else {} + state = [...state]; + }); + } + void reloadBalances() async { if (!_reload) return await TFChainService.disconnect(); if (!_loading) { diff --git a/app/lib/screens/wallets/wallet_info.dart b/app/lib/screens/wallets/wallet_info.dart index dd016d4b..353551b0 100644 --- a/app/lib/screens/wallets/wallet_info.dart +++ b/app/lib/screens/wallets/wallet_info.dart @@ -115,6 +115,8 @@ class _WalletDetailsWidgetState extends ConsumerState { tfchainAddressController.text = widget.wallet.tfchainAddress; walletNameController.text = widget.wallet.name; walletName = widget.wallet.name; + final wallet = + ref.watch(walletsNotifier).firstWhere((w) => w.name == walletName); return SingleChildScrollView( child: Padding( @@ -272,7 +274,7 @@ class _WalletDetailsWidgetState extends ConsumerState { ), ), ), - const SizedBox(height: 40), + const SizedBox(height: 40), Text( 'KYC Verification', style: Theme.of(context).textTheme.titleLarge!.copyWith( @@ -284,28 +286,25 @@ class _WalletDetailsWidgetState extends ConsumerState { width: MediaQuery.of(context).size.width - 40, child: ElevatedButton( onPressed: () async { - if (widget.wallet.verificationStatus != 'VERIFIED') { + if (wallet.verificationStatus != 'VERIFIED') { await termsAndConditionsDialog( - context: context, - wallet: widget.wallet - ); - setState(() {}); + context: context, wallet: wallet); + await walletsRef.verifyWallet(wallet.name); } else { - showIdentityDetails(context, widget.wallet.tfchainSecret); + showIdentityDetails(context, wallet.tfchainSecret); } }, style: ElevatedButton.styleFrom( - backgroundColor: - widget.wallet.verificationStatus != 'VERIFIED' - ? Theme.of(context).colorScheme.errorContainer - : Theme.of(context).colorScheme.primaryContainer, + backgroundColor: wallet.verificationStatus != 'VERIFIED' + ? Theme.of(context).colorScheme.errorContainer + : Theme.of(context).colorScheme.primaryContainer, ), child: Text( - widget.wallet.verificationStatus != 'VERIFIED' + wallet.verificationStatus != 'VERIFIED' ? 'Verify your Identity' : 'Show Verified Data', style: Theme.of(context).textTheme.bodyLarge!.copyWith( - color: widget.wallet.verificationStatus != 'VERIFIED' + color: wallet.verificationStatus != 'VERIFIED' ? Theme.of(context).colorScheme.onErrorContainer : Theme.of(context) .colorScheme From e2721b608f86aa5f291f071d620c6d865ed02d81 Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Thu, 16 Jan 2025 10:43:37 +0200 Subject: [PATCH 23/24] WIP: fix refreshing state --- app/lib/providers/wallets_provider.dart | 4 ++-- app/lib/widgets/kyc_widget.dart | 2 +- app/lib/widgets/wallets/wallet_card.dart | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/lib/providers/wallets_provider.dart b/app/lib/providers/wallets_provider.dart index 14823300..0ef6e756 100644 --- a/app/lib/providers/wallets_provider.dart +++ b/app/lib/providers/wallets_provider.dart @@ -50,15 +50,15 @@ class WalletsNotifier extends StateNotifier> { } Future verifyWallet(String walletName) async { + final idenfyServiceUrl = Globals().idenfyServiceUrl; await _mutex.protect(() async { - final idenfyServiceUrl = Globals().idenfyServiceUrl; final wallet = state.where((w) => w.name == walletName).firstOrNull; final updatedVerificationStatus = await getVerificationStatus( address: wallet!.tfchainAddress, idenfyServiceUrl: idenfyServiceUrl); if (wallet.verificationStatus != updatedVerificationStatus.status.name) { wallet.verificationStatus = updatedVerificationStatus.status.name; - } else {} + } state = [...state]; }); } diff --git a/app/lib/widgets/kyc_widget.dart b/app/lib/widgets/kyc_widget.dart index 49a8afb0..77437b3f 100644 --- a/app/lib/widgets/kyc_widget.dart +++ b/app/lib/widgets/kyc_widget.dart @@ -435,7 +435,7 @@ Future showIdentityDetails(BuildContext context, String walletSeed) { final firstName = utf8.decode(latin1.encode(data.orgFirstName!)); final lastName = utf8.decode(latin1.encode(data.orgLastName!)); - final fullName = '$firstName $lastName'; + final fullName = '$lastName $firstName'; return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/app/lib/widgets/wallets/wallet_card.dart b/app/lib/widgets/wallets/wallet_card.dart index c45be8c4..5fe56e83 100644 --- a/app/lib/widgets/wallets/wallet_card.dart +++ b/app/lib/widgets/wallets/wallet_card.dart @@ -62,6 +62,7 @@ class _WalletCardWidgetState extends ConsumerState { Widget build(BuildContext context) { List cardContent = []; wallets = ref.watch(walletsNotifier); + final wallet = wallets.where((w) => w.name == widget.wallet.name).firstOrNull; if (widget.wallet.type == WalletType.NATIVE && widget.wallet.stellarBalance == '-1') { cardContent = [ @@ -173,8 +174,8 @@ class _WalletCardWidgetState extends ConsumerState { ), const Spacer(), Text( - widget.wallet.verificationStatus, - style: widget.wallet.verificationStatus == 'VERIFIED' + wallet!.verificationStatus, + style: wallet.verificationStatus == 'VERIFIED' ? Theme.of(context).textTheme.bodySmall!.copyWith( color: Theme.of(context).colorScheme.primary, fontWeight: FontWeight.bold, From 38749aa02154eefe4a67fa472c40c7208854316d Mon Sep 17 00:00:00 2001 From: AlaaElattar Date: Thu, 16 Jan 2025 12:46:15 +0200 Subject: [PATCH 24/24] added listener on idenfy state for refreshing --- app/lib/models/wallet.dart | 2 +- app/lib/providers/wallets_provider.dart | 28 ++++++++++++++--------- app/lib/screens/wallets/wallet_info.dart | 29 +++++++++++++++++++++++- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/app/lib/models/wallet.dart b/app/lib/models/wallet.dart index 1d11dabf..eb75c05b 100644 --- a/app/lib/models/wallet.dart +++ b/app/lib/models/wallet.dart @@ -24,7 +24,7 @@ class Wallet { String stellarBalance; String tfchainBalance; final WalletType type; - late String verificationStatus; + String verificationStatus; } class PkidWallet { diff --git a/app/lib/providers/wallets_provider.dart b/app/lib/providers/wallets_provider.dart index 0ef6e756..1685db70 100644 --- a/app/lib/providers/wallets_provider.dart +++ b/app/lib/providers/wallets_provider.dart @@ -1,5 +1,6 @@ import 'package:mutex/mutex.dart'; import 'package:threebotlogin/helpers/globals.dart'; +import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/models/wallet.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:threebotlogin/services/idenfy_service.dart'; @@ -49,19 +50,24 @@ class WalletsNotifier extends StateNotifier> { }); } - Future verifyWallet(String walletName) async { - final idenfyServiceUrl = Globals().idenfyServiceUrl; - await _mutex.protect(() async { - final wallet = state.where((w) => w.name == walletName).firstOrNull; - final updatedVerificationStatus = await getVerificationStatus( - address: wallet!.tfchainAddress, idenfyServiceUrl: idenfyServiceUrl); - - if (wallet.verificationStatus != updatedVerificationStatus.status.name) { +Future verifyWallet(String walletName) async { + final idenfyServiceUrl = Globals().idenfyServiceUrl; + await _mutex.protect(() async { + final wallet = state.where((w) => w.name == walletName).firstOrNull; + if (wallet != null) { + try { + final updatedVerificationStatus = await getVerificationStatus( + address: wallet.tfchainAddress, + idenfyServiceUrl: idenfyServiceUrl, + ); wallet.verificationStatus = updatedVerificationStatus.status.name; + state = [...state]; + } catch (e) { + logger.e('[verifyWallet] Error during verification: $e'); } - state = [...state]; - }); - } + } + }); +} void reloadBalances() async { if (!_reload) return await TFChainService.disconnect(); diff --git a/app/lib/screens/wallets/wallet_info.dart b/app/lib/screens/wallets/wallet_info.dart index 353551b0..7f9b89ed 100644 --- a/app/lib/screens/wallets/wallet_info.dart +++ b/app/lib/screens/wallets/wallet_info.dart @@ -1,6 +1,10 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:threebotlogin/events/events.dart'; +import 'package:threebotlogin/events/identity_callback_event.dart'; import 'package:threebotlogin/helpers/logger.dart'; import 'package:threebotlogin/models/wallet.dart'; import 'package:threebotlogin/providers/wallets_provider.dart'; @@ -289,7 +293,30 @@ class _WalletDetailsWidgetState extends ConsumerState { if (wallet.verificationStatus != 'VERIFIED') { await termsAndConditionsDialog( context: context, wallet: wallet); - await walletsRef.verifyWallet(wallet.name); + + final completer = Completer(); + StreamSubscription? subscription; + try { + subscription = + Events().onEvent(IdentityCallbackEvent, (event) { + if (event is IdentityCallbackEvent && + event.type == 'success') { + logger.i( + '[Event Listener] IdentityCallbackEvent with success received.'); + completer.complete(); + } + }); + + await completer.future; + await walletsRef.verifyWallet(wallet.name); + } catch (e) { + logger.e( + '[Event Listener] Error while waiting for event: $e'); + } finally { + if (subscription != null) { + await subscription.cancel(); + } + } } else { showIdentityDetails(context, wallet.tfchainSecret); }