npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

9japay-nestjs

v1.1.0

Published

A comprehensive NestJS SDK for integrating with 9jaPay Virtual Account APIs

Readme

9jaPay NestJS SDK

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.

Features

  • 🚀 Full API Coverage: Complete implementation of all 9jaPay API endpoints
  • 🛡️ Type Safety: Full TypeScript support with comprehensive type definitions
  • 🏗️ NestJS Integration: Native NestJS module with dependency injection support
  • 🌍 Multi-Environment: Support for both sandbox and production environments
  • 📚 Well Documented: Comprehensive documentation and examples
  • Tested: Unit tests included
  • 🔧 Configurable: Flexible configuration options

Installation

npm install 9japay-nestjs
# or
yarn add 9japay-nestjs

Quick Start

1. Module Registration

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 {}

2. Async Configuration

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 {}

3. Using the Service

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;
    }
  }
}

API Reference

Virtual Account Management

Permanent Virtual Accounts

// 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',
});

Transient Virtual Accounts

// 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,
});

General Virtual Account Operations

// 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();

Transfers

// 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');

Transactions

// 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',
});

Configuration Options

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)
}

Local Testing Configuration

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!

Environment URLs

  • Sandbox: https://test.developer.9japay.com/v1/api
  • Production: https://developer.9japay.com/v1/api

Error Handling

The SDK provides comprehensive error handling with two types of exceptions:

1. NineJaPayException (Recommended)

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');
    }
  }
}

2. Standard _9JaPayError

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);
}

Error Response Structure

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"
  }
}

Response Codes

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 |

Status Code Usage

  • 9jaPay Code: Used for business logic and specific error handling
  • HTTP Status: Used for REST API responses and proper HTTP semantics
  • Both codes are available in the NineJaPayException for comprehensive error handling

Development

Building the Package

npm run build

Running Tests

npm test
npm run test:watch
npm run test:coverage

Linting

npm run lint
npm run lint:fix

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Run the test suite
  6. Submit a pull request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

For support and questions:

Changelog

v1.0.2

  • Enhanced Error Handling: Added NineJaPayException class with proper HTTP status code mapping
  • Fixed Status Code Conflict: Separated 9jaPay status codes from HTTP status codes to prevent ERR_HTTP_INVALID_STATUS_CODE
  • Improved Error Structure: Better error response format with both business logic and HTTP codes
  • Status Code Mapping: Automatic mapping of 9jaPay codes (e.g., "26") to appropriate HTTP codes (e.g., 409)

v1.0.1

  • Global Module: Made module global to resolve dependency injection issues
  • Fix: Service now available throughout application without additional imports

v1.0.0

  • Initial release
  • Complete API coverage
  • TypeScript support
  • NestJS integration
  • Unit tests
  • Comprehensive documentation