Balance and Settlement Reports
Balance and settlement reports provide comprehensive insights into your account balance, payouts, fees, and fund transfers. Use these reports for financial reconciliation, cash flow management, and accounting purposes.
Overviewโ
Balance reporting features include:
- Real-time Balance: View current available and pending balance
- Balance History: Track balance changes over time
- Settlement Reports: Detailed breakdown of payouts
- Payout Tracking: Monitor bank transfers and their status
- Fee Analysis: Understand transaction and service fees
- Multi-currency Support: Manage balances in multiple currencies
- Reconciliation Tools: Match settlements with bank statements
- Reserve Tracking: Monitor rolling reserve balances
- Scheduled Payouts: View upcoming settlement schedules
Balance Overviewโ
Account Balance Structureโ
Your Omise account balance consists of several components:
{
"available": 1250000, // Available for payout (in smallest currency unit)
"pending": 350000, // Pending settlement
"total": 1600000, // Total = available + pending
"currency": "THB",
"transferable": 1250000, // Amount that can be transferred
"reserve": 200000 // Rolling reserve (if applicable)
}
Balance Componentsโ
-
Available Balance
- Funds ready for payout
- Can be transferred to your bank account
- Updated after settlement period
-
Pending Balance
- Transactions in settlement period
- Not yet available for payout
- Typically clears in 3-7 business days
-
Reserve Balance
- Held for risk management (if applicable)
- Released according to reserve schedule
- Shown separately in reports
Viewing Balance via Dashboardโ
- Navigate to Balance section
- View current balances:
- Available balance
- Pending balance
- Total balance
- Select currency (if multi-currency)
- View balance history chart
Retrieving Balance via APIโ
// Node.js - Retrieve account balance
const omise = require('omise')({
secretKey: 'skey_test_xxxxx'
});
async function getAccountBalance() {
try {
const balance = await omise.balance.retrieve();
console.log('Account Balance:');
console.log('================');
console.log(`Available: ${balance.available / 100} ${balance.currency}`);
console.log(`Pending: ${balance.total - balance.available} ${balance.currency}`);
console.log(`Total: ${balance.total / 100} ${balance.currency}`);
return balance;
} catch (error) {
console.error('Error retrieving balance:', error);
throw error;
}
}
getAccountBalance();
# Python - Balance tracking and alerts
import omise
from datetime import datetime
omise.api_secret = 'skey_test_xxxxx'
def check_balance_and_alert(threshold):
"""Check balance and send alert if below threshold."""
balance = omise.Balance.retrieve()
available = balance.available / 100
pending = (balance.total - balance.available) / 100
currency = balance.currency.upper()
print(f"Balance Check - {datetime.now().isoformat()}")
print(f"Available: {available:.2f} {currency}")
print(f"Pending: {pending:.2f} {currency}")
if available < threshold:
print(f"โ ๏ธ WARNING: Available balance ({available:.2f}) below threshold ({threshold})")
# Send alert (email, SMS, etc.)
else:
print(f"โ Balance OK")
return {
'available': available,
'pending': pending,
'currency': currency,
'below_threshold': available < threshold
}
# Check balance with 10,000 THB threshold
check_balance_and_alert(10000)
# Ruby - Balance history tracker
require 'omise'
require 'json'
Omise.api_key = 'skey_test_xxxxx'
class BalanceTracker
def initialize
@history = []
end
def record_balance
balance = Omise::Balance.retrieve
record = {
timestamp: Time.now.iso8601,
available: balance.available / 100.0,
pending: (balance.total - balance.available) / 100.0,
total: balance.total / 100.0,
currency: balance.currency.upcase
}
@history << record
record
end
def analyze_trend(days = 7)
# Record current balance
current = record_balance
# Calculate daily change (simplified example)
if @history.length >= 2
previous = @history[-2]
change = current[:available] - previous[:available]
change_percent = (change / previous[:available] * 100).round(2)
{
current_balance: current[:available],
previous_balance: previous[:available],
change: change,
change_percent: change_percent,
trend: change >= 0 ? 'increasing' : 'decreasing'
}
else
{ message: 'Insufficient data for trend analysis' }
end
end
def export_history(filename)
File.write(filename, JSON.pretty_generate(@history))
puts "Balance history exported to #{filename}"
end
end
# Track balance
tracker = BalanceTracker.new
tracker.record_balance
# Analyze trend
trend = tracker.analyze_trend
puts "Balance Trend:"
puts JSON.pretty_generate(trend)
<?php
// PHP - Balance monitoring dashboard
require_once 'vendor/autoload.php';
define('OMISE_SECRET_KEY', 'skey_test_xxxxx');
class BalanceMonitor {
public function getCurrentBalance() {
$balance = OmiseBalance::retrieve();
return [
'available' => $balance['available'] / 100,
'pending' => ($balance['total'] - $balance['available']) / 100,
'total' => $balance['total'] / 100,
'currency' => strtoupper($balance['currency']),
'timestamp' => date('c')
];
}
public function getBalanceBreakdown() {
$balance = OmiseBalance::retrieve();
$charges = OmiseCharge::retrieve([
'from' => date('c', strtotime('-7 days')),
'to' => date('c'),
'limit' => 100
]);
$breakdown = [
'current_balance' => $balance['available'] / 100,
'recent_activity' => [
'charges' => 0,
'refunds' => 0,
'fees' => 0
]
];
foreach ($charges['data'] as $charge) {
if ($charge['paid']) {
$breakdown['recent_activity']['charges'] += $charge['amount'] / 100;
$breakdown['recent_activity']['fees'] += ($charge['fee'] ?? 0) / 100;
}
$breakdown['recent_activity']['refunds'] += $charge['refunded_amount'] / 100;
}
return $breakdown;
}
public function generateBalanceReport() {
$balance = $this->getCurrentBalance();
$breakdown = $this->getBalanceBreakdown();
return [
'report_date' => date('Y-m-d H:i:s'),
'balance' => $balance,
'breakdown' => $breakdown
];
}
}
// Generate balance report
$monitor = new BalanceMonitor();
$report = $monitor->generateBalanceReport();
echo json_encode($report, JSON_PRETTY_PRINT);
?>
// Go - Real-time balance monitoring
package main
import (
"encoding/json"
"fmt"
"time"
"github.com/omise/omise-go"
)
type BalanceSnapshot struct {
Timestamp time.Time `json:"timestamp"`
Available float64 `json:"available"`
Pending float64 `json:"pending"`
Total float64 `json:"total"`
Currency string `json:"currency"`
Transferable float64 `json:"transferable"`
}
type BalanceMonitor struct {
client *omise.Client
snapshots []BalanceSnapshot
}
func NewBalanceMonitor(publicKey, secretKey string) (*BalanceMonitor, error) {
client, err := omise.NewClient(publicKey, secretKey)
if err != nil {
return nil, err
}
return &BalanceMonitor{
client: client,
snapshots: make([]BalanceSnapshot, 0),
}, nil
}
func (bm *BalanceMonitor) RecordBalance() (*BalanceSnapshot, error) {
balance := &omise.Balance{}
err := bm.client.Do(balance, &omise.RetrieveBalance{})
if err != nil {
return nil, err
}
snapshot := BalanceSnapshot{
Timestamp: time.Now(),
Available: float64(balance.Available) / 100,
Pending: float64(balance.Total-balance.Available) / 100,
Total: float64(balance.Total) / 100,
Currency: balance.Currency,
Transferable: float64(balance.Transferable) / 100,
}
bm.snapshots = append(bm.snapshots, snapshot)
return &snapshot, nil
}
func (bm *BalanceMonitor) GetBalanceHistory() []BalanceSnapshot {
return bm.snapshots
}
func (bm *BalanceMonitor) CalculateChange() map[string]interface{} {
if len(bm.snapshots) < 2 {
return map[string]interface{}{
"error": "Insufficient data",
}
}
current := bm.snapshots[len(bm.snapshots)-1]
previous := bm.snapshots[len(bm.snapshots)-2]
change := current.Available - previous.Available
changePercent := 0.0
if previous.Available > 0 {
changePercent = (change / previous.Available) * 100
}
return map[string]interface{}{
"current": current.Available,
"previous": previous.Available,
"change": change,
"change_percent": changePercent,
"period_seconds": current.Timestamp.Sub(previous.Timestamp).Seconds(),
}
}
func main() {
monitor, err := NewBalanceMonitor("pkey_test_xxxxx", "skey_test_xxxxx")
if err != nil {
panic(err)
}
// Record initial balance
snapshot, err := monitor.RecordBalance()
if err != nil {
panic(err)
}
jsonData, _ := json.MarshalIndent(snapshot, "", " ")
fmt.Println("Current Balance:")
fmt.Println(string(jsonData))
}
Balance Historyโ
Historical Balance Trackingโ
View how your balance has changed over time:
# Python - Detailed balance history analysis
import omise
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
omise.api_secret = 'skey_test_xxxxx'
def analyze_balance_history(days=30):
"""Analyze balance changes over specified period."""
# Fetch transfers (payouts)
to_date = datetime.now()
from_date = to_date - timedelta(days=days)
transfers = omise.Transfer.list(
from_date=from_date.isoformat(),
to_date=to_date.isoformat(),
limit=100
)
# Fetch charges
charges = omise.Charge.list(
from_date=from_date.isoformat(),
to_date=to_date.isoformat(),
paid=True,
limit=100
)
# Calculate daily balance changes
daily_changes = {}
# Add charge amounts
for charge in charges.data:
date = datetime.fromtimestamp(charge.created).date()
if date not in daily_changes:
daily_changes[date] = {'incoming': 0, 'outgoing': 0, 'fees': 0}
daily_changes[date]['incoming'] += charge.amount
daily_changes[date]['fees'] += charge.fee or 0
# Subtract transfer amounts
for transfer in transfers.data:
date = datetime.fromtimestamp(transfer.created).date()
if date not in daily_changes:
daily_changes[date] = {'incoming': 0, 'outgoing': 0, 'fees': 0}
daily_changes[date]['outgoing'] += transfer.amount
# Calculate running balance (simplified)
current_balance = omise.Balance.retrieve()
running_balance = current_balance.available / 100
history = []
for date in sorted(daily_changes.keys(), reverse=True):
changes = daily_changes[date]
net_change = (changes['incoming'] - changes['outgoing'] - changes['fees']) / 100
history.append({
'date': date.isoformat(),
'incoming': changes['incoming'] / 100,
'outgoing': changes['outgoing'] / 100,
'fees': changes['fees'] / 100,
'net_change': net_change,
'balance': running_balance
})
running_balance -= net_change
return sorted(history, key=lambda x: x['date'])
# Analyze last 30 days
history = analyze_balance_history(30)
# Print summary
print("Balance History (Last 30 Days)")
print("=" * 70)
for record in history[-7:]: # Last 7 days
print(f"{record['date']}: "
f"In: {record['incoming']:,.2f} | "
f"Out: {record['outgoing']:,.2f} | "
f"Fees: {record['fees']:,.2f} | "
f"Net: {record['net_change']:+,.2f} | "
f"Balance: {record['balance']:,.2f}")
Settlement Reportsโ
Understanding Settlementsโ
Settlements are the process of transferring funds from your Omise account to your bank account.
Settlement Scheduleโ
- Standard: T+3 to T+7 business days
- Custom: Based on your merchant agreement
- Automatic: Scheduled transfers when balance reaches threshold
- Manual: On-demand transfers (if enabled)
Viewing Settlement Reportsโ
// Node.js - Comprehensive settlement report
const omise = require('omise')({
secretKey: 'skey_test_xxxxx'
});
async function generateSettlementReport(fromDate, toDate) {
try {
// Fetch transfers (settlements/payouts)
const transfers = await omise.transfers.list({
from: fromDate,
to: toDate,
limit: 100,
order: 'reverse_chronological'
});
const report = {
period: {
from: fromDate,
to: toDate
},
summary: {
total_settlements: transfers.total,
total_amount: 0,
total_fees: 0,
net_amount: 0,
by_status: {}
},
settlements: []
};
// Process each settlement
for (const transfer of transfers.data) {
const amount = transfer.amount / 100;
const fee = (transfer.fee || 0) / 100;
const net = amount - fee;
// Update summary
report.summary.total_amount += amount;
report.summary.total_fees += fee;
report.summary.net_amount += net;
// Count by status
if (!report.summary.by_status[transfer.status]) {
report.summary.by_status[transfer.status] = 0;
}
report.summary.by_status[transfer.status]++;
// Get associated transactions
const transactions = await omise.transactions.list({
transfer: transfer.id,
limit: 100
});
report.settlements.push({
id: transfer.id,
amount: amount,
fee: fee,
net_amount: net,
currency: transfer.currency.toUpperCase(),
status: transfer.status,
bank_account: {
name: transfer.bank_account.name,
last4: transfer.bank_account.last_digits
},
created: new Date(transfer.created * 1000).toISOString(),
paid_at: transfer.paid_at ? new Date(transfer.paid_at * 1000).toISOString() : null,
transaction_count: transactions.total
});
}
return report;
} catch (error) {
console.error('Error generating settlement report:', error);
throw error;
}
}
// Generate report for last month
const toDate = new Date();
const fromDate = new Date(toDate.getTime() - 30 * 24 * 60 * 60 * 1000);
generateSettlementReport(fromDate.toISOString(), toDate.toISOString())
.then(report => {
console.log('Settlement Report');
console.log('================');
console.log(`Period: ${report.period.from} to ${report.period.to}`);
console.log(`Total Settlements: ${report.summary.total_settlements}`);
console.log(`Total Amount: ${report.summary.total_amount.toFixed(2)}`);
console.log(`Total Fees: ${report.summary.total_fees.toFixed(2)}`);
console.log(`Net Amount: ${report.summary.net_amount.toFixed(2)}`);
console.log('\nBy Status:');
Object.entries(report.summary.by_status).forEach(([status, count]) => {
console.log(` ${status}: ${count}`);
});
});
Settlement Detailsโ
Each settlement includes:
{
"id": "trsf_test_5xyz789",
"amount": 50000, // Amount in smallest currency unit
"fee": 500, // Transfer fee
"net": 49500, // Net amount (amount - fee)
"currency": "THB",
"status": "paid", // pending, sent, paid, failed
"bank_account": {
"brand": "bbl",
"number": "****1234",
"name": "MERCHANT NAME"
},
"created": "2024-01-15T10:30:00Z",
"paid_at": "2024-01-16T08:15:00Z",
"transaction_count": 127, // Number of transactions included
"failure_code": null,
"failure_message": null
}
Payout Reportsโ
Tracking Payoutsโ
Monitor all bank transfers from your Omise account:
# Ruby - Detailed payout tracking
require 'omise'
require 'csv'
Omise.api_key = 'skey_test_xxxxx'
class PayoutTracker
def initialize(from_date, to_date)
@from_date = from_date
@to_date = to_date
end
def generate_payout_report
transfers = Omise::Transfer.list({
from: @from_date.iso8601,
to: @to_date.iso8601,
limit: 100
})
report = {
period: {
from: @from_date.to_date.to_s,
to: @to_date.to_date.to_s
},
payouts: [],
summary: {
total: 0,
completed: 0,
pending: 0,
failed: 0,
total_amount: 0,
total_fees: 0
}
}
transfers.data.each do |transfer|
payout = {
id: transfer.id,
amount: transfer.amount / 100.0,
fee: (transfer.fee || 0) / 100.0,
net: (transfer.amount - (transfer.fee || 0)) / 100.0,
currency: transfer.currency.upcase,
status: transfer.status,
bank: transfer.bank_account.brand.upcase,
account_last4: transfer.bank_account.last_digits,
created: Time.at(transfer.created).utc.iso8601,
paid_at: transfer.paid_at ? Time.at(transfer.paid_at).utc.iso8601 : nil
}
report[:payouts] << payout
report[:summary][:total] += 1
report[:summary][:total_amount] += payout[:amount]
report[:summary][:total_fees] += payout[:fee]
case transfer.status
when 'paid'
report[:summary][:completed] += 1
when 'pending', 'sent'
report[:summary][:pending] += 1
when 'failed'
report[:summary][:failed] += 1
end
end
report
end
def export_to_csv(filename)
report = generate_payout_report
CSV.open(filename, 'w') do |csv|
csv << ['Payout ID', 'Amount', 'Fee', 'Net Amount', 'Currency',
'Status', 'Bank', 'Account', 'Created', 'Paid']
report[:payouts].each do |payout|
csv << [
payout[:id],
payout[:amount],
payout[:fee],
payout[:net],
payout[:currency],
payout[:status],
payout[:bank],
payout[:account_last4],
payout[:created],
payout[:paid_at] || 'N/A'
]
end
end
puts "Payout report exported to #{filename}"
end
def analyze_payout_performance
report = generate_payout_report
completed = report[:payouts].select { |p| p[:status] == 'paid' }
if completed.any?
# Calculate average processing time
processing_times = completed.map do |payout|
next unless payout[:paid_at]
created = Time.parse(payout[:created])
paid = Time.parse(payout[:paid_at])
(paid - created) / 3600.0 # Hours
end.compact
avg_time = processing_times.sum / processing_times.length
{
success_rate: (completed.length.to_f / report[:summary][:total] * 100).round(2),
average_processing_hours: avg_time.round(2),
total_completed: completed.length,
total_failed: report[:summary][:failed]
}
else
{ message: 'No completed payouts in period' }
end
end
end
# Generate payout report
from_date = DateTime.now - 30
to_date = DateTime.now
tracker = PayoutTracker.new(from_date, to_date)
report = tracker.generate_payout_report
puts "Payout Report Summary"
puts "=" * 50
puts "Period: #{report[:period][:from]} to #{report[:period][:to]}"
puts "Total Payouts: #{report[:summary][:total]}"
puts "Completed: #{report[:summary][:completed]}"
puts "Pending: #{report[:summary][:pending]}"
puts "Failed: #{report[:summary][:failed]}"
puts "Total Amount: #{report[:summary][:total_amount].round(2)}"
puts "Total Fees: #{report[:summary][:total_fees].round(2)}"
# Export to CSV
tracker.export_to_csv('payouts.csv')
# Analyze performance
performance = tracker.analyze_payout_performance
puts "\nPayout Performance:"
puts JSON.pretty_generate(performance)
Payout Status Trackingโ
<?php
// PHP - Real-time payout status monitoring
require_once 'vendor/autoload.php';
define('OMISE_SECRET_KEY', 'skey_test_xxxxx');
class PayoutStatusMonitor {
public function checkPayoutStatus($transferId) {
$transfer = OmiseTransfer::retrieve($transferId);
return [
'id' => $transfer['id'],
'status' => $transfer['status'],
'amount' => $transfer['amount'] / 100,
'currency' => strtoupper($transfer['currency']),
'created' => date('Y-m-d H:i:s', $transfer['created']),
'paid_at' => $transfer['paid_at'] ? date('Y-m-d H:i:s', $transfer['paid_at']) : null,
'bank_account' => $transfer['bank_account']['name'],
'failure_code' => $transfer['failure_code'],
'failure_message' => $transfer['failure_message']
];
}
public function getPendingPayouts() {
$transfers = OmiseTransfer::retrieve([
'limit' => 100
]);
$pending = array_filter($transfers['data'], function($transfer) {
return in_array($transfer['status'], ['pending', 'sent']);
});
return array_map(function($transfer) {
$hoursSinceCreated = (time() - $transfer['created']) / 3600;
return [
'id' => $transfer['id'],
'amount' => $transfer['amount'] / 100,
'status' => $transfer['status'],
'hours_pending' => round($hoursSinceCreated, 1),
'expected_completion' => date('Y-m-d', $transfer['created'] + (3 * 24 * 3600))
];
}, array_values($pending));
}
public function getFailedPayouts($days = 30) {
$transfers = OmiseTransfer::retrieve([
'from' => date('c', strtotime("-{$days} days")),
'to' => date('c'),
'limit' => 100
]);
$failed = array_filter($transfers['data'], function($transfer) {
return $transfer['status'] === 'failed';
});
return array_map(function($transfer) {
return [
'id' => $transfer['id'],
'amount' => $transfer['amount'] / 100,
'created' => date('Y-m-d H:i:s', $transfer['created']),
'failure_code' => $transfer['failure_code'],
'failure_message' => $transfer['failure_message'],
'bank' => $transfer['bank_account']['brand']
];
}, array_values($failed));
}
}
// Monitor payouts
$monitor = new PayoutStatusMonitor();
// Check pending payouts
$pending = $monitor->getPendingPayouts();
echo "Pending Payouts: " . count($pending) . "\n";
foreach ($pending as $payout) {
echo sprintf(
" %s: %.2f (%s) - Pending for %.1f hours\n",
$payout['id'],
$payout['amount'],
$payout['status'],
$payout['hours_pending']
);
}
// Check failed payouts
$failed = $monitor->getFailedPayouts();
if (!empty($failed)) {
echo "\nFailed Payouts: " . count($failed) . "\n";
foreach ($failed as $payout) {
echo sprintf(
" %s: %.2f - %s\n",
$payout['id'],
$payout['amount'],
$payout['failure_message']
);
}
}
?>
Fee Breakdownsโ
Understanding Feesโ
Omise charges various fees for processing payments:
-
Transaction Fees
- Percentage of transaction amount
- Varies by payment method
- Charged when payment succeeds
-
Transfer Fees
- Fixed fee per bank transfer
- Charged when funds are paid out
- Varies by bank and amount
-
Service Fees
- Additional service charges
- 3D Secure fees
- Installment fees
Fee Analysisโ
// Go - Comprehensive fee analysis
package main
import (
"encoding/json"
"fmt"
"time"
"github.com/omise/omise-go"
"github.com/omise/omise-go/operations"
)
type FeeAnalysis struct {
Period Period `json:"period"`
TotalFees float64 `json:"total_fees"`
TransactionFees float64 `json:"transaction_fees"`
TransferFees float64 `json:"transfer_fees"`
FeesByMethod map[string]float64 `json:"fees_by_method"`
AverageFeeRate float64 `json:"average_fee_rate"`
Details []FeeDetail `json:"details"`
}
type Period struct {
From string `json:"from"`
To string `json:"to"`
}
type FeeDetail struct {
Type string `json:"type"`
Amount float64 `json:"amount"`
Date string `json:"date"`
Method string `json:"method"`
}
func analyzeFees(from, to time.Time) (*FeeAnalysis, error) {
client, err := omise.NewClient("pkey_test_xxxxx", "skey_test_xxxxx")
if err != nil {
return nil, err
}
analysis := &FeeAnalysis{
Period: Period{
From: from.Format("2006-01-02"),
To: to.Format("2006-01-02"),
},
FeesByMethod: make(map[string]float64),
Details: make([]FeeDetail, 0),
}
// Fetch charges
charges := &omise.ChargeList{}
err = client.Do(charges, &operations.ListCharges{
ListParams: operations.ListParams{
From: from,
To: to,
Limit: 100,
},
})
if err != nil {
return nil, err
}
var totalRevenue int64
// Analyze transaction fees
for _, charge := range charges.Data {
if charge.Paid && charge.FeeVat > 0 {
fee := float64(charge.FeeVat) / 100
analysis.TransactionFees += fee
analysis.TotalFees += fee
method := "card"
if charge.Card != nil {
method = charge.Card.Brand
}
analysis.FeesByMethod[method] += fee
analysis.Details = append(analysis.Details, FeeDetail{
Type: "transaction",
Amount: fee,
Date: charge.Created.Format("2006-01-02"),
Method: method,
})
totalRevenue += charge.Amount
}
}
// Fetch transfers for transfer fees
transfers := &omise.TransferList{}
err = client.Do(transfers, &operations.ListTransfers{
ListParams: operations.ListParams{
From: from,
To: to,
Limit: 100,
},
})
if err != nil {
return nil, err
}
for _, transfer := range transfers.Data {
if transfer.Fee > 0 {
fee := float64(transfer.Fee) / 100
analysis.TransferFees += fee
analysis.TotalFees += fee
analysis.Details = append(analysis.Details, FeeDetail{
Type: "transfer",
Amount: fee,
Date: transfer.Created.Format("2006-01-02"),
Method: "bank_transfer",
})
}
}
// Calculate average fee rate
if totalRevenue > 0 {
analysis.AverageFeeRate = (analysis.TransactionFees / (float64(totalRevenue) / 100)) * 100
}
return analysis, nil
}
func main() {
to := time.Now()
from := to.AddDate(0, -1, 0) // Last month
analysis, err := analyzeFees(from, to)
if err != nil {
panic(err)
}
jsonData, _ := json.MarshalIndent(analysis, "", " ")
fmt.Println("Fee Analysis:")
fmt.Println(string(jsonData))
fmt.Printf("\nSummary:\n")
fmt.Printf("Total Fees: %.2f\n", analysis.TotalFees)
fmt.Printf("Transaction Fees: %.2f\n", analysis.TransactionFees)
fmt.Printf("Transfer Fees: %.2f\n", analysis.TransferFees)
fmt.Printf("Average Fee Rate: %.2f%%\n", analysis.AverageFeeRate)
}
Reconciliation Toolsโ
Bank Statement Reconciliationโ
Match settlement reports with bank statements:
// Node.js - Automated bank reconciliation
const omise = require('omise')({
secretKey: 'skey_test_xxxxx'
});
const fs = require('fs');
const csv = require('csv-parser');
class BankReconciliation {
constructor() {
this.results = {
matched: [],
omise_only: [],
bank_only: [],
discrepancies: []
};
}
async loadBankStatement(csvFile) {
return new Promise((resolve, reject) => {
const transactions = [];
fs.createReadStream(csvFile)
.pipe(csv())
.on('data', (row) => {
transactions.push({
date: row['Date'],
description: row['Description'],
amount: parseFloat(row['Amount']),
reference: this.extractReference(row['Description'])
});
})
.on('end', () => resolve(transactions))
.on('error', reject);
});
}
extractReference(description) {
// Extract Omise transfer ID from bank description
const match = description.match(/trsf_test_\w+/);
return match ? match[0] : null;
}
async reconcile(fromDate, toDate, bankTransactions) {
// Fetch Omise transfers
const transfers = await omise.transfers.list({
from: fromDate,
to: toDate,
limit: 100
});
// Create lookup maps
const omiseMap = new Map();
transfers.data.forEach(t => {
omiseMap.set(t.id, {
id: t.id,
amount: t.amount / 100,
net: (t.amount - (t.fee || 0)) / 100,
date: new Date(t.paid_at * 1000).toISOString().split('T')[0],
status: t.status
});
});
const bankMap = new Map();
bankTransactions.forEach(t => {
if (t.reference) {
bankMap.set(t.reference, t);
}
});
// Match transactions
omiseMap.forEach((omiseTx, id) => {
const bankTx = bankMap.get(id);
if (bankTx) {
const amountDiff = Math.abs(omiseTx.net - bankTx.amount);
if (amountDiff < 0.01) {
this.results.matched.push({
reference: id,
omise_amount: omiseTx.net,
bank_amount: bankTx.amount,
date: omiseTx.date
});
} else {
this.results.discrepancies.push({
reference: id,
omise_amount: omiseTx.net,
bank_amount: bankTx.amount,
difference: bankTx.amount - omiseTx.net
});
}
bankMap.delete(id);
} else if (omiseTx.status === 'paid') {
this.results.omise_only.push(omiseTx);
}
});
// Remaining bank transactions
bankMap.forEach(tx => {
this.results.bank_only.push(tx);
});
return this.results;
}
generateReport() {
const report = {
summary: {
total_matched: this.results.matched.length,
total_discrepancies: this.results.discrepancies.length,
omise_not_in_bank: this.results.omise_only.length,
bank_not_in_omise: this.results.bank_only.length,
reconciliation_rate: (
this.results.matched.length /
(this.results.matched.length + this.results.discrepancies.length + this.results.omise_only.length) * 100
).toFixed(2) + '%'
},
details: this.results
};
return report;
}
}
// Perform reconciliation
async function performReconciliation() {
const reconciler = new BankReconciliation();
// Load bank statement
const bankTransactions = await reconciler.loadBankStatement('bank_statement.csv');
// Reconcile with Omise data
const fromDate = new Date('2024-01-01').toISOString();
const toDate = new Date('2024-01-31').toISOString();
await reconciler.reconcile(fromDate, toDate, bankTransactions);
// Generate report
const report = reconciler.generateReport();
console.log('Reconciliation Report');
console.log('===================');
console.log(JSON.stringify(report.summary, null, 2));
if (report.details.discrepancies.length > 0) {
console.log('\nDiscrepancies:');
report.details.discrepancies.forEach(d => {
console.log(` ${d.reference}: Omise ${d.omise_amount} vs Bank ${d.bank_amount} (Diff: ${d.difference})`);
});
}
// Save full report
fs.writeFileSync('reconciliation_report.json', JSON.stringify(report, null, 2));
console.log('\nFull report saved to reconciliation_report.json');
}
performReconciliation().catch(console.error);
Multi-Currency Reportsโ
Managing Multiple Currenciesโ
# Python - Multi-currency balance management
import omise
from collections import defaultdict
from datetime import datetime, timedelta
omise.api_secret = 'skey_test_xxxxx'
class MultiCurrencyReporter:
def __init__(self):
self.currencies = {}
def get_balance_by_currency(self):
"""Get current balance (Note: Omise returns single currency balance)."""
balance = omise.Balance.retrieve()
return {
balance.currency.upper(): {
'available': balance.available / 100,
'pending': (balance.total - balance.available) / 100,
'total': balance.total / 100
}
}
def analyze_transactions_by_currency(self, from_date, to_date):
"""Analyze transactions grouped by currency."""
charges = omise.Charge.list(
from_date=from_date.isoformat(),
to_date=to_date.isoformat(),
limit=100
)
by_currency = defaultdict(lambda: {
'count': 0,
'successful': 0,
'failed': 0,
'total_amount': 0,
'refunded_amount': 0,
'fees': 0
})
for charge in charges.data:
currency = charge.currency.upper()
stats = by_currency[currency]
stats['count'] += 1
if charge.paid:
stats['successful'] += 1
stats['total_amount'] += charge.amount / 100
stats['fees'] += (charge.fee or 0) / 100
else:
stats['failed'] += 1
stats['refunded_amount'] += charge.refunded_amount / 100
return dict(by_currency)
def generate_currency_report(self, from_date, to_date):
"""Generate comprehensive multi-currency report."""
balance = self.get_balance_by_currency()
transactions = self.analyze_transactions_by_currency(from_date, to_date)
report = {
'report_date': datetime.now().isoformat(),
'period': {
'from': from_date.isoformat(),
'to': to_date.isoformat()
},
'current_balance': balance,
'transaction_analysis': transactions
}
return report
def convert_currency(self, amount, from_currency, to_currency, rates):
"""Convert amount between currencies using provided rates."""
if from_currency == to_currency:
return amount
# Convert to base currency (USD) then to target
base_amount = amount / rates.get(from_currency, 1)
return base_amount * rates.get(to_currency, 1)
def consolidated_report(self, from_date, to_date, base_currency='USD', rates=None):
"""Generate consolidated report in single currency."""
if rates is None:
# Example rates (in production, fetch from currency API)
rates = {
'USD': 1.0,
'THB': 35.5,
'SGD': 1.35,
'JPY': 150.0
}
report = self.generate_currency_report(from_date, to_date)
consolidated = {
'base_currency': base_currency,
'exchange_rates': rates,
'total_balance': 0,
'total_revenue': 0,
'total_fees': 0,
'by_currency': {}
}
# Consolidate balance
for currency, balance in report['current_balance'].items():
converted = self.convert_currency(
balance['total'],
currency,
base_currency,
rates
)
consolidated['total_balance'] += converted
consolidated['by_currency'][currency] = {
'original_balance': balance['total'],
'converted_balance': converted
}
# Consolidate transactions
for currency, stats in report['transaction_analysis'].items():
revenue = self.convert_currency(
stats['total_amount'],
currency,
base_currency,
rates
)
fees = self.convert_currency(
stats['fees'],
currency,
base_currency,
rates
)
consolidated['total_revenue'] += revenue
consolidated['total_fees'] += fees
if currency in consolidated['by_currency']:
consolidated['by_currency'][currency].update({
'original_revenue': stats['total_amount'],
'converted_revenue': revenue,
'transaction_count': stats['count']
})
return consolidated
# Generate multi-currency report
reporter = MultiCurrencyReporter()
to_date = datetime.now()
from_date = to_date - timedelta(days=30)
# Standard report
report = reporter.generate_currency_report(from_date, to_date)
print("Multi-Currency Report:")
print("=" * 50)
for currency, stats in report['transaction_analysis'].items():
print(f"\n{currency}:")
print(f" Transactions: {stats['count']}")
print(f" Successful: {stats['successful']}")
print(f" Total Amount: {stats['total_amount']:,.2f}")
print(f" Fees: {stats['fees']:,.2f}")
# Consolidated report
consolidated = reporter.consolidated_report(from_date, to_date, 'USD')
print(f"\n\nConsolidated Report (Base: {consolidated['base_currency']}):")
print("=" * 50)
print(f"Total Balance: {consolidated['total_balance']:,.2f}")
print(f"Total Revenue: {consolidated['total_revenue']:,.2f}")
print(f"Total Fees: {consolidated['total_fees']:,.2f}")
Best Practicesโ
1. Regular Reconciliationโ
- Reconcile daily for high-volume merchants
- Match settlements with bank statements weekly
- Investigate discrepancies immediately
- Maintain detailed records
2. Balance Monitoringโ
- Set up low balance alerts
- Monitor pending balance trends
- Track settlement schedules
- Plan for cash flow needs
3. Fee Managementโ
- Review fee breakdowns monthly
- Optimize payment method mix
- Consider fee structure in pricing
- Track fee trends over time
4. Report Automationโ
- Schedule regular balance reports
- Automate reconciliation processes
- Export data to accounting systems
- Set up alert notifications
5. Data Retentionโ
- Archive settlement reports
- Keep records for tax purposes
- Maintain audit trail
- Store securely and encrypted
Troubleshootingโ
Settlement Delaysโ
Problem: Expected payout hasn't arrived
Checklist:
- Check payout status in dashboard
- Verify bank account details are correct
- Confirm balance meets minimum payout threshold
- Check for bank holidays/weekends
- Review settlement schedule in merchant agreement
- Contact support if delay exceeds expected timeframe
// Check payout status
async function checkPayoutStatus(transferId) {
const transfer = await omise.transfers.retrieve(transferId);
console.log(`Status: ${transfer.status}`);
console.log(`Created: ${new Date(transfer.created * 1000).toISOString()}`);
console.log(`Paid: ${transfer.paid_at ? new Date(transfer.paid_at * 1000).toISOString() : 'Pending'}`);
if (transfer.failure_code) {
console.log(`Failure: ${transfer.failure_code} - ${transfer.failure_message}`);
}
return transfer;
}
Balance Discrepanciesโ
Problem: Balance doesn't match expectations
Solutions:
- Check for pending refunds
- Review recent settlements
- Verify all transactions are accounted for
- Check for fees and adjustments
- Review reserve balance (if applicable)
Failed Payoutsโ
Problem: Payout marked as failed
Common Causes:
- Incorrect bank account details
- Insufficient balance
- Bank account closed
- Bank system issues
Resolution:
- Check failure code and message
- Verify bank account information
- Update bank details if needed
- Contact support for assistance
FAQโ
Q1: How long does it take for funds to reach my bank account?
A: Standard settlement is T+3 to T+7 business days from the transaction date, depending on your merchant agreement. Once a payout is initiated, bank transfers typically take 1-2 business days.
Q2: Can I request manual payouts?
A: Manual payouts may be available depending on your merchant agreement. Contact Omise support to check if this feature is enabled for your account and any associated requirements.
Q3: Why is my available balance lower than expected?
A: Your available balance may be lower due to: pending transactions still in settlement period, rolling reserve requirements, recent refunds processed, or transaction fees deducted. Check your balance breakdown for details.
Q4: How do I reconcile Omise settlements with my bank statement?
A: Match settlement transfer IDs (trsf_*) with bank statement descriptions. Download settlement reports from the dashboard and compare net amounts with bank deposits. Use the reconciliation tools provided or build custom matching using the API.
Q5: What fees are included in balance calculations?
A: Balance amounts shown are after transaction fees are deducted. Transfer fees are deducted when payouts are processed. Review fee breakdown reports for detailed fee information.
Q6: Can I have different payout schedules for different currencies?
A: Payout schedules are typically set at the account level. For multi-currency accounts, contact Omise support to discuss custom settlement arrangements if needed.
Q7: How do I export balance reports for accounting?
A: Navigate to Balance > Reports, select your date range, and click Export. Choose CSV or Excel format for easy import into accounting software. You can also use the API to automate exports.
Q8: What happens to my balance if I have a chargeback?
A: Chargeback amounts are immediately deducted from your available balance. The deduction appears as a negative transaction in your balance history. Monitor disputes in the Dashboard to respond appropriately.
Related Resourcesโ
Documentationโ
- Dashboard Overview - Dashboard navigation
- Transaction Reports - Transaction reporting
- Transfers API - Transfer API reference
- Balance API - Balance API reference
Toolsโ
- Balance Dashboard - View current balance
- Transfer History - Payout history
- Reconciliation Tool - Automated matching
Supportโ
- Settlement FAQ - Common settlement questions
- Contact Support - Get help
Next Stepsโ
-
- Configure low balance warnings
- Enable payout notifications
- Set up email alerts
-
- Build automated matching
- Integrate with accounting systems
- Schedule regular reconciliation
-
- Understand settlement schedules
- Plan for payment timing
- Manage working capital