Skip to content

feat: Integrate KYC/AML provider with retry, status callbacks, and replayable audit trail - #903

Open
EmannuellaO wants to merge 1 commit into
RevoraOrg:masterfrom
EmannuellaO:feat/kyc-aml-provider
Open

feat: Integrate KYC/AML provider with retry, status callbacks, and replayable audit trail#903
EmannuellaO wants to merge 1 commit into
RevoraOrg:masterfrom
EmannuellaO:feat/kyc-aml-provider

Conversation

@EmannuellaO

Copy link
Copy Markdown

Closes #875

Summary

Provider-agnostic KYC/AML integration behind a single feature flag (KYC_PROVIDER_ADAPTER_ENABLED, default off). When off, behaviour is byte-for-byte the legacy path (passthrough adapter, no webhooks, no gate).

What changed

  • src/services/kyc/KycProviderAdapter.tsKycProviderAdapter interface, DefaultKycProviderAdapter (exponential-backoff retries configurable via KYC_ADAPTER_MAX_RETRIES / KYC_ADAPTER_BASE_RETRY_DELAY_MS), PassthroughKycProviderAdapter, createKycProviderAdapter factory, kycNotApprovedError().
  • src/services/kyc/kycVerificationService.tsKycReplayGuard/InMemoryKycReplayGuard (per-provider-tx-id claim/commit), parseKycCallbackEvent, KycVerificationService.recordVerifiedCallback writing a replayable audit trail (action = kyc.verification.received, keyed by provider_tx_id), then mutating kyc_status (flag on) or audit-only (flag off). Fail-closed on malformed payloads/unknown investors.
  • src/routes/kycWebhooks.tsPOST /api/v1/kyc/webhooks with raw-body HMAC verification (kycWebhookAuth, dual-key rotation, timestamp replay window) and outcome mapping (accepted/duplicate/rejected/user_not_found).
  • src/db/migrations/061_add_kyc_status_to_users.sqlkyc_status, kyc_provider, kyc_reference_id on users (with CHECK constraint + index).
  • src/db/repositories/userRepository.ts — KYC columns surfaced through User, CreateUserInput, UpdateUserInput, and new updateKycVerification().
  • Investment gate — when the flag is on, InvestmentService.createInvestment rejects submission unless kyc_status = 'approved' (fail-closed) and writes an audit-log block entry.
  • src/index.ts — mounts /api/v1/kyc (initiate) and, only when the flag is on, /api/v1/kyc/webhooks before the global JSON parser for byte-exact signatures; wires the adapter + signed jurisdiction route table into KycRouter.
  • Register hook — optional onRegistered hook on the register endpoint for post-signup KYC initiation; a throwing hook is logged but never fails the signup.

Behaviour matrix

Flag Adapter Webhooks route kyc_status mutation Investment gate
off Passthrough (legacy) not mounted (404, fail-closed) never (audit-only) off
on Default (retries + audit + gate) mounted + verified yes enforced

Checks

  • npx tsc --noEmit clean for touched files (remaining repo errors are pre-existing).
  • Local jest suite not yet run in this PR — coverage work (>95% on new modules) is the next commit; flag is off by default so prod behaviour is unchanged.

…s, and replayable audit trail

Implements Revora-Backend#875 behind KYC_PROVIDER_ADAPTER_ENABLED:

- KycProviderAdapter: hardened adapter with exponential-backoff retries around
  initiateCheck/getStatus; passthrough adapter preserves legacy behaviour when
  the flag is off.
- Signed webhook callbacks: raw-body HMAC verification, timestamp-bound replay
  window, and per-provider-tx-id replay guard (InMemoryKycReplayGuard).
- Replayable audit trail: every verified callback is persisted to the audit
  log keyed by provider_tx_id with provider, status, investor, and timestamps.
- kyc_status/kyc_provider/kyc_reference_id columns (migration 061) surfaced
  through UserRepository with updateKycVerification.
- Investment gate: when enabled, submissions are blocked until the investor's
  kyc_status is 'approved' (fail-closed) and the block is audit-logged.
- Register endpoint exposes an optional onRegistered hook for post-signup KYC
  initiation; a throwing hook never fails the signup.
- KYC initiate routes are now mounted at /api/v1/kyc; webhooks at
  /api/v1/kyc/webhooks (mounted before the JSON parser for byte-exact HMAcs).
@drips-wave

drips-wave Bot commented Sep 6, 2026

Copy link
Copy Markdown

@EmannuellaO Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Integrate KYC/AML provider with retry, status callbacks, and replayable audit trail

2 participants