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

@rikology/adonisjs-xendit

v1.0.0

Published

AdonisJS v7 package for Xendit payment gateway integration

Downloads

80

Readme

@rikology/adonisjs-xendit

npm version License: MIT Node.js Version AdonisJS Version

AdonisJS v7 package for Xendit payment gateway integration. Provides a typed, idiomatic wrapper around Xendit's API with support for invoices, virtual accounts, e-wallets, QRIS, retail outlets, credit cards, direct debit, disbursements, and balance queries.

Features

  • 9 Payment Products — Invoice, Virtual Account, E-Wallet, QRIS, Retail Outlet, Credit Card, Direct Debit, Disbursement, Balance
  • Type-safe — Full TypeScript types for all request/response shapes
  • IoC Container Integrationxendit.manager singleton registered via provider
  • Webhook Helpers — Built-in webhook payload parsing with callback token verification
  • Configurablenode ace configure prompts for API key and generates config
  • Error Handling — Custom XenditException with structured error responses
  • Idempotency Support — Optional idempotency keys for safe retries
  • Production Ready — Comprehensive test suite with >80% coverage
  • Retry Logic — Automatic retry with exponential backoff for failed requests

Table of Contents

Installation

npm install @rikology/adonisjs-xendit
# or
yarn add @rikology/adonisjs-xendit
# or
pnpm add @rikology/adonisjs-xendit

Then configure the package:

node ace configure @rikology/adonisjs-xendit

The configure command will:

  • Prompt for your Xendit secret key
  • Generate config/xendit.ts
  • Add XENDIT_SECRET_KEY, XENDIT_ENVIRONMENT, and XENDIT_CALLBACK_TOKEN to .env
  • Register the provider in adonisrc.ts

Configuration

// config/xendit.ts
import { defineConfig } from '@rikology/adonisjs-xendit'
import env from '#start/env'

export default defineConfig({
  secretKey: env.get('XENDIT_SECRET_KEY'),
  environment: env.get('XENDIT_ENVIRONMENT', 'sandbox'),
  callbackToken: env.get('XENDIT_CALLBACK_TOKEN'),
  timeoutMs: 30_000,
})

| Option | Required | Description | |--------|----------|-------------| | secretKey | Yes | Your Xendit API secret key | | environment | Yes | sandbox or production | | callbackToken | No | Token for webhook callback verification | | timeoutMs | No | HTTP request timeout (default: 30000ms) |

Usage

Via IoC Container

import { inject } from '@adonisjs/core'
import type { XenditManager } from '@rikology/adonisjs-xendit'

@inject()
export default class PaymentsController {
  constructor(private xendit: XenditManager) {}

  async createInvoice() {
    const invoice = await this.xendit.invoice().create({
      external_id: 'order-123',
      amount: 50000,
      description: 'Test invoice',
      invoice_duration: 86400,
    })

    return invoice
  }
}

Available Clients

const manager = new XenditManager(config)

manager.invoice()        // InvoiceClient
manager.va()             // VirtualAccountClient
manager.ewallet()        // EWalletClient
manager.qris()           // QrisClient
manager.retailOutlet()   // RetailOutletClient
manager.creditCard()     // CreditCardClient
manager.directDebit()    // DirectDebitClient
manager.disbursement()   // DisbursementClient
manager.balance()        // BalanceClient

Invoice

Create and manage invoices for one-time payments.

// Create an invoice
const invoice = await xendit.invoice().create({
  external_id: 'order-123',
  amount: 50000,
  description: 'Test invoice',
  invoice_duration: 86400,
  payer_email: '[email protected]',
  customer: {
    given_names: 'John',
    surname: 'Doe',
    email: '[email protected]',
    mobile_number: '+6281234567890',
  },
  items: [
    { name: 'Product A', quantity: 1, price: 50000 },
  ],
  success_redirect_url: 'https://example.com/success',
  failure_redirect_url: 'https://example.com/failure',
})

// Get invoice by ID
const invoice = await xendit.invoice().getById('inv_123')

// List invoices with filters
const invoices = await xendit.invoice().list({
  status: 'PAID',
  limit: 10,
})

// Expire an invoice
const expired = await xendit.invoice().expire('inv_123')

