Guides
Balance Check

Balance Check Guide

Learn how to check your wallet balances and implement balance monitoring in your PayVanta integration.

Overview

The Balance Check API allows you to:

  • Monitor your payin and payout wallet balances
  • Validate sufficient funds before processing transactions
  • Set up automated balance alerts and monitoring
  • Track balance changes over time
  • Implement balance-based business logic

Getting Started

Basic Balance Check

import axios from 'axios';
 
async function checkBalance() {
  try {
    const response = await axios.get('https://api.payvanta.in/balance-check', {
      headers: {
        'Authorization': `Bearer <base64_encoded_credentials>`,
        'Content-Type': 'application/json'
      }
    });
    
    const { payin_balance, payout_balance } = response.data;
    
    console.log(`Payin Balance: ₹${payin_balance}`);
    console.log(`Payout Balance: ₹${payout_balance}`);
    
    return response.data;
  } catch (error) {
    console.error('Balance check failed:', error.message);
    throw error;
  }
}

Response Format

{
    "payin_balance": 490,
    "payout_balance": 6.602000000000004
}

Field Descriptions:

  • payin_balance: Available funds for collecting payments from customers
  • payout_balance: Available funds for sending money to bank accounts

Pre-Transaction Validation

Check Balance Before Payout

async function validatePayoutBalance(amount) {
  try {
    const balance = await checkBalance();
    
    if (balance.payout_balance < amount) {
      throw new Error(`Insufficient balance. Required: ₹${amount}, Available: ₹${balance.payout_balance}`);
    }
    
    console.log(`✅ Sufficient balance for payout of ₹${amount}`);
    return true;
  } catch (error) {
    console.error('❌ Balance validation failed:', error.message);
    return false;
  }
}
 
// Usage example
async function processPayoutWithValidation(payoutData) {
  const isValid = await validatePayoutBalance(payoutData.amount);
  
  if (!isValid) {
    return { success: false, error: 'Insufficient balance' };
  }
  
  // Proceed with payout
  const payoutResult = await processPayout(payoutData);
  return payoutResult;
}

Balance Check Middleware

// Express.js middleware for balance validation
function balanceCheckMiddleware(requiredType, amountField = 'amount') {
  return async (req, res, next) => {
    try {
      const balance = await checkBalance();
      const requiredAmount = req.body[amountField];
      
      const balanceField = requiredType === 'payout' ? 'payout_balance' : 'payin_balance';
      
      if (balance[balanceField] < requiredAmount) {
        return res.status(400).json({
          success: false,
          error: 'Insufficient balance',
          required: requiredAmount,
          available: balance[balanceField]
        });
      }
      
      // Attach balance to request for further use
      req.balance = balance;
      next();
    } catch (error) {
      res.status(500).json({
        success: false,
        error: 'Balance check failed'
      });
    }
  };
}
 
// Usage in routes
app.post('/api/payout', balanceCheckMiddleware('payout'), async (req, res) => {
  // Balance is already validated, proceed with payout
  const result = await processPayout(req.body);
  res.json(result);
});

Balance Monitoring

Real-time Balance Alerts

class BalanceMonitor {
  constructor(options = {}) {
    this.thresholds = {
      payin_low: options.payinThreshold || 100,
      payout_low: options.payoutThreshold || 50,
      ...options.thresholds
    };
    this.alertCallbacks = [];
    this.monitoringInterval = null;
  }
  
  // Add alert callback
  onAlert(callback) {
    this.alertCallbacks.push(callback);
  }
  
  // Check balance and trigger alerts
  async checkAndAlert() {
    try {
      const balance = await checkBalance();
      
      // Check payin balance
      if (balance.payin_balance < this.thresholds.payin_low) {
        this.triggerAlert('payin_low', {
          type: 'payin',
          balance: balance.payin_balance,
          threshold: this.thresholds.payin_low
        });
      }
      
      // Check payout balance
      if (balance.payout_balance < this.thresholds.payout_low) {
        this.triggerAlert('payout_low', {
          type: 'payout',
          balance: balance.payout_balance,
          threshold: this.thresholds.payout_low
        });
      }
      
      return balance;
    } catch (error) {
      this.triggerAlert('check_failed', { error: error.message });
    }
  }
  
