@paychainhq/sdk
v0.1.2
Published
Official PayChainHQ TypeScript SDK for backend payment integrations.
Maintainers
Readme
PayChainHQ TypeScript SDK
Official PayChainHQ SDK for trusted Node.js backends. Use it to create customers and invoices, attach approved payout routes, create dynamic payout-recipient invoices with a payout key, quote and create withdrawals, verify webhooks, and read balances, transactions, networks, and tokens.
This SDK is server-side only. Never expose PayChain API keys in browsers, mobile apps, or public client code.
Install
pnpm add @paychainhq/sdknpm install @paychainhq/sdkRequires Node.js 18+ with native fetch.
Create a client
Use a standard business API key for invoices, customers, reads, balances, webhooks, and approved payout-route attachment.
import { PayChain } from '@paychainhq/sdk';
const paychain = new PayChain({
apiKey: process.env.PAYCHAIN_API_KEY!,
businessId: process.env.PAYCHAIN_BUSINESS_ID!,
keyType: 'standard',
environment: 'live'
});Use a dedicated payout API key only for programmatic withdrawals and dynamic payout recipients.
const payoutClient = new PayChain({
apiKey: process.env.PAYCHAIN_PAYOUT_API_KEY!,
businessId: process.env.PAYCHAIN_BUSINESS_ID!,
keyType: 'payout',
environment: 'live'
});Sandbox integrations must pass an explicit baseUrl:
const sandbox = new PayChain({
apiKey: process.env.PAYCHAIN_SANDBOX_API_KEY!,
businessId: process.env.PAYCHAIN_BUSINESS_ID!,
keyType: 'standard',
environment: 'sandbox',
baseUrl: 'https://your-sandbox-api.example.com/api/v1'
});Customers
const customer = await paychain.customers.create({
externalRef: 'customer_123'
});
const fetched = await paychain.customers.get(customer.id);
const customers = await paychain.customers.list({ limit: 25 });Invoices
Create an invoice with an idempotency key from your order ID.
const invoice = await paychain.invoices.create(
{
customerId: customer.id,
amount: '100.00',
token: 'USDC',
chain: 'eth',
networkId: 'base-mainnet',
description: 'Order 1234'
},
{ idempotencyKey: 'order_1234' }
);
console.log(invoice.id, invoice.depositAddress, invoice.status);Poll an invoice when you need confirmation progress for slower-finality networks.
const invoiceStatus = await paychain.invoices.get(invoice.id);
if (invoiceStatus.confirmationProgress?.status === 'confirming') {
console.log(
`${invoiceStatus.confirmationProgress.current}/${invoiceStatus.confirmationProgress.required} confirmations`
);
}
if (invoiceStatus.status === 'paid' || invoiceStatus.status === 'overpaid') {
// Fulfill the order after verifying the webhook and fetching canonical state.
}Attach an approved payout route template. Route templates are created and approved in the PayChain dashboard.
const invoiceWithRoute = await paychain.invoices.create(
{
amount: '250.00',
token: 'USDC',
chain: 'eth',
networkId: 'base-mainnet',
payoutRouteId: 'payout_route_123'
},
{ idempotencyKey: 'order_1234_route' }
);Use dynamic payout recipients only with a payout API key. The SDK accepts percentages and converts them to shareBps for the API.
const dynamicPayoutInvoice = await payoutClient.invoices.create(
{
amount: '500.00',
token: 'USDC',
chain: 'eth',
networkId: 'base-mainnet',
payoutRecipients: [
{
label: 'Seller',
destinationAddress: '0x1111111111111111111111111111111111111111',
percentage: 80
},
{
label: 'Platform',
destinationAddress: '0x2222222222222222222222222222222222222222',
percentage: 20
}
]
},
{ idempotencyKey: 'marketplace_order_1234' }
);The SDK rejects dynamic split totals that do not equal 100%, more than 10 recipients, and zero or negative shares before making a request.
Payout routes
Payout route templates are dashboard-managed in v1. The SDK can list and fetch active templates so your backend can attach them to invoices.
const routes = await paychain.payoutRoutes.list({ status: 'active' });
const route = await paychain.payoutRoutes.get('payout_route_123');The SDK intentionally does not create, archive, or delete payout route templates because those actions require dashboard session auth and step-up verification.
Withdrawals
Quote withdrawals with either a standard or payout client.
const quote = await paychain.withdrawals.quote({
amount: '25.00',
token: 'USDC',
chain: 'eth',
networkId: 'base-mainnet'
});Create withdrawals only with a payout API key.
const withdrawal = await payoutClient.withdrawals.create(
{
amount: '25.00',
token: 'USDC',
chain: 'eth',
networkId: 'base-mainnet',
destination: '0x3333333333333333333333333333333333333333',
clientReference: 'payout_1234'
},
{ idempotencyKey: 'payout_1234' }
);Webhooks
Always verify PayChain webhook signatures using the raw request body. Do not parse JSON before verification.
import express from 'express';
import { verifyWebhookSignature } from '@paychainhq/sdk';
const app = express();
app.post('/paychain/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.header('x-paychain-signature');
const valid = verifyWebhookSignature({
rawBody: req.body,
signature,
secret: process.env.PAYCHAIN_WEBHOOK_SECRET!,
toleranceSeconds: 300
});
if (!valid) {
return res.status(400).send('invalid signature');
}
const event = JSON.parse(req.body.toString('utf8'));
// Process idempotently using event.id.
return res.sendStatus(200);
});Webhook management helpers:
await paychain.webhooks.getConfig();
await paychain.webhooks.updateConfig({
webhookEndpoints: [
{ label: 'Primary', url: 'https://example.com/paychain/webhook', enabled: true }
]
});
await paychain.webhooks.sendTest();
await paychain.webhooks.listEvents({ status: 'failed' });
await paychain.webhooks.retryEvent('webhook_event_123');
await paychain.webhooks.replayEvent('webhook_event_123');Balances and transactions
const balances = await paychain.balances.list();
const total = await paychain.balances.total();
const byNetwork = await paychain.balances.byNetwork();
const history = await paychain.balances.history({ limit: 50, token: 'USDC' });
const tokenBalance = await paychain.balances.getTokenBalance({
chain: 'eth',
networkId: 'base-mainnet',
token: 'USDC'
});
const transactions = await paychain.transactions.list({
type: 'invoice',
token: 'USDC',
networkId: 'base-mainnet'
});
const summary = await paychain.transactions.summary({
startDate: '2026-05-01',
endDate: '2026-05-31'
});Networks and tokens
const networks = await paychain.networks.list();
const supported = await paychain.networks.supported();
const tokens = await paychain.tokens.list({ networkId: 'base-mainnet' });
const baseTokens = await paychain.tokens.forNetwork('base-mainnet');
const usdc = await paychain.tokens.get('USDC', { networkId: 'base-mainnet' });Idempotency and retries
Every mutating method accepts an idempotencyKey option.
await paychain.invoices.create(
{
amount: '100.00',
token: 'USDC',
chain: 'eth',
networkId: 'base-mainnet'
},
{ idempotencyKey: 'order_1234' }
);The SDK retries network errors, 408, 429, and 5xx. Mutating requests are retried only when an idempotency key is present.
Errors
import {
PayChainApiError,
PayChainAuthError,
PayChainRateLimitError,
PayChainValidationError
} from '@paychainhq/sdk';
try {
await paychain.invoices.get('invoice_123');
} catch (error) {
if (error instanceof PayChainApiError) {
console.log(error.status, error.code, error.requestId);
}
if (error instanceof PayChainValidationError) {
console.log(error.details);
}
}SDK errors intentionally exclude API keys, webhook secrets, auth tokens, request headers, and raw request bodies.
Public surface
V1 includes:
customers.create,customers.list,customers.getinvoices.create,invoices.list,invoices.getpayoutRoutes.list,payoutRoutes.getwithdrawals.quote,withdrawals.create,withdrawals.list,withdrawals.getwebhooks.getConfig,webhooks.updateConfig,webhooks.rotateSecret,webhooks.sendTestwebhooks.listEvents,webhooks.retryEvent,webhooks.replayEvent,webhooks.verifySignaturebalances.list,balances.total,balances.byNetwork,balances.byChain,balances.history,balances.getTokenBalancetransactions.list,transactions.summarynetworks.list,networks.supportedtokens.list,tokens.forNetwork,tokens.get
V1 intentionally excludes admin APIs, dashboard session flows, step-up auth, payout route mutation, API-key management, billing mutations, internal gas sponsorship controls, provider-specific infrastructure controls, and private wallet operations.
Security checklist
- Keep API keys on your backend.
- Use standard API keys for collection and read workflows.
- Use dedicated payout API keys for withdrawals and dynamic payout recipients.
- Verify webhooks with the raw request body.
- Do not log SDK config, API keys, webhook secrets, auth tokens, payout destination auth tokens, or raw webhook bodies.
- Do not trust client-submitted payout destinations without your own compliance and risk checks.
- Do not use this SDK in browsers, mobile apps, or public client code.
Documentation
Full API docs: https://paychainhq.io/docs
