@pspline/sdk
v1.0.0
Published
Official Node.js SDK for PSP Line Merchant API — crypto invoices, payments, wallets
Downloads
6
Maintainers
Readme
PSP Line Node.js SDK
Official Node.js/TypeScript SDK for the PSP Line Merchant API — create crypto invoices, check payment statuses, and verify webhook signatures.
Features
- TypeScript-first — full type definitions out of the box
- Zero dependencies — uses native
fetch(Node.js 18+) - ESM + CommonJS — works everywhere
- Async/await — all methods return
Promise - Auto-retry — exponential backoff on 5xx and network errors
- Webhook verification — HMAC-SHA256 signature validation
Requirements
- Node.js 18 or higher
Installation
npm install @pspline/sdkQuick Start
import PspLine from '@pspline/sdk';
const psp = new PspLine({
merchantId: 232,
publicKey: 'your-public-key',
privateKey: 'your-private-key',
});
// Create a crypto invoice
const invoice = await psp.invoices.create({
serviceId: 885,
walletId: 52205,
currency: 'USDT_TRON',
estimatedAmount: 15_000_000, // 15 USDT (precision 6)
callbackUrl: 'https://your-site.test/webhook',
confirmUrl: 'https://your-site.test/success',
fields: { userId: 'customer-123' },
});
console.log(invoice.walletAddress); // Crypto address
console.log(invoice.verifyUrl); // Web form URL
console.log(invoice.status); // 0 = waitingCommonJS
const { PspLine } = require('@pspline/sdk');
const psp = new PspLine({
merchantId: 232,
publicKey: 'your-public-key',
privateKey: 'your-private-key',
});Configuration
| Option | Type | Default | Description |
|---|---|---|---|
| merchantId | number | required | Merchant ID |
| publicKey | string | required | Public key for authentication |
| privateKey | string | required | Private key for HMAC signature |
| baseUrl | string | https://app.pspline.com/api/cpci/v1 | API base URL |
| timeout | number | 30000 | Request timeout in milliseconds |
| retries | number | 2 | Retry count on 5xx/network errors |
| httpClient | HttpClient | FetchHttpClient | Custom HTTP transport |
Custom Base URL
const psp = new PspLine({
merchantId: 232,
publicKey: process.env.PSPLINE_PUBLIC_KEY!,
privateKey: process.env.PSPLINE_PRIVATE_KEY!,
baseUrl: process.env.PSPLINE_BASE_URL!,
});API Reference
Invoices
Create Invoice
const invoice = await psp.invoices.create({
// Required
serviceId: 885, // Service ID
walletId: 52205, // Wallet to credit
currency: 'USDT_TRON', // Currency code
estimatedAmount: 15_000_000, // Amount in min units
// Optional
externalId: 'order-789', // Your system ID
callbackUrl: 'https://...',
confirmUrl: 'https://...',
fields: { userId: 'u001' },
});Response type:
interface Invoice {
id: string; // Invoice UUID
externalId: string | null; // Your system ID
merchantId: number;
serviceId: number;
walletId: number;
currency: string; // e.g. 'USDT_TRON'
estimatedAmount: number; // Expected amount
receivedAmount: number; // Actually received
verifyUrl: string | null; // Web payment form URL
walletAddress: string | null; // Crypto address
walletMemo: string | null; // Memo (XRP, TON, etc.)
status: number; // Status code
statusLabel: string; // Human-readable status
fields: Record<string, unknown> | null;
// Computed helpers
isSuccessful: boolean;
isFailed: boolean;
isPending: boolean;
isFinal: boolean;
raw: InvoiceRaw; // Original API data
}Find Invoice
// By PSP Line invoice ID
const invoice = await psp.invoices.find({ id: 'invoice-uuid' });
// By your external ID
const invoice = await psp.invoices.find({ externalId: 'order-789' });
// Check status
if (invoice.isSuccessful) {
// Payment completed — deliver goods
} else if (invoice.isPending) {
// Still waiting — check again later
} else if (invoice.isFailed) {
// Payment failed or timed out
}Invoice Statuses
import { InvoiceStatus } from '@pspline/sdk';
// Final
InvoiceStatus.SUCCESS // 1
InvoiceStatus.FAILED // 2
InvoiceStatus.CANCELLED // 3
InvoiceStatus.TIMEOUT // 4
// Non-final
InvoiceStatus.WAITING // 0
InvoiceStatus.CHECKING // 10
InvoiceStatus.PARTIAL_SUCCESS // 11
InvoiceStatus.UNCOMPLETED_MERGE // 12Cryptocurrency Amounts
PSP Line uses integer amounts in minimum currency units:
| Currency | Precision | Example |
|---|---|---|
| USDT_TRON | 6 | 15 USDT → 15_000_000 |
| BTC | 8 | 0.001 BTC → 100_000 |
| ETH | 18 | 0.027 ETH → 27_000_000_000_000_000 |
Webhook Verification
PSP Line sends POST callbacks with HMAC signatures. Always verify them:
import { verifyWebhook } from '@pspline/sdk/webhook';
// Express example
app.post('/webhook', (req, res) => {
const isValid = verifyWebhook({
body: req.rawBody, // raw string, not parsed JSON
signature: req.headers['x-signature'] as string,
timestamp: req.headers['timestamp'] as string,
privateKey: process.env.PSPLINE_PRIVATE_KEY!,
tolerance: 120, // max age in seconds (default)
});
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook safely...
res.json({ status: 'ok' });
});Important: You need the raw request body for signature verification. With Express:
app.use(express.json({
verify: (req: any, _res, buf) => {
req.rawBody = buf.toString();
},
}));Error Handling
import {
PspLineError,
AuthenticationError,
ApiError,
ValidationError,
NetworkError,
} from '@pspline/sdk';
try {
const invoice = await psp.invoices.create({ ... });
} catch (err) {
if (err instanceof AuthenticationError) {
// Invalid keys, timestamp, or signature (API codes 2001-2006)
console.error('Auth error:', err.message);
} else if (err instanceof ValidationError) {
// Missing or invalid parameters (client-side)
console.error('Validation:', err.message, err.field);
} else if (err instanceof ApiError) {
// PSP Line API returned an error
console.error(`API error [${err.apiCode}]: ${err.message}`);
console.error('HTTP:', err.httpCode);
console.error('Raw:', err.rawBody);
} else if (err instanceof NetworkError) {
// Connection timeout, DNS failure, etc.
console.error('Network error:', err.message);
} else if (err instanceof PspLineError) {
// Base catch-all
console.error('SDK error:', err.message);
}
}Error Hierarchy
PspLineError (base)
├── NetworkError — fetch/connection failures
├── ValidationError — client-side input validation
└── ApiError — API returned error code
└── AuthenticationError — auth errors (2001-2006)Framework Examples
Express
import express from 'express';
import PspLine from '@pspline/sdk';
const psp = new PspLine({
merchantId: Number(process.env.PSPLINE_MERCHANT_ID),
publicKey: process.env.PSPLINE_PUBLIC_KEY!,
privateKey: process.env.PSPLINE_PRIVATE_KEY!,
});
const app = express();
app.post('/create-payment', async (req, res) => {
const invoice = await psp.invoices.create({
serviceId: 885,
walletId: 52205,
currency: 'USDT_TRON',
estimatedAmount: req.body.amount,
callbackUrl: `${process.env.BASE_URL}/webhook`,
});
res.json({
paymentUrl: invoice.verifyUrl,
walletAddress: invoice.walletAddress,
invoiceId: invoice.id,
});
});NestJS
import { Injectable } from '@nestjs/common';
import PspLine from '@pspline/sdk';
@Injectable()
export class PaymentService {
private readonly psp: PspLine;
constructor() {
this.psp = new PspLine({
merchantId: Number(process.env.PSPLINE_MERCHANT_ID),
publicKey: process.env.PSPLINE_PUBLIC_KEY!,
privateKey: process.env.PSPLINE_PRIVATE_KEY!,
});
}
async createInvoice(amount: number) {
return this.psp.invoices.create({
serviceId: 885,
walletId: 52205,
currency: 'USDT_TRON',
estimatedAmount: amount,
});
}
}Next.js API Route
// app/api/payment/route.ts
import PspLine from '@pspline/sdk';
const psp = new PspLine({
merchantId: Number(process.env.PSPLINE_MERCHANT_ID),
publicKey: process.env.PSPLINE_PUBLIC_KEY!,
privateKey: process.env.PSPLINE_PRIVATE_KEY!,
});
export async function POST(request: Request) {
const { amount } = await request.json();
const invoice = await psp.invoices.create({
serviceId: 885,
walletId: 52205,
currency: 'USDT_TRON',
estimatedAmount: amount,
});
return Response.json({ invoiceId: invoice.id, paymentUrl: invoice.verifyUrl });
}Development
# Install dependencies
npm install
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Type check
npm run typecheck
# Lint
npm run lint
# Format
npm run format
# Build
npm run buildContributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Write tests for your changes
- Ensure all checks pass:
npm test && npm run lint && npm run typecheck - Submit a Pull Request
Security
If you discover a security vulnerability, please email [email protected] instead of creating a public issue.
License
MIT — see LICENSE for details.
