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 APIs
Key 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