IDRX Module
The idrx module provides comprehensive IDR onramp and offramp functionality for Indonesian market, enabling seamless conversion between Indonesian Rupiah (IDR) and digital currencies (IDRX/USDC). Built with BRDZ System branding for enterprise-grade operations with automated payment processing, bank account management, and real-time transaction tracking.
Import
const idrx = await brdzSDK.idrx;
Methods Overview
| Method | Description | Auth Required | HTTP Endpoint |
|---|---|---|---|
initiateOnramp | Create onramp payment request | ✅ | POST /idrx/onramp/initiate |
getOnrampStatus | Get onramp transaction status | ✅ | GET /idrx/onramp/status/:reference_id |
initiateOfframp | Create offramp withdrawal request | ✅ | POST /idrx/offramp/initiate |
getOfframpStatus | Get offramp transaction status | ✅ | GET /idrx/offramp/status/:reference_id |
addBank | Add bank account for withdrawals | ✅ | POST /idrx/banks/add |
listBanks | Get all bank accounts | ✅ | GET /idrx/banks/list |
deleteBank | Delete bank account | ✅ | DELETE /idrx/banks/:bank_id |
getMintTransactions | Get mint transaction history | ✅ | GET /idrx/transactions/mint |
getRedeemTransactions | Get redeem transaction history | ✅ | GET /idrx/transactions/redeem |
processAdminRefund | Process refund (admin only) | ✅ Admin | POST /idrx/admin/refund |
getPaymentMethods | Get available payment methods | ✅ | GET /idrx/payment-methods |
getFees | Get fee information | ✅ | GET /idrx/fees |
Onramp Operations (IDR → Digital Currency)
initiateOnramp
Create payment request to convert IDR to IDRX/USDC. User pays via payment gateway (QRIS, Virtual Account, or E-Wallet), and receives digital currency in their wallet after payment confirmation.
const idrx = await brdzSDK.idrx;
const onramp = await idrx.initiateOnramp({
amount: 100000,
paymentMethod: "VA",
walletAddress: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
});
console.log('Reference ID:', onramp.data.referenceId);
console.log('Amount:', onramp.data.amount.toLocaleString('id-ID'));
console.log('Fee:', onramp.data.fee.toLocaleString('id-ID'));
console.log('Total to Pay:', onramp.data.totalAmount.toLocaleString('id-ID'));
console.log('Payment URL:', onramp.data.paymentUrl);
console.log('Expires at:', onramp.data.expiresAt);
// Open payment URL for user
window.open(onramp.data.paymentUrl, '_blank');
Parameters:
amount(number, required): Amount in IDR (minimum 20,000, maximum 100,000,000)paymentMethod(string, required): Payment method: 'QRIS', 'VA', or 'EWALLET'walletAddress(string, required): Destination wallet address (0x... for EVM chains)
Returns: Payment request details with payment URL and expiry
getOnrampStatus
Track onramp transaction status by reference ID. Monitor payment confirmation, minting process, and final completion.
const idrx = await brdzSDK.idrx;
const status = await idrx.getOnrampStatus('MINT-1705308000000-123');
console.log('Reference ID:', status.data.referenceId);
console.log('Status:', status.data.status);
console.log('Payment Status:', status.data.paymentStatus);
console.log('Amount:', status.data.amount.toLocaleString('id-ID'));
console.log('Fee:', status.data.fee.toLocaleString('id-ID'));
// Status indicators
const statusEmoji = {
'PENDING': '⏳ Waiting for payment',
'PAID': '✅ Payment received, minting in progress',
'MINTED': '🎉 IDRX minted to blockchain',
'SUCCESS': '✅ Transaction completed',
'FAILED': '❌ Transaction failed'
};
console.log(statusEmoji[status.data.status]);
Parameters:
referenceId(string, required): Transaction reference ID (e.g., MINT-1705308000000-123)
Returns: Transaction status with payment details
Offramp Operations (Digital Currency → IDR)
initiateOfframp
Create withdrawal request to convert IDRX to IDR and send to bank account. User must first burn IDRX tokens on blockchain, then provide burn transaction hash to initiate offramp.
const idrx = await brdzSDK.idrx;
// Step 1: Get user's banks
const banks = await idrx.listBanks();
const primaryBank = banks.data.find(b => b.is_primary);
// Step 2: Burn IDRX on blockchain (using ethers.js)
const { ethers } = require('ethers');
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const idrxContract = new ethers.Contract(
IDRX_CONTRACT_ADDRESS,
IDRX_ABI,
signer
);
const amount = ethers.utils.parseUnits('100000', 18);
// Create bank account hash
const bankAccountString = `${primaryBank.bank_name}_${primaryBank.bank_account_number}`;
const bankAccountHash = ethers.utils.keccak256(
ethers.utils.toUtf8Bytes(bankAccountString)
);
// Execute burn
const burnTx = await idrxContract.burnWithAccountNumber(
amount,
bankAccountHash
);
console.log('Burning IDRX...');
await burnTx.wait();
console.log('✅ Burn confirmed:', burnTx.hash);
// Step 3: Initiate offramp
const offramp = await idrx.initiateOfframp({
burnTxHash: burnTx.hash,
amount: 100000,
bankId: primaryBank.idrx_bank_id,
walletAddress: await signer.getAddress()
});
console.log('Reference ID:', offramp.data.referenceId);
console.log('Amount:', offramp.data.amount.toLocaleString('id-ID'));
console.log('Fee:', offramp.data.fee.toLocaleString('id-ID'));
console.log('Net to Bank:', offramp.data.netAmount.toLocaleString('id-ID'));
console.log('Bank:', offramp.data.bankAccount.bankName);
console.log('Account:', offramp.data.bankAccount.accountNumber);
Parameters:
burnTxHash(string, required): Burn transaction hash from blockchainamount(number, required): Amount in IDRX to redeem (minimum 20,000)bankId(number, required): Bank account ID from user's registered bankswalletAddress(string, required): Wallet address that performed the burn
Returns: Withdrawal request details with bank information
getOfframpStatus
Track offramp transaction status by reference ID. Monitor withdrawal processing and bank transfer confirmation.
const idrx = await brdzSDK.idrx;
const status = await idrx.getOfframpStatus('REDEEM-1705308000000-123');
console.log('Reference ID:', status.data.referenceId);
console.log('Status:', status.data.status);
console.log('Bank Transfer:', status.data.bankTransferStatus);
console.log('Amount:', status.data.amount.toLocaleString('id-ID'));
console.log('Fee:', status.data.fee.toLocaleString('id-ID'));
console.log('Net Amount:', status.data.netAmount.toLocaleString('id-ID'));
// Status indicators
const statusEmoji = {
'PENDING': '⏳ Processing withdrawal',
'PROCESSING': '🔄 Bank transfer in progress',
'COMPLETED': '✅ IDR transferred to bank',
'FAILED': '❌ Transfer failed'
};
console.log(statusEmoji[status.data.status]);
Parameters:
referenceId(string, required): Transaction reference ID (e.g., REDEEM-1705308000000-123)
Returns: Transaction status with bank transfer details
Bank Account Management
addBank
Register bank account for offramp withdrawals. Users can add multiple bank accounts and set one as primary.
const idrx = await brdzSDK.idrx;
const bank = await idrx.addBank({
bankName: "BANK CENTRAL ASIA",
bankCode: "014",
accountNumber: "1234567890",
accountName: "JOHN SMITH",
isPrimary: true
});
console.log('Bank ID:', bank.data.idrx_bank_id);
console.log('Bank:', bank.data.bank_name);
console.log('Account:', bank.data.bank_account_number);
console.log('Holder:', bank.data.bank_account_name);
console.log('Primary:', bank.data.is_primary);
Parameters:
bankName(string, required): Bank name (e.g., 'BANK CENTRAL ASIA')bankCode(string, required): Bank code (e.g., '014' for BCA)accountNumber(string, required): Bank account numberaccountName(string, required): Account holder name (must match user's name)isPrimary(boolean, optional): Set as primary account (default: false)
Returns: Bank account details with ID
listBanks
Retrieve all active bank accounts for the authenticated user.
const idrx = await brdzSDK.idrx;
const banks = await idrx.listBanks();
console.log(`Total banks: ${banks.data.length}\n`);
banks.data.forEach((bank, index) => {
const primaryFlag = bank.is_primary ? '⭐ PRIMARY' : '';
console.log(`${index + 1}. ${bank.bank_name} ${primaryFlag}`);
console.log(` Account: ${bank.bank_account_number}`);
console.log(` Holder: ${bank.bank_account_name}`);
console.log(` Status: ${bank.status}\n`);
});
// Find primary bank
const primaryBank = banks.data.find(b => b.is_primary);
if (primaryBank) {
console.log('Primary bank for withdrawals:');
console.log(` ${primaryBank.bank_name}`);
console.log(` ${primaryBank.bank_account_number}`);
}
Returns: List of all user bank accounts with primary designation
deleteBank
Soft delete (deactivate) a bank account. Cannot delete if it's the only bank account or if there are pending transactions.
const idrx = await brdzSDK.idrx;
const result = await idrx.deleteBank(2);
console.log(result.message);
// Get updated list
const banks = await idrx.listBanks();
console.log(`Remaining banks: ${banks.data.length}`);
Parameters:
bankId(number, required): Bank account ID to delete
Returns: Deletion confirmation
Transaction History
getMintTransactions
Retrieve user's mint (onramp) transaction history with pagination and status filtering.
const idrx = await brdzSDK.idrx;
const history = await idrx.getMintTransactions();
console.log(`Total transactions: ${history.pagination.total}\n`);
history.data.forEach((tx, index) => {
console.log(`${index + 1}. Reference: ${tx.reference_id}`);
console.log(` Amount: IDR ${tx.amount_idr.toLocaleString('id-ID')}`);
console.log(` Fee: IDR ${tx.fee_amount.toLocaleString('id-ID')}`);
console.log(` Method: ${tx.payment_method}`);
console.log(` Status: ${tx.status}`);
console.log(` Date: ${new Date(tx.created_at).toLocaleString('id-ID')}\n`);
});
// Filter by status
const successTx = await idrx.getMintTransactions({
status: 'SUCCESS',
page: 1,
limit: 20
});
console.log(`Successful transactions: ${successTx.data.length}`);
Parameters:
status(string, optional): Filter by status: PENDING, PAID, MINTED, SUCCESS, FAILEDpage(number, optional): Page number (default: 1)limit(number, optional): Items per page (default: 10)
Returns: List of mint transactions with pagination
getRedeemTransactions
Retrieve user's redeem (offramp) transaction history with pagination and status filtering.
const idrx = await brdzSDK.idrx;
const history = await idrx.getRedeemTransactions();
console.log(`Total transactions: ${history.pagination.total}\n`);
history.data.forEach((tx, index) => {
console.log(`${index + 1}. Reference: ${tx.reference_id}`);
console.log(` Amount: IDR ${tx.amount_idr.toLocaleString('id-ID')}`);
console.log(` Fee: IDR ${tx.fee_amount.toLocaleString('id-ID')}`);
console.log(` Net to Bank: IDR ${tx.net_amount_idr.toLocaleString('id-ID')}`);
console.log(` Bank: ${tx.bank_name}`);
console.log(` Account: ${tx.bank_account_number}`);
console.log(` Status: ${tx.status}`);
console.log(` Date: ${new Date(tx.created_at).toLocaleString('id-ID')}\n`);
});
// Filter by status
const completed = await idrx.getRedeemTransactions({
status: 'COMPLETED',
page: 1,
limit: 20
});
console.log(`Completed withdrawals: ${completed.data.length}`);
Parameters:
status(string, optional): Filter by status: PENDING, PROCESSING, COMPLETED, FAILEDpage(number, optional): Page number (default: 1)limit(number, optional): Items per page (default: 10)
Returns: List of redeem transactions with pagination
Admin Operations
processAdminRefund
Process refund for failed mint or redeem transaction. Refunds user's funds minus refund fee. Admin-only operation.
const idrx = await brdzSDK.idrx;
// Refund failed mint transaction
const refund = await idrx.processAdminRefund({
transactionType: "MINT",
transactionId: 123,
reason: "Payment received but minting failed due to technical error"
});
console.log('Refund Transaction ID:', refund.data.refundTransactionId);
console.log('Refund Fee:', refund.data.refundFee.toLocaleString('id-ID'));
console.log('Net Refund to User:', refund.data.netRefund.toLocaleString('id-ID'));
Parameters:
transactionType(string, required): Transaction type: 'MINT' or 'REDEEM'transactionId(number, required): Original transaction IDreason(string, required): Refund reason for audit trail
Returns: Refund details with transaction ID
Utility Operations
getPaymentMethods
Fetch live payment methods with current fee structures.
const idrx = await brdzSDK.idrx;
const methods = await idrx.getPaymentMethods();
console.log(`Payment methods (${methods.source}):\n`);
methods.data.forEach(method => {
const feeDisplay = method.fee_type === 'PERCENTAGE'
? `${method.fee}%`
: `IDR ${method.fee.toLocaleString('id-ID')}`;
console.log(`${method.name}`);
console.log(` Fee: ${feeDisplay}`);
console.log(` Description: ${method.description}\n`);
});
Returns: List of payment methods with fees and descriptions
getFees
Fetch comprehensive fee information for all operations.
const idrx = await brdzSDK.idrx;
const allFees = await idrx.getFees();
console.log('=== IDRX FEE STRUCTURE ===\n');
// Display mint fees
console.log('ONRAMP (Mint) Fees:');
allFees.data.mint.methods.forEach(m => {
console.log(` ${m.method}: ${m.fee}`);
console.log(` ${m.description}`);
});
console.log(` Processing: ${allFees.data.mint.processingTime}\n`);
// Display redeem fees
console.log('OFFRAMP (Redeem) Fees:');
allFees.data.redeem.fees.forEach(f => {
console.log(` ${f.range}: ${f.fee}`);
console.log(` Processing: ${f.processingTime}`);
});
console.log(` Max: ${allFees.data.redeem.maxProcessingTime}\n`);
// Get specific fee type
const mintFees = await idrx.getFees({ type: 'MINT' });
console.log('Mint Fees Only:', mintFees.data);
Parameters:
type(string, optional): Fee type: 'MINT', 'REDEEM', 'REFUND', or 'BRIDGE'
Returns: Fee structure for specified type or all fees
Complete Usage Examples
Basic IDRX Setup
// Configure SDK
const config = await brdzSDK.config;
config.setApiKey('your-api-key');
config.setToken('your-jwt-token');
const idrx = await brdzSDK.idrx;
// Check payment methods
const methods = await idrx.getPaymentMethods();
console.log('Available methods:', methods.data.map(m => m.method));
// Check fees
const fees = await idrx.getFees({ type: 'MINT' });
console.log('Mint fees:', fees.data);
Complete Onramp Flow
async function completeOnrampFlow(amount, paymentMethod, walletAddress) {
console.log('=== STARTING ONRAMP FLOW ===\n');
const idrx = await brdzSDK.idrx;
try {
// Step 1: Initiate onramp
console.log('Step 1: Creating payment request...');
const onramp = await idrx.initiateOnramp({
amount,
paymentMethod,
walletAddress
});
console.log('✅ Payment request created');
console.log(`Reference: ${onramp.data.referenceId}`);
console.log(`Amount: IDR ${onramp.data.amount.toLocaleString('id-ID')}`);
console.log(`Fee: IDR ${onramp.data.fee.toLocaleString('id-ID')}`);
console.log(`Total: IDR ${onramp.data.totalAmount.toLocaleString('id-ID')}`);
console.log(`Payment URL: ${onramp.data.paymentUrl}`);
// Step 2: Open payment page
console.log('\nStep 2: Opening payment page...');
window.open(onramp.data.paymentUrl, '_blank');
// Step 3: Monitor status
console.log('\nStep 3: Monitoring transaction status...');
const referenceId = onramp.data.referenceId;
let completed = false;
let attempts = 0;
const maxAttempts = 288;
while (!completed && attempts < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, 5000));
const status = await idrx.getOnrampStatus(referenceId);
console.log(`[${new Date().toLocaleTimeString()}] Status: ${status.data.status}`);
if (status.data.status === 'SUCCESS') {
console.log('\n✅ ONRAMP COMPLETED!');
console.log(`IDRX Amount: ${status.data.amount.toLocaleString('id-ID')}`);
console.log(`Wallet: ${walletAddress}`);
completed = true;
return {
success: true,
referenceId,
amount: status.data.amount,
status: status.data.status
};
} else if (status.data.status === 'FAILED') {
console.log('\n❌ Payment failed');
completed = true;
return {
success: false,
referenceId,
status: status.data.status
};
}
attempts++;
}
} catch (error) {
console.error('\n❌ Onramp flow failed:', error.message);
return {
success: false,
error: error.message
};
}
}
// Usage
await completeOnrampFlow(
100000,
"VA",
"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
);
Complete Offramp Flow
async function completeOfframpFlow(amount, walletAddress) {
console.log('=== STARTING OFFRAMP FLOW ===\n');
const idrx = await brdzSDK.idrx;
const { ethers } = require('ethers');
try {
// Step 1: Check/add bank account
console.log('Step 1: Checking bank accounts...');
let banks = await idrx.listBanks();
if (banks.data.length === 0) {
console.log('No bank accounts found. Adding bank...');
const bankData = {
bankName: "BANK CENTRAL ASIA",
bankCode: "014",
accountNumber: "1234567890",
accountName: "JOHN SMITH",
isPrimary: true
};
await idrx.addBank(bankData);
banks = await idrx.listBanks();
console.log('✅ Bank account added');
}
const primaryBank = banks.data.find(b => b.is_primary);
console.log(`Primary bank: ${primaryBank.bank_name}`);
console.log(`Account: ${primaryBank.bank_account_number}\n`);
// Step 2: Burn IDRX on blockchain
console.log('Step 2: Burning IDRX on blockchain...');
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const idrxContract = new ethers.Contract(
IDRX_CONTRACT_ADDRESS,
IDRX_ABI,
signer
);
const amountWei = ethers.utils.parseUnits(amount.toString(), 18);
const bankString = `${primaryBank.bank_name}_${primaryBank.bank_account_number}`;
const bankHash = ethers.utils.keccak256(
ethers.utils.toUtf8Bytes(bankString)
);
console.log('Executing burn transaction...');
const burnTx = await idrxContract.burnWithAccountNumber(amountWei, bankHash);
console.log(`Burn TX: ${burnTx.hash}`);
console.log('Waiting for confirmation...');
await burnTx.wait();
console.log('✅ Burn confirmed\n');
// Step 3: Initiate offramp
console.log('Step 3: Creating withdrawal request...');
const offramp = await idrx.initiateOfframp({
burnTxHash: burnTx.hash,
amount,
bankId: primaryBank.idrx_bank_id,
walletAddress
});
console.log('✅ Withdrawal request created');
console.log(`Reference: ${offramp.data.referenceId}`);
console.log(`Amount: IDR ${offramp.data.amount.toLocaleString('id-ID')}`);
console.log(`Fee: IDR ${offramp.data.fee.toLocaleString('id-ID')}`);
console.log(`Net to Bank: IDR ${offramp.data.netAmount.toLocaleString('id-ID')}\n`);
// Step 4: Monitor status
console.log('Step 4: Monitoring withdrawal status...');
const referenceId = offramp.data.referenceId;
let completed = false;
let attempts = 0;
const maxAttempts = 288;
while (!completed && attempts < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, 10000));
const status = await idrx.getOfframpStatus(referenceId);
console.log(`[${new Date().toLocaleTimeString()}] Status: ${status.data.status}`);
if (status.data.status === 'COMPLETED') {
console.log('\n✅ OFFRAMP COMPLETED!');
console.log(`Net Amount: IDR ${status.data.netAmount.toLocaleString('id-ID')}`);
console.log(`Bank: ${primaryBank.bank_name}`);
console.log(`Account: ${primaryBank.bank_account_number}`);
console.log('💰 Check your bank account for funds');
completed = true;
return {
success: true,
referenceId,
netAmount: status.data.netAmount,
status: status.data.status
};
} else if (status.data.status === 'FAILED') {
console.log('\n❌ Withdrawal failed');
completed = true;
return {
success: false,
referenceId,
status: status.data.status
};
}
attempts++;
}
} catch (error) {
console.error('\n❌ Offramp flow failed:', error.message);
return {
success: false,
error: error.message
};
}
}
// Usage
await completeOfframpFlow(
100000,
"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
);
Transaction Status Tracker
async function trackTransaction(referenceId) {
console.log(`=== TRACKING TRANSACTION: ${referenceId} ===\n`);
const idrx = await brdzSDK.idrx;
try {
const txType = referenceId.startsWith('MINT') ? 'ONRAMP' : 'OFFRAMP';
console.log(`Transaction Type: ${txType}\n`);
const status = txType === 'ONRAMP'
? await idrx.getOnrampStatus(referenceId)
: await idrx.getOfframpStatus(referenceId);
console.log('Current Status:', status.data.status);
console.log('Amount:', `IDR ${status.data.amount.toLocaleString('id-ID')}`);
console.log('Fee:', `IDR ${status.data.fee.toLocaleString('id-ID')}`);
if (txType === 'ONRAMP') {
console.log('Payment Method:', status.data.paymentMethod || 'N/A');
console.log('Payment Status:', status.data.paymentStatus);
if (status.data.expiresAt) {
const expiresAt = new Date(status.data.expiresAt);
const timeLeft = Math.round((expiresAt - new Date()) / 1000 / 60);
console.log(`Expires in: ${timeLeft > 0 ? timeLeft + ' minutes' : 'EXPIRED'}`);
}
} else {
console.log('Net Amount:', `IDR ${status.data.netAmount.toLocaleString('id-ID')}`);
console.log('Bank Transfer:', status.data.bankTransferStatus || 'N/A');
}
console.log('Created:', new Date(status.data.createdAt).toLocaleString('id-ID'));
const statusSteps = txType === 'ONRAMP'
? ['PENDING', 'PAID', 'MINTED', 'SUCCESS']
: ['PENDING', 'PROCESSING', 'COMPLETED'];
const currentIndex = statusSteps.indexOf(status.data.status);
console.log('\nProgress:');
statusSteps.forEach((step, index) => {
const icon = index < currentIndex ? '✅'
: index === currentIndex ? '🔄'
: '⏳';
console.log(`${icon} ${step}`);
});
return status.data;
} catch (error) {
console.error('Error tracking transaction:', error.message);
return null;
}
}
// Usage
await trackTransaction('MINT-1705308000000-123');
await trackTransaction('REDEEM-1705308000000-456');
Bank Account Management
async function manageBankAccounts() {
console.log('=== BANK ACCOUNT MANAGEMENT ===\n');
const idrx = await brdzSDK.idrx;
try {
// Add multiple banks
const banksToAdd = [
{
bankName: "BANK CENTRAL ASIA",
bankCode: "014",
accountNumber: "1234567890",
accountName: "JOHN SMITH",
isPrimary: true
},
{
bankName: "BANK MANDIRI",
bankCode: "008",
accountNumber: "9876543210",
accountName: "JOHN SMITH",
isPrimary: false
}
];
console.log('Adding bank accounts...');
for (const bankData of banksToAdd) {
const result = await idrx.addBank(bankData);
console.log(`✅ Added: ${result.data.bank_name}`);
}
console.log('\n');
// List all banks
const banks = await idrx.listBanks();
console.log(`Total banks: ${banks.data.length}\n`);
banks.data.forEach((bank, index) => {
const primaryFlag = bank.is_primary ? '⭐ PRIMARY' : '';
console.log(`${index + 1}. ${bank.bank_name} ${primaryFlag}`);
console.log(` Account: ${bank.bank_account_number}`);
console.log(` Holder: ${bank.bank_account_name}`);
console.log(` Status: ${bank.status}\n`);
});
// Delete a bank (if more than one exists)
if (banks.data.length > 1) {
const nonPrimaryBank = banks.data.find(b => !b.is_primary);
if (nonPrimaryBank) {
console.log(`Deleting: ${nonPrimaryBank.bank_name}...`);
await idrx.deleteBank(nonPrimaryBank.idrx_bank_id);
console.log('✅ Bank deleted\n');
const updatedBanks = await idrx.listBanks();
console.log(`Remaining banks: ${updatedBanks.data.length}`);
}
}
} catch (error) {
console.error('Error managing banks:', error.message);
}
}
// Usage
await manageBankAccounts();
Admin Refund Process
async function adminRefundProcess(txType, txId) {
console.log('=== ADMIN REFUND PROCESS ===\n');
const idrx = await brdzSDK.idrx;
try {
// Step 1: Get transaction details
console.log('Step 1: Retrieving transaction details...');
const refId = txType === 'MINT'
? `MINT-${txId}`
: `REDEEM-${txId}`;
const status = txType === 'MINT'
? await idrx.getOnrampStatus(refId)
: await idrx.getOfframpStatus(refId);
console.log('Transaction found:');
console.log(` Reference: ${status.data.referenceId}`);
console.log(` Status: ${status.data.status}`);
console.log(` Amount: IDR ${status.data.amount.toLocaleString('id-ID')}`);
console.log(` Fee: IDR ${status.data.fee.toLocaleString('id-ID')}\n`);
// Step 2: Check if refund eligible
console.log('Step 2: Checking refund eligibility...');
if (status.data.status === 'SUCCESS' || status.data.status === 'COMPLETED') {
console.log('❌ Cannot refund completed transaction');
return { success: false, reason: 'Transaction already completed' };
}
console.log('✅ Transaction eligible for refund\n');
// Step 3: Calculate refund amount
console.log('Step 3: Calculating refund amount...');
const originalAmount = status.data.amount;
const refundFee = 10000; // Simplified
const netRefund = originalAmount - refundFee;
console.log(`Original Amount: IDR ${originalAmount.toLocaleString('id-ID')}`);
console.log(`Refund Fee: IDR ${refundFee.toLocaleString('id-ID')}`);
console.log(`Net Refund: IDR ${netRefund.toLocaleString('id-ID')}\n`);
// Step 4: Process refund
console.log('Step 4: Processing refund...');
const reason = `Failed ${txType} transaction - technical error during processing`;
const refund = await idrx.processAdminRefund({
transactionType: txType,
transactionId: txId,
reason
});
console.log('✅ REFUND PROCESSED!\n');
console.log('Refund Details:');
console.log(` Refund TX ID: ${refund.data.refundTransactionId}`);
console.log(` Refund Fee: IDR ${refund.data.refundFee.toLocaleString('id-ID')}`);
console.log(` Net Refund: IDR ${refund.data.netRefund.toLocaleString('id-ID')}`);
return {
success: true,
refundTransactionId: refund.data.refundTransactionId,
netRefund: refund.data.netRefund
};
} catch (error) {
console.error('\n❌ Refund process failed:', error.message);
return {
success: false,
error: error.message
};
}
}
// Usage
await adminRefundProcess('MINT', 123);
Error Handling
class IdrxErrorHandler {
static async safeOnramp(amount, paymentMethod, walletAddress) {
try {
const idrx = await brdzSDK.idrx;
return await idrx.initiateOnramp({
amount,
paymentMethod,
walletAddress
});
} catch (error) {
return this.handleError(error, 'ONRAMP');
}
}
static async safeOfframp(burnTxHash, amount, bankId, walletAddress) {
try {
const idrx = await brdzSDK.idrx;
return await idrx.initiateOfframp({
burnTxHash,
amount,
bankId,
walletAddress
});
} catch (error) {
return this.handleError(error, 'OFFRAMP');
}
}
static handleError(error, operation) {
const errorMap = {
'Missing required fields': {
message: 'Please provide all required information',
action: 'Check your input and try again',
retry: true
},
'Amount must be between': {
message: 'Amount is outside allowed range',
action: 'Minimum: Rp 20,000, Maximum: Rp 100,000,000',
retry: true
},
'Bank account not found': {
message: 'Bank account not registered',
action: 'Please add a bank account first',
retry: false
},
'Invalid burn transaction': {
message: 'Burn transaction is invalid or not found',
action: 'Please verify the transaction hash',
retry: false
},
'Transaction not found': {
message: 'Transaction reference not found',
action: 'Please check the reference ID',
retry: false
}
};
let errorInfo = null;
for (const [key, value] of Object.entries(errorMap)) {
if (error.message && error.message.includes(key)) {
errorInfo = value;
break;
}
}
if (!errorInfo) {
errorInfo = {
message: error.message || 'An unexpected error occurred',
action: 'Please try again or contact support',
retry: false
};
}
console.error(`\n❌ ${operation} Error:`);
console.error(`Message: ${errorInfo.message}`);
console.error(`Action: ${errorInfo.action}`);
console.error(`Can Retry: ${errorInfo.retry ? 'Yes' : 'No'}`);
return {
success: false,
operation,
error: errorInfo.message,
action: errorInfo.action,
canRetry: errorInfo.retry
};
}
}
// Usage
const result = await IdrxErrorHandler.safeOnramp(
100000,
'VA',
'0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
);
if (!result.success && result.canRetry) {
console.log('You can try again');
}
Platform-Specific Integration
class IdrxMonitor {
static async monitorPendingTransactions() {
const idrx = await brdzSDK.idrx;
// Monitor mint transactions
const pendingMints = await idrx.getMintTransactions({
status: 'PENDING'
});
console.log(`Pending mints: ${pendingMints.data.length}`);
// Monitor redeem transactions
const pendingRedeems = await idrx.getRedeemTransactions({
status: 'PENDING'
});
console.log(`Pending redeems: ${pendingRedeems.data.length}`);
// Check for expired payments
const allMints = await idrx.getMintTransactions({ status: 'PENDING' });
const expired = allMints.data.filter(tx => {
const expiresAt = new Date(tx.expired_at);
return expiresAt < new Date();
});
if (expired.length > 0) {
console.warn(`⚠️ ${expired.length} expired payment requests`);
}
return {
pending_mints: pendingMints.data.length,
pending_redeems: pendingRedeems.data.length,
expired_payments: expired.length
};
}
static async checkTransactionHealth() {
const idrx = await brdzSDK.idrx;
const statusCounts = {
mint: { PENDING: 0, PAID: 0, MINTED: 0, SUCCESS: 0, FAILED: 0 },
redeem: { PENDING: 0, PROCESSING: 0, COMPLETED: 0, FAILED: 0 }
};
// Get mint stats
const mints = await idrx.getMintTransactions({ limit: 100 });
mints.data.forEach(tx => {
if (statusCounts.mint[tx.status] !== undefined) {
statusCounts.mint[tx.status]++;
}
});
// Get redeem stats
const redeems = await idrx.getRedeemTransactions({ limit: 100 });
redeems.data.forEach(tx => {
if (statusCounts.redeem[tx.status] !== undefined) {
statusCounts.redeem[tx.status]++;
}
});
console.log('Transaction Health Summary:');
console.log('Mint:', statusCounts.mint);
console.log('Redeem:', statusCounts.redeem);
return statusCounts;
}
static async notifyAdmin(subject, data) {
console.log(`🚨 ADMIN ALERT: ${subject}`);
console.log(JSON.stringify(data, null, 2));
}
}
// Schedule monitoring
setInterval(async () => {
await IdrxMonitor.monitorPendingTransactions();
await IdrxMonitor.checkTransactionHealth();
}, 60 * 60 * 1000); // Every hour
Transaction Flow States
Onramp Flow
- PENDING: Payment request created, awaiting user payment
- PAID: Payment received, minting in progress
- MINTED: IDRX minted on blockchain
- SUCCESS: IDRX delivered to wallet
- FAILED: Transaction failed
Offramp Flow
- PENDING: Withdrawal request created
- PROCESSING: Bank transfer in progress
- COMPLETED: IDR transferred to bank account
- FAILED: Transaction failed
Payment Methods
Supported Payment Methods
- QRIS: Free, instant QR code payment
- Virtual Account (VA): Rp 3,000 flat fee, all major banks
- E-Wallet: 1.67% fee, GoPay/OVO/DANA/ShopeePay
Fee Structure
- Onramp Fees: Varies by payment method (0% - 1.67%)
- Offramp Fees: Rp 5,000 - Rp 35,000 based on amount
- Refund Fees: Rp 5,000 + applicable redeem fee
Important Notes
- Users must provide a valid wallet address before initiating onramp
- Bank account holder name must match user's registered name
- Payment requests expire after 24 hours
- Complete onramp typically takes up to 24 hours
- Complete offramp typically takes up to 24 hours
- Burn transaction must be confirmed before initiating offramp
- All operations require proper authentication
- Admin operations require admin role
Best Practices
- Always verify wallet address format before initiating transactions
- Use error handling for all operations
- Monitor transaction statuses regularly
- Set up proper bank accounts before offramp
- Implement retry logic for failed operations
- Log all operations for audit trails
- Validate amounts are within limits
- Check payment expiry times
- Provide clear user feedback during long operations
- Implement admin notifications for critical errors