# Handoff: Request Network In-House Checkout — 2026-05-28 Status: **fully end-to-end working on dev.amn.gg as of 2.6.38 backend / 2.6.41 frontend**. A 0.01 USDC payment (tx `0x494c77a2…`) flowed: page render → wallet connect (Rabby/injected) → approve → `transferFromWithReferenceAndFee` → RN webhook → backend marks completed → page flips to "پرداخت تأیید شد ✓" → continue → `/dashboard/payment/`. ## What's live - **Backend 2.6.38** — `/api/payment/request-network/intents` returns an `inHouseCheckout` block (destination, tokenAddress, decimals, chainId, proxyAddress, paymentReference 8-byte hex, feeAmount, feeAddress, amountWei). `GET /api/payment/request-network/:paymentId/checkout` rehydrates the block for an existing Payment record (lazy-enriches legacy records that pre-date 2.6.34 by calling RN's `GET /v2/request/:id`). Public `GET /api/version` for the version badge. `PaymentCoordinator.updatePurchaseRequestStatus` guards both `template-checkout-` and `template-tc-` prefixes (plus regex fallback for any non-ObjectId) — earlier the `template-tc-` blindspot crashed webhook processing on template-checkout payments and stranded escrow. - **Frontend 2.6.41** — `/checkout/request-network/[paymentId]` page with wagmi state machine: connect → switch-chain → check-allowance → approve → pay → wait-for-webhook. Destination + payment-reference + approve-tx + pay-tx hashes are copyable and click through to BscScan. Once a pay tx is in flight the page no longer reverts to "approve" even though the proxy call consumed the allowance. A 10-second `GET /api/payment/:id` poll runs as a fallback when the socket misses `payment-update`. Success-state continue button handles synthetic purchaseRequestId prefixes (`template-checkout-`, `template-tc-`) by routing to `/dashboard/payment/` instead of the 404-prone `/dashboard/request/`. WagmiProvider is now rendered unconditionally + the checkout page also self-wraps in its own WagmiProvider for defensive isolation. Verify which versions are running by hovering the version chip at bottom-left of any page on dev.amn.gg, or `curl https://dev.amn.gg/api/version`. ## Where things stand A real 0.01 USDC payment ran clean through the in-house path on 2026-05-28. Webhook delivery is durable enough for dev usage; durability for prod is Phase 5 (Cloudflare Worker ingress, not started). Five follow-up tasks were scoped immediately after — see `PRD - Wallet, Multichain, Confirmations, AML, Trezor.md` and Taskmaster `#7..#11`. ## Known issues / open work - **TypeScript-error CI false-success**: pipelines #40 and #41 reported ✅ green in Woodpecker while `yarn build` was actually failing at the TS step and no image was pushed. Memory entry: `woodpecker_silent_build_fail.md`. **Always verify** `dev-` exists in `git.manko.yoga` before trusting CI green. The wagmi `chainId` field requires `as any` because of its literal-union type — keep that pattern when adding new wagmi calls. - **Existing/legacy Payment records** (created before backend 2.6.34) don't have RN's request details cached. The GET endpoint lazy-enriches them via `GET /v2/request/:requestId` on first visit, then persists. If RN's API is down at that moment, falls back to the hosted-page link. - **Mongo access is denied** to the auto-mode classifier on dev — debugging payment records currently requires either the user's mongo creds or relying on the 409 `debug` block surfaced through the frontend. - **Wagmi provider isolation (2.6.39)**: The checkout page wraps itself in its own `WagmiProvider`. The root `Web3Provider` also renders `WagmiProvider` unconditionally as of 2.6.38. The doubling is intentional defensiveness — if one provider has an issue, the other still serves the checkout flow. Can be simplified later if both prove stable. - **PRD Phase 5 — Cloudflare Worker durable webhook ingress** — not started. Taskmaster `3.13`. Current dev relies on `dev.amn.gg` being up at the moment RN's webhook fires. For prod, RN webhooks need to land in a durable Cloudflare Worker that buffers + replays into the backend. ## Files changed (recent) Backend (`/Users/manwe/CascadeProjects/escrow/backend`): - `src/services/payment/requestNetwork/contract.ts` — spreads full RN response into `raw` - `src/services/payment/requestNetwork/inHouseCheckout.ts` — block builder, reads `paymentReference` from `rnRaw.requestDetails.paymentReference` - `src/services/payment/requestNetwork/merchantReference.ts`, `tokens.ts`, `proxyAddresses.ts`, `paymentReference.ts` — helpers - `src/services/payment/requestNetwork/requestNetworkPayInService.ts` — calls `GET /v2/request` after intent creation - `src/services/payment/requestNetwork/requestNetworkRoutes.ts` — `GET /:paymentId/checkout` + lazy enrichment + debug response - `src/services/payment/requestNetwork/networkClient.ts` — already had `getRequestStatus` - `src/app.ts` — `GET /api/version`, exempt from rate limit - `__tests__/rn-in-house-checkout.test.ts` — 12 unit tests, all green Frontend (`/Users/manwe/CascadeProjects/escrow/frontend`): - `src/web3/contracts/rn-fee-proxy.ts` — RN proxy + ERC20 ABIs - `src/web3/context/wagmi-provider.tsx` — removed the mount-gate that caused `WagmiProviderNotFoundError` - `src/web3/components/provider-payment.tsx` — `router.push` to in-house page + sessionStorage stash - `src/sections/payment/checkout/types.ts` + `rn-in-house-checkout-view.tsx` — state machine, local WagmiProvider wrap - `src/app/checkout/request-network/[paymentId]/page.tsx` — app router entry - `src/components/version-logger.tsx` — version chip + tooltip showing backend version ## Memory entries added - `MEMORY.md` index updated with: - `arcane_dev_stack.md` (env/project IDs, two-step deploy note) - `woodpecker_silent_build_fail.md` (CI green ≠ image pushed) - and existing `rn_webhook_event_field.md`, `backend_rate_limits.md`, `telegram_notify_no_parse_mode.md`, `devEscrow_nginx_after_redeploy.md`, `parallel_agents_on_escrow.md` ## Open PRD questions still to decide From `PRD - Request Network In-House Checkout.md` §10: - Proxy address universality across chains (currently BSC + Arb confirmed; Task #8 will probe Polygon/ETH/Base) - API pricing for hosted-UI-less usage (need RN account-mgmt question) - Approval UX — exact-amount vs MAX_UINT256 (current: exact-amount) - Cancel / timeout semantics for abandoned intents - Telemetry events for in-house vs hosted A/B ## Follow-up tasks (Taskmaster + PRD) Five follow-ups scoped for kimi to pick up independently. Full spec in `PRD - Wallet, Multichain, Confirmations, AML, Trezor.md`. Quick index: | # | Task | Priority | Status | |---|---------------------------------------------------------------|----------|--------| | 7 | Per-(buyer, sellerOffer) ephemeral RN destination wallets | high | 🟡 In progress — backend + admin UI shipped in 2.6.42, cart-aware buyer UX + tests + live RN-accepts-divergent-destination probe remain | | 8 | Multichain RN proxy registry + USDC/USDT support | high | ⏳ Not started | | 9 | Per-chain confirmation thresholds + admin UI | medium | ⏳ Not started | | 10 | Optional AML screening on incoming payments (seller-paid) | medium | ⏳ Not started | | 11 | Trezor signing for admin actions (release/refund/sweep) | high | ⏳ Not started | ## Task #7 — what landed in 2.6.42 **Backend** (`backend/src/services/payment/wallets/` + plumbing) - `DerivedDestination` model: `(buyerId, sellerOfferId, chainId)` → address, derivation path, status, sweep history. - `derivedDestinations.ts`: xpub-driven HD address derivation, atomic counter-based index allocation, idempotent `getDestinationFor`, race-safe upsert. Backend holds `DERIVED_DESTINATION_XPUB` only — master seed lives in KMS / Trezor (Task #11). - `sweepService.ts`: pluggable signer abstraction (`build-only` default; `hot-key` for dev), ERC-20 balance queries, sweep orchestration, interval-based cron. - `derivedDestinationRoutes.ts`: admin-only REST endpoints (list, sweep-all, sweep-one, config health, cron start/stop/status). Mounted at `/api/payment/derived-destinations`. - `requestNetworkPayInService.ts` now calls `getDestinationFor(buyer, sellerOffer, chainId)`, builds the per-payment merchant reference via `buildMerchantReference`, persists `metadata.derivedDestination`, and passes the override to RN. - `inHouseCheckout.ts` accepts a `destinationOverride`; the on-chain `paymentReference` compute-fallback now uses the actual destination (previously read `parsed.recipient` — hidden bug because RN's response provides the ref directly, but the fallback was broken for derived destinations). - `TransactionSafetyProvider.resolveExpectedRecipient` checks `metadata.derivedDestination.address` first, then legacy fallback. **Frontend** (admin only) - `/dashboard/admin/derived-destinations` page (table view, filters by status/chain/address, pagination, sweep-all, cron start/stop). - Per-row UI: address with copy + BscScan link, status chip, derivation path, balance, sweep count, last sweep tx link. **Env additions** (see `backend/.env.example`): - `DERIVED_DESTINATION_XPUB` — required for address derivation. - `DERIVED_DESTINATION_XPRIV` — only when `DERIVED_DESTINATION_SWEEP_SIGNER=hot-key` (dev shortcut). - `DERIVED_DESTINATION_BASE_PATH=m/44'/60'/0'` - `DERIVED_DESTINATION_CHAIN_ID=56` - `DERIVED_DESTINATION_SWEEP_SIGNER=build-only` - `DERIVED_DESTINATION_MIN_SWEEP_AMOUNT=0` - `DERIVED_DESTINATION_SWEEP_INTERVAL_MS=300000` **Remaining in task #7:** 1. Cart-aware buyer UX on the in-house checkout (sequential multi-seller approval flow with clear progress UI). 2. Unit tests for `derivedDestinations.ts` (idempotency, race handling) and `sweepService.ts`. 3. Live probe on dev: confirm RN accepts divergent `destinationId` across consecutive `POST /v2/secure-payments` calls from the same client. 4. Optional: auto-start sweep cron on backend boot via `app.ts` (currently manual via admin endpoint).