  // Trigger alert callbacks
  triggerAlert(alertType, data) {
    this.alertCallbacks.forEach(callback => {
      try {
        callback(alertType, data);
      } catch (error) {
        console.error('Alert callback failed:', error);
      }
    });
  }
  
  // Start monitoring
  startMonitoring(intervalMinutes = 5) {
    if (this.monitoringInterval) {
      this.stopMonitoring();
    }
    
    this.monitoringInterval = setInterval(() => {
      this.checkAndAlert();
    }, intervalMinutes * 60 * 1000);
    
    // Initial check
    this.checkAndAlert();
  }
  
  // Stop monitoring
  stopMonitoring() {
    if (this.monitoringInterval) {
      clearInterval(this.monitoringInterval);
      this.monitoringInterval = null;
    }
  }
}
 
// Usage example
const monitor = new BalanceMonitor({
  payinThreshold: 200,
  payoutThreshold: 100
});
 
// Set up alert handlers
monitor.onAlert((alertType, data) => {
  switch (alertType) {
    case 'payin_low':
      console.warn(`🚨 Low payin balance: ₹${data.balance} (threshold: ₹${data.threshold})`);
      // Send notification, email, etc.
      break;
      
    case 'payout_low':
      console.warn(`🚨 Low payout balance: ₹${data.balance} (threshold: ₹${data.threshold})`);
      // Send notification, email, etc.
      break;
      
    case 'check_failed':
      console.error('❌ Balance check failed:', data.error);
      // Handle error, retry, etc.
      break;
  }
});
 
// Start monitoring every 5 minutes
monitor.startMonitoring(5);

Balance Tracking and Analytics

class BalanceTracker {
  constructor() {
    this.history = [];
  }
  
  // Record balance snapshot
  async recordBalance() {
    try {
      const balance = await checkBalance();
      const record = {
        timestamp: new Date().toISOString(),
        payin_balance: balance.payin_balance,
        payout_balance: balance.payout_balance
      };
      
      this.history.push(record);
      
      // Keep only last 100 records
      if (this.history.length > 100) {
        this.history = this.history.slice(-100);
      }
      
      return record;
    } catch (error) {
      console.error('Failed to record balance:', error);
    }
  }
  
  // Get balance trend
  getBalanceTrend(type = 'payout', periods = 10) {
    const recent = this.history.slice(-periods);
    if (recent.length < 2) return null;
    
    const field = `${type}_balance`;
    const first = recent[0][field];
    const last = recent[recent.length - 1][field];
    
    return {
      change: last - first,
      percentage: ((last - first) / first) * 100,
      trend: last > first ? 'increasing' : 'decreasing'
    };
  }
  
  // Get average balance
  getAverageBalance(type = 'payout', periods = 10) {
    const recent = this.history.slice(-periods);
    if (recent.length === 0) return null;
    
    const field = `${type}_balance`;
    const sum = recent.reduce((acc, record) => acc + record[field], 0);
    return sum / recent.length;
  }
}
 
// Usage
const tracker = new BalanceTracker();
 
// Record balance every hour
setInterval(() => {
  tracker.recordBalance();
}, 60 * 60 * 1000);

Integration Patterns

React Hook for Balance Management

import { useState, useEffect, useCallback } from 'react';
 
