feat(payments): add bank provider port and sandbox adapter - #224
Merged
Conversation
Add a generic, interface-only BankProvider plug-in port: submit takes a bank payment request and returns a sealed accept/reject result, with technical failures surfaced as an exception so a bank decision is never confused with a transport error. Add a deterministic sandbox adapter, off by default, that accepts or rejects based on a reference marker so the lifecycle can be driven end to end without any real bank. Real adapters live out of tree. Closes #212
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fifth slice of E-02. A clean-room
BankProviderplug-in port (mirrors the #174 RuleSynthesizer pattern) plus a deterministic in-tree sandbox, so payment orchestration (#213) can route to a bank seam with no real provider.com.fincore.payments.application.bank, pure Kotlin, no Spring):BankProvider.submit(BankPaymentRequest): BankSubmissionResult.BankPaymentRequest(paymentId, amount: Money, reference)validates non-blank;BankSubmissionResultis a sealedAccepted(providerReference)/Rejected(reason);BankProviderExceptioncarries technical/transient failure. A bank decision is a result, a transport failure is an exception (never conflated).Accepted= accepted-for-processing, not settled (settlement is async, F-02.7 Inbound webhook handler (HMAC verify + dedup) #214).infrastructure.bank):SandboxBankProvider,@ConditionalOnProperty(...sandbox.enabled, matchIfMissing=false)so it is off by default and cannot auto-activate. Deterministic: a reference carrying the reject marker yieldsRejected, otherwiseAccepted("sbx-<paymentId>"). No randomness, no clock, no business threshold.OSS boundary (§5.3): generic seam + fake only. No real bank/provider/partner name, no maritime, no business value. Real adapters are private.
Gates
Local (
-x integrationTest)::services:payments:{test,detekt,detektTest,spotlessCheck,assemble,compileIntegrationTestKotlin}green. critic GO, security-auditor PASS (§5.3 clean-room verified), evaluator 0.93.Closes #212