Virtual Account

Create virtual accounts for bank transfers.

// Create a virtual account
const va = await xendit.va().create({
  external_id: 'va-123',
  bank_code: 'BCA',
  name: 'John Doe',
  is_closed: true,
  expected_amount: 100000,
  is_single_use: true,
})

// Get VA by ID
const va = await xendit.va().getById('va_123')

// Update VA
const updated = await xendit.va().update('va_123', {
  name: 'Jane Doe',
  expected_amount: 150000,
})

// Get VA payments
const payments = await xendit.va().getPayment('va_123')

Supported banks: BCA, BNI, BRI, MANDIRI, PERMATA, CIMB, BSI, and more.

E-Wallet

Process payments through popular e-wallets.

// Create OVO payment
const charge = await xendit.ewallet().createOvo({
  reference_id: 'order-123',
  currency: 'IDR',
  amount: 50000,
  channel_code: 'ID_OVO',
  channel_properties: {
    mobile_number: '+6281234567890',
  },
})

// Create DANA payment
const charge = await xendit.ewallet().createDana({
  reference_id: 'order-124',
  currency: 'IDR',
  amount: 75000,
})

// Create LinkAja payment
const charge = await xendit.ewallet().createLinkAja({
  reference_id: 'order-125',
  currency: 'IDR',
  amount: 100000,
})

// Create ShopeePay payment
const charge = await xendit.ewallet().createShopeepay({
  reference_id: 'order-126',
  currency: 'IDR',
  amount: 25000,
})

// Check payment status
const status = await xendit.ewallet().getStatus('ewc_123')

QRIS

Generate QR codes for QRIS payments.

// Create dynamic QR code
const qr = await xendit.qris().create({
  external_id: 'order-123',
  type: 'DYNAMIC',
  amount: 50000,
  currency: 'IDR',
})

// Create static QR code
const qr = await xendit.qris().create({
  external_id: 'store-123',
  type: 'STATIC',
  currency: 'IDR',
})

// Get QR code by ID
const qr = await xendit.qris().getById('qr_123')

// Simulate payment (sandbox only)
const payment = await xendit.qris().simulate('qr_123', { amount: 50000 })

Retail Outlet

Create payments through retail outlets like Alfamart and Indomaret.

// Create retail outlet payment
const payment = await xendit.retailOutlet().create({
  external_id: 'order-123',
  retail_outlet_name: 'ALFAMART',
  name: 'John Doe',
  expected_amount: 50000,
  is_single_use: true,
})

// Get payment by ID
const payment = await xendit.retailOutlet().getById('ro_123')

// Update payment
const updated = await xendit.retailOutlet().update('ro_123', {
  name: 'Jane Doe',
  expected_amount: 75000,
})

Supported outlets: ALFAMART, ALFAMIDI, INDOMARET, and more.

Credit Card

Process credit card payments.

// Create authorization
const auth = await xendit.creditCard().createAuthorization({
  token_id: 'token_123',
  external_id: 'order-123',
  amount: 100000,
})

// Create charge
const charge = await xendit.creditCard().createCharge({
  token_id: 'token_123',
  external_id: 'order-123',
  amount: 100000,
  capture: true,
})

// Create refund
const refund = await xendit.creditCard().createRefund('charge_123', {
  external_id: 'refund-123',
  amount: 50000,
})

// Get charge details
const charge = await xendit.creditCard().getCharge('charge_123')

Direct Debit

Process direct debit payments.

// Create payment method
const method = await xendit.directDebit().createPaymentMethod({
  customer_id: 'cust_123',
  channel_code: 'BRI',
  properties: {
    account_number: '1234567890',
  },
})

// Validate OTP
const validated = await xendit.directDebit().validateOTP('pm_123', {
  otp_code: '123456',
})

// Create payment
const payment = await xendit.directDebit().createPayment({
  reference_id: 'order-123',
  payment_method_id: 'pm_123',
  currency: 'IDR',
  amount: 100000,
})

// Get payment details
const payment = await xendit.directDebit().getPayment('pay_123')

Disbursement

Send money to bank accounts and e-wallets.