function useBalance(autoRefresh = true, refreshInterval = 300000) { // 5 minutes
  const [balance, setBalance] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const fetchBalance = useCallback(async () => {
    setLoading(true);
    setError(null);
    
    try {
      const balanceData = await checkBalance();
      setBalance(balanceData);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  }, []);
  
  // Auto-refresh balance
  useEffect(() => {
    fetchBalance(); // Initial fetch
    
    if (autoRefresh) {
      const interval = setInterval(fetchBalance, refreshInterval);
      return () => clearInterval(interval);
    }
  }, [fetchBalance, autoRefresh, refreshInterval]);
  
  return {
    balance,
    loading,
    error,
    refresh: fetchBalance
  };
}
 
// Usage in React component
function BalanceDashboard() {
  const { balance, loading, error, refresh } = useBalance();
  
  if (loading && !balance) {
    return <div>Loading balance...</div>;
  }
  
  if (error) {
    return (
      <div>
        <p>Error: {error}</p>
        <button onClick={refresh}>Retry</button>
      </div>
    );
  }
  
  return (
    <div>
      <h2>Wallet Balance</h2>
      <div>
        <p>Payin Balance: ₹{balance?.payin_balance || 0}</p>
        <p>Payout Balance: ₹{balance?.payout_balance || 0}</p>
      </div>
      <button onClick={refresh} disabled={loading}>
        {loading ? 'Refreshing...' : 'Refresh'}
      </button>
    </div>
  );
}

Python Balance Manager

import requests
import time
import threading
from datetime import datetime
import os
 
class BalanceManager:
    def __init__(self, token=None):
        self.token = token or os.getenv('PAYVANTA_TOKEN')
        self.base_url = 'https://api.payvanta.in'
        self.headers = {
            'Authorization': f'Bearer {self.token}',
            'Content-Type': 'application/json'
        }
        self.balance_cache = None
        self.cache_timestamp = None
        self.cache_duration = 60  # Cache for 60 seconds
        
    def get_balance(self, use_cache=True):
        """Get current balance with optional caching"""
        now = time.time()
        
        if (use_cache and self.balance_cache and 
            self.cache_timestamp and 
            now - self.cache_timestamp < self.cache_duration):
            return self.balance_cache
            
        try:
            response = requests.get(
                f'{self.base_url}/balance-check',
                headers=self.headers,
                timeout=10
            )
            response.raise_for_status()
            
            balance = response.json()
            
            # Update cache
            self.balance_cache = balance
            self.cache_timestamp = now
            
            return balance
            
        except requests.exceptions.RequestException as e:
            raise Exception(f"Balance check failed: {str(e)}")
    
    def validate_payout_balance(self, amount):
        """Validate if payout balance is sufficient"""
        balance = self.get_balance()
        return balance['payout_balance'] >= amount
    
    def get_balance_status(self, thresholds=None):
        """Get balance status with threshold checks"""
        if thresholds is None:
            thresholds = {'payin_low': 100, 'payout_low': 50}
            
        balance = self.get_balance()
        
        status = {
            'balance': balance,
            'timestamp': datetime.now().isoformat(),
            'alerts': []
        }
        
        if balance['payin_balance'] < thresholds['payin_low']:
            status['alerts'].append({
                'type': 'payin_low',
                'message': f"Payin balance ({balance['payin_balance']}) below threshold ({thresholds['payin_low']})"
            })
            
        if balance['payout_balance'] < thresholds['payout_low']:
            status['alerts'].append({
                'type': 'payout_low', 
                'message': f"Payout balance ({balance['payout_balance']}) below threshold ({thresholds['payout_low']})"
            })
            
        return status
 
# Usage example
balance_manager = BalanceManager()
 
# Check balance
try:
    balance = balance_manager.get_balance()
    print(f"Payin Balance: ₹{balance['payin_balance']}")
    print(f"Payout Balance: ₹{balance['payout_balance']}")
    
    # Validate before payout
    if balance_manager.validate_payout_balance(100):
        print("✅ Sufficient balance for payout")
    else:
        print("❌ Insufficient balance for payout")
        
except Exception as e:
    print(f"Error: {e}")

Best Practices

1. Caching Strategy

class BalanceCache {
  constructor(cacheDurationMs = 60000) { // 1 minute default
    this.cache = null;
    this.cacheTimestamp = null;
    this.cacheDuration = cacheDurationMs;
  }
  
  async getBalance(forceRefresh = false) {
    const now = Date.now();
    
    if (!forceRefresh && 
        this.cache && 
        this.cacheTimestamp && 
        now - this.cacheTimestamp < this.cacheDuration) {
      return this.cache;
    }
    
    // Fetch fresh balance
    const balance = await checkBalance();
    
    this.cache = balance;
    this.cacheTimestamp = now;
    
    return balance;
  }
  
  invalidateCache() {
    this.cache = null;
    this.cacheTimestamp = null;
  }
}

2. Error Handling and Retry Logic

async function robustBalanceCheck(maxRetries = 3, retryDelay = 1000) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await checkBalance();
    } catch (error) {
      console.warn(`Balance check attempt ${attempt} failed:`, error.message);
      
      if (attempt === maxRetries) {
        throw new Error(`Balance check failed after ${maxRetries} attempts: ${error.message}`);
      }
      
      // Wait before retry
      await new Promise(resolve => setTimeout(resolve, retryDelay * attempt));
    }
  }
}

