docs: sync from backend 896f17f - persist webhook confirmations

This commit is contained in:
Siavash Sameni
2026-05-31 15:08:50 +04:00
parent 0bd3fe5598
commit 798fa2f48e
4 changed files with 25 additions and 9 deletions

View File

@@ -6,12 +6,12 @@ aliases: [Payment Record, Escrow, IPayment]
# Payment # Payment
> **Last updated:** 2026-05-31 — added AMN scanner provider, oracle quote mirror, and Postgres `payment_quotes` linkage. > **Last updated:** 2026-05-31 — added AMN scanner provider, oracle quote mirror, Postgres `payment_quotes` linkage, and webhook confirmation persistence.
Records every monetary movement in the marketplace: buyer pay-ins, seller payouts, and refunds. The current model is centered on Request Network pay-in, in-house checkout metadata, on-chain transaction verification, escrow state, and provider request IDs. The `provider` and `direction` discriminators let one collection hold incoming buyer payments, outgoing seller releases, refunds, and legacy/other provider records. Records every monetary movement in the marketplace: buyer pay-ins, seller payouts, and refunds. The current model is centered on Request Network pay-in, in-house checkout metadata, on-chain transaction verification, escrow state, and provider request IDs. The `provider` and `direction` discriminators let one collection hold incoming buyer payments, outgoing seller releases, refunds, and legacy/other provider records.
> [!warning] Runtime store > [!warning] Runtime store
> The `Payment` document is still created, read, and updated through Mongoose on normal request paths. Backend `2.6.80` can persist oracle quotes to Postgres `payment_quotes`, but that is conditional on `ORACLE_QUOTING_ENABLED=true` and does not make the whole payment domain PG-authoritative. See [[Postgres Runtime Cutover Status]]. > The `Payment` document is still created, read, and updated through Mongoose on normal request paths. Backend `2.6.81` can persist oracle quotes to Postgres `payment_quotes`, but that is conditional on `ORACLE_QUOTING_ENABLED=true` and does not make the whole payment domain PG-authoritative. See [[Postgres Runtime Cutover Status]].
> [!note] Source > [!note] Source
> `backend/src/models/Payment.ts:3` — schema definition > `backend/src/models/Payment.ts:3` — schema definition
@@ -52,7 +52,7 @@ Records every monetary movement in the marketplace: buyer pay-ins, seller payout
| `blockchain.sender` | String | no | — | — | — | Source address. | | `blockchain.sender` | String | no | — | — | — | Source address. |
| `blockchain.receiver` | String | no | — | — | — | Destination address. | | `blockchain.receiver` | String | no | — | — | — | Destination address. |
| `blockchain.confirmedAt` | Date | no | — | — | — | When tx confirmed. | | `blockchain.confirmedAt` | Date | no | — | — | — | When tx confirmed. |
| `blockchain.confirmations` | Number | no | `0` | — | — | Confirmation count. | | `blockchain.confirmations` | Number | no | `0` | — | — | Confirmation count persisted from transaction-safety verifier evidence, provider payload `confirmations`, or the configured per-chain threshold fallback when a webhook already reports `confirmed`/`completed`. |
| `status` | String | no | `pending` | enum: `pending` / `processing` / `confirmed` / `completed` / `failed` / `cancelled` / `refunded` | yes (compound) | Lifecycle status. ⚠️ `confirmed` vs `completed`: only `confirmed` is counted as a successful payment in stats. See status note below. | | `status` | String | no | `pending` | enum: `pending` / `processing` / `confirmed` / `completed` / `failed` / `cancelled` / `refunded` | yes (compound) | Lifecycle status. ⚠️ `confirmed` vs `completed`: only `confirmed` is counted as a successful payment in stats. See status note below. |
| `escrowState` | String | no | — | enum: `funded` / `releasable` / `released` / `refunded` / `releasing` / `failed` / `cancelled` / `partial` | — | Escrow lifecycle. Note the intermediate states `releasable` (delivery confirmed, ready to pay out) and `releasing` (payout in flight) between `funded` and `released`. | | `escrowState` | String | no | — | enum: `funded` / `releasable` / `released` / `refunded` / `releasing` / `failed` / `cancelled` / `partial` | — | Escrow lifecycle. Note the intermediate states `releasable` (delivery confirmed, ready to pay out) and `releasing` (payout in flight) between `funded` and `released`. |
| `providerPaymentId` | String | no | — | — | yes (sparse) | External provider id for idempotency. | | `providerPaymentId` | String | no | — | — | yes (sparse) | External provider id for idempotency. |