// Create disbursement
const disbursement = await xendit.disbursement().create({
  external_id: 'disb-123',
  amount: 100000,
  bank_code: 'BCA',
  account_holder_name: 'John Doe',
  account_number: '1234567890',
  description: 'Payout for order #123',
})

// Get disbursement by ID
const disbursement = await xendit.disbursement().getById('disb_123')

// Create batch disbursement
const batch = await xendit.disbursement().createBatch({
  reference: 'batch-123',
  disbursements: [
    {
      external_id: 'disb-1',
      amount: 50000,
      bank_code: 'BCA',
      account_holder_name: 'Alice',
      account_number: '1111111111',
    },
    {
      external_id: 'disb-2',
      amount: 50000,
      bank_code: 'BNI',
      account_holder_name: 'Bob',
      account_number: '2222222222',
    },
  ],
})

// Get available banks
const banks = await xendit.disbursement().getAvailableBanks()

Balance

Check your Xendit account balance.

// Get total balance
const balance = await xendit.balance().get()

// Get balance by account type
const cashBalance = await xendit.balance().getByAccountType('CASH')
const holdingBalance = await xendit.balance().getByAccountType('HOLDING')
const taxBalance = await xendit.balance().getByAccountType('TAX')

Webhook Handling

import { XenditWebhook } from '@rikology/adonisjs-xendit'

export default class WebhooksController {
  async handle(req: HttpContext) {
    const callbackToken = process.env.XENDIT_CALLBACK_TOKEN!
    const signature = req.request.header('x-callback-token')!
    const payload = JSON.stringify(req.request.all())

    // Verify callback token
    if (!XenditWebhook.verify(payload, callbackToken, signature)) {
      return req.response.forbidden('Invalid callback token')
    }

    // Parse and validate payload
    const event = XenditWebhook.parseEvent(payload)

    // Process based on event type
    switch (event.event) {
      case 'invoice.paid':
        // Handle paid invoice
        console.log('Invoice paid:', event.data)
        break
      case 'invoice.expired':
        // Handle expired invoice
        console.log('Invoice expired:', event.data)
        break
      case 'va.paid':
        // Handle VA payment
        console.log('VA paid:', event.data)
        break
      case 'disbursement.completed':
        // Handle completed disbursement
        console.log('Disbursement completed:', event.data)
        break
      default:
        console.log('Unhandled event:', event.event)
    }
  }
}

Error Handling

import { XenditException } from '@rikology/adonisjs-xendit'

try {
  await xendit.invoice().create({ ... })
} catch (error) {
  if (error instanceof XenditException) {
    console.log(error.code)      // XENDIT_API_ERROR
    console.log(error.status)    // HTTP status code
    console.log(error.message)   // Error message
    console.log(error.rawResponse)  // Full error response body
  }
}

Error Types

| Error Class | Status Code | Description | |-------------|-------------|-------------| | XenditValidationError | 400 | Invalid request parameters | | XenditAuthenticationError | 401 | Invalid API key | | XenditNotFoundError | 404 | Resource not found | | XenditConflictError | 409 | Duplicate external ID | | XenditRateLimitError | 429 | Rate limit exceeded | | XenditServerError | 500 | Xendit server error | | XenditNetworkError | 0 | Network/timeout error |

Exported Types

import type {
  XenditConfig,
  CreateInvoiceRequest,
  Invoice,
  CreateVirtualAccountRequest,
  VirtualAccount,
  CreateEWalletChargeRequest,
  EWalletCharge,
  CreateQRCodeRequest,
  QRCode,
  CreateRetailOutletRequest,
  RetailOutlet,
  CreateCreditCardChargeRequest,
  CreditCardCharge,
  CreateDirectDebitPaymentRequest,
  DirectDebitPayment,
  CreateDisbursementRequest,
  Disbursement,
  Balance,
  // ... and more
} from '@rikology/adonisjs-xendit'

See src/types.ts for the complete type definitions.

Requirements

  • Node.js >= 20.0.0
  • AdonisJS v7

Changelog

See CHANGELOG.md for a history of changes.

Contributing

Contributions are welcome! Please read our Contributing Guide for details on how to get started.

Security

If you discover any security-related issues, please email [email protected] instead of using the issue tracker. See our Security Policy for more details.

Support

License

MIT