Initial commit: nick docs
This commit is contained in:
387
03 - API Reference/Payment API.md
Normal file
387
03 - API Reference/Payment API.md
Normal file
@@ -0,0 +1,387 @@
|
||||
---
|
||||
title: Payment API
|
||||
tags: [api, payment, reference, shkeeper]
|
||||
---
|
||||
|
||||
# Payment API
|
||||
|
||||
The payment surface is split across four routers, all mounted under `/api/payment/*`:
|
||||
|
||||
| Path prefix | File | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `/api/payment/*` | [`paymentControllerRoutes.ts`](../../backend/src/services/payment/paymentControllerRoutes.ts) | New controller pattern (CRUD + configuration) |
|
||||
| `/api/payment/*` | [`paymentRoutes.ts`](../../backend/src/services/payment/paymentRoutes.ts) | Additional legacy endpoints (tx fetch, exports) |
|
||||
| `/api/payment/decentralized/*` | [`decentralizedPaymentRoutes.ts`](../../backend/src/services/payment/decentralizedPaymentRoutes.ts) | DePay / Web3 confirmations |
|
||||
| `/api/payment/shkeeper/*` | [`shkeeper/shkeeperRoutes.ts`](../../backend/src/services/payment/shkeeper/shkeeperRoutes.ts) | SHKeeper pay-in, webhook, release/refund |
|
||||
| `/api/payment/shkeeper/payout*` | [`shkeeper/shkeeperPayoutRoutes.ts`](../../backend/src/services/payment/shkeeper/shkeeperPayoutRoutes.ts) | SHKeeper payouts to sellers |
|
||||
|
||||
Core model: [[Payment]]. Coordination logic to avoid race conditions when multiple sources update the same payment is in `paymentCoordinator.ts`.
|
||||
|
||||
## Configuration / health
|
||||
|
||||
### POST /api/payment/configuration
|
||||
|
||||
**Description:** Returns the payment provider configuration the SHKeeper widget needs (accepted blockchains, escrow receiver address, redirect URLs, webhook URL).
|
||||
**Auth required:** No
|
||||
**Request body:** `{ amount?, currency?, purchaseRequestId? }` (used to scope returned config)
|
||||
**Response 200:**
|
||||
```json
|
||||
{
|
||||
"accept": [{ "blockchain": "bsc", "token": "0x55d3...", "receiver": "0xa30..." }],
|
||||
"redirect": { "success": "...", "cancel": "..." },
|
||||
"webhook": "https://.../api/payment/shkeeper/webhook"
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/payment/health
|
||||
|
||||
**Description:** Lightweight health probe.
|
||||
**Auth required:** No
|
||||
**Response 200:** `{ success, message, endpoints: { shkeeper, decentralized, health } }`
|
||||
|
||||
### GET /api/payment/shkeeper/config
|
||||
|
||||
**Description:** Same payload as `/configuration` but tailored for the SHKeeper-hosted widget; includes explicit CORS `*` headers.
|
||||
**Auth required:** No
|
||||
|
||||
## Payment records (CRUD)
|
||||
|
||||
### POST /api/payment
|
||||
|
||||
**Description:** Create a payment record (manual entry — usually the SHKeeper intent path is preferred).
|
||||
**Auth required:** Bearer JWT
|
||||
**Request body:**
|
||||
```ts
|
||||
{
|
||||
purchaseRequestId: string;
|
||||
sellerOfferId: string;
|
||||
buyerId: string;
|
||||
sellerId: string;
|
||||
amount: { amount: number; currency: string };
|
||||
blockchain?: { network: string; token: string };
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
```
|
||||
**Response 201:** `{ /* Payment document */ }`
|
||||
|
||||
### PUT /api/payment/:id
|
||||
|
||||
**Description:** Update a payment record (status, transactionHash, metadata).
|
||||
**Auth required:** Bearer JWT
|
||||
**Request body:**
|
||||
```ts
|
||||
{
|
||||
status?: "pending" | "processing" | "completed" | "failed" | "cancelled";
|
||||
transactionHash?: string;
|
||||
blockchain?: { ... };
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/payment
|
||||
|
||||
**Description:** List the caller's payments (defaults to `completed,success` status).
|
||||
**Auth required:** Bearer JWT
|
||||
**Query params:** `status`, `limit` (default 50), `offset` (default 0)
|
||||
|
||||
### GET /api/payment/:id
|
||||
|
||||
**Description:** Fetch a payment by id.
|
||||
**Auth required:** Bearer JWT
|
||||
**Errors:** `404` not found.
|
||||
|
||||
### GET /api/payment/:id/debug
|
||||
|
||||
**Description:** Debug bundle including the raw payment, blockchain metadata, and wallet-monitor status.
|
||||
**Auth required:** Bearer JWT
|
||||
**Notes:** Intended for admin / development.
|
||||
|
||||
### GET /api/payment/user/:userId
|
||||
|
||||
**Description:** Payments for a specific user (admin or self).
|
||||
**Auth required:** Bearer JWT
|
||||
**Query params:** `status`, `limit`, `offset`
|
||||
|
||||
### GET /api/payment/stats / GET /api/payment/stats/:userId
|
||||
|
||||
**Description:** Aggregated counts and sums per status.
|
||||
**Auth required:** Bearer JWT
|
||||
|
||||
### GET /api/payment/export / GET /api/payment/export/:userId
|
||||
|
||||
**Description:** Export payments as `json` or `csv`.
|
||||
**Auth required:** Bearer JWT
|
||||
**Query params:** `format=json|csv`
|
||||
|
||||
### POST /api/payment/payments/cleanup-pending
|
||||
|
||||
**Description:** Admin cleanup of stale `pending` payments.
|
||||
**Auth required:** Bearer JWT (admin)
|
||||
**Response 200:** `{ success, deletedCount, message }`
|
||||
|
||||
### POST /api/payment/payments/:id/fetch-tx
|
||||
|
||||
**Description:** Re-queries the blockchain to fetch the missing `transactionHash` for a completed payment.
|
||||
**Auth required:** Bearer JWT
|
||||
**Response 200:** `{ success, transactionHash, network, source, message }`
|
||||
|
||||
### POST /api/payment/payments/auto-fetch-missing
|
||||
|
||||
**Description:** Batch tx-hash backfill across the database.
|
||||
**Auth required:** Bearer JWT
|
||||
**Request body:** `{ limit?: number }` (default 10)
|
||||
|
||||
### POST /api/payment/callback
|
||||
|
||||
**Description:** Generic payment callback (called by the older client SDK).
|
||||
**Auth required:** No (verified by `paymentRef` matching)
|
||||
**Request body:** `{ paymentId, transactionHash, status, data }`
|
||||
|
||||
### POST /api/payment/verify
|
||||
|
||||
**Description:** Frontend verification endpoint used by the Web3 flow. Confirms a payment and updates the related [[PurchaseRequest]].
|
||||
**Auth required:** Bearer JWT
|
||||
|
||||
## SHKeeper - Pay-in
|
||||
|
||||
### POST /api/payment/shkeeper/intents
|
||||
|
||||
**Description:** Creates a SHKeeper pay-in intent. The server provisions an invoice on SHKeeper, stores a [[Payment]] with `provider: "shkeeper"`, `direction: "in"`, returns the hosted-widget URL.
|
||||
**Auth required:** Bearer JWT (buyer)
|
||||
**Request body:**
|
||||
```ts
|
||||
{
|
||||
purchaseRequestId: string;
|
||||
sellerOfferId: string;
|
||||
amount: number;
|
||||
sellerId: string;
|
||||
token?: string; // default "USDT"
|
||||
network?: string; // default "bsc"
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
```
|
||||
**Response 200:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"paymentId": "...",
|
||||
"paymentUrl": "https://pay.amn.gg/invoice/...",
|
||||
"externalId": "AMN_...",
|
||||
"expiresAt": "2026-05-23T11:00:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
**Errors:** `400` missing fields, `401` not authenticated, `500` SHKeeper error.
|
||||
**Side effects:** Emits `payment-created` globally and to the request room.
|
||||
|
||||
### POST /api/payment/shkeeper/webhook
|
||||
|
||||
**Description:** SHKeeper posts here when an invoice changes state. Handles both raw-string and JSON bodies and verifies the HMAC signature (`x-shkeeper-signature` against the raw body using `SHKEEPER_WEBHOOK_SECRET`).
|
||||
**Auth required:** No (signature-protected)
|
||||
**Body:** The SHKeeper callback envelope (`external_id`, `crypto`, `addr`, `fiat`, `balance_fiat`, `balance_crypto`, `paid`, `status`, `transactions[]`).
|
||||
**Response 200:** `{ success: true }`
|
||||
**Side effects:**
|
||||
- Updates the matching [[Payment]] to `completed` (`OVERPAID` and `PAID` both count).
|
||||
- Releases or rejects [[SellerOffer]] siblings (the chosen offer becomes `accepted`, others `rejected`).
|
||||
- Updates [[PurchaseRequest]] status to `payment` / `processing`.
|
||||
- Emits `seller-offer-update` to each affected seller room and `purchase-request-update` to the request room.
|
||||
|
||||
### POST /api/payment/shkeeper/confirm-transaction
|
||||
|
||||
**Description:** Manual fallback when the webhook misses — the frontend calls this after the buyer signs the EVM transaction directly. Coordinated through `PaymentCoordinator` to avoid double updates.
|
||||
**Auth required:** Bearer JWT
|
||||
**Request body:** `{ paymentId, transactionHash, network? }`
|
||||
**Response 200:** `{ success, message, data: { paymentId, transactionHash, status } }`
|
||||
**Side effects:** Closes the SHKeeper invoice session, then runs the same offer/request updates as the webhook.
|
||||
|
||||
### POST /api/payment/shkeeper/test
|
||||
|
||||
**Description:** Smoke-tests the real SHKeeper API. Development only.
|
||||
**Auth required:** No
|
||||
|
||||
### POST /api/payment/shkeeper/callback-test (and GET equivalent)
|
||||
|
||||
**Description:** Echo-style endpoints used during webhook configuration.
|
||||
**Auth required:** No
|
||||
|
||||
### POST /api/payment/shkeeper/create-test-payment
|
||||
|
||||
**Description:** Inserts a sample [[Payment]] row to exercise the webhook handler.
|
||||
**Auth required:** No
|
||||
|
||||
### POST /api/payment/shkeeper/trigger-webhook
|
||||
|
||||
**Description:** Sends a fake webhook payload to the local webhook endpoint for end-to-end testing.
|
||||
**Auth required:** Bearer JWT
|
||||
|
||||
### GET /api/payment/shkeeper/wallet-monitor/status
|
||||
|
||||
**Description:** Returns the wallet-monitor state (`isMonitoring`, watched wallet addresses).
|
||||
**Auth required:** No
|
||||
|
||||
### GET /api/payment/shkeeper/auto-webhook/status
|
||||
|
||||
**Description:** Returns the auto-webhook fallback monitor state.
|
||||
**Auth required:** No
|
||||
|
||||
### GET /api/payment/shkeeper/webhook-stats
|
||||
|
||||
**Description:** Counters for webhook deliveries (success / failure / duplicates).
|
||||
**Auth required:** Bearer JWT (admin)
|
||||
|
||||
## SHKeeper - Release / Refund (escrow)
|
||||
|
||||
These build an admin-signed transaction off-chain and require a follow-up confirm with the broadcast tx hash. Source: `shkeeperService.buildAdminSignedTxPayload` and `confirmAdminTx`.
|
||||
|
||||
### POST /api/payment/shkeeper/:id/release
|
||||
|
||||
**Description:** Prepares the admin-signed payload to release escrow to the seller. Returns the raw payload — the admin client signs and broadcasts.
|
||||
**Auth required:** Bearer JWT (admin)
|
||||
**Response 200:** `{ success: true, data: { /* tx payload */ } }`
|
||||
|
||||
### POST /api/payment/shkeeper/:id/release/confirm
|
||||
|
||||
**Description:** Records the broadcast transaction hash for the release; marks the payment as released, updates [[PurchaseRequest]] to `seller_paid` and emits `purchase-request-update` (`type: payment_released`).
|
||||
**Auth required:** Bearer JWT (admin)
|
||||
**Request body:** `{ txHash: string }`
|
||||
**Errors:** `400` missing `txHash`.
|
||||
|
||||
### POST /api/payment/shkeeper/:id/refund
|
||||
|
||||
**Description:** Mirror of release, but returns the escrow to the buyer.
|
||||
**Auth required:** Bearer JWT (admin)
|
||||
|
||||
### POST /api/payment/shkeeper/:id/refund/confirm
|
||||
|
||||
**Description:** Records the refund tx hash; emits `purchase-request-update` (`type: payment_refunded`).
|
||||
**Auth required:** Bearer JWT (admin)
|
||||
**Request body:** `{ txHash: string }`
|
||||
|
||||
## SHKeeper - Payouts
|
||||
|
||||
Payouts are SHKeeper-side outbound transfers (admin pays the seller from a hot wallet).
|
||||
|
||||
### POST /api/payment/shkeeper/payout
|
||||
|
||||
**Description:** Create a payout task. The server creates a [[Payment]] row with `direction: "out"` and provider task id, then returns the SHKeeper task descriptor.
|
||||
**Auth required:** Bearer JWT (admin)
|
||||
**Request body:**
|
||||
```ts
|
||||
{
|
||||
purchaseRequestId: string;
|
||||
sellerOfferId: string;
|
||||
buyerId: string;
|
||||
sellerId: string;
|
||||
amount: number;
|
||||
recipientAddress: string;
|
||||
token?: string; // default "USDT"
|
||||
network?: string; // default "bsc"
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
```
|
||||
**Response 200:** `{ success, data: { payoutId, taskId, status }, message }`
|
||||
**Side effects:** `emitGlobalEvent('payout-created', { ... })`.
|
||||
**Errors:** `400` for each missing required field, `500` upstream error.
|
||||
|
||||
### GET /api/payment/shkeeper/payout/status/:taskId
|
||||
|
||||
**Description:** Polls SHKeeper for the current task status.
|
||||
**Auth required:** Bearer JWT
|
||||
**Response 200:** `{ success, data: { /* status payload */ } }`
|
||||
|
||||
### POST /api/payment/shkeeper/payout/webhook
|
||||
|
||||
**Description:** SHKeeper webhook for payout state changes. Handled by `processPayoutWebhookEvent`. Emits `payout-completed` (or `payout-updated`) global socket events on success.
|
||||
**Auth required:** No (signature checked)
|
||||
**Response 200/400:** `{ success, message, data }`
|
||||
|
||||
## DePay / Web3 (decentralized)
|
||||
|
||||
### POST /api/payment/decentralized/save
|
||||
|
||||
**Description:** Persists a Web3-initiated payment record.
|
||||
**Auth required:** No
|
||||
**Request body:**
|
||||
```ts
|
||||
{
|
||||
purchaseRequestId: string;
|
||||
buyerId: string;
|
||||
sellerId?: string;
|
||||
amount: number;
|
||||
currency?: string;
|
||||
transactionHash?: string;
|
||||
network?: string;
|
||||
token?: string;
|
||||
walletAddress?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/payment/decentralized/status/:paymentId
|
||||
|
||||
**Description:** Returns the latest status for a decentralized payment.
|
||||
**Auth required:** No
|
||||
|
||||
### PUT /api/payment/decentralized/update
|
||||
|
||||
**Description:** Update a decentralized payment's status / confirmations.
|
||||
**Auth required:** No
|
||||
**Request body:** `{ paymentId, status, confirmations? }`
|
||||
|
||||
### GET /api/payment/decentralized/receiver
|
||||
|
||||
**Description:** Returns the configured escrow receiver wallet address.
|
||||
**Auth required:** No
|
||||
|
||||
### GET /api/payment/decentralized/history/:userId
|
||||
|
||||
**Description:** Decentralized payment history for a user.
|
||||
**Auth required:** No
|
||||
|
||||
### POST /api/payment/decentralized/verify/:paymentId
|
||||
|
||||
**Description:** Re-verifies a single decentralized payment against the chain.
|
||||
**Auth required:** No
|
||||
|
||||
### POST /api/payment/decentralized/verify-all-pending
|
||||
|
||||
**Description:** Iterates all `pending` decentralized payments and re-verifies them.
|
||||
**Auth required:** No (typically called by a cron)
|
||||
|
||||
### POST /api/payment/decentralized/admin-payout
|
||||
|
||||
**Description:** Pay a seller directly from an admin hot wallet (no SHKeeper).
|
||||
**Auth required:** Bearer JWT (admin)
|
||||
**Request body:**
|
||||
```ts
|
||||
{
|
||||
purchaseRequestId: string;
|
||||
receiverWalletAddress: string;
|
||||
amount: number;
|
||||
currency?: string; // default "USDT"
|
||||
network?: string; // default "BSC"
|
||||
}
|
||||
```
|
||||
**Response 200:** `{ success, data: { /* payout receipt */ } }`
|
||||
|
||||
## Status model
|
||||
|
||||
[[Payment]] uses the statuses below across all providers:
|
||||
|
||||
- `pending` - intent created, awaiting on-chain settlement
|
||||
- `processing` - settlement seen, awaiting confirmations
|
||||
- `completed` - confirmed, escrow funded
|
||||
- `failed` - intentionally failed (expired, declined, refused)
|
||||
- `cancelled` - cancelled by user/admin
|
||||
- `released` - escrow released to seller (`shkeeper` flow)
|
||||
- `refunded` - escrow returned to buyer
|
||||
|
||||
Escrow state (`escrowState`): `unfunded` → `funded` → `released` | `refunded`.
|
||||
|
||||
## Related
|
||||
|
||||
- [[Payment Flow]]
|
||||
- [[Escrow Flow]]
|
||||
- [[SHKeeper Webhook Flow]]
|
||||
- [[Socket Events]]
|
||||
Reference in New Issue
Block a user