Line data Source code
1 : // Copyright 2026 The terCAD team. All rights reserved.
2 : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
3 :
4 : import 'dart:convert';
5 :
6 : import 'package:app_finance/_classes/controller/deep_link/deep_link_handler.dart';
7 : import 'package:app_finance/_classes/controller/encryption_handler.dart';
8 : import 'package:app_finance/_classes/integration/enablebanking/enablebanking_client.dart';
9 : import 'package:app_finance/_classes/storage/app_preferences.dart';
10 : import 'package:flutter/foundation.dart';
11 :
12 : class EnableBankingSettings {
13 : // Native applications are opened by the bank's redirect directly (deep link)
14 : static const callbackUrl = '${DeepLinkHandler.scheme}://integration${DeepLinkHandler.enableBankingPath}';
15 :
16 : static const callbackPage = 'callback.html';
17 :
18 4 : static String get defaultRedirectUrl => redirectUrlFor(kIsWeb ? Uri.base : null);
19 :
20 2 : @visibleForTesting
21 : static String redirectUrlFor(Uri? base) {
22 0 : if (base != null && (base.scheme == 'https' || base.scheme == 'http') && base.host.isNotEmpty) {
23 0 : return base.resolve(callbackPage).toString();
24 : }
25 : return callbackUrl;
26 : }
27 :
28 : String applicationId;
29 : String redirectUrl;
30 : String country;
31 : String aspsp;
32 :
33 : String authorizationState;
34 :
35 : // Bank account (IBAN, else uid) to the uuid of the application's account
36 : final Map<String, String> links;
37 :
38 : // Bank account (IBAN, else uid) to the last day transactions were imported for
39 : final Map<String, DateTime> imported;
40 :
41 2 : EnableBankingSettings({
42 : this.applicationId = '',
43 : this.redirectUrl = '',
44 : this.country = '',
45 : this.aspsp = '',
46 : this.authorizationState = '',
47 : Map<String, String>? links,
48 : Map<String, DateTime>? imported,
49 2 : }) : links = links ?? {},
50 2 : imported = imported ?? {};
51 :
52 4 : Map<String, dynamic> toJson() => {
53 2 : 'applicationId': applicationId,
54 2 : 'redirectUrl': redirectUrl,
55 2 : 'country': country,
56 2 : 'aspsp': aspsp,
57 2 : 'authorizationState': authorizationState,
58 2 : 'links': links,
59 7 : 'imported': imported.map((key, value) => MapEntry(key, value.toIso8601String())),
60 : };
61 :
62 4 : factory EnableBankingSettings.fromJson(Map<String, dynamic> json) => EnableBankingSettings(
63 2 : applicationId: json['applicationId'] ?? '',
64 2 : redirectUrl: json['redirectUrl'] ?? '',
65 2 : country: json['country'] ?? '',
66 2 : aspsp: json['aspsp'] ?? '',
67 2 : authorizationState: json['authorizationState'] ?? '',
68 7 : links: (json['links'] as Map<String, dynamic>? ?? {}).map((key, value) => MapEntry(key, value.toString())),
69 4 : imported: (json['imported'] as Map<String, dynamic>? ?? {}).map(
70 4 : (key, value) => MapEntry(key, DateTime.parse(value.toString())),
71 : ),
72 : );
73 :
74 2 : static EnableBankingSettings load() {
75 2 : final value = AppPreferences.get(AppPreferences.prefEnableBanking);
76 2 : if (value == null || value.isEmpty) {
77 1 : return EnableBankingSettings();
78 : }
79 : try {
80 4 : return EnableBankingSettings.fromJson(jsonDecode(value));
81 0 : } on FormatException {
82 0 : return EnableBankingSettings();
83 : }
84 : }
85 :
86 8 : Future<void> save() => AppPreferences.set(AppPreferences.prefEnableBanking, jsonEncode(toJson()));
87 :
88 2 : static String? _decrypt(String key) {
89 2 : final value = AppPreferences.get(key);
90 2 : if (value == null || value.isEmpty) {
91 : return null;
92 : }
93 : try {
94 2 : return EncryptionHandler.decrypt(value);
95 : } catch (_) {
96 : return null;
97 : }
98 : }
99 :
100 4 : static String? loadKey() => _decrypt(AppPreferences.prefEnableBankingKey);
101 :
102 2 : static Future<void> saveKey(String pem) =>
103 6 : AppPreferences.set(AppPreferences.prefEnableBankingKey, EncryptionHandler.encrypt(pem.trim()));
104 :
105 0 : static Future<void> clearKey() => AppPreferences.clear(AppPreferences.prefEnableBankingKey);
106 :
107 2 : static EnableBankingSession? loadSession() {
108 2 : final value = _decrypt(AppPreferences.prefEnableBankingSession);
109 : if (value == null) {
110 : return null;
111 : }
112 : try {
113 4 : final session = EnableBankingSession.fromJson(jsonDecode(value));
114 2 : return session.isValid ? session : null;
115 : } catch (_) {
116 : return null;
117 : }
118 : }
119 :
120 4 : static Future<void> saveSession(EnableBankingSession session) => AppPreferences.set(
121 6 : AppPreferences.prefEnableBankingSession, EncryptionHandler.encrypt(jsonEncode(session.toJson())));
122 :
123 2 : static Future<void> clearSession() => AppPreferences.clear(AppPreferences.prefEnableBankingSession);
124 : }
|