PaychainHQ Docs
Payment Lifecycle Guides
End-to-end implementation docs organized around the 3 production flows: receive payment, confirm payment, and withdraw payment. Includes request/response and webhook payload shapes.
Overview
Use API keys from your backend for payment APIs. Use dashboard login only for operator UI actions.
Idempotency-Key on mutating calls.requestId for support and incident debugging.Integration Model
Most teams implement PaychainHQ as a 3-step server workflow.
Lifecycle 1
Receive payment
Create customer (optional), create invoice, share payer card/link.
Lifecycle 2
Confirm payment
Verify webhook, update order state, handle underpay/overpay paths.
Lifecycle 3
Withdraw payment
Quote first, execute payout with step-up, then track status to completion.
1. Receive Payment
Create a payable invoice and give the sender a clear destination, amount, token, and chain.
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/v1/businesses/:id/customers | x-api-key or Bearer | Optional: create reusable customer identity before invoicing. |
| POST | /api/v1/businesses/:id/invoices | x-api-key or Bearer | Create invoice and allocate deposit address. |
| GET | /api/v1/invoices/:invoiceId | Public | Read shareable invoice payload for payer-facing page/card. |
1. Create customer (optional)
If the payer is recurring, create a customer first so invoices can be grouped by identity.
2. Create invoice
Send amount/token/chain/network with a unique Idempotency-Key.
3. Render share card/link
Use public invoice payload to show QR, address, exact amount, and countdown.
{
"customerId": "cus_9f7d7c42",
"amount": "10",
"token": "USDC",
"chain": "eth",
"networkId": "base-sepolia",
"expiresIn": 86400,
"description": "Design sprint payment"
}{
"id": "inv_46a1a472",
"businessId": "biz_123",
"customerId": "cus_9f7d7c42",
"amount": "10",
"token": "USDC",
"status": "pending",
"chain": "eth",
"networkId": "base-sepolia",
"depositAddress": "0xE5fa2F71065fD49823D33EdD84ecFD2D6245c916",
"expiresAt": "2026-03-04T12:00:00.000Z",
"createdAt": "2026-03-03T12:00:00.000Z",
"feePreview": {
"platformFee": 0.2,
"gasReserveFee": 0.05,
"totalFee": 0.25,
"effectiveRate": "2.50%",
"note": "Fees will be deducted when invoice is paid"
},
"expectedNet": {
"usd": 9.75,
"token": "9.75 USDC"
}
}2. Confirm Payment
Confirm invoice settlement using webhook-first logic, with invoice reads as fallback.
| Method | Path | Auth | Description |
|---|---|---|---|
| PATCH | /api/v1/businesses/:id/webhook | Bearer + step-up | Configure webhook URL, signing secret, and auth token. |
| GET | /api/v1/businesses/invoices/:invoiceId | x-api-key or Bearer | Operator view of status and reconciliation data. |
| GET | /api/v1/invoices/:invoiceId | Public | Payer view with progress (underpaid/overpaid/paid). |
| GET | /api/v1/businesses/:id/webhook/events | Bearer | Inspect delivery attempts and replay failed events. |
1. Verify webhook signature
Always validate X-Webhook-Signature against the raw request body before processing.
2. Apply state transition
Use webhook event + invoice status to move your order state (pending/confirming/paid/underpaid/overpaid).
3. Resolve edge cases
If underpaid before expiry, keep invoice open for top-up. If overpaid, mark paid and trigger remediation workflow.
{
"id": "evt_2b3f1fa0",
"event": "invoice.paid",
"timestamp": "2026-03-03T12:01:04.903Z",
"data": {
"invoiceId": "inv_46a1a472",
"businessId": "biz_123",
"status": "paid",
"state": "completed",
"amount": {
"raw": "10000000",
"decimals": 6,
"display": "10",
"symbol": "USDC",
"chain": "eth",
"networkId": "base-sepolia"
},
"paidAmount": {
"raw": "10000000",
"decimals": 6,
"display": "10",
"symbol": "USDC",
"chain": "eth",
"networkId": "base-sepolia"
},
"overpaidAmount": {
"raw": "0",
"decimals": 6,
"display": "0",
"symbol": "USDC",
"chain": "eth",
"networkId": "base-sepolia"
},
"txHash": "0x9ee6...",
"confirmations": 12,
"occurredAt": "2026-03-03T12:01:04.903Z"
}
}import crypto from 'node:crypto';
export function verifyPaychainHQWebhook(rawBody: string, signatureHex: string, webhookSecret: string): boolean {
const expected = crypto.createHmac('sha256', webhookSecret).update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signatureHex, 'hex'), Buffer.from(expected, 'hex'));
}
// Headers sent by PaychainHQ:
// X-Webhook-Signature
// X-Webhook-ID
// X-Webhook-Attempt
// X-Webhook-Timestamp
// X-Webhook-Signature-Alg{
"id": "inv_46a1a472",
"status": "confirming",
"state": "confirming",
"amount": {
"raw": "10000000",
"decimals": 6,
"display": "10",
"symbol": "USDC",
"chain": "eth",
"networkId": "base-sepolia"
},
"paidAmount": {
"raw": "7000000",
"decimals": 6,
"display": "7",
"symbol": "USDC",
"chain": "eth",
"networkId": "base-sepolia"
},
"paymentProgress": {
"percentPaid": "70.00",
"status": "Partially paid - additional payment required",
"remainingAmount": {
"display": "3"
}
},
"depositAddress": "0xE5fa2F71065fD49823D33EdD84ecFD2D6245c916"
}3. Withdraw Payment
Move funds out safely with quote-first execution and status tracking.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/v1/businesses/:id/withdrawals/quote | x-api-key or Bearer | Preview sweep cost, available balance, and best payout option. |
| POST | /api/v1/businesses/:id/withdrawals | Bearer + step-up | Queue withdrawal execution. |
| GET | /api/v1/businesses/:id/withdrawals | x-api-key or Bearer | Track payout status (pending -> processing -> completed/failed). |
1. Get quote
Check availability, sweep requirement, cost, and recommendation before creating withdrawal.
2. Create withdrawal
Use dashboard session + step-up for the final payout mutation.
3. Track lifecycle
Poll/list withdrawals for pending -> processing -> completed/failed and surface txHash to operators.
{
"requestedAmountUSD": 500,
"token": "USDC",
"chain": "eth",
"networkId": "base-sepolia",
"treasuryBalanceUSD": 220,
"totalAvailableUSD": 760.5,
"addressesWithFunds": 8,
"sweepNeeded": true,
"recommended": "exact",
"message": "Withdrawal feasible. Will sweep 3 address(es) first.",
"options": [
{
"type": "exact_withdrawal",
"label": "Withdraw Exact Amount",
"payoutUSD": 500,
"sweepCount": 3,
"totalCostUSD": 2.34,
"feePercent": 0.47,
"available": true
}
]
}{
"amount": "500",
"token": "USDC",
"chain": "eth",
"networkId": "base-sepolia",
"destination": "0x5fBDB2315678afecb367f032d93F642f64180aa3",
"clientReference": "vendor-payout-2026-03-0031",
"reference": "Vendor March settlement",
"metadata": {
"vendorId": "ven_29831",
"batch": "march-settlement"
}
}{
"id": "wdr_89f0c9",
"status": "pending",
"state": "queued",
"amount": {
"raw": "500000000",
"decimals": 6,
"display": "500",
"symbol": "USDC",
"chain": "eth",
"networkId": "base-sepolia"
},
"withdrawalFee": {
"raw": "1200000",
"decimals": 6,
"display": "1.2",
"symbol": "USDC",
"chain": "eth",
"networkId": "base-sepolia"
},
"destination": "0x5fBDB2315678afecb367f032d93F642f64180aa3",
"txHash": null,
"message": "Withdrawal accepted for processing"
}Security & Auth
Split machine auth from operator auth.
x-api-key.Turnkey-powered treasury operations
PayChainHQ uses Turnkey for MPC-backed key infrastructure behind treasury and wallet operations. In practice, that means operational signing and key management are handled through hardened custody infrastructure rather than raw private keys living in your application environment.
Your integration does not talk to Turnkey directly for normal payment collection. You integrate with PayChainHQ APIs, while PayChainHQ uses Turnkey internally for controlled wallet creation, treasury movement, and operational signing flows.
Go-Live Checklist
Minimum checklist before production traffic.
- Webhook signature verification enabled in production.
- Invoice underpaid/overpaid handling paths implemented.
- Withdrawal quote + create + tracking flow tested on staging.
- Alerting on webhook delivery failures and payout failures.