3. Rate Limiting Compliance

class RateLimitedBalanceChecker {
  constructor(maxRequestsPerMinute = 50) {
    this.maxRequests = maxRequestsPerMinute;
    this.requests = [];
  }
  
  async checkBalance() {
    await this.waitForRateLimit();
    
    const now = Date.now();
    this.requests.push(now);
    
    // Clean old requests
    this.requests = this.requests.filter(time => now - time < 60000);
    
    return await checkBalance();
  }
  
  async waitForRateLimit() {
    const now = Date.now();
    const recentRequests = this.requests.filter(time => now - time < 60000);
    
    if (recentRequests.length >= this.maxRequests) {
      const oldestRequest = Math.min(...recentRequests);
      const waitTime = 60000 - (now - oldestRequest);
      
      if (waitTime > 0) {
        console.log(`Rate limit reached, waiting ${waitTime}ms`);
        await new Promise(resolve => setTimeout(resolve, waitTime));
      }
    }
  }
}

Common Use Cases

1. Pre-Transaction Validation

Always check balance before processing payouts to avoid failed transactions:

async function validateAndProcessPayout(payoutData) {
  const balance = await checkBalance();
  
  if (balance.payout_balance < payoutData.amount) {
    return {
      success: false,
      error: 'Insufficient balance',
      required: payoutData.amount,
      available: balance.payout_balance
    };
  }
  
  return await processPayout(payoutData);
}

2. Balance Alerts

Set up automated alerts for low balance:

function setupBalanceAlerts() {
  setInterval(async () => {
    try {
      const balance = await checkBalance();
      
      if (balance.payout_balance < 100) {
        // Send email, SMS, or push notification
        await sendAlert('Low payout balance', balance);
      }
    } catch (error) {
      console.error('Balance alert check failed:', error);
    }
  }, 5 * 60 * 1000); // Check every 5 minutes
}

3. Dashboard Integration

Display real-time balance in your admin dashboard:

// Dashboard API endpoint
app.get('/api/dashboard/balance', async (req, res) => {
  try {
    const balance = await checkBalance();
    
    res.json({
      success: true,
      data: {
        payin_balance: balance.payin_balance,
        payout_balance: balance.payout_balance,
        last_updated: new Date().toISOString()
      }
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      error: 'Failed to fetch balance'
    });
  }
});

Troubleshooting

Common Issues

  1. Authentication Errors

    • Verify Bearer token is correctly generated
    • Check API credentials are valid
    • Ensure token is properly formatted
  2. Rate Limiting

    • Implement caching to reduce API calls
    • Add retry logic with exponential backoff
    • Monitor your request frequency
  3. Network Timeouts

    • Set appropriate timeout values
    • Implement retry mechanisms
    • Handle network failures gracefully

Debug Example

async function debugBalanceCheck() {
  console.log('🔍 Starting balance check debug...');
  
  try {
    console.log('📡 Making API request...');
    const startTime = Date.now();
    
    const balance = await checkBalance();
    
    const responseTime = Date.now() - startTime;
    console.log(`✅ Balance retrieved in ${responseTime}ms`);
    console.log('💰 Balance data:', balance);
    
    return balance;
  } catch (error) {
    console.error('❌ Balance check failed:', {
      message: error.message,
      status: error.response?.status,
      data: error.response?.data
    });
    throw error;
  }
}

Next Steps

Support

For questions about balance checking or integration help, contact our support team at support@payvanta.in