audit: 2026-05-30 full-codebase audit — report, issues, docs, runbooks

Full-codebase-audit 2026-05-30 outputs:
- Audit report: 09 - Audits/Full Codebase Audit - 2026-05-30.md
- 81 issue files ISSUE-055..135 (decisions + 1 skipped no-brainer).
- Scanner docs from scratch (was zero): architecture, data model, API ref, payment
  flow, operations runbook + repo README.
- Doc-sync updates across API reference, data models, flows, design system.
- Secret Rotation Runbook (08 - Operations) for the exposed credentials.
- Reusable workflow guide (07 - Development) + .claude/workflows/full-codebase-audit.js.

Issues remain status:open intentionally — the code fixes are uncommitted-then-committed
working-tree changes per repo and aren't "resolved" until merged/deployed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-05-30 18:41:44 +04:00
parent eab1d77582
commit dceaf82934
153 changed files with 6276 additions and 179 deletions

View File

@@ -0,0 +1,179 @@
---
title: Payment Flow - Scanner (In-House)
tags: [flow, scanner, payment]
created: 2026-05-30
---
# Payment Flow — AMN Pay Scanner (In-House)
End-to-end payment flow using the in-house AMN Pay Scanner, replacing the Request Network integration. The scanner is a separate microservice; the backend talks to it over an internal HTTP API.
See also: [Scanner Architecture](../01%20-%20Architecture/Scanner%20Architecture.md), [Scanner API](../03%20-%20API%20Reference/Scanner%20API.md)
---
## 1. High-level sequence
```
Buyer Backend Scanner Chain
│ │ │ │
│ initiate payment │ │ │
│────────────────────►│ │ │
│ │ POST /intents │ │
│ │───────────────────►│ │
│ │ 200 checkoutBlock │ │
│ │◄───────────────────│ │
│ checkoutBlock │ │ │
│◄────────────────────│ │ │
│ │ │ │
│ sign + submit tx ──────────────────────────────────────►│
│ │ │ (polling) │
│ │ │◄────────────────│
│ │ │ log matched │
│ │ │ confirmations… │
│ │◄───────────────────│ │
│ │ POST callbackUrl │ │
│ │ (webhook) │ │
│ │ │ │
│ payment confirmed │ │ │
│◄────────────────────│ │ │
```
---
## 2. Step-by-step
### Step 1 — Backend creates an intent
When the buyer chooses a payment method (e.g. USDT on BSC), the backend calls:
```
POST http://scanner:8080/intents
Authorization: Bearer <SCANNER_API_KEY>
{
"intentId": "<payment._id>",
"chainId": 56,
"tokenAddress": "0x55d398326f99059ff775485246999027b3197955",
"destination": "0xSellerWalletAddress",
"amount": "10000000000000000000",
"callbackUrl": "https://api.amn.gg/api/payment/scanner-callback",
"callbackSecret": "<per-intent HMAC secret stored in payment doc>",
"confirmations": 12
}
```
The scanner responds with a `checkoutBlock` that the backend passes to the frontend.
### Step 2 — Frontend shows checkout
The `checkoutBlock` contains everything the frontend needs to build the `ERC20FeeProxy.transferWithReferenceAndFee` calldata:
| Field | Used for |
|---|---|
| `proxyAddress` | contract to call |
| `tokenAddress` | ERC20 token |
| `destination` | `_to` param |
| `paymentReference` | `_paymentReference` param (8-byte reference) |
| `amountWei` | `_amount` param |
| `feeAmount` | `_feeAmount` param (always `"0"` currently) |
| `feeAddress` | `_feeAddress` param (always dead address) |
For Tron/TON the buyer sends a plain TRC20/Jetton transfer to `destination`; there is no proxy contract.
### Step 3 — Buyer submits transaction
The buyer signs and broadcasts the transaction using their wallet. The scanner independently monitors the chain and does not require the transaction hash.
### Step 4 — Scanner detects and confirms
**EVM path:**
1. `eth_getLogs` returns a `TransferWithReferenceAndFee` log matching `topicRef`
2. `validateLogMatchesIntent` verifies token address, destination, and amount
3. Intent moves to `confirming`; scanner waits for N blocks
4. Once `confirmationsRequired` blocks have been built on top, intent moves to `confirmed`
**Tron path:**
1. TronGrid `Transfer` event matches `destination` (EVM-hex normalized)
2. Amount validated ≥ intent amount
3. Intent goes directly to `confirmed` (TronGrid returns only confirmed txs)
**TON path:**
1. TonCenter Jetton transfer matches `destination` (exact base64url) and `jetton_master_address`
2. Amount validated ≥ intent amount
3. Intent goes directly to `confirmed`
### Step 5 — Webhook delivery
The scanner POSTs to `callbackUrl` with:
```json
{
"intentId": "...",
"paymentReference": "0x...",
"txHash": "0x...",
"blockNumber": 39000010,
"amount": "10000000000000000000",
"token": "0x55d...",
"chainId": 56,
"status": "confirmed"
}
```
Header `X-AMN-Signature` = `HMAC-SHA256(body, callbackSecret)`.
The backend verifies the signature, matches the intentId to a Payment record, and marks it paid.
### Step 6 — Backend acknowledges
Backend returns a 2xx response. Scanner records `webhook_delivered_at` and the intent lifecycle ends.
---
## 3. Failure paths
### Webhook delivery failure
If the backend returns non-2xx or is unreachable, the scanner retries:
```
attempt 1: after 5 s
attempt 2: after 30 s
attempt 3: after 2 min
attempt 4: after 10 min
attempt 5: after 1 h
→ status = webhook_failed
```
`webhook_failed` intents are retried every `WEBHOOK_RETRY_HOURS` (default 6 h) and on `POST /admin/webhooks/retry`.
On startup the scanner reconciles any `confirmed` intents with `webhook_delivered_at IS NULL` (crash recovery).
### Intent expiry
Intents in `pending` or `confirming` status older than `INTENT_TTL_HOURS` (default 24 h) are moved to `expired` by a background ticker running every hour.
`confirming` intents can get stuck if a transaction is deep-reorganised and never re-included; the TTL frees the destination address for reuse.
### Amount underpayment
Transfers where the on-chain amount is less than `intent.Amount` are silently skipped. The intent remains `pending` until the TTL.
### Wrong token or destination
The EVM log decoder validates all three fields (token, destination, amount). Mismatches are logged as `REJECT` and skipped. The intent remains `pending`.
---
## 4. Key differences from Request Network integration
| Dimension | Request Network | AMN Pay Scanner |
|---|---|---|
| Dependency | RN SDK + API | None (direct RPC) |
| Payment reference | RN-generated | Internal HMAC derivation |
| EVM matching | By reference hash (RN) | By Topics[1] / topicRef (indexed) |
| Tron | Not supported | TRC20 Transfer events via TronGrid |
| TON | Not supported | Jetton transfers via TonCenter v3 |
| Confirmations | RN handled | Per-chain configurable |
| Webhook | RN webhook → backend adapter | Scanner → backend directly |
| State store | External (RN cloud) | Internal SQLite |