feat(banking): add provider contracts and Enable Banking foundation - #536
Conversation
DTOs for ASPSPs, authorization, session, account, balance and transactions, with tests over sample JSON fixtures.
Adds EnableBankingConfig (environment/redirect/base URL) and a FlutterSecureStorage-backed store for the user's BYOC app_id and private key PEM, plus a draft riverpod provider exposing them.
Signs Enable Banking API JWTs on-device from the user's BYOC app_id and private key PEM, caching the token in memory and refreshing it shortly before expiry.
Thin http client for the Enable Banking endpoints (aspsps, auth, sessions, balances, transactions), mapping responses onto the DTOs and raising EnableBankingException on HTTP errors.
mikev-cw
left a comment
There was a problem hiding this comment.
Thanks for putting this key foundation! Generally I think it's very good!
In addition to the comment regarding the CI workflow and dates, my main concerns are around architecture and design.
Since this PR introduces the first banking integration layer, I think we should be careful not to make the app architecture too EnableBanking-centric. What if, in the future we replace EB, or add other providers in parallel?
What do you think on isolating Enable Banking under a provider-specific module, and expose a more generic banking abstraction to the rest of the app?
For example, the app-facing layer could use generic concepts such as BankingProvider, BankingConnection, BankInstitution, BankAccount, BankTransaction, and BankingCredentials, while the Enable Banking DTOs/client stay internal to enable_banking.
I see that lib/providers/banking_provider.dart already creates a central access point for the banking services, which is a good start. My concern is that this app-facing provider layer still exposes Enable Banking-specific names and types, so the rest of the app may naturally start depending on EB concepts directly.
I'm just reasoning out loud, I'm interested in getting your opinion @marianialessandro, @lucaantonelli and @theperu. Because yes, this might be better, but I'm confident it would make development longer and more complex, which might not be what we want.
On the other hand, we're building the foundations right now: a lot of these decisions can only be made at this stage, if we don't want to rewrite half the app later.
|
I agree with your concern. The Riverpod layer currently exposes Enable Banking specific types, so the rest of the app could easily become coupled to them. This would require adapting the consent and sync work already in progress, but I think establishing that boundary before wiring up the UI is worthwhile. This afternoon I'll get to work immediately to begin implementing this aspect because I think it could be an added value if in the future we want to add alternatives to Enable Banking rather than replace it. |
|
@mikev-cw I’ve updated the foundation to separate provider-independent banking contracts from the Enable Banking implementation. Institution discovery, remote consent operations, and account data now have dedicated interfaces, adapters, and mappers, making them independently replaceable and testable |
|
Ok, it moves in a SOLID-friendly direction, i like it! So let's skip it for now. |
theperu
left a comment
There was a problem hiding this comment.
LGTM! ✅
I agree with what was pointed out by Mike in his review and I think we can now proceed with the merge!
Adds a banking foundation with provider-independent contracts and an Enable Banking implementation. Application services can depend on institution discovery, remote consent operations or account data separately, and replace each capability through Riverpod.
Structure
lib/services/banking/definesBankInstitutionDirectory,BankConsentService,BankAccountDataSourceand their shared domain models.BankingProvidergroups the three capabilities.lib/services/banking/enable_banking/contains the REST client, wire DTOs, RS256 JWT authentication, BYOC setup and secure credential storage, plus separate adapters and mappers for each capability.lib/providers/banking_provider.dartcomposes the implementation and exposes independently replaceable dependencies.The adapters support institution discovery, authorization and remote session create/read/revoke, account details, balances and transaction pages. Mapping preserves exact decimal amounts, separate transaction identifiers, account identity hashes, balance metadata, calendar-date filters and opaque pagination cursors. Invalid responses and foreign-provider references fail explicitly; implementation errors are translated at the boundary.
The foundation also registers the callback scheme, verifies candidate credentials through
GET /applicationbefore atomically replacing the encrypted configuration, derives the environment and supported countries from the server, and checks Android release network access. Credential storage keys and authentication behavior are preserved by the module move.Scope
Callback acceptance and state validation belong to the application consent lifecycle in the next PR. Persistence, reconnect orchestration, transaction synchronization and user-facing screens remain in subsequent stacked PRs. This foundation exposes no banking UI entry point. The following branches will need to adopt the new module paths and contracts.
Validation
Verified locally with Flutter 3.38.8 / Dart 3.10.7 and JDK 21:
Adapter tests use synthetic HTTP responses; this change does not claim a new live-bank or UI end-to-end verification.
Remote CI on
5660478: upstream PR checks passed. The fork push workflow passed dependency resolution, code generation, formatting and tests, then failed atDump keystorebecause the Android signing secrets are missing. Its Android build remains blocked; the local release build passed as listed above.