Add Paga sandbox NGN accounts and banking adapter - #96
Gift-Stack wants to merge 1 commit into
Conversation
| input.recipient.accountNumber, | ||
| ], | ||
| ); | ||
| this.match(data, input.reference); |
There was a problem hiding this comment.
Once Paga has accepted a money-moving call, a failed check here throws INVALID_RESPONSE, not UNKNOWN_OUTCOME. That covers match(), the currency/name comparison, toMinor() and the body parse at L143. If depositToBank returns responseCode 0 but destinationAccountHolderNameAtBank differs in casing or spacing, or the body read hits the 15s abort, the caller never gets the transactionId and can mark the payout failed and retry or refund, so the recipient gets paid twice. transferSubsidiary (L302-311), charge and topup have the same problem. After the request succeeds, validation failures should become UNKNOWN_OUTCOME so the operation goes to reconciliation.
| } catch { | ||
| throw new PagaError('INVALID_RESPONSE'); | ||
| } | ||
| if ( |
There was a problem hiding this comment.
Every non-zero responseCode becomes REJECTED, including on the read-only /transactionStatus lookup in getPayout. If the status query comes back non-zero (the transaction is still processing, the reference is not indexed yet, or there was a transient error), getPayout throws the same REJECTED that submitPayout throws for a declined deposit. Reconciliation could then release or refund funds for a payout that still settles. getPayout should map a non-zero status to status: 'unknown' or UNKNOWN_OUTCOME.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 797fac1adb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| try { | ||
| data = parse(await response.text()); | ||
| } catch { | ||
| throw new PagaError('INVALID_RESPONSE'); |
There was a problem hiding this comment.
Classify unreadable mutation responses as unknown outcomes
When Paga accepts a payout, transfer, charge, or top-up but the 2xx response body is truncated, unreadable, or malformed, the money may already have moved; this catch instead reports INVALID_RESPONSE, losing the ambiguity signal needed to prevent unsafe retries and trigger reconciliation. Distinguish mutating requests here and return UNKNOWN_OUTCOME when their successful HTTP responses cannot be decoded.
Useful? React with 👍 / 👎.
| const reference = ref.parse(input.reference); | ||
| const accountReference = ref.min(12).max(30).parse(input.accountReference); |
There was a problem hiding this comment.
Normalize schema failures to INVALID_INPUT
For malformed caller input such as an invalid reference, account reference, email, amount, bank code, or account number, these direct Zod parses throw ZodError rather than the adapter's declared PagaError('INVALID_INPUT'); only the self-transfer check currently emits that code. Consumers therefore cannot consistently distinguish a bad request from an unexpected adapter failure, so public-method validation failures should be translated to the typed error.
Useful? React with 👍 / 👎.
Adds a sandbox-only Paga banking adapter for per-customer NGN subsidiary accounts, balance reads, account-to-account transfers, bank recipient validation, and payout submission/status checks. The adapter keeps exact minor-unit amounts, signs requests on the server, matches returned references, and keeps banking separate from currency conversion.
This is the first fiat layer, based on foundation PR #93. The consumer API, database ownership, recovery flow, and mobile screens are in the top integration PR. Paga's latest authenticated probe returned HTTP 401; account creation and funded transfers are not yet verified. This PR does not provide NGN↔USDC conversion or enable production execution.
Validation: adapter contract tests pass as part of the 303-test backend fiat run on the integrated stack. Provider responses in automated tests are controlled doubles, not proof of settlement.
Stack (bottom → top): foundation #93 → Paga #96 → Nomba #97 → consumer integration #98. Native GitHub stack #99. Merge from the bottom upward.