View File

@@ -5,7 +5,7 @@ tags: [api, payment, reference, request-network, escrow]
# Payment API # Payment API
> **Last updated:** 2026-05-31 — Postgres integration promotion, oracle quote persistence, AMN scanner rail-switch fix, and partial gasless permit endpoints. > **Last updated:** 2026-05-31 — Postgres integration promotion, oracle quote persistence, AMN scanner rail-switch fix, webhook confirmation persistence, and partial gasless permit endpoints.
The payment surface is split across provider-neutral payment routers, Request Network checkout/webhook routes, derived-destination custody routes, and admin safety routes: The payment surface is split across provider-neutral payment routers, Request Network checkout/webhook routes, derived-destination custody routes, and admin safety routes:
@@ -23,7 +23,7 @@ The payment surface is split across provider-neutral payment routers, Request Ne
Core model: [[Payment]]. Coordination logic to avoid race conditions when multiple sources update the same payment is in `paymentCoordinator.ts`. Core model: [[Payment]]. Coordination logic to avoid race conditions when multiple sources update the same payment is in `paymentCoordinator.ts`.
> [!warning] Persistence status > [!warning] Persistence status
> Payment APIs still create/read/update Mongo `Payment` documents on backend `2.6.79`. The Postgres branch adds schemas, repos, migrations, and optional quote persistence, but it is not a full payment-domain cutover. `/api/payment/request-network/intents` can write `payment_quotes` only when `ORACLE_QUOTING_ENABLED=true`; the payment record itself remains Mongo-backed unless future service wiring changes that boundary. > Payment APIs still create/read/update Mongo `Payment` documents on backend `2.6.81`. The Postgres branch adds schemas, repos, migrations, and optional quote persistence, but it is not a full payment-domain cutover. `/api/payment/request-network/intents` can write `payment_quotes` only when `ORACLE_QUOTING_ENABLED=true`; the payment record itself remains Mongo-backed unless future service wiring changes that boundary.
## Configuration / health ## Configuration / health
@@ -208,6 +208,7 @@ Core model: [[Payment]]. Coordination logic to avoid race conditions when multip
**Description:** Request Network posts settlement updates here. The route verifies `x-request-network-signature` over the raw body, deduplicates delivery IDs, evaluates the Transaction Safety Provider, and coordinates the payment/ledger update. **Description:** Request Network posts settlement updates here. The route verifies `x-request-network-signature` over the raw body, deduplicates delivery IDs, evaluates the Transaction Safety Provider, and coordinates the payment/ledger update.
**Auth required:** No (signature-protected) **Auth required:** No (signature-protected)
**Response:** `200` when processed or duplicate; `202` when accepted but safety checks are pending; `401` for invalid signature. **Response:** `200` when processed or duplicate; `202` when accepted but safety checks are pending; `401` for invalid signature.
**Side effects:** For confirmed/completed events, `blockchain.confirmations` is stored from safety verifier evidence, provider payload `confirmations`, or the configured per-chain threshold fallback before the payment/ledger update is emitted.
> [!note] RN payout/release/refund routes > [!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. > `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.
@@ -220,9 +221,9 @@ AMN Pay Scanner is a custom in-house blockchain scanner that replaces the hosted
**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. **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`) **Auth required:** No (signature-protected via `AMN_SCANNER_WEBHOOK_SECRET`)
**Request body:** `{ intentId, status, transactionHash?, chainId?, ... }` — scanner-specific envelope **Request body:** `{ intentId, status, txHash?, transactionHash?, chainId?, confirmations?, ... }` — scanner-specific envelope. Current scanner payloads usually use `txHash`; `confirmations` may be omitted once the scanner has already waited for the configured threshold.
**Response:** `200` processed; `401` bad signature; `400` missing `intentId` or unknown format; `404` payment not found. **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. **Side effects:** Same as the RN webhook — updates [[Payment]], advances [[PurchaseRequest]], accepts/rejects offers, emits socket events when safety checks pass. Backend `2.6.81+` treats scanner `status: "confirmed"` as a settlement status for Transaction Safety Provider evaluation and confirmation persistence; if neither verifier evidence nor payload `confirmations` exists, it stores the configured chain threshold so the dashboard does not show a paid scanner transaction with `0` confirmations.
> [!note] Provider value > [!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`. > Payments created via the AMN Pay Scanner have `provider: 'amn.scanner'` in the database. This is distinct from `request.network` and `shkeeper`.

View File

@@ -6,6 +6,8 @@ created: 2026-05-30
# Payment Flow — AMN Pay Scanner (In-House) # Payment Flow — AMN Pay Scanner (In-House)
> **Last updated:** 2026-05-31 — documented backend `2.6.81` confirmation persistence for scanner `confirmed` webhooks.
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. 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) See also: [Scanner Architecture](../01%20-%20Architecture/Scanner%20Architecture.md), [Scanner API](../03%20-%20API%20Reference/Scanner%20API.md)
@@ -57,7 +59,7 @@ Authorization: Bearer <SCANNER_API_KEY>
"tokenAddress": "0x55d398326f99059ff775485246999027b3197955", "tokenAddress": "0x55d398326f99059ff775485246999027b3197955",
"destination": "0xSellerWalletAddress", "destination": "0xSellerWalletAddress",
"amount": "10000000000000000000", "amount": "10000000000000000000",
"callbackUrl": "https://api.amn.gg/api/payment/scanner-callback", "callbackUrl": "https://api.amn.gg/api/payment/amn-scanner/webhook",
"callbackSecret": "<per-intent HMAC secret stored in payment doc>", "callbackSecret": "<per-intent HMAC secret stored in payment doc>",
"confirmations": 12 "confirmations": 12
} }
@@ -116,13 +118,14 @@ The scanner POSTs to `callbackUrl` with:
"amount": "10000000000000000000", "amount": "10000000000000000000",
"token": "0x55d...", "token": "0x55d...",
"chainId": 56, "chainId": 56,
"confirmations": 12,
"status": "confirmed" "status": "confirmed"
} }
``` ```
Header `X-AMN-Signature` = `HMAC-SHA256(body, callbackSecret)`. Header `X-AMN-Signature` = `HMAC-SHA256(body, callbackSecret)`.
The backend verifies the signature, matches the intentId to a Payment record, and marks it paid. The backend verifies the signature, matches the `intentId` to a Payment record, and marks it paid. Backend `2.6.81+` treats scanner `status: "confirmed"` as final enough to run Transaction Safety Provider checks and persist `blockchain.confirmations`. The stored confirmation count comes from verifier evidence first, then the webhook payload, then the configured per-chain threshold fallback when the scanner has already waited for enough blocks but did not send a raw count.
### Step 6 — Backend acknowledges ### Step 6 — Backend acknowledges

View File

@@ -11,6 +11,18 @@ entries on top. Maintained by agents per the rule in `../AGENTS.md`.
--- ---
### 2026-05-31 — backend@896f17f, frontend@fd8e797 — persist webhook confirmations for scanner payments
**Commits:** backend `896f17f`, frontend `fd8e797` (backend `2.6.81`, frontend `2.7.21`)
**Touched:**
- Backend: `src/routes/amnScannerWebhookRoutes.ts`, `src/services/payment/requestNetwork/requestNetworkRoutes.ts`, `src/services/payment/safety/transactionSafetyProvider.ts`, `__tests__/transaction-safety-provider.test.ts`, `package.json`, `package-lock.json`
- Frontend: `package.json`, `package-lock.json` version bump only.
**Why:** `dev.amn.gg` showed a confirmed AMN scanner payment with `blockchain.confirmations = 0`. Scanner webhooks report `status: "confirmed"`, but transaction safety only evaluated `"completed"` statuses, so no verifier evidence was produced. The webhook routes now persist confirmations from safety evidence, scanner/RN payloads, or the chain confirmation threshold fallback when the provider already says confirmed/completed.
**Verification:** Backend `npm test -- --runTestsByPath __tests__/transaction-safety-provider.test.ts`; backend `npm run typecheck`; backend `git diff --check`; frontend `npx tsc --noEmit --ignoreDeprecations 6.0`; frontend `git diff --check`. Could not inspect the actual `dev.amn.gg` payment row because SSH to `193.180.213.68` rejected the available key.
**Linked docs updated:** [[Payment]], [[Payment API]], [[Payment Flow - Scanner]]
---
### 2026-05-31 — backend@cab0719, frontend@ec2f765 — align request budget validation after Postgres migration ### 2026-05-31 — backend@cab0719, frontend@ec2f765 — align request budget validation after Postgres migration
**Commits:** backend `cab0719`, frontend `ec2f765` (backend `2.6.80`, frontend `2.7.20`) **Commits:** backend `cab0719`, frontend `ec2f765` (backend `2.6.80`, frontend `2.7.20`)