6.3 KiB
6.3 KiB
title, tags, related_models, related_apis
| title | tags | related_models | related_apis | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Payout Flow |
|
|
|
Payout Flow
This page describes how escrowed funds leave Amanat custody after an order is complete or a dispute is resolved.
The current flow is no longer SHKeeper payout-task centric. Release and refund are instruction-based:
- Backend validates policy, dispute hold, and ledger availability.
- Backend builds a release/refund instruction.
- A custody signer executes the on-chain transaction.
- Backend confirms the tx hash and appends the ledger entry.
Today the custody signer can be an admin/Trezor path when enabled. The roadmap target is Safe multisig execution before any custom escrow contract pilot. See PRD - Decentralized Custody and Smart-Contract Escrow Roadmap.
Actors
- Admin / mediator -- initiates release/refund after delivery confirmation or dispute resolution.
- Custody signer -- Trezor proof today when enabled; target state is Safe multisig owners.
- Seller -- recipient for release.
- Buyer -- recipient for refund.
- Backend --
releaseRefundService.ts, payment adapter, ledger service, Trezor service. - Blockchain -- final on-chain settlement.
- MongoDB --
PaymentandFundsLedgerEntry.
Preconditions
- The pay-in
Paymentis funded or releasable. - The release/refund amount is positive and does not exceed available ledger balance.
- No active dispute hold blocks the operation, unless the operation is the explicit dispute resolution path.
- Recipient wallet is known and verified.
- If
TREZOR_SAFEKEEPING_REQUIRED=true, the confirm step includes the expected Trezor operation signature. - Production target: Safe multisig execution is required for custody movement.
Release Narrative
- Buyer confirms delivery, an auto-release policy matures, or a dispute resolves for the seller.
- Admin calls
POST /api/payment/:id/releasewith optional partial amount. - Backend loads the
Payment, validates ledger availability whenPAYMENT_LEDGER_ENFORCEMENT=true, and returns an instruction payload. - Custody signer broadcasts the seller payment transaction.
- Admin calls
POST /api/payment/:id/release/confirmwithtxHashand optional Trezor proof. - Backend verifies signer proof when required, confirms adapter state, appends a
releaseledger entry, and marks escrow released.
Refund Narrative
- Dispute resolves for the buyer, order is cancelled before fulfillment, or support executes an approved recovery.
- Admin calls
POST /api/payment/:id/refund. - Backend validates available funds and policy.
- Custody signer broadcasts the refund transaction.
- Admin calls
POST /api/payment/:id/refund/confirmwithtxHashand optional Trezor proof. - Backend appends a
refundledger entry and marks escrow refunded.
Sequence Diagram
sequenceDiagram
autonumber
actor A as Admin
actor C as Custody signer
participant BE as Backend
participant DB as MongoDB
participant BC as EVM Chain
actor R as Recipient
A->>BE: POST /api/payment/{id}/release or refund
BE->>DB: Load Payment + FundsLedger balance
BE->>BE: Check dispute hold + ledger availability
BE-->>A: unsigned release/refund instruction
A->>C: Request Trezor/Safe execution
C->>BC: Broadcast transfer
BC-->>C: txHash
A->>BE: POST /confirm { txHash, signer proof }
BE->>BE: Verify proof if required
BE->>DB: append release/refund ledger entry
BE->>DB: update Payment escrowState
BE-->>R: notification
API Calls
| Method | Endpoint | Purpose |
|---|---|---|
POST |
/api/payment/:id/release |
Build release instruction |
POST |
/api/payment/:id/release/confirm |
Confirm release transaction |
POST |
/api/payment/:id/refund |
Build refund instruction |
POST |
/api/payment/:id/refund/confirm |
Confirm refund transaction |
GET |
/api/admin/payments/awaiting-confirmation |
Admin view of payments blocked on confirmation depth |
GET |
/api/payment/derived-destinations |
Admin view of derived destination sweep state |
Database Writes
payments-- status,escrowState,blockchain.transactionHash, signer metadata.funds_ledger_entries-- append-onlyreleaseorrefundentry with idempotency key.purchaserequests-- terminal business state after release/refund completes.notifications-- release/refund receipt to the relevant party.
Error / Edge Cases
- Insufficient ledger balance -- reject instruction build/confirm.
- Active dispute hold -- reject release/refund unless the operation is the explicit dispute outcome.
- Missing signer proof -- reject when
TREZOR_SAFEKEEPING_REQUIRED=true. - Custody tx sent but not confirmed in app -- reconcile by tx hash and append the missing ledger entry once verified.
- Partial split -- build separate release and refund instructions whose sum does not exceed available balance.
- Payout reverted -- leave escrow in failed/retryable state and do not append the terminal ledger entry.
Legacy SHKeeper Note
Older versions used SHKeeper payout tasks and scripts such as fix-transaction-hashes.js. Those references remain useful for historical reconciliation, but new release/refund work should use the instruction, ledger, and custody-signer flow described here.
Linked Flows
- Escrow Flow -- sets up the conditions under which release/refund is allowed.
- Delivery Confirmation Flow -- happy-path release trigger.
- Dispute Flow -- can divert release to refund or split.
- Trezor Safekeeping Flow -- hardware-backed operation approval.
- PRD - Decentralized Custody and Smart-Contract Escrow Roadmap -- Safe-first custody roadmap.
Source Files
- Backend:
backend/src/services/payment/orchestration/releaseRefundService.ts - Backend:
backend/src/services/payment/ledger/fundsLedgerService.ts - Backend:
backend/src/services/payment/adapters/requestNetworkAdapter.ts - Backend:
backend/src/services/trezor/trezorService.ts - Backend:
backend/src/services/dispute/releaseHoldService.ts - Frontend: admin payment/release/refund surfaces under
frontend/src/sections/