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:
@@ -5,7 +5,7 @@ tags: [api, payment, reference, request-network, escrow]
|
||||
|
||||
# Payment API
|
||||
|
||||
> **Last updated:** 2026-05-29 — aligned with code (see [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md))
|
||||
> **Last updated:** 2026-05-30 — AMN Pay Scanner integration, on-demand RN reconcile in GET /payment/:id, pay-in route renamed, reload/probe routes now implemented
|
||||
|
||||
The payment surface is split across provider-neutral payment routers, Request Network checkout/webhook routes, derived-destination custody routes, and admin safety routes:
|
||||
|
||||
@@ -16,6 +16,7 @@ The payment surface is split across provider-neutral payment routers, Request Ne
|
||||
| `/api/payment/request-network/*` | [`requestNetwork/requestNetworkRoutes.ts`](../../backend/src/services/payment/requestNetwork/requestNetworkRoutes.ts) | Request Network intent creation, in-house checkout payloads, webhook processing |
|
||||
| `/api/payment/derived-destinations/*` | [`wallets/derivedDestinationRoutes.ts`](../../backend/src/services/payment/wallets/derivedDestinationRoutes.ts) | Derived destination inspection, balance checks, and sweeping |
|
||||
| `/api/payment/decentralized/*` | [`decentralizedPaymentRoutes.ts`](../../backend/src/services/payment/decentralizedPaymentRoutes.ts) | Legacy wallet-direct confirmations |
|
||||
| `/api/payment/amn-scanner/*` | [`routes/amnScannerWebhookRoutes.ts`](../../backend/src/routes/amnScannerWebhookRoutes.ts) | AMN Pay Scanner webhook receiver |
|
||||
| `/api/admin/rn/networks/*` | [`requestNetwork/networkRegistryRoutes.ts`](../../backend/src/services/payment/requestNetwork/networkRegistryRoutes.ts) | Request Network chain/token registry |
|
||||
| `/api/admin/payments/awaiting-confirmation/*` | `awaitingConfirmationRoutes.ts` | Admin queue for payments waiting on confirmation/safety checks |
|
||||
|
||||
@@ -52,7 +53,7 @@ Core model: [[Payment]]. Coordination logic to avoid race conditions when multip
|
||||
|
||||
### POST /api/payment
|
||||
|
||||
**Description:** Create a payment record manually. Normal buyer checkout should use `POST /api/payment/request-network/intents`.
|
||||
**Description:** Create a payment record manually. Normal buyer checkout should use `POST /api/payment/request-network/pay-in`.
|
||||
**Auth required:** Bearer JWT
|
||||
**Request body:**
|
||||
```ts
|
||||
@@ -90,7 +91,7 @@ Core model: [[Payment]]. Coordination logic to avoid race conditions when multip
|
||||
|
||||
### GET /api/payment/:id
|
||||
|
||||
**Description:** Fetch a payment by id.
|
||||
**Description:** Fetch a payment by id. For payments with `provider: 'request.network'` that are still `pending`, this endpoint also performs an **on-demand RN reconcile**: it queries the Request Network node live, and if RN reports the request as paid it immediately marks the payment `completed`, advances the purchase request to `processing`, persists `selectedOfferId`, and accepts the winning offer while rejecting all others. This reconcile path exists because RN webhooks cannot reach a local dev server and the reconcile cron is not started there; the same logic fires in production as a safety net.
|
||||
**Auth required:** Bearer JWT
|
||||
**Errors:** `404` not found.
|
||||
|
||||
@@ -126,19 +127,19 @@ Core model: [[Payment]]. Coordination logic to avoid race conditions when multip
|
||||
### POST /api/payment/payments/:id/fetch-tx
|
||||
|
||||
**Description:** Re-queries the blockchain to fetch the missing `transactionHash` for a completed payment.
|
||||
**⚠️ SECURITY — NO AUTHENTICATION:** This endpoint has no authentication guard. Any unauthenticated caller can trigger a blockchain re-query for any payment ID.
|
||||
**Auth required:** Bearer JWT (admin) — `authenticateToken` + `authorizeRoles('admin')` added in commit `1d881c5` (ISSUE-005 fix).
|
||||
**Response 200:** `{ success, transactionHash, network, source, message }`
|
||||
|
||||
### POST /api/payment/payments/auto-fetch-missing
|
||||
|
||||
**Description:** Batch tx-hash backfill across the database.
|
||||
**⚠️ SECURITY — NO AUTHENTICATION:** This endpoint has no authentication guard. Any unauthenticated caller can trigger a full database backfill scan.
|
||||
**Auth required:** Bearer JWT (admin) — `authenticateToken` + `authorizeRoles('admin')` added in commit `1d881c5` (ISSUE-005 fix).
|
||||
**Request body:** `{ limit?: number }` (default 10)
|
||||
|
||||
### GET /api/payment/payments/:id/debug
|
||||
|
||||
**Description:** Debug bundle including the raw payment, blockchain metadata, and wallet-monitor status. Intended for admin / development.
|
||||
**⚠️ SECURITY — NO AUTHENTICATION:** Despite exposing full payment data, this endpoint has no authentication guard. Any unauthenticated caller can retrieve complete payment details for any payment ID.
|
||||
**Auth required:** Bearer JWT (admin) — `authenticateToken` + `authorizeRoles('admin')` added in commit `1d881c5` (ISSUE-005 fix).
|
||||
|
||||
### POST /api/payment/callback
|
||||
|
||||
@@ -153,9 +154,9 @@ Core model: [[Payment]]. Coordination logic to avoid race conditions when multip
|
||||
|
||||
## Request Network - Pay-in
|
||||
|
||||
### POST /api/payment/request-network/intents
|
||||
### POST /api/payment/request-network/pay-in
|
||||
|
||||
**Description:** Creates a Request Network pay-in intent and stores a [[Payment]] with `provider: "request.network"`. The service can attach a per-payment derived destination before creating the provider request.
|
||||
**Description:** Creates a Request Network pay-in intent and stores a [[Payment]] with `provider: "request.network"`. The service can attach a per-payment derived destination before creating the provider request. This is the **current active route** (mounted at `/api/payment/request-network/pay-in`). The `/intents` path listed in older docs is an alias; use `pay-in` for new integrations.
|
||||
**Auth required:** Bearer JWT (buyer)
|
||||
**Request body:**
|
||||
```ts
|
||||
@@ -182,7 +183,35 @@ Core model: [[Payment]]. Coordination logic to avoid race conditions when multip
|
||||
**Auth required:** No (signature-protected)
|
||||
**Response:** `200` when processed or duplicate; `202` when accepted but safety checks are pending; `401` for invalid signature.
|
||||
|
||||
> ⚠️ **NOT IMPLEMENTED:** `POST /api/payment/request-network/:id/payout/initiate`, `POST /api/payment/request-network/:id/payout/confirm`, `POST /api/payment/request-network/:id/release/confirm`, and `POST /api/payment/request-network/:id/refund/confirm` do not exist in the codebase. Do not call these paths.
|
||||
> [!note] RN payout/release/refund routes
|
||||
> `POST /api/payment/request-network/:paymentId/payout/initiate`, `POST /api/payment/request-network/:paymentId/payout/confirm`, `POST /api/payment/request-network/:paymentId/release/confirm`, and `POST /api/payment/request-network/:paymentId/refund/confirm` are registered in `requestNetworkRoutes.ts` but are stub-level implementations. They accept the request and return a 200 but do not yet drive the ledger-gated release/refund orchestration. Use `POST /api/payment/:id/release` and `POST /api/payment/:id/refund` for actual escrow releases.
|
||||
|
||||
## AMN Pay Scanner - Pay-in
|
||||
|
||||
AMN Pay Scanner is a custom in-house blockchain scanner that replaces the hosted Request Network page for payment monitoring. It speaks the same `PaymentProviderAdapter` interface as the RN adapter.
|
||||
|
||||
### POST /api/payment/amn-scanner/webhook
|
||||
|
||||
**Description:** AMN Pay Scanner posts settlement confirmations here. The route verifies a `webhookSecret`-based HMAC signature, then runs the Transaction Safety Provider and `PaymentCoordinator` pipeline identical to the RN webhook path.
|
||||
**Auth required:** No (signature-protected via `AMN_SCANNER_WEBHOOK_SECRET`)
|
||||
**Request body:** `{ intentId, status, transactionHash?, chainId?, ... }` — scanner-specific envelope
|
||||
**Response:** `200` processed; `401` bad signature; `400` missing `intentId` or unknown format; `404` payment not found.
|
||||
**Side effects:** Same as the RN webhook — updates [[Payment]], advances [[PurchaseRequest]], accepts/rejects offers, emits socket events when safety checks pass.
|
||||
|
||||
> [!note] Provider value
|
||||
> Payments created via the AMN Pay Scanner have `provider: 'amn.scanner'` in the database. This is distinct from `request.network` and `shkeeper`.
|
||||
|
||||
### GET /api/admin/scanner/status
|
||||
|
||||
**Description:** Proxies to `AMN_SCANNER_URL/scanner/status` and returns the scanner's internal state.
|
||||
**Auth required:** Bearer JWT (`admin`) — `authenticateToken` + `authorizeRoles('admin')` are now applied (the previously documented security gap — unauthenticated access — has been fixed in commit `1d881c5`).
|
||||
**Response 200:** Scanner status JSON forwarded from the upstream service.
|
||||
|
||||
### POST /api/admin/scanner/webhooks/retry
|
||||
|
||||
**Description:** Triggers a manual retry of failed/pending scanner webhooks.
|
||||
**Auth required:** Bearer JWT (`admin`)
|
||||
**Request body:** `{ intentId?: string }` — omit to retry all pending.
|
||||
|
||||
## Legacy SHKeeper - Pay-in
|
||||
|
||||
@@ -555,7 +584,13 @@ Escrow state (`escrowState`): `unfunded` → `funded` → `released` | `refunded
|
||||
}
|
||||
```
|
||||
|
||||
> ⚠️ **NOT IMPLEMENTED:** `GET /api/admin/settings/confirmation-thresholds/history` does not exist. Only the current-values GET and per-chain PATCH endpoints are implemented.
|
||||
### `GET /api/admin/settings/confirmation-thresholds/history`
|
||||
|
||||
**Auth:** Admin only
|
||||
**Description:** Returns paginated audit log of past confirmation threshold changes. Each entry records the admin who made the change, old/new threshold values, chain ID, and timestamp. Backed by the `ConfigSettingHistory` Mongoose model added in commit `27fb15a` (task #9).
|
||||
**Response 200:** `{ success: true, data: [{ chainId, oldThreshold, newThreshold, changedBy, changedAt }] }`
|
||||
|
||||
> **Note:** This endpoint was previously documented as NOT IMPLEMENTED. It was added in commit `27fb15a` and is now live at `/api/admin/settings/confirmation-thresholds/history`.
|
||||
|
||||
## Payments awaiting confirmation (admin)
|
||||
|
||||
@@ -613,7 +648,33 @@ Escrow state (`escrowState`): `unfunded` → `funded` → `released` | `refunded
|
||||
}
|
||||
```
|
||||
|
||||
> ⚠️ **NOT IMPLEMENTED:** `POST /api/admin/rn/networks/reload` and `POST /api/admin/rn/networks/probe/:chainId` do not exist in the codebase.
|
||||
### `POST /api/admin/rn/networks/reload`
|
||||
|
||||
**Auth:** Admin only
|
||||
**Description:** Reloads the chain and token registries from disk (`supportedChains.json` and `tokens.json`). Returns `{ success: true, message: 'Registry reloaded from disk' }`. Use this after updating the JSON files without restarting the server.
|
||||
|
||||
> **Note:** This route is now implemented (commit `5681abf`). Earlier docs incorrectly listed it as not implemented.
|
||||
|
||||
### `POST /api/admin/rn/networks/probe/:chainId`
|
||||
|
||||
**Auth:** Admin only
|
||||
**Description:** Performs a live on-chain probe for the specified chain: verifies RPC reachability, checks for deployed proxy contract bytecode (`eth_getCode`), and test-calls the proxy with a dummy payload to confirm it reverts meaningfully. Returns:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"chainId": 56,
|
||||
"reachable": true,
|
||||
"hasCode": true,
|
||||
"callValid": true,
|
||||
"blockNumber": "0x...",
|
||||
"latencyMs": 120
|
||||
}
|
||||
}
|
||||
```
|
||||
Errors: `400` if `chainId` is not a number; `404` if the chain is not in the registry; `500` on RPC failure.
|
||||
|
||||
> **Note:** This route is now implemented (commit `5681abf`). Earlier docs incorrectly listed it as not implemented.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
Reference in New Issue
Block a user