131 lines
6.3 KiB
Markdown
131 lines
6.3 KiB
Markdown
---
|
|
title: Payout Flow
|
|
tags: [flow, payment, payout, release, refund, custody]
|
|
related_models: ["[[Payment]]", "[[Funds Ledger and Escrow State Machine Specification]]"]
|
|
related_apis: ["POST /api/payment/:id/release", "POST /api/payment/:id/release/confirm", "POST /api/payment/:id/refund", "POST /api/payment/:id/refund/confirm"]
|
|
---
|
|
|
|
# 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:
|
|
|
|
1. Backend validates policy, dispute hold, and ledger availability.
|
|
2. Backend builds a release/refund instruction.
|
|
3. A custody signer executes the on-chain transaction.
|
|
4. 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** -- `Payment` and `FundsLedgerEntry`.
|
|
|
|
## Preconditions
|
|
|
|
- The pay-in `Payment` is 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
|
|
|
|
1. Buyer confirms delivery, an auto-release policy matures, or a dispute resolves for the seller.
|
|
2. Admin calls `POST /api/payment/:id/release` with optional partial amount.
|
|
3. Backend loads the `Payment`, validates ledger availability when `PAYMENT_LEDGER_ENFORCEMENT=true`, and returns an instruction payload.
|
|
4. Custody signer broadcasts the seller payment transaction.
|
|
5. Admin calls `POST /api/payment/:id/release/confirm` with `txHash` and optional Trezor proof.
|
|
6. Backend verifies signer proof when required, confirms adapter state, appends a `release` ledger entry, and marks escrow released.
|
|
|
|
## Refund Narrative
|
|
|
|
1. Dispute resolves for the buyer, order is cancelled before fulfillment, or support executes an approved recovery.
|
|
2. Admin calls `POST /api/payment/:id/refund`.
|
|
3. Backend validates available funds and policy.
|
|
4. Custody signer broadcasts the refund transaction.
|
|
5. Admin calls `POST /api/payment/:id/refund/confirm` with `txHash` and optional Trezor proof.
|
|
6. Backend appends a `refund` ledger entry and marks escrow refunded.
|
|
|
|
## Sequence Diagram
|
|
|
|
```mermaid
|
|
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-only `release` or `refund` entry 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/`
|