docs: ship in-house RN checkout, scope 5 follow-up tasks (#7-11)
In-house Request Network checkout went fully end-to-end on dev today. A real 0.01 USDC payment flowed through wallet connect -> approve -> ERC20FeeProxy.transferFromWithReferenceAndFee -> RN webhook -> TransactionSafetyProvider -> Payment.status=completed -> page success state. Tx 0x494c77a29161b5100d8e0b1ac675f1822955d0bb3633ecdbfafb886f84f2f320. Docs: - New PRD: Wallet, Multichain, Confirmations, AML, Trezor (5 follow-ups, each sized for an independent contributor) - Updated PRD: Request Network In-House Checkout (phases 0..3 done, phase 4 partial, phases 5-6 not started) - Updated handoff: deployed versions, what is working end-to-end, follow-up tasks index Taskmaster: 5 new top-level tasks (#7..#11) covering ephemeral destination wallets, multichain proxy registry + USDC/USDT, runtime confirmation thresholds, optional seller-paid AML screening, and Trezor signing for admin actions. Tasks are scoped fine-grained so each is independent enough for kimi to pick up. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
127
PRD - Request Network In-House Checkout.md
Normal file
127
PRD - Request Network In-House Checkout.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# PRD: Request Network In-House Checkout
|
||||
|
||||
> Source spec: `.taskmaster/docs/prd-request-network-in-house-checkout.md`
|
||||
> Status: **Draft — updated after 2026-05-28 dev webhook probe**
|
||||
> Related: `01 - Architecture/Request Network Integration Constraints.md` §1
|
||||
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
Buyers paying through Request Network are redirected to `pay.request.network/?token=…`. That page doesn't support **Rabby** (a meaningful slice of our user base), takes the buyer off `amn.gg` mid-flow, and wraps the payment in Safe + ERC-4337 + Pimlico paymaster + public BSC RPC — a stack we already saw rate-limit in real use.
|
||||
|
||||
The RN *protocol* is fine. Their *UI* is the problem.
|
||||
|
||||
## 2026-05-28 reality update
|
||||
|
||||
The dev BSC probe changed the risk model:
|
||||
|
||||
- Test transaction: `0x3a23febd9abd43d7e0851c1ea86c4ceaf08c11098852cb0425fa074e9c88350b`.
|
||||
- On-chain result: successful BSC USDC transfer to Amanat's configured destination wallet.
|
||||
- RN webhook result: RN did call `POST /api/payment/request-network/webhook` on `dev.amn.gg` four times from `34.34.233.192`.
|
||||
- Application result: backend returned `404` because the handler only correlated one local provider id shape and missed the RN request/payment-reference fields stored on the `Payment` record.
|
||||
- Frontend result: callback stayed on "processing payment" and the 3-second polling loop later hit `429`.
|
||||
|
||||
So the original "webhook silence" risk is no longer the observed failure. RN delivered the webhook; Amanat dropped it. Before another paid probe, dev must run the correlation fix and the callback polling fix, then pass the Request Network webhook smoke test.
|
||||
|
||||
## Core idea
|
||||
|
||||
RN's webhook detects payments by listening for a single on-chain event — `TransferWithReferenceAndFee` — emitted by their `ERC20FeeProxy` contract. We confirmed this by cold-inspecting an actual `$12` payment on their hosted page: under the Safe/4337 wrapper, the real work is two calls — `approve(proxy, amount)` and `transferFromWithReferenceAndFee(...)`.
|
||||
|
||||
If we make those same two calls from our own UI, RN's webhook fires. The hosted page is decorative.
|
||||
|
||||
However, webhook delivery alone is not enough to credit escrow. The backend now needs a **Transaction Safety Provider** gate between "provider says paid" and "Amanat marks funded". That provider approves only after configured checks pass: transaction hash present, chain confirmations deep enough, transfer recipient/token/amount matched on-chain, and any external AML/sanctions provider response is acceptable.
|
||||
|
||||
## What we replace
|
||||
|
||||
| Today | Proposed |
|
||||
|---|---|
|
||||
| Click "Pay with RN" → `window.location = securePaymentUrl` | Click "Pay with RN" → `/checkout/request-network/:paymentId` (Amanat-rendered) |
|
||||
| RN's wallet picker (no Rabby) | wagmi `injected()` + `metaMask()` — Rabby works via EIP-6963 |
|
||||
| Safe + 4337 + paymaster | Buyer's EOA, two direct txs, buyer pays own gas |
|
||||
| Public BSC RPC | Whatever the buyer's wallet uses (under their control) |
|
||||
|
||||
## What does NOT change
|
||||
|
||||
- Backend webhook route stays the same, but the handler must correlate every RN reference shape (`providerPaymentId`, request id, payment reference, nested raw data) and pass the Transaction Safety Provider before marking a payment funded.
|
||||
- `Payment` lifecycle is unchanged.
|
||||
- Settlement, refunds, dispute paths are unchanged.
|
||||
- Non-RN payment providers (SHKeeper, etc.) are unaffected.
|
||||
- Buyer can still opt out: hosted-page link stays available as an escape hatch.
|
||||
|
||||
## Hard-known protocol facts (from inspection)
|
||||
|
||||
| Item | Value |
|
||||
|---|---|
|
||||
| RN ERC20FeeProxy (BSC + Arb, likely all EVMs) | `0x0DfbEe143b42B41eFC5A6F87bFD1fFC78c2f0aC9` |
|
||||
| Function | `transferFromWithReferenceAndFee(token, to, amount, ref, feeAmount, feeAddress)` |
|
||||
| Selector | `0xc219a14d` |
|
||||
| `paymentReference` derivation | `last8Bytes(keccak256(requestId + salt + destination))` |
|
||||
| Fee config currently used | `feeAmount = 0`, `feeAddress = 0x…dEaD` |
|
||||
|
||||
## Backend deliverables (sketch)
|
||||
|
||||
Extend `POST /payment/request-network/intents` response with an `inHouseCheckout` object containing: `destination`, `tokenAddress`, `decimals`, `chainId`, `proxyAddress`, `paymentReference`, `feeAmount`, `feeAddress`, `amountWei`. Full field sourcing in the spec.
|
||||
|
||||
## Frontend deliverables (sketch)
|
||||
|
||||
New page `/checkout/request-network/:paymentId` with a wagmi-driven state machine: connect → switch-chain → check-allowance → approve → pay → wait-for-webhook → done. Reuses layout chrome from `web3/components/manual-payment.tsx` (SHKeeper). Escape-hatch link to the hosted page stays visible.
|
||||
|
||||
## Acceptance criteria (short list)
|
||||
|
||||
1. A Rabby user completes an RN payment without leaving `amn.gg`.
|
||||
2. RN's webhook fires from a transaction built and submitted by our UI.
|
||||
3. The `Payment` doc transitions to `completed` / `escrowState='funded'` exactly as it does today, but only after safety checks pass.
|
||||
4. Wrong-chain → one-click switch. Sufficient allowance → approve step skipped. Abandoned page → resumable.
|
||||
5. Hosted-page link still works and is documented as the fallback.
|
||||
|
||||
Full list in the spec.
|
||||
|
||||
## Open questions for the review
|
||||
|
||||
1. Is the proxy address identical on every chain we plan to support, or only some?
|
||||
2. RN's pricing — are we still in their terms if we never use their hosted UI?
|
||||
3. `approve(MAX_UINT256)` (RN's pattern, best UX) vs exact-amount approve (better security)?
|
||||
4. Cancel / timeout semantics for abandoned intents — TTL, explicit cancel endpoint, or both?
|
||||
5. Feature flag + A/B in dev for one cycle before prod, or hard-cut once acceptance passes?
|
||||
|
||||
## Risks
|
||||
|
||||
- **Webhook handling failure.** RN did deliver the 2026-05-28 dev webhook, but Amanat returned `404`. A deployed correlation fix and smoke test are now the gate before any more paid probes.
|
||||
- **Webhook durability.** The main app is too unstable to be the only webhook landing zone. Roadmap item: put a Cloudflare Worker in front of RN webhooks, store raw delivery metadata durably, then forward/replay into the backend.
|
||||
- **False-positive payment credit.** A signed webhook must not be enough to credit escrow. Transaction Safety Provider gates payment completion on on-chain transfer verification, confirmation depth, and future AML/sanctions checks.
|
||||
- Chain-specific proxy address divergence (mitigation: per-chain constants, fall back to hosted page for unknown chains).
|
||||
|
||||
## Implementation phases
|
||||
|
||||
| # | Phase | Gate |
|
||||
|---|---|---|
|
||||
| 0 | Probe — real dev BSC payment | ✅ Completed: RN webhook reached nginx/backend, but app returned `404` |
|
||||
| 0A | Confirmation repair — deploy correlation fix, callback polling fix, and Transaction Safety Provider | ✅ Completed: smoke test passes; unsigned/test webhooks rejected |
|
||||
| 1 | Backend: expand intent payload + helpers | ✅ Completed (2.6.32 → 2.6.38) |
|
||||
| 2 | Frontend: page skeleton + wallet connect | ✅ Completed (2.6.30 → 2.6.41) |
|
||||
| 3 | On-chain wiring (approve + proxy call) | ✅ Completed: real 0.01 USDC payment on dev BSC 2026-05-28 (tx `0x494c77a2…`), Payment.status=completed, safety approved |
|
||||
| 4 | Hardening: errors, timer, persistence, telemetry, escape hatch | 🟡 Partial — copy/BscScan links, pay-button trap, 10s status poll, template-tc routing fix shipped 2.6.41. Timer + persistence + telemetry left. |
|
||||
| 5 | Durable ingress — Cloudflare Worker receives RN webhooks, stores delivery record, forwards to backend, exposes replay | ⏳ Not started (Taskmaster `3.13`) |
|
||||
| 6 | Rollout: flag + A/B | ⏳ Not started |
|
||||
|
||||
Five follow-up workstreams scoped 2026-05-28 in `PRD - Wallet, Multichain, Confirmations, AML, Trezor.md` (Taskmaster `#7…#11`) cover ephemeral destination wallets, multichain proxy registry, runtime confirmation thresholds, optional AML screening, and Trezor signing for admin actions.
|
||||
|
||||
## Durable webhook ingress roadmap
|
||||
|
||||
Add a small Cloudflare Worker as the public Request Network webhook target:
|
||||
|
||||
1. Receive RN webhook with raw body, headers, request id, delivery id, source IP, timestamp.
|
||||
2. Verify RN signature at the edge if secret handling/raw-body verification is compatible; otherwise store first and let backend perform canonical verification.
|
||||
3. Write an immutable delivery record to durable storage (Cloudflare Queues + D1/R2/KV, final choice TBD).
|
||||
4. Forward to the primary backend and optionally a secondary backend endpoint.
|
||||
5. Return `2xx` only after durable enqueue/store succeeds.
|
||||
6. Provide operator replay by delivery id or time window.
|
||||
|
||||
The Worker is an ingress and evidence buffer, not the trust oracle. The backend still owns signature verification, idempotency, Transaction Safety Provider checks, ledger updates, and marketplace state transitions.
|
||||
|
||||
## See also
|
||||
|
||||
- `.taskmaster/docs/prd-request-network-in-house-checkout.md` — full spec with field sources, file lists, state-machine details, telemetry events.
|
||||
- `01 - Architecture/Request Network Integration Constraints.md` — §1 (this PRD addresses), §2/§3/§4 are explicitly out of scope here.
|
||||
- `PRD - Request Network Migration and Funds Management.md` — the broader RN context.
|
||||
Reference in New Issue
Block a user