docs: sync from backend 19f7eb9, frontend 60ee6fb — Task #10 AML screening

This commit is contained in:
Siavash Sameni
2026-05-28 20:35:38 +04:00
parent fd2aa71ef4
commit ddc0434819
34 changed files with 709 additions and 453 deletions

View File

@@ -44,7 +44,8 @@ backend/src/
│ │ ├── migration/ # Legacy data backfill utilities
│ │ ├── observability/ # Logging and incident controls
│ │ ├── requestNetwork/ # Request Network pay-in, routes, webhook signature
│ │ ── shkeeper/ # SHKeeper API, webhook, payout
│ │ ── safety/ # Transaction Safety Provider + confirmation thresholds
│ │ └── wallets/ # Derived destination wallets + sweep orchestration
│ ├── points/ # Loyalty points, levels, redemption
│ ├── redis/ # Redis client, cache helpers
│ ├── telegram/ # Bot webhook, Mini App session, identity linking, notifications
@@ -125,17 +126,19 @@ The full route table mounted by `app.ts`:
| `/api/marketplace/categories` | `services/marketplace/controllerRoutes.ts` | public read | Category list |
| `/api/marketplace/shop-settings` | `services/marketplace/shopSettingsController.ts` | JWT (seller) | Shop profile |
| `/api/payment` | `services/payment/paymentControllerRoutes.ts` + `paymentRoutes.ts` | JWT | Payment CRUD, health, export |
| `/api/payment/decentralized` | `services/payment/decentralizedPaymentRoutes.ts` | mixed | Web3 save, verify, receiver |
| `/api/payment/shkeeper` | `services/payment/shkeeper/shkeeperRoutes.ts` | mixed | Intents, webhook, release, refund, config |
| `/api/payment/shkeeper/payout` | `services/payment/shkeeper/shkeeperPayoutRoutes.ts` | JWT (seller/admin) | Withdraw to wallet |
| `/api/payment/request-network` | `services/payment/requestNetwork/requestNetworkRoutes.ts` | HMAC sig | Request Network pay-in creation, Secure Payment Page, webhooks |
| `/api/payment/decentralized` | `services/payment/decentralizedPaymentRoutes.ts` | mixed | Legacy/manual Web3 save, verify, receiver |
| `/api/payment/request-network` | `services/payment/requestNetwork/requestNetworkRoutes.ts` | mixed + HMAC sig on webhook | Request Network pay-in creation, in-house checkout rehydrate, webhooks |
| `/api/payment/derived-destinations` | `services/payment/wallets/derivedDestinationRoutes.ts` | JWT (admin) | Derived address list, sweeps, cron, config health |
| `/api/admin/rn/networks` | `services/payment/requestNetwork/networkRegistryRoutes.ts` | JWT (admin) | Supported RN chain/token registry |
| `/api/admin/settings/confirmation-thresholds` | `services/admin/confirmationThresholdRoutes.ts` | JWT (admin) | Runtime min-confirmation thresholds |
| `/api/admin/payments/awaiting-confirmation` | `services/admin/awaitingConfirmationRoutes.ts` | JWT (admin) | Payments blocked on safety confirmations |
| `/api/telegram` | `services/telegram/telegramRoutes.ts` | mixed (some JWT, webhook uses secret-token) | Mini App verify/session, identity link/unlink, bot webhook |
| `/api/chat` | `services/chat/chatRoutes.ts` | JWT | Conversations, messages |
| `/api/notification` | `services/notification/notificationRoutes.ts` + `notificationControllerRouter` | JWT | List, mark read |
| `/api/dispute` | `services/dispute/disputeRoutes.ts` | JWT | **Not implemented** — planned |
| `/api/blog` | `services/blog/blogRoutes.ts` | mixed | **Not implemented** — planned |
| `/api/admin` | `services/admin/adminRoutes.ts` | JWT (admin) | **Not implemented** — planned |
| `/api/points` | `services/points/pointsRoutes.ts` | JWT | **Not implemented** — planned |
| `/api/disputes` | `routes/disputeRoutes.ts` + `services/dispute/disputeRoutes.ts` | JWT | Dispute CRUD plus release-hold helpers |
| `/api/blog` | `services/blog/blogRoutes.ts` | mixed | Public reads, admin writes |
| `/api/admin/cleanup` | `services/admin/dataCleanupRoutes.ts` | JWT (admin) | Data cleanup operations |
| `/api/points` | `services/points/pointsRoutes.ts` | JWT | Points, levels, referrals |
| `/api/ai` | `services/ai/aiRoutes.ts` | JWT | OpenAI-backed helpers |
| `/api/files` | `services/file/fileRoutes.ts` | JWT | Multipart upload |
| `/api/email` | `services/email/emailRoutes.ts` | JWT | Email dispatch |
@@ -253,9 +256,12 @@ Full table in [[Environment Variables]]. Critical ones:
| `JWT_EXPIRES_IN` | `7d` | |
| `REFRESH_TOKEN_EXPIRES_IN` | `30d` | |
| `FRONTEND_URL` | `http://localhost:3000` | CORS origin |
| `SHKEEPER_API_URL` | `https://pay.amn.gg` | |
| `SHKEEPER_API_KEY` | required | |
| `SHKEEPER_WEBHOOK_SECRET` | required | HMAC key |
| `REQUEST_NETWORK_API_BASE_URL` | `https://api.request.network` | Request Network API |
| `REQUEST_NETWORK_API_KEY` | required | Request Network API credential |
| `REQUEST_NETWORK_WEBHOOK_SECRET` | required | Webhook HMAC key |
| `PAYMENT_LEDGER_ENFORCEMENT` | `false` | Target `true` before launch-scale releases |
| `TRANSACTION_SAFETY_*` | required for payments | Confirmation, transfer-match, and AML controls |
| `DERIVED_DESTINATION_SWEEP_SIGNER` | `build-only` | Target hardware/Safe-backed signer |
| `SMTP_*` | required | Nodemailer |
| `OPENAI_API_KEY` | required | |
@@ -279,7 +285,7 @@ Redis client (in `src/services/redis/`) provides:
The codebase has no dedicated queue runner — scheduled / async work is triggered inline from request handlers and uses `setTimeout` / `setInterval` patterns where needed (e.g., delayed retries). Consider introducing Bull / BullMQ if you grow:
- Payment status reconciliation (polling SHKeeper for stragglers)
- Request Network webhook replay/reconciliation and derived-destination balance checks
- Notification email digests
- Auto-release escrow timers
- Token / refresh-token cleanup
@@ -295,7 +301,7 @@ Jest test suites in `backend/__tests__/`:
| `models.test.ts` | Schema validation, virtuals, hooks |
| `payment-services.test.ts` | Payment orchestration logic |
| `complete-backend.test.ts` | Cross-service integration |
| `shkeeper-backend.test.ts` | SHKeeper service + webhook |
| Request Network / payment tests | Request Network adapter, webhook signature, ledger, release/refund orchestration |
Run with `npm run test:all`. CI runs the same. Reach for `npm run test:models`, `npm run test:payment`, etc. when iterating on a slice.
@@ -310,7 +316,8 @@ Run with `npm run test:all`. CI runs the same. Reach for `npm run test:models`,
| `src/shared/utils/response-handler.ts` | Standard response shape |
| `src/shared/middleware/auth.ts` | JWT verify + RBAC |
| `src/infrastructure/socket/socketService.ts` | All socket plumbing |
| `src/services/payment/shkeeper/shkeeperWebhook.ts` | Webhook signature scheme |
| `src/services/payment/requestNetwork/requestNetworkRoutes.ts` | Request Network checkout and webhook route |
| `src/services/payment/ledger/fundsLedgerService.ts` | Immutable payment ledger writes |
| `src/services/marketplace/PurchaseRequestService.ts` | Core marketplace state machine |
| `src/services/auth/authService.ts` | Auth flows, lockout, hashing |
| `src/models/User.ts` | Central entity with role/preferences |
@@ -325,4 +332,4 @@ Run with `npm run test:all`. CI runs the same. Reach for `npm run test:models`,
- [[Real-time Layer]] — Socket.IO room model
- [[Security Architecture]] — JWT, passkeys, webhook HMAC
- [[Data Model Overview]] — entity-relationship map
- [[Authentication Flow]] · [[Payment Flow - SHKeeper]] · [[Dispute Flow]]
- [[Authentication Flow]] · [[Escrow Flow]] · [[Dispute Flow]]

