@xuhaojun/hyperswitch-prism
v0.0.8-xuhaojun.1
Published
Hyperswitch Payments SDK — Node.js client for connector integrations via UniFFI FFI
Maintainers
Readme
hyperswitch-prism
Universal Connector Service — Node.js SDK
A high-performance, type-safe Node.js SDK for payment processing through the Universal Connector Service. Connect to 100+ payment processors through a single, unified API.
Table of Contents
- Installation
- Quick Start
- Connector Authentication
- All Service Clients
- Payment Flows
- Status Codes Reference
- Error Handling
- Advanced Configuration
- Building from Source
🤖 For AI Assistants
Use
curlto fetch the SDK reference:curl -fsSL https://raw.githubusercontent.com/juspay/hyperswitch-prism/main/llm/llm.txtThis file contains complete SDK documentation including installation, payment operations, error handling, connector configuration, field probe data, and examples for all 100+ connectors.
AI Assistant Context
This SDK is part of Hyperswitch Prism — a unified connector library for payment processors.
What This SDK Does
- Request Transformation: Converts unified payment requests to connector-specific formats
- Response Normalization: Transforms connector responses back to a unified schema
- Error Handling: Provides consistent error types (
IntegrationError,ConnectorError,NetworkError) regardless of connector
Architecture
Your Node.js App
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Service Clients (PaymentClient, CustomerClient, etc.) │
└───────────────────────────┬──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ ConnectorClient (undici connection pool + HTTP execution) │
└───────────────────────────┬──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ koffi FFI Bindings (connector-service-ffi.node) │
└───────────────────────────┬──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Rust Core (connector transformation logic) │
└───────────────────────────┬──────────────────────────────────┘
│
▼
Payment Processor APIsKey Files
| File | Purpose |
|------|---------|
| src/index.ts | Public API exports (clients, types, errors) |
| src/connector-client.ts | HTTP execution layer with undici |
| src/ffi/connector-service-ffi.ts | koffi FFI bindings |
| src/proto/payment_pb.ts | Protobuf message definitions |
Package & Import
- Package Name:
hyperswitch-prism - Installation:
npm install hyperswitch-prism - Import:
import { PaymentClient, types } from 'hyperswitch-prism'
Installation
npm install hyperswitch-prismRequirements:
- Node.js 18+ (LTS recommended)
- macOS (x64, arm64), Linux (x64, arm64), or Windows (x64)
Quick Start
import { PaymentClient, types } from 'hyperswitch-prism';
const config: types.ConnectorConfig = {
connectorConfig: {
// Configure your connector credentials here
// See connector documentation for specific auth patterns
}
};
const client = new PaymentClient(config);
const response = await client.authorize({
merchantTransactionId: 'txn_001',
amount: { minorAmount: 1000, currency: types.Currency.USD },
captureMethod: types.CaptureMethod.AUTOMATIC,
paymentMethod: {
card: {
cardNumber: { value: '4111111111111111' },
cardExpMonth: { value: '12' },
cardExpYear: { value: '2027' },
cardCvc: { value: '123' },
cardHolderName: { value: 'John Doe' },
}
},
address: { billingAddress: {} },
authType: types.AuthenticationType.NO_THREE_DS,
returnUrl: 'https://example.com/return',
orderDetails: [],
testMode: true,
});
console.log('Status:', response.status);
console.log('Transaction ID:', response.connectorTransactionId);Connector Authentication
Each connector uses a different authentication scheme. All configs are set inside connectorConfig as a single key matching the connector name.
See the SDK reference for complete connector authentication patterns:
curl -fsSL https://raw.githubusercontent.com/juspay/hyperswitch-prism/main/llm/llm.txtCommon authentication patterns include:
// Single API Key
{ connectorConfig: { [connectorName]: { apiKey: { value: '...' } } } }
// API Key + Merchant Account
{ connectorConfig: { [connectorName]: { apiKey: { value: '...' }, merchantAccount: { value: '...' } } } }
// Client ID + Secret (OAuth-style)
{ connectorConfig: { [connectorName]: { clientId: { value: '...' }, clientSecret: { value: '...' } } } }
// Username + Password
{ connectorConfig: { [connectorName]: { username: { value: '...' }, password: { value: '...' } } } }All Service Clients
import {
PaymentClient,
CustomerClient,
PaymentMethodClient,
MerchantAuthenticationClient,
PaymentMethodAuthenticationClient,
RecurringPaymentClient,
RefundClient,
DisputeClient,
PayoutClient,
EventClient,
GrpcPaymentClient,
GrpcCustomerClient,
types,
IntegrationError,
ConnectorError,
NetworkError,
} from 'hyperswitch-prism';| Client | Methods |
|--------|---------|
| PaymentClient | authorize(), capture(), refund(), void(), createOrder(), get(), sync(), incrementalAuthorization() |
| RefundClient | get(), createRefund(), updateRefund() |
| CustomerClient | create() |
| PaymentMethodClient | tokenize() |
| MerchantAuthenticationClient | createServerAuthenticationToken(), createClientAuthenticationToken(), createServerSessionAuthenticationToken() |
| PaymentMethodAuthenticationClient | preAuthenticate(), authenticate(), postAuthenticate() |
| RecurringPaymentClient | setup(), charge(), revoke() |
| DisputeClient | accept(), defend(), submitEvidence(), get() |
| PayoutClient | Payout operations |
| EventClient | handleEvent() (webhook processing) |
Payment Flows
Authorize with Auto Capture
const client = new PaymentClient(config);
const response = await client.authorize({
merchantTransactionId: 'txn_001',
amount: { minorAmount: 1000, currency: types.Currency.USD },
captureMethod: types.CaptureMethod.AUTOMATIC,
paymentMethod: {
card: {
cardNumber: { value: '4111111111111111' },
cardExpMonth: { value: '12' },
cardExpYear: { value: '2027' },
cardCvc: { value: '123' },
cardHolderName: { value: 'John Doe' },
}
},
address: { billingAddress: {} },
authType: types.AuthenticationType.NO_THREE_DS,
returnUrl: 'https://example.com/return',
orderDetails: [],
testMode: true,
});
// response.status === 8 (CHARGED) on successAuthorize + Manual Capture
// Step 1: Authorize only
const authResponse = await client.authorize({
// ...
captureMethod: types.CaptureMethod.MANUAL,
});
// authResponse.status === 6 (AUTHORIZED)
// Step 2: Capture later
const captureResponse = await client.capture({
merchantCaptureId: 'cap_001',
connectorTransactionId: authResponse.connectorTransactionId!,
amountToCapture: { minorAmount: 1000, currency: types.Currency.USD },
testMode: true,
});
// captureResponse.status === 8 (CHARGED) or 20 (PENDING) — both are successRefund
const refundResponse = await client.refund({
merchantRefundId: 'ref_001',
connectorTransactionId: authResponse.connectorTransactionId!,
refundAmount: { minorAmount: 500, currency: types.Currency.USD },
paymentAmount: 1000,
reason: 'RETURN',
testMode: true,
});
// refundResponse.status === 4 (REFUND_SUCCESS) or 3 (REFUND_PENDING) — both are successVoid (Cancel Authorization)
const voidResponse = await client.void({
merchantVoidId: 'void_001',
connectorTransactionId: authResponse.connectorTransactionId!,
cancellationReason: 'Customer cancelled',
testMode: true,
});
// voidResponse.status === 11 (VOIDED)Status Codes Reference
PaymentStatus
The response.status field is always a number, not a string:
// ❌ Always false — response.status is a number
if (response.status === 'CHARGED') { ... }
// ✅ Correct — compare against the numeric enum constant
if (response.status === types.PaymentStatus.CHARGED) { ... }Important: a FAILURE status is returned in the response body — it does NOT throw an exception. Always check response.status explicitly.
PaymentStatusandRefundStatusare two separate enums with overlapping integer values. Usetypes.PaymentStatusfor authorize/capture/void responses andtypes.RefundStatusfor refund responses.
| Name | Value | Meaning |
|------|-------|---------|
| PAYMENT_STATUS_UNSPECIFIED | 0 | Unknown |
| STARTED | 1 | Payment initiated |
| AUTHENTICATION_PENDING | 4 | Awaiting 3DS redirect |
| AUTHENTICATION_SUCCESSFUL | 5 | 3DS passed |
| AUTHENTICATION_FAILED | 2 | 3DS failed |
| AUTHORIZED | 6 | Auth succeeded, not yet captured |
| AUTHORIZATION_FAILED | 7 | Auth declined |
| CHARGED | 8 | Captured / auto-captured successfully |
| PARTIAL_CHARGED | 17 | Partially captured |
| CAPTURE_INITIATED | 13 | Async capture in progress |
| CAPTURE_FAILED | 14 | Capture failed |
| VOIDED | 11 | Authorization voided/cancelled |
| VOID_INITIATED | 12 | Async void in progress |
| VOID_FAILED | 15 | Void failed |
| PENDING | 20 | Processing / async |
| FAILURE | 21 | Soft decline — check response.error |
| ROUTER_DECLINED | 3 | Declined by routing layer |
| EXPIRED | 26 | Payment expired |
| PARTIALLY_AUTHORIZED | 25 | Partial authorization |
| UNRESOLVED | 19 | Requires manual review |
Checking status safely:
const response = await client.authorize(request);
if (response.status === types.PaymentStatus.FAILURE) {
console.error('Declined:', response.error?.message, response.error?.code);
} else if (response.status === types.PaymentStatus.CHARGED ||
response.status === types.PaymentStatus.AUTHORIZED) {
console.log('Success:', response.connectorTransactionId);
} else if (response.status === types.PaymentStatus.AUTHENTICATION_PENDING) {
console.log('Redirect to:', response.redirectionData);
}RefundStatus
| Name | Value | Meaning |
|------|-------|---------|
| REFUND_STATUS_UNSPECIFIED | 0 | Unknown |
| REFUND_FAILURE | 1 | Refund failed |
| REFUND_MANUAL_REVIEW | 2 | Pending manual review |
| REFUND_PENDING | 3 | Processing |
| REFUND_SUCCESS | 4 | Completed |
| REFUND_TRANSACTION_FAILURE | 5 | Transaction-level failure |
REFUND_PENDINGis a normal success state for many connectors. Treat bothREFUND_PENDINGandREFUND_SUCCESSas successful outcomes.
Error Handling
The SDK raises exceptions only for hard failures (network errors, invalid configuration, serialization errors). Soft payment declines come back as an in-band status: FAILURE in the response body.
import { IntegrationError, ConnectorError, NetworkError, types } from 'hyperswitch-prism';
try {
const response = await client.authorize(request);
if (response.status === types.PaymentStatus.FAILURE) {
console.error('Payment declined:', response.error?.message);
return;
}
} catch (error) {
if (error instanceof IntegrationError) {
// Request-phase error: bad config, missing required field, serialization failure
console.error('Integration error:', error.errorCode, error.message);
} else if (error instanceof ConnectorError) {
// Response-phase error: connector returned unexpected format, transform failed
console.error('Connector error:', error.errorCode, error.message);
} else if (error instanceof NetworkError) {
// Network-level: timeout, connection refused, DNS failure
console.error('Network error:', error.message);
}
}response.error is a Protobuf Object — Not JSON-Serializable
// ❌ Throws or produces empty object
res.json({ error: response.error });
JSON.stringify(response.error);
// ✅ Extract the primitive fields you need
res.json({
error: {
message: response.error?.message,
code: response.error?.code,
reason: response.error?.reason,
}
});Common Error Codes
| Code | Type | Cause | Fix |
|------|------|-------|-----|
| MISSING_REQUIRED_FIELD: browser_info | IntegrationError | Connector requires browserInfo | Add browserInfo to request |
| INVALID_CONFIGURATION | IntegrationError | Wrong credentials or missing required config field | Check connector config fields |
| CLIENT_INITIALIZATION | IntegrationError | SDK failed to initialize native library | Check platform compatibility |
| CONNECT_TIMEOUT | NetworkError | Could not reach connector | Check network / proxy config |
| RESPONSE_TIMEOUT | NetworkError | Connector took too long | Increase totalTimeoutMs |
| TOTAL_TIMEOUT | NetworkError | Request exceeded total timeout | Increase totalTimeoutMs |
Advanced Configuration
Timeouts
const client = new PaymentClient(config, {
http: {
totalTimeoutMs: 30000,
connectTimeoutMs: 10000,
responseTimeoutMs: 25000,
keepAliveTimeoutMs: 60000,
}
});Proxy
const client = new PaymentClient(config, {
http: {
proxy: {
httpsUrl: 'https://proxy.company.com:8443',
bypassUrls: ['http://localhost']
}
}
});Per-Request Overrides
const response = await client.authorize(request, {
http: { totalTimeoutMs: 60000 }
});Connection Pooling
Create the client once and reuse it:
// Good: create once, reuse
const client = new PaymentClient(config);
for (const payment of payments) {
await client.authorize(payment);
}CA Certificate Pinning
const client = new PaymentClient(config, {
http: {
caCert: fs.readFileSync('ca.pem', 'utf8')
}
});Building from Source
# Clone the repository
git clone https://github.com/juspay/hyperswitch-prism.git
cd hyperswitch-prism/sdk/javascript
# Build native library, generate bindings, and pack
make pack
# Run tests
make test-pack
# With live API credentials
STRIPE_API_KEY=sk_test_xxx make test-pack