Files
nick-doc/04 - Flows/Payment Flow - Scanner.md
2026-05-31 15:21:28 +04:00

7.7 KiB

title, tags, created
title tags created
Payment Flow - Scanner (In-House)
flow
scanner
payment
2026-05-30

Payment Flow — AMN Pay Scanner (In-House)

Last updated: 2026-05-31 — documented backend 2.6.82 / scanner 0.1.7 capped accepted confirmation floors.

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, Scanner API


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/amn-scanner/webhook",
  "callbackSecret": "<per-intent HMAC secret stored in payment doc>",
  "confirmations":  200
}

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. The scanner stores and reports the accepted threshold count, not an ever-growing live count.

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:

{
  "intentId": "...",
  "paymentReference": "0x...",
  "txHash": "0x...",
  "blockNumber": 39000010,
  "amount": "10000000000000000000",
  "token": "0x55d...",
  "chainId": 56,
  "confirmations": 200,
  "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. Backend 2.6.82+ 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, but settled counts are capped at the accepted threshold so the UI can show values like 200+ instead of chasing the live chain height forever.

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