Recipients - Bank Account Management
Recipients represent bank accounts that you want to transfer funds to. This guide covers creating, managing, and validating recipients for secure and efficient payouts.
Overviewโ
Recipients in Omise allow you to:
- Store Bank Details: Securely save bank account information
- Validate Accounts: Verify bank account details before transfers
- Manage Multiple Recipients: Handle multiple payout destinations
- Automate Transfers: Use recipients in scheduled transfers
- Track History: See all transfers made to each recipient
- Update Information: Modify recipient details as needed
Key Featuresโ
- Bank Account Validation: Verify account numbers and bank codes
- Multiple Bank Support: Support for various Thai and international banks
- Metadata: Add custom information to recipients
- Status Tracking: Monitor recipient verification status
- Secure Storage: Bank details are securely encrypted
- Easy Updates: Modify recipient information anytime
Creating Recipientsโ
Basic Recipient Creationโ
- Node.js
- Python
- Ruby
- PHP
- Go
const omise = require('omise')({
secretKey: 'skey_test_123456789',
});
// Create a recipient for Thai bank account
async function createRecipient() {
try {
const recipient = await omise.recipients.create({
name: 'John Doe',
email: 'john.doe@example.com',
type: 'individual',
bank_account: {
brand: 'bbl',
number: '1234567890',
name: 'John Doe'
},
description: 'Supplier payment account',
metadata: {
supplier_id: 'SUP-001',
category: 'raw_materials'
}
});
console.log('Recipient created:', recipient.id);
console.log('Bank:', recipient.bank_account.brand);
console.log('Status:', recipient.active ? 'Active' : 'Inactive');
return recipient;
} catch (error) {
console.error('Failed to create recipient:', error.message);
throw error;
}
}
// Create recipient with validation
async function createValidatedRecipient(recipientData) {
// Validate required fields
if (!recipientData.name || !recipientData.bank_account) {
throw new Error('Name and bank account are required');
}
// Validate bank account number format
const accountNumber = recipientData.bank_account.number;
if (!/^\d{10,12}$/.test(accountNumber)) {
throw new Error('Invalid bank account number format');
}
try {
const recipient = await omise.recipients.create({
name: recipientData.name,
email: recipientData.email,
type: recipientData.type || 'individual',
bank_account: {
brand: recipientData.bank_account.brand,
number: recipientData.bank_account.number,
name: recipientData.bank_account.name
},
description: recipientData.description,
metadata: recipientData.metadata
});
console.log(`โ Recipient created successfully: ${recipient.id}`);
return recipient;
} catch (error) {
console.error('โ Recipient creation failed:', error.message);
throw error;
}
}
// Example usage
createRecipient();
createValidatedRecipient({
name: 'Jane Smith',
email: 'jane@example.com',
type: 'individual',
bank_account: {
brand: 'scb',
number: '9876543210',
name: 'Jane Smith'
},
description: 'Freelancer payment',
metadata: {
contractor_id: 'CONT-042',
department: 'marketing'
}
});
import omise
import re
omise.api_secret = 'skey_test_123456789'
def create_recipient(recipient_data):
"""Create a new recipient"""
try:
recipient = omise.Recipient.create(
name=recipient_data['name'],
email=recipient_data.get('email'),
type=recipient_data.get('type', 'individual'),
bank_account={
'brand': recipient_data['bank_account']['brand'],
'number': recipient_data['bank_account']['number'],
'name': recipient_data['bank_account']['name']
},
description=recipient_data.get('description'),
metadata=recipient_data.get('metadata', {})
)
print(f"Recipient created: {recipient.id}")
print(f"Bank: {recipient.bank_account.brand}")
print(f"Status: {'Active' if recipient.active else 'Inactive'}")
return recipient
except omise.errors.BaseError as e:
print(f"Failed to create recipient: {str(e)}")
raise
def validate_bank_account(bank_code, account_number):
"""Validate bank account format"""
# Validate bank code
valid_banks = ['bbl', 'kbank', 'scb', 'bay', 'ktb', 'tmb', 'cimb', 'uob', 'tbank']
if bank_code.lower() not in valid_banks:
return False, f"Invalid bank code: {bank_code}"
# Validate account number (typically 10-12 digits)
if not re.match(r'^\d{10,12}$', account_number):
return False, "Account number must be 10-12 digits"
return True, "Valid"
def create_recipient_with_validation(recipient_data):
"""Create recipient with validation"""
# Validate required fields
required_fields = ['name', 'bank_account']
for field in required_fields:
if field not in recipient_data:
raise ValueError(f"Missing required field: {field}")
# Validate bank account
bank_code = recipient_data['bank_account']['brand']
account_number = recipient_data['bank_account']['number']
is_valid, message = validate_bank_account(bank_code, account_number)
if not is_valid:
raise ValueError(f"Validation failed: {message}")
# Create recipient
recipient = create_recipient(recipient_data)
print(f"โ Recipient validated and created: {recipient.id}")
return recipient
# Example usage
recipient_data = {
'name': 'Acme Corporation',
'email': 'finance@acme.com',
'type': 'corporation',
'bank_account': {
'brand': 'bbl',
'number': '1234567890',
'name': 'Acme Corporation'
},
'description': 'Main company account',
'metadata': {
'tax_id': '0123456789012',
'company_type': 'limited'
}
}
create_recipient_with_validation(recipient_data)
require 'omise'
Omise.api_key = 'skey_test_123456789'
# Create a recipient
def create_recipient(recipient_data)
begin
recipient = Omise::Recipient.create(
name: recipient_data[:name],
email: recipient_data[:email],
type: recipient_data[:type] || 'individual',
bank_account: {
brand: recipient_data[:bank_account][:brand],
number: recipient_data[:bank_account][:number],
name: recipient_data[:bank_account][:name]
},
description: recipient_data[:description],
metadata: recipient_data[:metadata] || {}
)
puts "Recipient created: #{recipient.id}"
puts "Bank: #{recipient.bank_account.brand}"
puts "Status: #{recipient.active ? 'Active' : 'Inactive'}"
recipient
rescue Omise::Error => e
puts "Failed to create recipient: #{e.message}"
raise
end
end
# Validate bank account details
class BankAccountValidator
VALID_BANKS = %w[bbl kbank scb bay ktb tmb cimb uob tbank].freeze
def self.validate(bank_code, account_number)
errors = []
# Validate bank code
unless VALID_BANKS.include?(bank_code.downcase)
errors << "Invalid bank code: #{bank_code}"
end
# Validate account number format
unless account_number.match?(/^\d{10,12}$/)
errors << "Account number must be 10-12 digits"
end
{
valid: errors.empty?,
errors: errors
}
end
def self.get_bank_name(code)
banks = {
'bbl' => 'Bangkok Bank',
'kbank' => 'Kasikornbank',
'scb' => 'Siam Commercial Bank',
'bay' => 'Bank of Ayudhya (Krungsri)',
'ktb' => 'Krungthai Bank',
'tmb' => 'TMB Bank',
'cimb' => 'CIMB Thai',
'uob' => 'United Overseas Bank',
'tbank' => 'Thanachart Bank'
}
banks[code.downcase] || code.upcase
end
end
# Create recipient with validation
def create_validated_recipient(recipient_data)
# Validate bank account
validation = BankAccountValidator.validate(
recipient_data[:bank_account][:brand],
recipient_data[:bank_account][:number]
)
unless validation[:valid]
raise ArgumentError, "Validation failed: #{validation[:errors].join(', ')}"
end
# Create recipient
recipient = create_recipient(recipient_data)
bank_name = BankAccountValidator.get_bank_name(recipient.bank_account.brand)
puts "โ Recipient created at #{bank_name}"
recipient
end
# Example usage
recipient_data = {
name: 'Supplier Co., Ltd.',
email: 'accounting@supplier.co.th',
type: 'corporation',
bank_account: {
brand: 'scb',
number: '1234567890',
name: 'Supplier Co., Ltd.'
},
description: 'Monthly supplier payments',
metadata: {
supplier_code: 'SUP-001',
payment_terms: 'net_30'
}
}
create_validated_recipient(recipient_data)
<?php
require_once 'vendor/autoload.php';
define('OMISE_SECRET_KEY', 'skey_test_123456789');
// Create a recipient
function createRecipient($recipientData) {
try {
$recipient = OmiseRecipient::create([
'name' => $recipientData['name'],
'email' => $recipientData['email'] ?? null,
'type' => $recipientData['type'] ?? 'individual',
'bank_account' => [
'brand' => $recipientData['bank_account']['brand'],
'number' => $recipientData['bank_account']['number'],
'name' => $recipientData['bank_account']['name']
],
'description' => $recipientData['description'] ?? null,
'metadata' => $recipientData['metadata'] ?? []
]);
echo "Recipient created: {$recipient['id']}\n";
echo "Bank: {$recipient['bank_account']['brand']}\n";
echo "Status: " . ($recipient['active'] ? 'Active' : 'Inactive') . "\n";
return $recipient;
} catch (Exception $e) {
echo "Failed to create recipient: {$e->getMessage()}\n";
throw $e;
}
}
// Bank account validator
class BankAccountValidator {
const VALID_BANKS = ['bbl', 'kbank', 'scb', 'bay', 'ktb', 'tmb', 'cimb', 'uob', 'tbank'];
const BANK_NAMES = [
'bbl' => 'Bangkok Bank',
'kbank' => 'Kasikornbank',
'scb' => 'Siam Commercial Bank',
'bay' => 'Bank of Ayudhya (Krungsri)',
'ktb' => 'Krungthai Bank',
'tmb' => 'TMB Bank',
'cimb' => 'CIMB Thai',
'uob' => 'United Overseas Bank',
'tbank' => 'Thanachart Bank'
];
public static function validate($bankCode, $accountNumber) {
$errors = [];
// Validate bank code
if (!in_array(strtolower($bankCode), self::VALID_BANKS)) {
$errors[] = "Invalid bank code: {$bankCode}";
}
// Validate account number
if (!preg_match('/^\d{10,12}$/', $accountNumber)) {
$errors[] = "Account number must be 10-12 digits";
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
public static function getBankName($code) {
$code = strtolower($code);
return self::BANK_NAMES[$code] ?? strtoupper($code);
}
}
// Create recipient with validation
function createValidatedRecipient($recipientData) {
// Validate bank account
$validation = BankAccountValidator::validate(
$recipientData['bank_account']['brand'],
$recipientData['bank_account']['number']
);
if (!$validation['valid']) {
throw new Exception('Validation failed: ' . implode(', ', $validation['errors']));
}
// Create recipient
$recipient = createRecipient($recipientData);
$bankName = BankAccountValidator::getBankName($recipient['bank_account']['brand']);
echo "โ Recipient created at {$bankName}\n";
return $recipient;
}
// Batch create recipients
function batchCreateRecipients($recipientsList) {
$results = [
'successful' => [],
'failed' => []
];
foreach ($recipientsList as $index => $recipientData) {
try {
$recipient = createValidatedRecipient($recipientData);
$results['successful'][] = [
'index' => $index,
'id' => $recipient['id'],
'name' => $recipient['name']
];
} catch (Exception $e) {
$results['failed'][] = [
'index' => $index,
'name' => $recipientData['name'],
'error' => $e->getMessage()
];
}
}
echo "\nBatch creation complete:\n";
echo "Successful: " . count($results['successful']) . "\n";
echo "Failed: " . count($results['failed']) . "\n";
return $results;
}
// Example usage
$recipientData = [
'name' => 'Thai Trading Co.',
'email' => 'finance@thaitrading.co.th',
'type' => 'corporation',
'bank_account' => [
'brand' => 'kbank',
'number' => '1234567890',
'name' => 'Thai Trading Co.'
],
'description' => 'Vendor payments',
'metadata' => [
'vendor_id' => 'VEN-123',
'category' => 'trading'
]
];
createValidatedRecipient($recipientData);
?>
package main
import (
"errors"
"fmt"
"log"
"regexp"
"strings"
"github.com/omise/omise-go"
"github.com/omise/omise-go/operations"
)
const secretKey = "skey_test_123456789"
// RecipientData holds recipient information
type RecipientData struct {
Name string
Email string
Type string
BankAccount BankAccountData
Description string
Metadata map[string]interface{}
}
// BankAccountData holds bank account information
type BankAccountData struct {
Brand string
Number string
Name string
}
// CreateRecipient creates a new recipient
func CreateRecipient(data RecipientData) (*omise.Recipient, error) {
client, err := omise.NewClient(secretKey, "")
if err != nil {
return nil, fmt.Errorf("failed to create client: %w", err)
}
recipient := &omise.Recipient{}
err = client.Do(recipient, &operations.CreateRecipient{
Name: data.Name,
Email: data.Email,
Type: omise.RecipientType(data.Type),
Description: data.Description,
BankAccount: &omise.BankAccount{
Brand: data.BankAccount.Brand,
Number: data.BankAccount.Number,
Name: data.BankAccount.Name,
},
Metadata: data.Metadata,
})
if err != nil {
return nil, fmt.Errorf("failed to create recipient: %w", err)
}
fmt.Printf("Recipient created: %s\n", recipient.ID)
fmt.Printf("Bank: %s\n", recipient.BankAccount.Brand)
fmt.Printf("Status: %v\n", recipient.Active)
return recipient, nil
}
// BankAccountValidator validates bank account details
type BankAccountValidator struct {
validBanks map[string]string
}
// NewBankAccountValidator creates a new validator
func NewBankAccountValidator() *BankAccountValidator {
return &BankAccountValidator{
validBanks: map[string]string{
"bbl": "Bangkok Bank",
"kbank": "Kasikornbank",
"scb": "Siam Commercial Bank",
"bay": "Bank of Ayudhya (Krungsri)",
"ktb": "Krungthai Bank",
"tmb": "TMB Bank",
"cimb": "CIMB Thai",
"uob": "United Overseas Bank",
"tbank": "Thanachart Bank",
},
}
}
// Validate validates bank account details
func (v *BankAccountValidator) Validate(bankCode, accountNumber string) []string {
var errors []string
// Validate bank code
bankCode = strings.ToLower(bankCode)
if _, ok := v.validBanks[bankCode]; !ok {
errors = append(errors, fmt.Sprintf("invalid bank code: %s", bankCode))
}
// Validate account number format
matched, _ := regexp.MatchString(`^\d{10,12}$`, accountNumber)
if !matched {
errors = append(errors, "account number must be 10-12 digits")
}
return errors
}
// GetBankName returns the full bank name
func (v *BankAccountValidator) GetBankName(code string) string {
code = strings.ToLower(code)
if name, ok := v.validBanks[code]; ok {
return name
}
return strings.ToUpper(code)
}
// CreateValidatedRecipient creates a recipient with validation
func CreateValidatedRecipient(data RecipientData) (*omise.Recipient, error) {
validator := NewBankAccountValidator()
// Validate required fields
if data.Name == "" {
return nil, errors.New("name is required")
}
if data.BankAccount.Brand == "" || data.BankAccount.Number == "" {
return nil, errors.New("bank account details are required")
}
// Validate bank account
validationErrors := validator.Validate(data.BankAccount.Brand, data.BankAccount.Number)
if len(validationErrors) > 0 {
return nil, fmt.Errorf("validation failed: %v", validationErrors)
}
// Create recipient
recipient, err := CreateRecipient(data)
if err != nil {
return nil, err
}
bankName := validator.GetBankName(recipient.BankAccount.Brand)
fmt.Printf("โ Recipient created at %s\n", bankName)
return recipient, nil
}
// BatchCreateResults holds batch creation results
type BatchCreateResults struct {
Successful []BatchSuccess
Failed []BatchFailure
}
// BatchSuccess represents a successful creation
type BatchSuccess struct {
Index int
ID string
Name string
}
// BatchFailure represents a failed creation
type BatchFailure struct {
Index int
Name string
Error string
}
// BatchCreateRecipients creates multiple recipients
func BatchCreateRecipients(recipientsList []RecipientData) BatchCreateResults {
results := BatchCreateResults{
Successful: []BatchSuccess{},
Failed: []BatchFailure{},
}
for index, data := range recipientsList {
recipient, err := CreateValidatedRecipient(data)
if err != nil {
results.Failed = append(results.Failed, BatchFailure{
Index: index,
Name: data.Name,
Error: err.Error(),
})
} else {
results.Successful = append(results.Successful, BatchSuccess{
Index: index,
ID: recipient.ID,
Name: recipient.Name,
})
}
}
fmt.Printf("\nBatch creation complete:\n")
fmt.Printf("Successful: %d\n", len(results.Successful))
fmt.Printf("Failed: %d\n", len(results.Failed))
return results
}
func main() {
// Example: Create single recipient
recipientData := RecipientData{
Name: "Example Company Ltd.",
Email: "finance@example.com",
Type: "corporation",
BankAccount: BankAccountData{
Brand: "scb",
Number: "1234567890",
Name: "Example Company Ltd.",
},
Description: "Primary payment account",
Metadata: map[string]interface{}{
"company_id": "COMP-001",
"department": "finance",
},
}
recipient, err := CreateValidatedRecipient(recipientData)
if err != nil {
log.Fatalf("Failed to create recipient: %v", err)
}
fmt.Printf("Created recipient: %s\n", recipient.ID)
}
API Responseโ
{
"object": "recipient",
"id": "recp_test_5xyz789abc",
"livemode": false,
"location": "/recipients/recp_test_5xyz789abc",
"verified": true,
"active": true,
"name": "John Doe",
"email": "john.doe@example.com",
"description": "Supplier payment account",
"type": "individual",
"tax_id": null,
"bank_account": {
"object": "bank_account",
"brand": "bbl",
"last_digits": "7890",
"name": "John Doe",
"created": "2024-01-15T10:30:00Z"
},
"failure_code": null,
"created": "2024-01-15T10:30:00Z",
"metadata": {
"supplier_id": "SUP-001",
"category": "raw_materials"
}
}
Retrieving Recipientsโ
Get Single Recipientโ
async function getRecipient(recipientId) {
const recipient = await omise.recipients.retrieve(recipientId);
console.log('Recipient:', recipient.name);
console.log('Bank:', recipient.bank_account.brand);
console.log('Account ending:', recipient.bank_account.last_digits);
console.log('Verified:', recipient.verified);
console.log('Active:', recipient.active);
return recipient;
}
List All Recipientsโ
def list_recipients(limit=100):
"""List all recipients with pagination"""
recipients = omise.Recipient.retrieve()
print(f"Total recipients: {recipients.total}")
for recipient in recipients.data:
print(f"\nID: {recipient.id}")
print(f"Name: {recipient.name}")
print(f"Bank: {recipient.bank_account.brand}")
print(f"Status: {'Active' if recipient.active else 'Inactive'}")
return recipients
def search_recipients(query):
"""Search recipients by name or email"""
all_recipients = omise.Recipient.retrieve()
query_lower = query.lower()
matches = [
r for r in all_recipients.data
if query_lower in r.name.lower() or
(r.email and query_lower in r.email.lower())
]
return matches
Updating Recipientsโ
Update Recipient Informationโ
def update_recipient(recipient_id, updates)
begin
recipient = Omise::Recipient.retrieve(recipient_id)
recipient.update(updates)
puts "Recipient updated: #{recipient.id}"
puts "New name: #{recipient.name}" if updates[:name]
puts "New email: #{recipient.email}" if updates[:email]
recipient
rescue Omise::Error => e
puts "Failed to update recipient: #{e.message}"
raise
end
end
# Update email
update_recipient('recp_test_123', email: 'newemail@example.com')
# Update description and metadata
update_recipient('recp_test_123', {
description: 'Updated description',
metadata: {
updated_at: Time.now.iso8601,
updated_by: 'admin'
}
})
Recipient Verificationโ
Verification Statusโ
function checkRecipientVerification($recipientId) {
$recipient = OmiseRecipient::retrieve($recipientId);
$status = [
'id' => $recipient['id'],
'verified' => $recipient['verified'],
'active' => $recipient['active'],
'failure_code' => $recipient['failure_code']
];
if (!$recipient['verified']) {
echo "โ Recipient not verified\n";
if ($recipient['failure_code']) {
echo "Reason: {$recipient['failure_code']}\n";
}
} else {
echo "โ Recipient verified and active\n";
}
return $status;
}
Common Use Casesโ
1. Supplier Management Systemโ
class SupplierRecipientManager {
constructor() {
this.suppliers = new Map();
}
async addSupplier(supplierData) {
// Create recipient
const recipient = await omise.recipients.create({
name: supplierData.companyName,
email: supplierData.email,
type: 'corporation',
bank_account: {
brand: supplierData.bankCode,
number: supplierData.accountNumber,
name: supplierData.accountName
},
description: `Supplier: ${supplierData.companyName}`,
metadata: {
supplier_id: supplierData.supplierId,
tax_id: supplierData.taxId,
payment_terms: supplierData.paymentTerms,
category: supplierData.category
}
});
// Store mapping
this.suppliers.set(supplierData.supplierId, recipient.id);
console.log(`Supplier ${supplierData.companyName} added as recipient ${recipient.id}`);
return recipient;
}
async getSupplierRecipient(supplierId) {
const recipientId = this.suppliers.get(supplierId);
if (!recipientId) {
throw new Error(`No recipient found for supplier ${supplierId}`);
}
return await omise.recipients.retrieve(recipientId);
}
async updateSupplierBankDetails(supplierId, newBankDetails) {
// Note: Bank account details can't be updated, need to create new recipient
const oldRecipient = await this.getSupplierRecipient(supplierId);
// Deactivate old recipient
await omise.recipients.update(oldRecipient.id, { active: false });
// Create new recipient
const newRecipient = await omise.recipients.create({
name: oldRecipient.name,
email: oldRecipient.email,
type: oldRecipient.type,
bank_account: newBankDetails,
description: oldRecipient.description,
metadata: {
...oldRecipient.metadata,
previous_recipient: oldRecipient.id,
updated_at: new Date().toISOString()
}
});
// Update mapping
this.suppliers.set(supplierId, newRecipient.id);
return newRecipient;
}
}
2. Employee Payout Systemโ
class EmployeePayoutManager:
def __init__(self):
self.employee_recipients = {}
def add_employee(self, employee_data):
"""Add employee as recipient for payroll"""
recipient = omise.Recipient.create(
name=employee_data['full_name'],
email=employee_data['email'],
type='individual',
bank_account={
'brand': employee_data['bank_code'],
'number': employee_data['account_number'],
'name': employee_data['account_name']
},
description=f"Employee: {employee_data['employee_id']}",
metadata={
'employee_id': employee_data['employee_id'],
'department': employee_data['department'],
'position': employee_data['position'],
'hire_date': employee_data['hire_date']
}
)
self.employee_recipients[employee_data['employee_id']] = recipient.id
print(f"Employee {employee_data['full_name']} added for payouts")
return recipient
def verify_all_employees(self):
"""Verify all employee recipients"""
unverified = []
for emp_id, recipient_id in self.employee_recipients.items():
recipient = omise.Recipient.retrieve(recipient_id)
if not recipient.verified:
unverified.append({
'employee_id': emp_id,
'recipient_id': recipient_id,
'name': recipient.name,
'failure_code': recipient.failure_code
})
if unverified:
print(f"\nโ {len(unverified)} employees have unverified bank accounts:")
for emp in unverified:
print(f" - {emp['name']} (ID: {emp['employee_id']})")
if emp['failure_code']:
print(f" Reason: {emp['failure_code']}")
else:
print("โ All employee bank accounts verified")
return unverified
def batch_add_employees(self, employees_list):
"""Batch add multiple employees"""
results = {
'successful': [],
'failed': []
}
for employee in employees_list:
try:
recipient = self.add_employee(employee)
results['successful'].append({
'employee_id': employee['employee_id'],
'recipient_id': recipient.id
})
except Exception as e:
results['failed'].append({
'employee_id': employee['employee_id'],
'error': str(e)
})
print(f"\nBatch processing complete:")
print(f"Success: {len(results['successful'])}")
print(f"Failed: {len(results['failed'])}")
return results
3. Marketplace Vendor Payoutsโ
class VendorPayoutManager
def initialize
@vendors = {}
end
def onboard_vendor(vendor_data)
# Validate vendor information
validate_vendor(vendor_data)
# Create recipient
recipient = Omise::Recipient.create(
name: vendor_data[:business_name],
email: vendor_data[:email],
type: vendor_data[:business_type], # 'individual' or 'corporation'
bank_account: {
brand: vendor_data[:bank_code],
number: vendor_data[:account_number],
name: vendor_data[:account_name]
},
description: "Vendor: #{vendor_data[:vendor_id]}",
tax_id: vendor_data[:tax_id],
metadata: {
vendor_id: vendor_data[:vendor_id],
commission_rate: vendor_data[:commission_rate],
category: vendor_data[:category],
onboarded_at: Time.now.iso8601
}
)
@vendors[vendor_data[:vendor_id]] = recipient.id
puts "โ Vendor #{vendor_data[:business_name]} onboarded"
puts " Recipient ID: #{recipient.id}"
puts " Commission: #{vendor_data[:commission_rate]}%"
recipient
end
def validate_vendor(vendor_data)
required_fields = [:business_name, :email, :vendor_id, :bank_code, :account_number]
missing = required_fields.select { |f| vendor_data[f].nil? || vendor_data[f].empty? }
unless missing.empty?
raise ArgumentError, "Missing required fields: #{missing.join(', ')}"
end
# Validate bank account
validator = BankAccountValidator.new
validation = validator.validate(vendor_data[:bank_code], vendor_data[:account_number])
unless validation[:valid]
raise ArgumentError, "Invalid bank account: #{validation[:errors].join(', ')}"
end
end
def get_vendor_recipient(vendor_id)
recipient_id = @vendors[vendor_id]
raise "Vendor #{vendor_id} not found" unless recipient_id
Omise::Recipient.retrieve(recipient_id)
end
def list_pending_verifications
pending = []
@vendors.each do |vendor_id, recipient_id|
recipient = Omise::Recipient.retrieve(recipient_id)
unless recipient.verified
pending << {
vendor_id: vendor_id,
recipient_id: recipient_id,
business_name: recipient.name,
failure_code: recipient.failure_code
}
end
end
pending
end
end
Best Practicesโ
1. Validate Before Creationโ
async function validateAndCreateRecipient(data) {
const errors = [];
// Validate name
if (!data.name || data.name.length < 2) {
errors.push('Name must be at least 2 characters');
}
// Validate email format
if (data.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
errors.push('Invalid email format');
}
// Validate bank account
if (!data.bank_account || !data.bank_account.brand || !data.bank_account.number) {
errors.push('Bank account details are required');
}
if (errors.length > 0) {
throw new Error(`Validation failed:\n${errors.join('\n')}`);
}
return await omise.recipients.create(data);
}
2. Handle Verification Failuresโ
def create_recipient_with_retry(recipient_data, max_retries=3):
"""Create recipient with automatic retry on verification failure"""
for attempt in range(max_retries):
try:
recipient = omise.Recipient.create(**recipient_data)
# Check verification status
if not recipient.verified:
print(f"โ Recipient created but not verified: {recipient.failure_code}")
if recipient.failure_code == 'invalid_account_number':
# Prompt user to verify account number
print("Please verify the account number and try again")
return None
elif attempt < max_retries - 1:
# Retry after delay
time.sleep(2 ** attempt)
continue
return recipient
except omise.errors.BaseError as e:
if attempt < max_retries - 1:
print(f"Attempt {attempt + 1} failed, retrying...")
time.sleep(2 ** attempt)
else:
raise
return None
3. Maintain Audit Trailโ
class RecipientAuditLog
def self.log_creation(recipient, context = {})
log_entry = {
event: 'recipient_created',
recipient_id: recipient.id,
name: recipient.name,
bank: recipient.bank_account.brand,
verified: recipient.verified,
created_by: context[:user_id],
timestamp: Time.now.iso8601,
metadata: recipient.metadata
}
# Save to audit log
save_audit_log(log_entry)
end
def self.log_update(recipient_id, changes, context = {})
log_entry = {
event: 'recipient_updated',
recipient_id: recipient_id,
changes: changes,
updated_by: context[:user_id],
timestamp: Time.now.iso8601
}
save_audit_log(log_entry)
end
def self.save_audit_log(entry)
File.open('recipient_audit.log', 'a') do |f|
f.puts JSON.generate(entry)
end
end
end
FAQโ
Can I update bank account details for a recipient?โ
No, bank account details cannot be updated once a recipient is created. If bank account information changes, you need to create a new recipient and optionally deactivate the old one.
What happens if recipient verification fails?โ
If verification fails, the recipient will have verified: false and a failure_code indicating the issue. Common codes include invalid_account_number and bank_account_not_found. You'll need to correct the information and create a new recipient.
How long does recipient verification take?โ
Recipient verification is typically instant when created. The system validates the bank account format and checks against bank databases in real-time.
Can I have multiple recipients with the same bank account?โ
Yes, you can create multiple recipients with the same bank account details. This might be useful for tracking different payment purposes or departments.
What's the difference between 'individual' and 'corporation' type?โ
The type field indicates whether the recipient is an individual person or a corporation. This affects how the recipient appears in your records and may be required for tax and compliance purposes.
How do I find a recipient by bank account number?โ
Recipients store only the last 4 digits of the account number for security. You'll need to track the mapping between your internal IDs and recipient IDs in your own database.
Can I delete a recipient?โ
Recipients cannot be deleted for audit trail purposes, but you can set active: false to deactivate them. Deactivated recipients cannot receive new transfers.
Do I need to create recipients for all transfers?โ
Yes, you must create a recipient before making a transfer. Recipients represent the bank accounts you want to send money to.
Related Resourcesโ
- Creating Transfers - Send money to recipients
- Transfer Schedules - Automate recurring transfers
- Balance Management - Monitor account balance
- API Reference - Complete recipient API
- Webhooks - Handle recipient events
Next Stepsโ
- Learn how to create transfers to recipients
- Set up automated schedules for recurring payments
- Monitor your account balance for transfers
- Review transaction history for transfers
- Implement reconciliation for accounting