# PRD - Request Network Migration and Funds Management Date: 2026-05-24 Status: Draft for architecture review Owner: Platform / Payments Codebase reviewed: `/Users/manwe/CascadeProjects/escrow` ## Executive Summary Replacing SHKeeper with Request Network is not a provider swap. SHKeeper currently acts like a deposit-wallet and payout-task provider: the backend creates a payment intent, receives a generated wallet address, watches SHKeeper webhooks and BSC wallet balances, then optionally creates an outgoing payout task. Request Network is request/payment-reference based. The platform creates a Request Network payment request or secure payment page, the payer pays through Request Network contracts or hosted flow, and reconciliation happens through Request Network payment events. Payouts are API-created payment requests/calldata that still require wallet execution. This means Amanat needs a first-class funds ledger and a release/refund orchestration layer, not just renamed `/payment/shkeeper/*` endpoints. Primary recommendation: run a phased migration behind a provider adapter. Introduce Request Network for new pay-ins first using Secure Payment Pages, keep SHKeeper read-only for existing payments, then add funds ledger, release/refund gates, and Request Network payout flows. ## Source Findings Current code: - Backend mounts SHKeeper at `/api/payment/shkeeper` through `backend/src/services/payment/shkeeper/shkeeperRoutes.ts` and `shkeeperPayoutRoutes.ts`. - Frontend calls SHKeeper through `frontend/src/actions/payment.ts`, `frontend/src/web3/components/shkeeper-payment.tsx`, `shkeeper-widget.tsx`, and `shkeeper-payout.tsx`. - `Payment.provider` only allows `shkeeper` or `other`; SHKeeper-specific fields live directly under `Payment.metadata`. - Pay-in completion is accepted from SHKeeper webhook, manual confirmation, or wallet monitor, all coordinated through `PaymentCoordinator`. - Current payout paths mix SHKeeper payout tasks, simulated marketplace release, and direct admin wallet payout. - There is no durable internal funds ledger that tracks available, held, releasable, disputed, released, refunded, and fee amounts per purchase request. Request Network documentation checked: - API v2 supports programmatic request creation, secure payment pages, payouts, webhooks, payment search, platform fees, and payee destinations: - Secure Payment Pages are hosted payment URLs returned by `POST /v2/secure-payments`, returning `requestIds`, `token`, and `securePaymentUrl`: - Secure Payment Pages validate official Request Network contracts before payer signing: - Webhooks are scoped by Client ID and include `x-request-network-signature`, delivery ID, retry count, and optional test header: - Request Network webhooks retry failed deliveries up to 3 retries with short backoff windows: - Payout endpoints return unsigned transaction data that the platform/wallet must execute: - Batch payouts can return approval and batch payment transactions; the application sends those wallet transactions: - Request Network protocol fee is 5 bps with stablecoin caps, and platform fees can be included through `feePercentage` and `feeAddress`: - Payment networks use payment references for detection, rather than unique deposit wallets per invoice: ## Target Architecture ### Payment Provider Adapter Create a provider-neutral payment interface: - `createPayInIntent` - `getPayInStatus` - `handleProviderWebhook` - `createHostedPaymentLink` - `createReleaseInstruction` - `createRefundInstruction` - `getPayoutStatus` - `searchProviderPayments` Implement: - `ShkeeperProvider` for legacy compatibility. - `RequestNetworkProvider` for new flows. ### Funds Ledger Add an internal ledger separate from provider metadata: - `fundsAccount`: one per purchase request or order. - `ledgerEntry`: immutable entries for authorization, payment detected, platform fee, provider fee, hold, release, refund, chargeback/dispute hold, adjustment. - `fundsBalance`: derived or cached view containing `expected`, `paid`, `held`, `releasable`, `released`, `refunded`, `disputed`, `fees`, and `available`. Ledger invariants: - Seller payout cannot exceed paid amount minus refunds, fees, and active dispute holds. - Release requires payment status `completed`, escrow state `releasable`, no open dispute, and a verified seller destination. - Refund requires available held balance and must mark the purchase request/payment state consistently. - Provider webhooks never directly mark funds as released; they create/reconcile ledger entries. ### Payment Flow Pay-in: 1. Buyer accepts seller offer. 2. Backend creates internal payment + funds account. 3. Backend calls Request Network `POST /v2/secure-payments` or `POST /v2/request`. 4. Frontend redirects to `securePaymentUrl` or executes returned calldata in wallet. 5. Request Network webhook confirms payment. 6. Backend verifies signature, idempotency, amount, request ID, payment reference, currency, and merchant reference. 7. Ledger moves from `expected` to `held`. 8. Purchase request enters `payment` or `processing`. Release: 1. Buyer delivery confirmation or admin release marks funds `releasable`. 2. Backend creates Request Network payout/release instruction, or admin wallet transaction instruction. 3. Admin/operator wallet signs and broadcasts transaction. 4. Backend records transaction hash as `release_pending`. 5. Webhook/search confirms settlement. 6. Ledger moves `held -> released`. Refund: 1. Refund request validates available held balance. 2. Backend creates refund transaction instruction. 3. Admin/operator wallet executes. 4. Confirmation updates ledger `held -> refunded`. ## PRD 1 - Provider-Neutral Payment Adapter Priority: P0 Goal: decouple checkout, webhook, and payout flows from SHKeeper-specific routes and metadata. Scope: - Add `paymentProvider` abstraction in backend. - Add `provider: 'shkeeper' | 'request_network' | 'manual' | 'admin_wallet'` to payment model. - Keep `/api/payment/shkeeper/*` operational for legacy records. - Add `/api/payment/providers/request-network/*` or `/api/payment/request-network/*` for new Request Network flows. - Add a feature flag: `PAYMENT_PROVIDER=request_network|shkeeper`. Acceptance Criteria: - New checkout can create a Request Network payment link without touching SHKeeper service code. - Existing SHKeeper payments remain readable and can still process late webhooks. - Frontend does not import provider-specific action names outside provider-specific components. - Provider metadata is namespaced under `metadata.providers.requestNetwork` or a dedicated provider table. Risk Assessment: - Impact: High. Payment creation is core revenue flow. - Likelihood: Medium. Existing code has many direct `shkeeper` references. - Main risks: broken checkout, duplicate payment records, orphaned webhooks, mixed provider states. - Mitigation: use provider feature flag, preserve SHKeeper routes, add idempotency keys, run Request Network only for new payments during rollout. - Rollback: set `PAYMENT_PROVIDER=shkeeper`; keep Request Network records read-only. ## PRD 2 - Request Network Pay-In Integration Priority: P0 Goal: replace generated SHKeeper wallet addresses with Request Network request/payment-reference based payment collection. Scope: - Store Request Network `requestId`, `paymentReference`, `securePaymentUrl`, `token`, `merchantReference`, `network`, `invoiceCurrency`, and `paymentCurrency`. - Use Secure Payment Pages for first launch to reduce frontend wallet/calldata complexity. - Include Amanat purchase request ID and payment ID as merchant reference or metadata. - Validate supported networks/currencies before creating payment links. - Remove frontend dependence on SHKeeper-generated `walletAddress` for new provider flow. Acceptance Criteria: - Buyer can start payment and receive a Request Network hosted payment URL. - Backend records internal payment as `pending` with Request Network identifiers. - Webhook `payment.confirmed` reconciles only the matching internal payment. - Amount, currency, provider request ID, and merchant reference are verified before setting `escrowState='funded'`. - Late or duplicate webhook deliveries are idempotent. Risk Assessment: - Impact: High. - Likelihood: Medium. - Main risks: currency/network mismatch, hosted redirect UX breakage, buyer pays wrong amount, missing webhook. - Mitigation: backend-only creation, strict metadata correlation, periodic fallback reconciliation using Request Network payment search, idempotency by delivery ID and request ID. - Rollback: disable Request Network provider for new payments; keep pending Request Network payments visible with manual support workflow. ## PRD 3 - Funds Ledger and Escrow State Machine Priority: P0 Goal: introduce internal funds management so payment, hold, release, refund, dispute, and fee states are auditable and provider-independent. Scope: - Add `FundsAccount` model keyed by purchase request/payment. - Add immutable `LedgerEntry` model with provider references and actor/source. - Add derived `FundsBalance` query/service. - Define state transitions: `expected -> held -> releasable -> releasing -> released`, plus `refunded`, `partially_refunded`, `disputed`, `failed`. - Prevent release/refund from reading raw `Payment.status` alone. Acceptance Criteria: - Every successful pay-in creates a ledger entry for gross paid amount. - Platform fee, Request Network protocol fee, and net seller amount are represented explicitly. - Release API checks ledger invariants before creating payout/refund instructions. - Dispute hold blocks payout until resolved. - Admin UI/payment detail view can show gross paid, fees, held, releasable, released, refunded. Risk Assessment: - Impact: Very High. - Likelihood: Medium. - Main risks: double-release, incorrect fee accounting, drift between Payment and ledger state. - Mitigation: immutable entries, unique idempotency keys, transaction/session writes where Mongo supports them, reconciliation job, read-only migration report before enforcing ledger. - Rollback: keep old payment fields as source of truth until ledger backfill passes; feature flag release enforcement. ## PRD 4 - Request Network Webhook and Reconciliation Service Priority: P1 Goal: replace SHKeeper webhook and wallet monitor semantics with signed Request Network event processing and periodic reconciliation. Scope: - Add `/api/payment/request-network/webhook`. - Verify raw body using `x-request-network-signature`. - Store delivery ID, retry count, event type, request ID, payment reference, payload hash, and processed status. - Support test webhooks via `x-request-network-test`. - Add scheduled reconciliation using Request Network payment search/status APIs. - Retire SHKeeper wallet monitor for new Request Network payments. Acceptance Criteria: - Invalid signatures are rejected in all environments except explicit local test mode. - Duplicate delivery IDs are acknowledged without duplicating ledger entries. - `payment.confirmed` and partial/over/failed states map to internal status consistently. - Reconciliation job can repair missed webhook state without relying on buyer/frontend callbacks. Risk Assessment: - Impact: High. - Likelihood: Medium. - Main risks: raw body parsing mismatch, webhook downtime, event mapping mistakes. - Mitigation: raw-body middleware scoped to webhook route, event fixture tests, dead-letter queue/table, operator replay endpoint. - Rollback: pause webhook processor and rely on manual/admin reconciliation for Request Network records. ## PRD 5 - Release, Refund, and Payout Orchestration Priority: P1 Goal: replace SHKeeper payout tasks and simulated marketplace release with auditable transaction instruction and confirmation flows. Scope: - Define release/refund service that consumes ledger balances, not raw request status. - Generate Request Network payout instructions or direct admin wallet transaction instructions. - Store unsigned transaction payloads, signer, submitted tx hash, confirmation status, and provider status. - Add batch payout support as a later optimization after single release is stable. - Require admin/operator authorization and dispute checks before instruction creation. Acceptance Criteria: - Seller payout requires verified seller wallet/payee destination. - Release cannot occur if funds are unpaid, already released, refunded, or disputed. - Transaction hash confirmation updates ledger only once. - Admin can see pending release instructions and retry/cancel safely. - Existing `/purchase-requests/:id/release-payment` simulated path is removed or hidden behind dev-only mode. Risk Assessment: - Impact: Very High. - Likelihood: High. - Main risks: Request Network payouts are not custodial magic; operator wallet still signs transactions, so process failure can leave funds pending. - Mitigation: explicit release queue, two-step admin approval, signer audit trail, reconciliation against tx hash/provider status. - Rollback: keep direct admin wallet payout as emergency fallback with mandatory ledger entry and reason code. ## PRD 6 - Frontend Checkout and Admin Migration Priority: P1 Goal: update buyer payment, admin release, seller payout, and payment details UI for Request Network flows. Scope: - Replace `ShkeeperPayment` with provider-neutral `CryptoPayment`. - Add `RequestNetworkPayment` redirect flow for secure payment pages. - Keep `ShkeeperPayment` available only for legacy/unpaid SHKeeper records if needed. - Replace `ShkeeperPayout` with release queue/admin payout UI. - Payment detail page shows provider, request ID, payment reference, hosted link, transaction hashes, ledger balances, webhook/reconciliation status. Acceptance Criteria: - Buyer checkout no longer expects provider-generated `walletAddress` for Request Network payments. - Admin release UI displays available/releasable funds and blocks unsafe release states. - Seller payout status comes from internal release instruction/ledger, not SHKeeper task polling. - Legacy SHKeeper labels are not shown for Request Network payments. Risk Assessment: - Impact: Medium-High. - Likelihood: Medium. - Main risks: confusing redirect UX, old Persian labels referencing SHKeeper, admin accidentally using old payout button. - Mitigation: provider-specific copy, feature flag UI sections, legacy badge, clear payment detail diagnostics. - Rollback: route checkout back to SHKeeper component via provider flag. ## PRD 7 - Data Migration and Legacy Decommission Priority: P2 Goal: safely migrate historical SHKeeper payment records and phase out provider-specific code. Scope: - Backfill provider metadata namespace for existing SHKeeper payments. - Create ledger entries for completed SHKeeper payments from trusted records. - Mark historical records as `legacyProvider='shkeeper'`. - Keep SHKeeper webhook active during webhook tail period. - Remove SHKeeper wallet monitor, simple auto webhook, test callback routes, and payout routes after cutoff. Acceptance Criteria: - Migration produces counts for total, migrated, skipped, ambiguous, and failed records. - No historical completed payment loses transaction hash/provider invoice/task metadata. - Legacy SHKeeper webhook can still reconcile existing pending records until cutoff date. - Decommission checklist includes env vars, docs, frontend labels, backend routes, and runbooks. Risk Assessment: - Impact: Medium. - Likelihood: Medium. - Main risks: ambiguous historical records, template checkout string IDs, mixed status meanings. - Mitigation: dry-run migration, manual review bucket, no destructive migration, keep original metadata. - Rollback: ledger backfill is additive; old records remain intact. ## Implementation Sequence 1. Add provider adapter and Request Network config without changing default provider. 2. Add funds ledger models/services and dry-run backfill report. 3. Implement Request Network secure pay-in flow behind feature flag. 4. Implement signed webhook receiver and reconciliation job. 5. Enable Request Network for limited new checkout cohort. 6. Add release/refund orchestration using ledger gates. 7. Migrate admin/frontend views. 8. Backfill legacy records. 9. Decommission SHKeeper once no active records depend on it. ## Open Decisions - Use Request Network Secure Payment Pages first, or execute Request Network calldata directly in the existing wallet modal? - Keep funds in a platform-controlled escrow wallet, pay sellers via admin/operator release, or route pay-ins directly to seller with only platform fee collection? - Which chains/currencies are required for launch: BSC USDT parity with today, or Request Network supported stablecoin routes first? - Should platform fee be paid by buyer, seller, or absorbed by Amanat? - Does Amanat need crypto-to-fiat/offramp later, which adds KYC/payment detail requirements? ## Recommendation Start with Secure Payment Pages and a platform escrow/payee destination controlled by Amanat. This best matches the current escrow mental model while reducing frontend transaction-building risk. Do not route pay-ins directly to sellers until dispute handling, refund logic, and service fee economics are fully redesigned.