View File

@@ -190,7 +190,7 @@ See [[Monitoring]] for the full table of metrics & recommended alerts.
| Browser → Backend | 5001 | HTTP + WS | via Nginx `/api`, `/socket.io` |
| Backend → MongoDB | 27017 | TCP | Docker network |
| Backend → Redis | 6379 | TCP | Docker network |
| Backend → SHKeeper | 443 | HTTPS | External |
| Backend → Request Network API | 443 | HTTPS | External payment provider |
| Backend → SMTP | 587 | TLS | External |
| Backend → OpenAI | 443 | HTTPS | External |
| Browser → Blockchain RPC | 443 | HTTPS | Alchemy URLs |

View File

@@ -215,6 +215,6 @@ Sticky sessions on the load balancer are also required so a given client always
## Related
- [[Backend Architecture]] · [[Frontend Architecture]]
- [[Chat Flow]] · [[Notification Flow]] · [[Payment Flow - SHKeeper]] · [[Dispute Flow]]
- [[Chat Flow]] · [[Notification Flow]] · [[Escrow Flow]] · [[Dispute Flow]]
- [[Security Architecture]] — socket auth concerns
- [[Socket Events]] — full event reference (developer-facing API doc)

View File

@@ -8,15 +8,15 @@ This document captures payment-flow issues that surfaced while integrating Reque
---
## 1. RN does not support Rabby — show-stopper for our wallet user base
## 1. RN hosted UI does not support Rabby -- mitigated by Amanat in-house checkout
### Problem
RN's hosted payment page (the `pay.request.network/?token=…` UI returned by `/v2/secure-payments`) does not detect / connect to Rabby. A meaningful slice of Amanat's user base pays from Rabby. Sending them to a screen that won't even let them connect is a hard block.
### Mitigation (designed, not yet implemented)
### Mitigation (implemented core path)
Skip the RN-hosted UI. We already call `/v2/secure-payments` and receive a `securePaymentUrl`, but we also receive `requestIds` and `token` — that's everything we need to know what the merchant request is. Behind that token there is a contract on the destination chain that anyone can fulfill.
Skip the RN-hosted UI. Amanat still calls `/v2/secure-payments`, stores the Request Network identifiers, and exposes an in-house checkout block. The frontend builds the same RN-compatible on-chain action from the buyer's wallet, so Rabby/MetaMask users stay inside the Amanat flow.
So the new flow becomes:
@@ -32,10 +32,11 @@ So the new flow becomes:
- RN's value to us at that point is the *settlement bookkeeping*, not the UI. We use them as "did this address receive the expected amount before timeout?" — the wallet UX stays in our control.
- Buyer never sees a third-party brand mid-checkout, which is a UX win regardless of Rabby.
### Open
### Remaining work
- Need to confirm RN settles a payment that arrives from a *proxy transaction we built*, not from their hosted page. The 2026-05-28 probe confirms RN webhook delivery to Amanat, but the app returned `404`; repeat the probe only after the confirmation repair is deployed.
- Need a fallback for the buyer who insists on the RN hosted UI (some users will already have the link copied). Keep `securePaymentUrl` exposed as a "advanced / pay with RN" link.
- Keep the RN hosted URL exposed as an escape hatch.
- Continue hardening timer/persistence/telemetry around the in-house checkout.
- Treat durable webhook ingress as a production gate, because the main Express app should not be the only landing zone for callback evidence.
---
@@ -51,7 +52,7 @@ The visible costs:
- Or seller gets less than they expected (worst — they'll dispute).
- Plus settlement latency goes from seconds to minutes-hours depending on the bridge.
### Mitigation (designed)
### Mitigation (partially implemented)
Take the chain choice away from RN's UI and bring it into ours, gated by what the *seller* will accept.
@@ -62,11 +63,11 @@ Two-step UX:
### Side benefit
This composes cleanly with #1 (own checkout screen): we already have to render the wallet picker, so adding a chain selector before the wallet step costs almost nothing.
This composes cleanly with #1 (own checkout screen): we already render the wallet picker, so seller-accepted chain selection can happen before wallet connection. The chain/token registry and admin networks page exist; seller-side accepted-chain policy remains a separate product/data-model task.
### Open
- We need a per-seller config table for accepted chains. Today the env-level `REQUEST_NETWORK_MERCHANT_REFERENCE` hard-codes a single chain (`bsc`). Needs to become per-seller, per-offer.
- We need a per-seller/per-offer config table for accepted chains. Today the global merchant reference is still the fallback, while derived destination work handles recipient variation.
- Does RN's API support creating a secure-payment that *rejects* off-chain payments rather than auto-bridging? Or do we have to enforce this purely on our side by never offering the cross-chain option to the buyer? **Confirm with RN docs/support.**
---
@@ -83,7 +84,7 @@ Today the entire escrow stack receives funds into one (or a handful of) wallets
This is a show-stopper for going live at scale. Same class of issue we already considered around SHKeeper.
### Mitigation (designed; needs RN feasibility check)
### Mitigation (implemented core path; operational probe pending)
Per-`(buyer, merchant)`-pair ephemeral wallets. Each new escrow gets a freshly-generated address that only ever receives that one transaction. If those funds turn out to be dirty:
@@ -93,23 +94,23 @@ Per-`(buyer, merchant)`-pair ephemeral wallets. Each new escrow gets a freshly-g
### What this requires (architectural work)
1. **Wallet abstraction layer** — service that on demand generates a fresh address (HD wallet derivation from a master seed kept in a hardware module / KMS) and returns it to the payment-intent flow.
2. **Address book / registry** — maps `(paymentId, chainId)` → derived address. Persists derivation path + sequence number so we can reproduce keys for sweeps later.
3. **Sweep job** — once a payment is confirmed AND has passed an on-chain screening check (Chainalysis API or similar), sweep the ephemeral wallet to the main treasury. If screening fails, the ephemeral wallet is quarantined and the payment refunded out of band.
4. **Key custody policy** — these are still our funds in custody briefly; need clear policy on who can sign sweeps, hot-key vs cold-key separation.
1. **Wallet abstraction layer** -- implemented in `backend/src/services/payment/wallets/derivedDestinations.ts` using xpub-only derivation.
2. **Address book / registry** -- implemented in `DerivedDestination`, keyed by `(buyerId, sellerOfferId, chainId)`.
3. **Sweep job** -- implemented with build-only/hot-key signer abstraction; production must keep build-only and move execution to Trezor/Safe.
4. **Key custody policy** -- still the important missing operational layer. See [[PRD - Decentralized Custody and Smart-Contract Escrow Roadmap]].
### Critical open question
**Does RN support creating a secure-payment with a destination wallet we specify per-request, rather than a static merchant reference?** If yes, this is straightforward — we generate a wallet, register it as the destination for one specific `/v2/secure-payments` call, done. If no (RN only allows pre-registered destinations), we have to either:
**Does RN support creating a secure-payment with a destination wallet we specify per request at production volume, rather than a static merchant reference?** The backend/frontend support the shape, but the live divergent-destination probe remains the operational proof point. If RN cannot support this reliably, fallback options are:
- Pre-register a large pool of addresses with RN and rotate through them, or
- Bypass RN's destination model and go full self-host (which is most of issue #4).
**Action: confirm with RN support whether per-request destinations are supported on the same API key.**
**Action: run the two-paid-intent divergent-destination probe and confirm with RN support whether this usage is supported on the same API key at expected volume.**
---
## 4. RN reduced to a notification service viable, but not yet validated
## 4. RN reduced to a notification service -- viable, partially validated
### Problem statement
@@ -131,19 +132,19 @@ Which is a *notification* primitive, not a payment platform. We'd be paying for
- We're outsourcing the *one thing* RN is good at (settlement) and keeping the parts they don't help with (UX, wallet generation, compliance).
- Alternative: do the same with our own chain watcher (Alchemy webhooks / Tenderly / Goldsky) and skip RN entirely.
### What needs testing before we commit
### What still needs testing before we commit at scale
1. **Webhook reliability at our volume.** What's RN's SLA for "address received funds → webhook delivered"? P50? P99?
2. **Custom destination support.** See open question in #3.
3. **Per-API-key rate limits.** If we end up calling `/v2/secure-payments` once per escrow, do we hit ceilings?
4. **Pricing for the notification-only flow** — is there a tier, or is it the same as the full-stack price?
5. **What happens when the payment arrives from a transaction WE built** (not theirs)? Does the webhook still fire? Is settlement still recognized? — this is the load-bearing test for the whole strategy.
5. **What happens when the payment arrives from a transaction WE built** (not theirs)? The 2026-05-28 in-house checkout probe proved the basic path for a real BSC USDC payment; this still needs repeated paid probes across tokens/chains and webhook durability coverage.
Until #5 is confirmed, the rest is just paper architecture.
Until webhook durability, destination divergence, pricing, and SLA are confirmed, treat RN as useful but not irreplaceable infrastructure.
---
## 5. Webhook durability and transaction safety are P0 before more paid probes
## 5. Webhook durability remains P0 before production rollout
### What the 2026-05-28 probe proved
@@ -153,12 +154,12 @@ The dev test transaction `0x3a23febd9abd43d7e0851c1ea86c4ceaf08c11098852cb0425fa
Do not treat the main Express app as the only webhook landing zone, and do not treat a signed provider callback as enough to credit escrow.
### Required mitigation
### Required mitigation and status
1. **Correlation repair:** lookup Request Network payments by every persisted reference shape, including `providerPaymentId`, top-level RN request id/payment reference, and nested raw RN data.
2. **Callback repair:** payment callback polling must unwrap the backend response shape, clear polling after terminal states, and avoid a 3-second loop that self-rate-limits.
3. **Transaction Safety Provider:** completion must pass configured safety checks: transaction hash present, minimum confirmations, token/recipient/amount transfer match, and future AML/sanctions provider approval.
4. **Durable ingress:** put a Cloudflare Worker in front of RN webhooks. The Worker stores raw delivery evidence durably, forwards to the backend, and supports replay. It is not the trust oracle; the backend still verifies, deduplicates, and applies safety/ledger transitions.
1. **Correlation repair:** implemented for the in-house checkout path; keep smoke coverage around every persisted RN reference shape.
2. **Callback repair:** implemented enough for the successful paid dev probe; keep polling/backoff hardening on the checkout roadmap.
3. **Transaction Safety Provider:** implemented for tx hash, confirmations, transfer match, and AML placeholder; real AML provider remains Task #10.
4. **Durable ingress:** not started. Put a Cloudflare Worker in front of RN webhooks. The Worker stores raw delivery evidence durably, forwards to the backend, and supports replay. It is not the trust oracle; the backend still verifies, deduplicates, and applies safety/ledger transitions.
---
@@ -166,13 +167,13 @@ Do not treat the main Express app as the only webhook landing zone, and do not t
| # | Action | Blocker / Owner |
|---|---|---|
| 1 | Deploy confirmation repair and repeat the dev payment probe | Backend payments |
| 2 | Test: `/v2/secure-payments` accepts a per-request destination wallet | Backend payments |
| 1 | Run the live divergent-destination probe: two paid intents to two derived addresses | Backend payments |
| 2 | Confirm `/v2/secure-payments` per-request destination usage with RN support and pricing | Product / RN account manager |
| 3 | Confirm RN doesn't auto-bridge when buyer pays on the destination chain natively | Backend payments |
| 4 | Get RN's webhook P99 latency + delivery guarantees in writing | Product / RN account manager |
| 5 | Spec the wallet-abstraction layer (HD derivation + sweep job + key policy) | Backend, before going live |
| 5 | Move sweep/release/refund custody to Trezor/Safe, not backend hot keys | Backend + ops |
| 6 | Spec the seller-side accepted-chains config | Backend + frontend |
| 7 | Add Cloudflare Worker durable webhook ingress to the roadmap | Backend / platform |
| 7 | Build Cloudflare Worker durable webhook ingress + replay | Backend / platform |
| 8 | Add AML/sanctions adapter behind Transaction Safety Provider | Compliance / backend |
Actions 14 are *information-gathering* and should run in parallel before any more architectural commitment to RN. Actions 56 are blocked on 13 confirming RN can actually support this shape.
Actions 1-4 are information-gathering and should run in parallel before deeper RN commitment. Actions 5, 7, and 8 are production-safety work regardless of whether Amanat keeps RN long-term or replaces it with a direct chain watcher.

View File

@@ -9,7 +9,7 @@ created: 2026-05-23
How identity, authorization, transport, and integrity are handled across the platform.
> [!important]
> Read alongside [[Authentication Flow]] (user-facing), [[Passkey (WebAuthn) Flow]], and [[Payment Flow - SHKeeper]] (webhook HMAC).
> Read alongside [[Authentication Flow]] (user-facing), [[Passkey (WebAuthn) Flow]], [[Escrow Flow]], and [[Request Network Integration Constraints]].
---
@@ -22,7 +22,7 @@ How identity, authorization, transport, and integrity are handled across the pla
| CSRF | JWT in `Authorization` header (not cookie), CORS allow-list |
| XSS | Helmet CSP, React auto-escaping, sanitize HTML before storage |
| SQL/NoSQL injection | Mongoose parameterized queries, no `$where` strings, schema validation |
| Webhook spoofing | HMAC SHA-256 over body + secret (SHKeeper, Request Network, Telegram), constant-time compare |
| Webhook spoofing | HMAC SHA-256 over raw body + provider secret (Request Network, Telegram), constant-time compare |
| File upload abuse | Multer MIME validation, 5 MB cap, non-executable storage, served by Nginx not Node |
| Replay attacks | Per-payment idempotency on `providerPaymentId`; Telegram initData in-memory replay map; per-request `X-Request-Id` |
| Account takeover | Email verification required, password reset code expiry (1h), passkey support |
@@ -155,34 +155,36 @@ A single User may be `buyer` and `seller` simultaneously (combined role).
## 5. Webhook integrity
### 5.1 SHKeeper
### 5.1 Request Network
```mermaid
sequenceDiagram
participant SHK
participant RN
participant WK as Durable ingress (roadmap)
participant BE
SHK->>BE: POST /api/payment/shkeeper/webhook<br/>X-Signature: sha256=<hmac>
BE->>BE: hmac = HMAC_SHA256(SHKEEPER_WEBHOOK_SECRET, body)
BE->>BE: crypto.timingSafeEqual(hmac, providedSig)
RN->>WK: POST /api/payment/request-network/webhook<br/>x-request-network-signature
WK->>WK: Store raw body + headers + delivery id
WK->>BE: Forward / replay raw webhook
BE->>BE: verifyRequestNetworkWebhookSignature(rawBody, headers)
alt mismatch
BE-->>SHK: 401 Unauthorized
BE-->>WK: 401 Unauthorized
else match
BE->>BE: process payment update
BE-->>SHK: 200 OK
BE->>BE: idempotency + Transaction Safety Provider
BE->>BE: process payment update / ledger entry
BE-->>WK: 200 OK
end
```
- Raw body must be used for HMAC — `express.raw({ type: 'application/json' })` is mounted on this route only (before the global `express.json()` parser).
- In dev (`NODE_ENV === 'development'`) signature verification can be bypassed for local testing — confirm this is gated and never reachable in prod.
- Idempotency: identical webhook delivered twice should be no-op. Check by `(providerPaymentId, status)` tuple before mutating.
### 5.2 Request Network
- Webhooks arrive at `/api/payment/request-network/webhook` with an `x-request-network-signature` header.
- The backend verifies the signature using `backend/src/services/payment/requestNetwork/signature.ts` before any state mutation.
- The route is mounted **before** the global `express.json()` body parser so raw body bytes are available for signature computation.
- The global rate-limit middleware is configured to skip this path to avoid blocking high-frequency payment events.
- Reconciliation service (`requestNetworkReconciliationService.ts`) handles replayed or out-of-order webhooks idempotently.
- Durable ingress is the target production shape: the Worker stores delivery evidence and supports replay, but the backend remains the trust oracle.
### 5.2 Legacy SHKeeper note
SHKeeper-specific webhook docs are historical migration context. The current backend payment tree uses Request Network as the primary provider; do not reintroduce SHKeeper signature bypasses or fallback webhook heuristics without a new security review.
### 5.3 Telegram Bot webhook
@@ -191,7 +193,7 @@ sequenceDiagram
- A per-update-id in-memory replay map prevents duplicate processing within the configured window.
- The global rate-limit middleware is configured to skip this path.
See [[Payment Flow - SHKeeper]] for the SHKeeper full flow.
See [[Escrow Flow]] and [[Request Network Integration Constraints]] for the current payment path.
---
@@ -219,7 +221,7 @@ See [[Payment Flow - SHKeeper]] for the SHKeeper full flow.
- Never log secrets — logger redaction recommended (winston/pino formatter).
- `.env*` files in `.gitignore`. Repo includes only `.env.development` / `.env.production` templates with **public** values (NEXT_PUBLIC_*).
- Rotate `JWT_SECRET` invalidates all existing JWTs — schedule a maintenance window.
- Rotate `SHKEEPER_WEBHOOK_SECRET` coordinated with SHKeeper dashboard (set new → verify → remove old).
- Rotate `REQUEST_NETWORK_WEBHOOK_SECRET` coordinated with Request Network configuration (set new → verify → remove old).
See [[Environment Variables]] for the catalog.
@@ -277,6 +279,6 @@ The codebase currently uses `morgan` (HTTP access logs) and ad-hoc `logger.info/
- [[Authentication Flow]] (includes Telegram first-class auth flow) · [[Google OAuth Flow]] · [[Passkey (WebAuthn) Flow]] · [[Password Reset Flow]]
- [[Backend Architecture]] · [[Frontend Architecture]] · [[Real-time Layer]]
- [[Payment Flow - SHKeeper]] — webhook HMAC details
- [[Request Network Integration Constraints]] — payment webhook, checkout, and reconciliation constraints
- [[Environment Variables]] — secret catalog
- [[Incident Response]] — what to do when something goes wrong

View File

@@ -24,7 +24,8 @@ flowchart LR
BE[Express Backend<br/>+ Socket.IO<br/>:5001]
Mongo[(MongoDB 8)]
Redis[(Redis 8)]
SHK[SHKeeper<br/>Crypto Gateway]
RN[Request Network<br/>Pay-in + webhooks]
CFWorker[Durable webhook ingress<br/>roadmap]
SMTP[SMTP<br/>Nodemailer]
OAI[OpenAI API]
BC[Blockchain RPC<br/>Alchemy / WalletConnect]
@@ -37,8 +38,9 @@ flowchart LR
FE -.->|Socket.IO| BE
BE --> Mongo
BE --> Redis
BE -->|Pay-in / Pay-out| SHK
SHK -.->|Webhook HMAC| BE
BE -->|Pay-in intent / status| RN
RN -.->|Signed webhook| CFWorker
CFWorker -.->|Forward / replay| BE
BE --> SMTP
BE --> OAI
FE -->|Wallet Connect| BC
@@ -142,25 +144,29 @@ Mutations follow optimistic-then-confirm:
### 5.3 Webhook path (inbound)
External services (SHKeeper) POST to `/api/payment/shkeeper/webhook`. The backend verifies HMAC signature, updates the `Payment` document, advances any linked `PurchaseRequest`/`SellerOffer` state, and emits Socket.IO events to both buyer and seller rooms.
External services POST payment callbacks to provider-specific webhook routes. The current primary path is Request Network at `/api/payment/request-network/webhook`; the target architecture puts a durable ingress worker in front of the backend so raw delivery evidence can be replayed after outages. The backend remains the trust oracle: it verifies signatures, deduplicates deliveries, applies Transaction Safety Provider checks, updates ledger/payment state, and emits Socket.IO events to both buyer and seller rooms.
```mermaid
sequenceDiagram
participant SHK as SHKeeper
participant RN as Request Network
participant WK as Durable ingress worker
participant BE as Backend
participant DB as MongoDB
participant Buyer
participant Seller
SHK->>BE: POST /api/payment/shkeeper/webhook<br/>X-Signature: HMAC-SHA256
BE->>BE: verifySignature(body, header, SHKEEPER_WEBHOOK_SECRET)
BE->>DB: Payment.updateOne({providerPaymentId}, {status:"completed"})
BE->>DB: PurchaseRequest.updateOne(..., {status:"funded"})
RN->>WK: POST signed webhook<br/>delivery id + raw body
WK->>WK: Store immutable delivery evidence
WK->>BE: Forward / replay webhook
BE->>BE: Verify RN signature + idempotency
BE->>BE: Transaction Safety Provider checks tx hash, recipient, token, amount, confirmations
BE->>DB: Append ledger entry + Payment escrowState="funded"
BE->>DB: PurchaseRequest.updateOne(..., {status:"payment"})
BE-->>Buyer: socket emit "payment:status-updated"
BE-->>Seller: socket emit "request:funded"
BE-->>SHK: 200 OK
BE-->>WK: 200 OK
```
See [[Payment Flow - SHKeeper]] for the full sequence.
See [[PRD - Request Network In-House Checkout]] and [[Request Network Integration Constraints]] for the full Request Network sequence.
---