Xendit Module
The xendit module provides comprehensive Xendit payment gateway functionality for Indonesian market including withdrawals, virtual accounts, reconciliation, and webhook management.
Import
const xendit = await brdzSDK.xendit;
Methods Overview
| Method | Description | Auth Required | HTTP Endpoint |
|---|---|---|---|
processWalletWithdraw | Process bank withdrawal | ✅ | POST /xendit/wallet/withdraw |
getWithdrawStatus | Get withdrawal status | ✅ | GET /xendit/wallet/withdraw/status/:reference_id |
requestFixedVA | Request Fixed VA | ✅ | POST /xendit/fixed-va/request |
getFixedVAList | Get Fixed VA list | ✅ | GET /xendit/fixed-va/list/:wallet_id |
getBalance | Get account balance | ✅ Admin | GET /xendit/balance |
getAllBalances | Get all balances | ✅ Admin | GET /xendit/balance/all |
listTransactions | List transactions | ✅ Admin | GET /xendit/transactions |
getTransaction | Get transaction details | ✅ Admin | GET /xendit/transactions/:transaction_id |
getTransactionCount | Count transactions | ✅ Admin | GET /xendit/transactions/count |
generateBalanceReport | Generate balance report | ✅ Admin | GET /xendit/reports/balance |
generateTransactionReport | Generate transaction report | ✅ Admin | GET /xendit/reports/transactions |
runDailyReconciliation | Run reconciliation | ✅ Admin | POST /xendit/reconciliation/run |
reconcileWalletBalance | Reconcile wallet | ✅ | POST /xendit/reconciliation/wallet/:wallet_id |
resolveReconciliation | Resolve discrepancy | ✅ Admin | POST /xendit/reconciliation/resolve |
getReconciliationDetails | Get reconciliation details | ✅ | GET /xendit/reconciliation/:reconciliation_id |
processWebhook | Process webhook | Public | POST /xendit/webhook |
processDLQ | Process DLQ | ✅ Admin | POST /xendit/webhook/dlq/process |
getWebhookStats | Get webhook statistics | ✅ Admin | GET /xendit/webhook/stats |
healthCheck | Health check | Public | GET /xendit/health |
validateConfiguration | Validate config | ✅ Admin | GET /xendit/config/validate |
getDashboardData | Get dashboard data | ✅ Admin | GET /xendit/admin/dashboard |
Withdrawal Operations
processWalletWithdraw
Process withdrawal from wallet to Indonesian bank account with automatic ledger integration.
const withdrawal = await brdzSDK.xendit.processWalletWithdraw({
wallet_id: "wallet_456789",
amount: 500000,
currency: "IDR",
bank_code: "BCA",
account_number: "1234567890",
account_holder_name: "John Doe",
description: "Withdrawal to personal account",
user_email: "john@example.com"
});
console.log(`Payout ID: ${withdrawal.data.payout_id}`);
console.log(`Reference: ${withdrawal.data.reference_id}`);
console.log(`Status: ${withdrawal.data.status}`);
Parameters:
wallet_id(string, required): Wallet ID to withdraw fromamount(number, required): Withdrawal amount in IDRcurrency(string, optional): Currency code (default: IDR)bank_code(string, required): Bank code (BCA, BNI, BRI, MANDIRI, etc.)account_number(string, required): Bank account numberaccount_holder_name(string, required): Account holder namedescription(string, optional): Withdrawal descriptionuser_email(string, optional): User email for notifications
Returns: Withdrawal initiation response with payout details
getWithdrawStatus
Check the current status of a withdrawal transaction.
const status = await brdzSDK.xendit.getWithdrawStatus("WALLET-PAYOUT-456789-1705123456");
console.log(`Status: ${status.data.status}`);
console.log(`Amount: ${status.data.amount} ${status.data.currency}`);
if (status.data.status === 'COMPLETED') {
console.log(`Completed at: ${status.data.completed_at}`);
}
Parameters:
reference_id(string, required): Withdrawal reference ID
Returns: Current withdrawal status with transfer details
Fixed Virtual Account Operations
requestFixedVA
Request a Fixed Virtual Account for wallet top-up.
const fixedVA = await brdzSDK.xendit.requestFixedVA({
wallet_id: "wallet_456789",
bank_code: "BCA",
is_single_use: false
});
console.log(`VA Number: ${fixedVA.data.va_number}`);
console.log(`Bank: ${fixedVA.data.bank_name}`);
console.log(`Status: ${fixedVA.data.status}`);
Parameters:
wallet_id(string, required): Wallet ID to link VA tobank_code(string, required): Bank code (BCA, BNI, BRI, MANDIRI, PERMATA, CIMB)amount(number, optional): Expected amount for single-use VAis_single_use(boolean, optional): Single-use VA flag (default: false)
Returns: Fixed VA creation response with VA number
getFixedVAList
Retrieve all Fixed Virtual Accounts for a wallet.
const vaList = await brdzSDK.xendit.getFixedVAList("wallet_456789");
console.log(`Total VAs: ${vaList.data.total_vas}`);
vaList.data.fixed_vas.forEach(va => {
console.log(`${va.bank_name}: ${va.va_number}`);
console.log(` Status: ${va.status}`);
});
Parameters:
wallet_id(string, required): Wallet ID to get VAs for
Returns: List of Fixed VAs with bank details
Balance Operations
getBalance
Get Xendit account balance for a specific account type.
// Get CASH balance (default)
const cashBalance = await brdzSDK.xendit.getBalance();
console.log(`CASH: IDR ${cashBalance.data.balance.toLocaleString()}`);
// Get HOLDING balance
const holdingBalance = await brdzSDK.xendit.getBalance({
account_type: 'HOLDING'
});
console.log(`HOLDING: IDR ${holdingBalance.data.balance.toLocaleString()}`);
Parameters:
params(object, optional): Query parametersaccount_type(string): CASH, HOLDING, or TAX (default: CASH)
Returns: Account balance information
getAllBalances
Get balances for all Xendit account types in a single request.
const allBalances = await brdzSDK.xendit.getAllBalances();
console.log(`Total: IDR ${allBalances.data.total_balance.toLocaleString()}`);
allBalances.data.balances.forEach(account => {
console.log(`${account.account_type}: IDR ${account.balance.toLocaleString()}`);
});
Returns: All account balances with total
Transaction Operations
listTransactions
List Xendit transactions with pagination and filtering.
const transactions = await brdzSDK.xendit.listTransactions({
limit: 20,
created_gte: '2024-01-01T00:00:00Z',
types: 'DISBURSEMENT',
statuses: 'COMPLETED'
});
console.log(`Found ${transactions.data.data.length} transactions`);
console.log(`Has more: ${transactions.data.has_more}`);
transactions.data.data.forEach(txn => {
console.log(`${txn.type}: ${txn.amount} - ${txn.status}`);
});
Parameters:
params(object, optional): Filter parameterscreated_gte(string): Start date (ISO 8601)created_lte(string): End date (ISO 8601)limit(number): Results per page (default: 50, max: 100)after_id(string): Pagination cursortypes(string): Comma-separated transaction typesstatuses(string): Comma-separated status values
Returns: Paginated transaction list
getTransaction
Get detailed information for a specific transaction.
const transaction = await brdzSDK.xendit.getTransaction("txn_1234567890");
console.log(`Type: ${transaction.data.type}`);
console.log(`Amount: ${transaction.data.amount} ${transaction.data.currency}`);
console.log(`Status: ${transaction.data.status}`);
console.log(`Reference: ${transaction.data.reference_id}`);
Parameters:
transaction_id(string, required): Transaction ID to retrieve
Returns: Transaction details
getTransactionCount
Get the total count of transactions matching filters.
const count = await brdzSDK.xendit.getTransactionCount({
created_gte: '2024-01-01T00:00:00Z',
types: 'DISBURSEMENT',
statuses: 'COMPLETED'
});
console.log(`Total completed disbursements: ${count.data.count}`);
Parameters:
params(object, optional): Filter parameterscreated_gte(string): Start datecreated_lte(string): End datetypes(string): Transaction typesstatuses(string): Status values
Returns: Transaction count with applied filters
Report Operations
generateBalanceReport
Generate a comprehensive balance report for a date range.
const report = await brdzSDK.xendit.generateBalanceReport({
start_date: '2024-01-01T00:00:00Z',
end_date: '2024-01-31T23:59:59Z',
format: 'json'
});
console.log(`Opening: IDR ${report.data.balances.opening_balance.toLocaleString()}`);
console.log(`Closing: IDR ${report.data.balances.closing_balance.toLocaleString()}`);
console.log(`Inflow: IDR ${report.data.balances.total_inflow.toLocaleString()}`);
console.log(`Outflow: IDR ${report.data.balances.total_outflow.toLocaleString()}`);
Parameters:
params(object, optional): Report parametersstart_date(string): Report start date (ISO 8601)end_date(string): Report end date (ISO 8601)format(string): json, csv, or pdf (default: json)
Returns: Balance report with breakdown
generateTransactionReport
Generate a detailed transaction report with filtering.
const report = await brdzSDK.xendit.generateTransactionReport({
start_date: '2024-01-01T00:00:00Z',
end_date: '2024-01-31T23:59:59Z',
transaction_types: 'DISBURSEMENT,VA_PAYMENT',
format: 'json'
});
console.log(`Total Transactions: ${report.data.summary.total_transactions}`);
console.log(`Total Amount: IDR ${report.data.summary.total_amount.toLocaleString()}`);
console.log(`Success Rate: ${(report.data.summary.successful_transactions / report.data.summary.total_transactions * 100).toFixed(2)}%`);
Parameters:
params(object, optional): Report parametersstart_date(string): Report start dateend_date(string): Report end datetransaction_types(string): Transaction types filterformat(string): json, csv, or pdf
Returns: Transaction report with statistics
Reconciliation Operations
runDailyReconciliation
Run automated daily reconciliation to match internal and Xendit balances.
const reconciliation = await brdzSDK.xendit.runDailyReconciliation({
date: '2024-01-15',
force_rerun: false
});
console.log(`Reconciliation ID: ${reconciliation.data.reconciliation_id}`);
console.log(`Wallets Checked: ${reconciliation.data.total_wallets_checked}`);
console.log(`Matched: ${reconciliation.data.matched_wallets}`);
console.log(`Discrepancies: ${reconciliation.data.discrepancies_found}`);
if (reconciliation.data.discrepancies_found > 0) {
console.log(`Total Discrepancy: IDR ${reconciliation.data.total_discrepancy_amount.toLocaleString()}`);
}
Parameters:
data(object, optional): Reconciliation parametersdate(string): Reconciliation date (default: today)force_rerun(boolean): Force rerun flag (default: false)
Returns: Reconciliation results with discrepancies
reconcileWalletBalance
Run reconciliation for a specific wallet.
const result = await brdzSDK.xendit.reconcileWalletBalance("wallet_456789", {
force_update: false
});
console.log(`Internal Balance: IDR ${result.data.internal_balance.toLocaleString()}`);
console.log(`Xendit Balance: IDR ${result.data.xendit_balance.toLocaleString()}`);
console.log(`Matched: ${result.data.is_matched}`);
if (!result.data.is_matched) {
console.log(`Discrepancy: IDR ${result.data.discrepancy_amount.toLocaleString()}`);
}
Parameters:
wallet_id(string, required): Wallet ID to reconciledata(object, optional): Reconciliation optionsforce_update(boolean): Force balance update (default: false)
Returns: Wallet reconciliation result
resolveReconciliation
Manually resolve a reconciliation discrepancy.
const resolution = await brdzSDK.xendit.resolveReconciliation({
reconciliation_id: 123,
resolved_by: 1,
resolution_notes: "Verified with Xendit support - pending transaction settlement",
manual_adjustment_amount: 0
});
console.log(`Status: ${resolution.data.status}`);
console.log(`Resolved at: ${resolution.data.resolved_at}`);
Parameters:
data(object, required): Resolution datareconciliation_id(number): Reconciliation IDresolved_by(number): User ID resolvingresolution_notes(string): Resolution explanationmanual_adjustment_amount(number, optional): Adjustment amount (default: 0)
Returns: Resolution confirmation
getReconciliationDetails
Get detailed information about a reconciliation.
const details = await brdzSDK.xendit.getReconciliationDetails("123");
console.log(`Date: ${details.data.date}`);
console.log(`Status: ${details.data.status}`);
console.log(`Discrepancies: ${details.data.discrepancies_found}`);
details.data.discrepancies.forEach(disc => {
console.log(`\nWallet: ${disc.wallet_id}`);
console.log(` Difference: IDR ${disc.discrepancy.toLocaleString()}`);
});
Parameters:
reconciliation_id(string, required): Reconciliation ID
Returns: Reconciliation details with discrepancy list
Webhook Operations
processWebhook
Process incoming webhook from Xendit servers.
// Note: This is typically called by Xendit servers, not client applications
const result = await brdzSDK.xendit.processWebhook({
event: "virtual_account.paid",
data: {
payment_id: "pay_1234567890",
external_id: "WALLET-TOPUP-456789",
account_number: "1234567890123456",
bank_code: "BCA",
amount: 1000000,
currency: "IDR",
status: "COMPLETED"
}
});
console.log(`Webhook ID: ${result.webhook_id}`);
Parameters:
data(object, required): Webhook payload from Xenditevent(string): Webhook event typedata(object): Event data
Returns: Webhook processing confirmation
processDLQ
Manually process failed webhooks from dead letter queue.
const result = await brdzSDK.xendit.processDLQ();
console.log(`Total Processed: ${result.data.total_processed}`);
console.log(`Successful: ${result.data.successful}`);
console.log(`Failed: ${result.data.failed}`);
console.log(`Remaining: ${result.data.remaining_in_queue}`);
Returns: DLQ processing results
getWebhookStats
Get webhook processing statistics.
const stats = await brdzSDK.xendit.getWebhookStats({
start_date: '2024-01-01T00:00:00Z',
end_date: '2024-01-31T23:59:59Z'
});
console.log(`Total Webhooks: ${stats.data.total_webhooks}`);
console.log(`Success Rate: ${(stats.data.success_rate * 100).toFixed(2)}%`);
console.log(`DLQ Count: ${stats.data.dlq_count}`);
stats.data.by_event_type.forEach(event => {
console.log(`${event.event}: ${event.count} (${(event.success_rate * 100).toFixed(2)}%)`);
});
Parameters:
params(object, optional): Statistics parametersstart_date(string): Start date (ISO 8601)end_date(string): End date (ISO 8601)
Returns: Webhook statistics with event breakdown
Administrative Operations
healthCheck
Perform health check for Xendit integration.
const health = await brdzSDK.xendit.healthCheck();
console.log(`Service: ${health.data.service}`);
console.log(`Status: ${health.data.status}`);
Object.entries(health.data.checks).forEach(([check, result]) => {
console.log(`${check}: ${result.status}`);
if (result.issues && result.issues.length > 0) {
console.log(` Issues: ${result.issues.join(', ')}`);
}
});
Returns: Health check results with component status
validateConfiguration
Validate Xendit API configuration.
const validation = await brdzSDK.xendit.validateConfiguration();
console.log(`Valid: ${validation.data.valid}`);
if (!validation.data.valid) {
console.error('Configuration Issues:');
validation.data.issues.forEach(issue => {
console.error(` - ${issue}`);
});
}
Returns: Configuration validation result
getDashboardData
Get comprehensive dashboard data for monitoring.
const dashboard = await brdzSDK.xendit.getDashboardData();
// Balances
console.log('=== BALANCES ===');
console.log(`Total: IDR ${dashboard.data.balances.total_balance.toLocaleString()}`);
// Webhook Stats
console.log('\n=== WEBHOOKS ===');
console.log(`Total: ${dashboard.data.webhook_stats.total_webhooks}`);
console.log(`Success Rate: ${(dashboard.data.webhook_stats.success_rate * 100).toFixed(2)}%`);
// Connectivity
console.log('\n=== CONNECTIVITY ===');
console.log(`Status: ${dashboard.data.connectivity.success ? 'Connected' : 'Failed'}`);
Returns: Dashboard data with balances, webhooks, and connectivity
Complete Usage Examples
Basic Xendit Setup
// Configure SDK
const config = await brdzSDK.config;
config.setApiKey('your-api-key');
config.setToken('your-jwt-token');
// Check Xendit health
const health = await brdzSDK.xendit.healthCheck();
if (health.data.status === 'healthy') {
console.log('Xendit integration ready');
// Check balance
const balance = await brdzSDK.xendit.getBalance();
console.log(`Available balance: IDR ${balance.data.balance.toLocaleString()}`);
} else {
console.error('Xendit integration has issues');
}
Complete Withdrawal Workflow
async function processWithdrawal(walletId, amount, bankDetails) {
// Step 1: Check Xendit balance
const balance = await brdzSDK.xendit.getBalance();
if (balance.data.balance < amount) {
throw new Error('Insufficient Xendit balance');
}
// Step 2: Process withdrawal
const withdrawal = await brdzSDK.xendit.processWalletWithdraw({
wallet_id: walletId,
amount: amount,
currency: 'IDR',
bank_code: bankDetails.bank_code,
account_number: bankDetails.account_number,
account_holder_name: bankDetails.account_holder_name,
description: 'User withdrawal',
user_email: bankDetails.email
});
console.log(`Withdrawal initiated: ${withdrawal.data.reference_id}`);
// Step 3: Track status
let status = 'PENDING';
let attempts = 0;
while (status === 'PENDING' && attempts < 60) {
await new Promise(resolve => setTimeout(resolve, 5000));
const statusCheck = await brdzSDK.xendit.getWithdrawStatus(
withdrawal.data.reference_id
);
status = statusCheck.data.status;
console.log(`Status: ${status}`);
attempts++;
}
return { status, reference_id: withdrawal.data.reference_id };
}
Fixed VA Management
async function setupFixedVAs(walletId) {
const banks = ['BCA', 'BNI', 'MANDIRI'];
for (const bankCode of banks) {
const va = await brdzSDK.xendit.requestFixedVA({
wallet_id: walletId,
bank_code: bankCode,
is_single_use: false
});
console.log(`${bankCode} VA: ${va.data.va_number}`);
}
// Get all VAs
const vaList = await brdzSDK.xendit.getFixedVAList(walletId);
console.log(`Total VAs: ${vaList.data.total_vas}`);
return vaList.data.fixed_vas;
}
Daily Reconciliation
async function performReconciliation(adminUserId) {
// Run reconciliation
const reconciliation = await brdzSDK.xendit.runDailyReconciliation();
console.log(`Wallets Checked: ${reconciliation.data.total_wallets_checked}`);
console.log(`Discrepancies: ${reconciliation.data.discrepancies_found}`);
if (reconciliation.data.discrepancies_found > 0) {
// Get details
const details = await brdzSDK.xendit.getReconciliationDetails(
reconciliation.data.reconciliation_id
);
// Auto-resolve small discrepancies
for (const disc of details.data.discrepancies) {
if (Math.abs(disc.discrepancy) < 1000) {
await brdzSDK.xendit.resolveReconciliation({
reconciliation_id: reconciliation.data.reconciliation_id,
resolved_by: adminUserId,
resolution_notes: `Auto-resolved: Small discrepancy (${disc.discrepancy} IDR)`,
manual_adjustment_amount: 0
});
}
}
}
return reconciliation.data;
}
Monitoring Dashboard
async function displayDashboard() {
const dashboard = await brdzSDK.xendit.getDashboardData();
// Display balances
console.log('=== ACCOUNT BALANCES ===');
dashboard.data.balances.balances.forEach(acc => {
console.log(`${acc.account_type}: IDR ${acc.balance.toLocaleString()}`);
});
// Display webhook stats
console.log('\n=== WEBHOOK STATISTICS ===');
const stats = dashboard.data.webhook_stats;
console.log(`Total: ${stats.total_webhooks}`);
console.log(`Success Rate: ${(stats.success_rate * 100).toFixed(2)}%`);
console.log(`DLQ: ${stats.dlq_count}`);
// Check for issues
if (stats.dlq_count > 10) {
console.warn('⚠️ High DLQ count - processing required');
await brdzSDK.xendit.processDLQ();
}
if (stats.success_rate < 0.95) {
console.warn('⚠️ Low webhook success rate');
}
}
Error Handling
// Robust error handling for Xendit operations
async function safeXenditOperation(operation) {
try {
return await operation();
} catch (error) {
if (error.message.includes('Insufficient Xendit balance')) {
return {
error: 'insufficient_balance',
suggestion: 'top_up_xendit_account'
};
} else if (error.message.includes('Invalid bank code')) {
return {
error: 'invalid_bank',
suggestion: 'use_supported_bank'
};
} else if (error.message.includes('Wallet not found')) {
return {
error: 'wallet_not_found',
suggestion: 'verify_wallet_id'
};
} else if (error.message.includes('Xendit API')) {
return {
error: 'xendit_unavailable',
suggestion: 'retry_later'
};
} else {
console.error('Unexpected Xendit error:', error.message);
throw error;
}
}
}
// Usage
const result = await safeXenditOperation(() =>
brdzSDK.xendit.processWalletWithdraw({
wallet_id: "wallet_123",
amount: 500000,
bank_code: "BCA",
account_number: "1234567890",
account_holder_name: "John Doe"
})
);
Platform-Specific Integration
// Automated balance monitoring
class XenditMonitor {
static async checkBalanceAlert(threshold = 10000000) {
const balance = await brdzSDK.xendit.getBalance();
if (balance.data.balance < threshold) {
console.warn(`⚠️ Balance below threshold: IDR ${balance.data.balance.toLocaleString()}`);
await this.notifyAdmin('Low Balance Alert', {
current_balance: balance.data.balance,
threshold: threshold
});
}
return balance.data.balance;
}
static async monitorWebhooks() {
const stats = await brdzSDK.xendit.getWebhookStats({
start_date: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()
});
if (stats.data.success_rate < 0.95) {
console.warn('⚠️ Webhook success rate below 95%');
}
if (stats.data.dlq_count > 10) {
console.warn('⚠️ High DLQ count, processing...');
await brdzSDK.xendit.processDLQ();
}
return stats.data;
}
static async notifyAdmin(subject, data) {
console.log(`🚨 ADMIN ALERT: ${subject}`);
console.log(JSON.stringify(data, null, 2));
}
}
// Schedule monitoring
setInterval(async () => {
await XenditMonitor.checkBalanceAlert(10000000);
await XenditMonitor.monitorWebhooks();
}, 60 * 60 * 1000); // Every hour