Observed since 20 Oct, 21:38 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
9japay-nestjs
A comprehensive NestJS SDK for integrating with 9jaPay Virtual Account APIs
A comprehensive NestJS SDK for integrating with 9jaPay Virtual Account APIs. This SDK provides a type-safe, easy-to-use interface for all 9jaPay API endpoints including virtual account management, transfers, and transaction handling.
npm install 9japay-nestjs
# or
yarn add 9japay-nestjs
import { Module } from '@nestjs/common';
import { NineJaPayModule } from '9japay-nestjs';
@Module({
imports: [
NineJaPayModule.register({
apiKey: 'your-api-key',
secretKey: 'your-secret-key',
environment: 'sandbox', // or 'production'
}),
],
})
export class AppModule {}
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { NineJaPayModule } from '9japay-nestjs';
@Module({
imports: [
ConfigModule.forRoot(),
NineJaPayModule.registerAsync({
useFactory: (configService: ConfigService) => ({
apiKey: configService.get('NINE_JA_PAY_API_KEY'),
secretKey: configService.get('NINE_JA_PAY_SECRET_KEY'),
environment: configService.get('NINE_JA_PAY_ENVIRONMENT'),
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}
import { Injectable } from '@nestjs/common';
import { NineJaPayService } from '9japay-nestjs';
@Injectable()
export class PaymentService {
constructor(private readonly nineJaPayService: NineJaPayService) {}
async createVirtualAccount(accountName: string) {
const response = await this.nineJaPayService.createPermanentVirtualAccount({
requestReference: `ref-${Date.now()}`,
accountName,
autoPayoutEnabled: true,
});
return response;
}
async makeTransfer(transferData: any) {
// First, validate the recipient account
const nameEnquiry = await this.nineJaPayService.nameEnquiry({
bankCode: transferData.bankCode,
accountNumber: transferData.accountNumber,
});
if (nameEnquiry.status === 'SUCCESS') {
// Proceed with transfer
const transfer = await this.nineJaPayService.transfer({
paymentReference: `pay-${Date.now()}`,
senderAccountNumber: transferData.senderAccountNumber,
senderAccountName: transferData.senderAccountName,
recipientAccountNumber: transferData.accountNumber,
recipientAccountName: nameEnquiry.data.accountName,
recipientBankCode: transferData.bankCode,
amount: transferData.amount.toString(),
nameEnquiryReference: nameEnquiry.data.nameEnquiryReference,
narration: transferData.narration,
});
return transfer;
}
}
}
// Create permanent virtual account
await nineJaPayService.createPermanentVirtualAccount({
requestReference: 'unique-ref-123',
accountName: 'John Doe Account',
autoPayoutEnabled: true,
});
// Update permanent virtual account
await nineJaPayService.updatePermanentVirtualAccount('9000001234', {
accountName: 'Updated Account Name',
blockStatus: false,
autoPayoutEnabled: false,
});
// Get single permanent virtual account
await nineJaPayService.getPermanentVirtualAccount('9000001234');
// Get all permanent virtual accounts
await nineJaPayService.getAllPermanentVirtualAccounts({
pageSize: 10,
pageNumber: 1,
});
// Close permanent virtual account
await nineJaPayService.closePermanentVirtualAccount({
requestReference: 'close-ref-123',
accountNumber: '9000001234',
reasonForClosure: 'Account no longer needed',
});
// Create transient virtual account
await nineJaPayService.createTransientVirtualAccount({
requestReference: 'temp-ref-123',
timeToLive: '3600', // 1 hour in seconds
amount: 100000, // Amount in kobo (optional)
isSinglePayment: true,
});
// Update transient virtual account
await nineJaPayService.updateTransientVirtualAccount('9000001234', {
blockStatus: false,
});
// Get single transient virtual account
await nineJaPayService.getTransientVirtualAccount('9000001234');
// Get all transient virtual accounts
await nineJaPayService.getAllTransientVirtualAccounts({
pageSize: 10,
pageNumber: 1,
});
// Get all virtual accounts (both permanent and transient)
await nineJaPayService.getAllVirtualAccounts({
pageSize: 20,
pageNumber: 1,
});
// Get single virtual account (regardless of type)
await nineJaPayService.getVirtualAccount('9000001234');
// Get account balance
await nineJaPayService.getAccountBalance('9000001234');
// Get total balance summary
await nineJaPayService.getTotalBalanceSummary();
// Get list of banks
const banks = await nineJaPayService.getBankList();
// Perform name enquiry
const nameEnquiry = await nineJaPayService.nameEnquiry({
bankCode: '057',
accountNumber: '1234567890',
});
// Transfer from regular account
await nineJaPayService.transfer({
paymentReference: 'pay-ref-123',
senderAccountNumber: '3000001234',
senderAccountName: 'Sender Name',
recipientAccountNumber: '1234567890',
recipientAccountName: 'Recipient Name',
recipientBankCode: '057',
amount: '100000', // Amount in kobo
nameEnquiryReference: nameEnquiry.data.nameEnquiryReference,
narration: 'Payment for services',
});
// Transfer from virtual account
await nineJaPayService.transferFromVirtualAccount({
// Same parameters as above
});
// Check transaction status
await nineJaPayService.getTransactionStatus('pay-ref-123');
// Get transactions with filters
await nineJaPayService.getTransactions({
pageSize: 10,
pageNumber: 1,
accountNumber: '9000001234', // optional
startDate: '2024-01-01', // optional
endDate: '2024-12-31', // optional
});
// Get single transaction by ID
await nineJaPayService.getTransactionById('transaction-id-123');
// Resend failed notification for a transaction
await nineJaPayService.resendNotification({
transactionId: 'transaction-id-123',
});
// Resend all failed notifications for an account
await nineJaPayService.resendNotificationsForAccount({
accountNumber: '9000001234',
});
// Resend all failed notifications
await nineJaPayService.resendAllNotifications();
// Simulate deposit (sandbox only)
await nineJaPayService.simulateDeposit({
recipientAccountNumber: '9000001234',
amount: '100000',
authKey: 'test-auth-key',
});
interface _9JaPayConfig {
apiKey: string; // Your 9jaPay API key
secretKey: string; // Your 9jaPay secret key
environment: 'sandbox' | 'production'; // Environment
baseUrl?: string; // Optional custom base URL
rejectUnauthorized?: boolean; // Optional: Set to false for local testing to ignore SSL cert errors (default: true)
}
For local testing environments where you might encounter SSL certificate issues, you can disable SSL verification:
NineJaPayModule.register({
apiKey: 'your-api-key',
secretKey: 'your-secret-key',
environment: 'sandbox',
rejectUnauthorized: false, // Disable SSL verification for local testing
})
⚠️ Security Warning: Only use rejectUnauthorized: false in development/testing environments. Never use this in production!
https://test.developer.9japay.com/v1/apihttps://developer.9japay.com/v1/apiThe SDK provides comprehensive error handling with two types of exceptions:
For 9jaPay API errors, the SDK throws NineJaPayException which properly maps 9jaPay status codes to HTTP status codes:
import { NineJaPayException } from '9japay-nestjs';
try {
const result = await nineJaPayService.createPermanentVirtualAccount({
requestReference: 'duplicate-ref',
accountName: 'Test Account',
autoPayoutEnabled: true,
});
} catch (error) {
if (error instanceof NineJaPayException) {
console.log('9jaPay Status Code:', error.nineJaPayStatusCode); // "26"
console.log('HTTP Status Code:', error.getStatus()); // 409
console.log('Message:', error.message); // "Duplicate record found."
console.log('Full Response:', error.nineJaPayResponse);
// Handle specific 9jaPay errors
switch (error.nineJaPayStatusCode) {
case '26':
// Handle duplicate record
console.log('Duplicate record detected');
break;
case '25':
// Handle no record found
console.log('Record not found');
break;
case '09':
// Handle validation error
console.log('Validation failed');
break;
default:
console.log('Other 9jaPay error');
}
}
}
For network errors or other issues:
try {
const result = await nineJaPayService.createPermanentVirtualAccount({
requestReference: 'ref-123',
accountName: 'Test Account',
autoPayoutEnabled: true,
});
} catch (error) {
console.error('Error:', error.message);
console.error('9jaPay Status Code:', error.nineJaPayStatusCode);
console.error('HTTP Status Code:', error.httpStatusCode);
console.error('Response:', error.response);
}
When using NineJaPayException, the error response structure is:
{
statusCode: 409, // HTTP status code (for proper REST API responses)
message: "Duplicate record found.",
nineJaPayStatusCode: "26", // 9jaPay's business logic status code
nineJaPayResponse: { // Full 9jaPay response
status: "FAILED",
message: "Duplicate record found.",
statusCode: "26"
}
}
The SDK maps 9jaPay status codes to appropriate HTTP status codes:
| 9jaPay Code | Description | HTTP Status | HTTP Code |
|---|---|---|---|
| 00 | Success | OK | 200 |
| 01 | Processing | Accepted | 202 |
| 06 | General error | Internal Server Error | 500 |
| 09 | Validation error | Bad Request | 400 |
| 25 | No record found | Not Found | 404 |
| 26 | Duplicate record | Conflict | 409 |
NineJaPayException for comprehensive error handlingnpm run build
npm test
npm run test:watch
npm run test:coverage
npm run lint
npm run lint:fix
This project is licensed under the MIT License - see the LICENSE file for details.
For support and questions:
NineJaPayException class with proper HTTP status code mappingERR_HTTP_INVALID_STATUS_CODEDownload counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
axiosEnvironment{
"local_name": "axios",
"alias": false,
"peer_metadata": null
} | ^1.6.0 | runtime |
jestEnvironment{
"local_name": "jest",
"alias": false,
"peer_metadata": null
} | ^29.0.0 | development |
rxjsEnvironment{
"local_name": "rxjs",
"alias": false,
"peer_metadata": null
} | ^7.0.0 | development |
eslintEnvironment{
"local_name": "eslint",
"alias": false,
"peer_metadata": null
} | ^8.0.0 | development |
ts-jestEnvironment{
"local_name": "ts-jest",
"alias": false,
"peer_metadata": null
} | ^29.0.0 | development |
typescriptEnvironment{
"local_name": "typescript",
"alias": false,
"peer_metadata": null
} | ^5.0.0 | development |
@types/jestEnvironment{
"local_name": "@types/jest",
"alias": false,
"peer_metadata": null
} | ^29.0.0 | development |
@types/nodeEnvironment{
"local_name": "@types/node",
"alias": false,
"peer_metadata": null
} | ^20.0.0 | development |
@nestjs/coreEnvironment{
"local_name": "@nestjs/core",
"alias": false,
"peer_metadata": null
} | ^10.0.0 | development |
@nestjs/commonEnvironment{
"local_name": "@nestjs/common",
"alias": false,
"peer_metadata": null
} | ^10.0.0 | development |
@nestjs/testingEnvironment{
"local_name": "@nestjs/testing",
"alias": false,
"peer_metadata": null
} | ^10.0.0 | development |
reflect-metadataEnvironment{
"local_name": "reflect-metadata",
"alias": false,
"peer_metadata": null
} | ^0.1.13 | development |
@typescript-eslint/parserEnvironment{
"local_name": "@typescript-eslint/parser",
"alias": false,
"peer_metadata": null
} | ^6.0.0 | development |
@typescript-eslint/eslint-pluginEnvironment{
"local_name": "@typescript-eslint/eslint-plugin",
"alias": false,
"peer_metadata": null
} | ^6.0.0 | development |
rxjsEnvironment{
"local_name": "rxjs",
"alias": false,
"peer_metadata": null
} | ^7.0.0 | peer |
@nestjs/coreEnvironment{
"local_name": "@nestjs/core",
"alias": false,
"peer_metadata": null
} | ^10.0.0 | peer |
@nestjs/commonEnvironment{
"local_name": "@nestjs/common",
"alias": false,
"peer_metadata": null
} | ^10.0.0 | peer |
Source distribution
{
"shasum": "0f7fba4023a584fab2cb4a711df28ba94d1ec2e8",
"tarball": "https://registry.npmjs.org/9japay-nestjs/-/9japay-nestjs-1.0.2.tgz",
"fileCount": 26,
"integrity": "sha512-BwHe/C2n8D90LNsNzQK6WXv5O30Q5vRhOYkVkAhU1hI3+Z9Tws5FQYZkMV8vp0v1HwJn3tZPssp9NWKtasi7pA==",
"signatures": [
{
"sig": "MEQCIDiArk5GPnfTnWBc0/NslgLVe9atn6Ivd/zsQqgS+D+SAiBsxDnGOpUsGU9HXXdAYfb5bJtAoJlWj3SALBeN/t0HzA==",
"keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"
}
],
"unpackedSize": 66749
}Observed since 20 Oct, 21:38 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:73aab1f8a6c680164c67f9fabf1afefdc4078bd7cee6039b5e00a6d2b31e2577