Payout Flow
Send money to customers and vendors using the two-step intent flow.
Payout Intent Flow (Recommended)
- JavaScript
- Python
import { ZenPays } from '@zenxdigitalholdings/zenpays'
async function payoutWithIntent() {
const zenpays = new ZenPays({
apiKey: process.env.ZENPAYS_API_KEY!,
})
// Step 1: Create payout intent with basic details
const intent = await zenpays.payouts.createPayoutIntent({
amount: 10000,
currency: 'INR',
country: 'IN',
beneficiaryName: 'John Doe',
beneficiaryEmail: 'john@example.com',
purpose: 'SALARY',
})
console.log('Intent created:', intent.intentId)
console.log('Status:', intent.status) // 'requires_confirmation'
console.log('Expires at:', intent.expiresAt)
// The response tells you exactly what fields to collect
console.log('Required fields:')
intent.requiredFields?.forEach(field => {
console.log(` - ${field.label} (${field.fieldName}) [${field.type}]`)
})
// Step 2: Confirm with the required fields
const result = await zenpays.payouts.confirmPayoutIntent(intent.intentId, {
beneficiaryAccount: '1234567890',
beneficiaryIfsc: 'HDFC0001234',
})
console.log('Payout confirmed:', result.payoutId)
console.log('Status:', result.status) // 'processing'
// Step 3: Poll for completion
const poll = async (id: string): Promise<void> => {
const current = await zenpays.payouts.getPayoutIntent(id)
if (current.status === 'succeeded') {
console.log('Payout completed!')
return
}
if (current.status === 'failed') {
console.log('Payout failed:', current.failureReason)
return
}
console.log('Status:', current.status)
await new Promise(r => setTimeout(r, 5000))
return poll(id)
}
await poll(intent.intentId)
}
// Crypto payout example
async function cryptoPayout() {
const zenpays = new ZenPays({
apiKey: process.env.ZENPAYS_API_KEY!,
})
// Step 1: Create intent for crypto (USDT)
const intent = await zenpays.payouts.createPayoutIntent({
amount: 500,
currency: 'USDT',
beneficiaryName: 'Crypto Wallet',
})
// For crypto, requiredFields will include "chain" and "walletAddress"
console.log(intent.requiredFields)
// [{ fieldName: "chain", type: "select", options: ["ethereum", "tron", ...] },
// { fieldName: "walletAddress", type: "text" }]
// Step 2: Confirm with chain and address
const result = await zenpays.payouts.confirmPayoutIntent(intent.intentId, {
chain: 'tron',
walletAddress: 'TXyz123abc...',
})
console.log('Crypto payout processing:', result.payoutId)
}
payoutWithIntent()
import os
import time
from zenpays import ZenPays
def payout_with_intent():
zenpays = ZenPays(api_key=os.environ["ZENPAYS_API_KEY"])
# Step 1: Create payout intent
intent = zenpays.payouts.create_payout_intent({
"amount": 10000,
"currency": "INR",
"country": "IN",
"beneficiary_name": "John Doe",
"beneficiary_email": "john@example.com",
"purpose": "SALARY",
})
print(f"Intent created: {intent['intent_id']}")
print(f"Status: {intent['status']}") # "requires_confirmation"
# The response tells you what fields to collect
for field in intent["required_fields"]:
print(f" - {field['label']} ({field['field_name']})")
# Step 2: Confirm with the required fields
result = zenpays.payouts.confirm_payout_intent(intent["intent_id"], {
"beneficiary_account": "1234567890",
"beneficiary_ifsc": "HDFC0001234",
})
print(f"Payout confirmed: {result['payout_id']}")
print(f"Status: {result['status']}") # "processing"
# Step 3: Poll for completion
def poll(intent_id):
current = zenpays.payouts.get_payout_intent(intent_id)
if current["status"] == "succeeded":
print("Payout completed!")
return
if current["status"] == "failed":
print(f"Payout failed: {current.get('failure_reason')}")
return
print(f"Status: {current['status']}")
time.sleep(5)
poll(intent_id)
poll(intent["intent_id"])
def crypto_payout():
zenpays = ZenPays(api_key=os.environ["ZENPAYS_API_KEY"])
# Step 1: Crypto intent
intent = zenpays.payouts.create_payout_intent({
"amount": 500,
"currency": "USDT",
"beneficiary_name": "Crypto Wallet",
})
# Step 2: Confirm with chain + address
result = zenpays.payouts.confirm_payout_intent(intent["intent_id"], {
"chain": "tron",
"wallet_address": "TXyz123abc...",
})
print(f"Crypto payout processing: {result['payout_id']}")
if __name__ == "__main__":
payout_with_intent()
Legacy Direct Payout
Deprecated
The direct payout API is deprecated. Use the Payout Intent flow above instead.
- JavaScript
- Python
import { ValidationError, ZenPays } from '@zenxdigitalholdings/zenpays'
async function processPayout() {
const zenpays = new ZenPays({
apiKey: process.env.ZENPAYS_API_KEY!,
})
// 1. Check available payout methods
const methods = await zenpays.payouts.getMethods()
console.log('Available payout methods:', methods.map(m => m.type).join(', '))
// 2. Check wallet balance
const balance = await zenpays.wallet.getBalance('USD')
console.log('Available balance:', balance)
// 3. Create payout
const payout = await zenpays.payouts.create({
customerId: 'cust_xxx',
amount: 10000, // $100.00
currency: 'USD',
payoutMethod: 'bank_transfer',
beneficiaryDetails: {
name: 'John Doe',
email: 'john@example.com',
bankName: 'Chase Bank',
accountNumber: '123456789',
accountType: 'checking',
routingNumber: '021000021',
},
description: 'Withdrawal request',
idempotencyKey: 'payout_unique_123', // Prevent duplicates
})
console.log('Payout created:', payout.id)
console.log('Status:', payout.status)
// 4. Monitor payout status
const waitForPayout = async (payoutId: string): Promise<boolean> => {
const p = await zenpays.payouts.get(payoutId)
switch (p.status) {
case 'completed':
console.log('Payout completed!')
console.log('Processed at:', p.processedAt)
return true
case 'failed':
console.log('Payout failed:', p.failureReason)
return false
case 'processing':
console.log('Payout processing...')
break
default:
console.log('Payout status:', p.status)
}
await new Promise(resolve => setTimeout(resolve, 5000))
return waitForPayout(payoutId)
}
return waitForPayout(payout.id)
}
// Retry a failed payout
async function retryFailedPayout(payoutId: string) {
const zenpays = new ZenPays({
apiKey: process.env.ZENPAYS_API_KEY!,
})
const payout = await zenpays.payouts.get(payoutId)
if (payout.status !== 'failed') {
console.log('Payout is not failed, cannot retry')
return
}
console.log('Retrying payout:', payoutId)
const retried = await zenpays.payouts.retry(payoutId)
console.log('Retry status:', retried.status)
}
// Get customer payouts
async function getCustomerPayouts(customerId: string) {
const zenpays = new ZenPays({
apiKey: process.env.ZENPAYS_API_KEY!,
})
const { data, total } = await zenpays.payouts.listByCustomer(customerId, {
limit: 10,
})
console.log(`Found ${total} payouts for customer ${customerId}:`)
data.forEach((p) => {
console.log(` ${p.id}: ${p.amount} ${p.currency} - ${p.status}`)
})
}
processPayout()
import os
import time
from zenpays import ZenPays
from zenpays.errors import ValidationError
def process_payout():
zenpays = ZenPays(api_key=os.environ["ZENPAYS_API_KEY"])
# 1. Check available payout methods
methods = zenpays.payouts.get_methods()
print(f"Available payout methods: {', '.join(m['type'] for m in methods)}")
# 2. Check wallet balance
balance = zenpays.wallet.get_balance("USD")
print(f"Available balance: {balance}")
# 3. Create payout
payout = zenpays.payouts.create({
"customer_id": "cust_xxx",
"amount": 10000, # $100.00
"currency": "USD",
"payout_method": "bank_transfer",
"beneficiary_details": {
"name": "John Doe",
"email": "john@example.com",
"bank_name": "Chase Bank",
"account_number": "123456789",
"account_type": "checking",
"routing_number": "021000021",
},
"description": "Withdrawal request",
"idempotency_key": "payout_unique_123", # Prevent duplicates
})
print(f"Payout created: {payout['id']}")
print(f"Status: {payout['status']}")
# 4. Monitor payout status
def wait_for_payout(payout_id: str) -> bool:
p = zenpays.payouts.get(payout_id)
status = p["status"]
if status == "completed":
print("Payout completed!")
print(f"Processed at: {p.get('processed_at')}")
return True
elif status == "failed":
print(f"Payout failed: {p.get('failure_reason')}")
return False
elif status == "processing":
print("Payout processing...")
else:
print(f"Payout status: {status}")
time.sleep(5)
return wait_for_payout(payout_id)
return wait_for_payout(payout["id"])
# Retry a failed payout
def retry_failed_payout(payout_id: str):
zenpays = ZenPays(api_key=os.environ["ZENPAYS_API_KEY"])
payout = zenpays.payouts.get(payout_id)
if payout["status"] != "failed":
print("Payout is not failed, cannot retry")
return
print(f"Retrying payout: {payout_id}")
retried = zenpays.payouts.retry(payout_id)
print(f"Retry status: {retried['status']}")
# Get customer payouts
def get_customer_payouts(customer_id: str):
zenpays = ZenPays(api_key=os.environ["ZENPAYS_API_KEY"])
result = zenpays.payouts.list_by_customer(customer_id, {"limit": 10})
data = result["data"]
total = result["total"]
print(f"Found {total} payouts for customer {customer_id}:")
for p in data:
print(f" {p['id']}: {p['amount']} {p['currency']} - {p['status']}")
if __name__ == "__main__":
process_payout()
Cancel a Payout
Cancel a payout that hasn't started processing yet.
- JavaScript
- Python
import { ZenPays } from '@zenxdigitalholdings/zenpays'
async function cancelPayout(payoutId: string) {
const zenpays = new ZenPays({
apiKey: process.env.ZENPAYS_API_KEY!,
})
// Check current status
const payout = await zenpays.payouts.get(payoutId)
if (payout.status !== 'pending') {
console.log(`Cannot cancel — payout is already ${payout.status}`)
return
}
// Cancel the payout
const cancelled = await zenpays.payouts.cancel(payoutId, 'Customer requested cancellation')
console.log('Cancelled:', cancelled.status) // 'cancelled'
}
cancelPayout('pay_xxx')
import os
from zenpays import ZenPays
def cancel_payout(payout_id: str):
zenpays = ZenPays(api_key=os.environ["ZENPAYS_API_KEY"])
# Check current status
payout = zenpays.payouts.get(payout_id)
if payout["status"] != "pending":
print(f"Cannot cancel — payout is already {payout['status']}")
return
# Cancel the payout
cancelled = zenpays.payouts.cancel(payout_id, "Customer requested cancellation")
print(f"Cancelled: {cancelled['status']}") # "cancelled"
if __name__ == "__main__":
cancel_payout("pay_xxx")
Batch Payout Flow
Send payouts to multiple beneficiaries in a single batch.
- JavaScript
- Python
import { ZenPays } from '@zenxdigitalholdings/zenpays'
async function processBatchPayout() {
const zenpays = new ZenPays({
apiKey: process.env.ZENPAYS_API_KEY!,
})
// 1. Create batch payout
const batch = await zenpays.payouts.createBatch({
items: [
{
beneficiaryName: 'Alice Smith',
beneficiaryAccount: '1234567890',
beneficiaryIfsc: 'HDFC0001234',
amount: 5000,
currency: 'INR',
payoutType: 'imps',
purpose: 'SALARY',
},
{
beneficiaryName: 'Bob Johnson',
beneficiaryVpa: 'bob@upi',
amount: 3000,
currency: 'INR',
payoutType: 'upi',
purpose: 'COMMISSION',
},
{
beneficiaryName: 'Carol Williams',
beneficiaryAccount: '9876543210',
beneficiaryIfsc: 'ICIC0004567',
amount: 7000,
currency: 'INR',
payoutType: 'neft',
purpose: 'PAYOUT',
},
],
currency: 'INR',
webhookUrl: 'https://example.com/webhooks/batch',
})
console.log('Batch created:', batch.batchId)
console.log('Total amount:', batch.totalAmount, batch.currency)
console.log('Estimated fees:', batch.estimatedFees)
console.log('Status:', batch.status) // 'validated'
// 2. Confirm the batch to start processing
const confirmed = await zenpays.payouts.confirmBatch(batch.batchId)
console.log('Processing started:', confirmed.status) // 'processing'
// 3. Poll for completion
const waitForBatch = async (batchId: string): Promise<void> => {
const status = await zenpays.payouts.getBatch(batchId)
console.log(`Progress: ${status.progressPercentage}% (${status.successfulPayouts} succeeded, ${status.failedPayouts} failed)`)
if (status.status === 'completed' || status.status === 'partially_completed') {
console.log('Batch finished!')
// 4. Check individual results
const { data: payouts } = await zenpays.payouts.getBatchPayouts(batchId)
payouts.forEach(p => {
console.log(` ${p.id}: ${p.amount} ${p.currency} → ${p.status}`)
})
// 5. Check for failures
const { data: failed } = await zenpays.payouts.getBatchPayouts(batchId, { status: 'failed' })
if (failed.length > 0) {
console.log(`${failed.length} payouts failed:`)
failed.forEach(p => console.log(` ${p.id}: ${p.failureReason}`))
}
return
}
await new Promise(resolve => setTimeout(resolve, 5000))
return waitForBatch(batchId)
}
await waitForBatch(batch.batchId)
}
processBatchPayout()
import os
import time
from zenpays import ZenPays
def process_batch_payout():
zenpays = ZenPays(api_key=os.environ["ZENPAYS_API_KEY"])
# 1. Create batch payout
batch = zenpays.payouts.create_batch({
"items": [
{
"beneficiary_name": "Alice Smith",
"beneficiary_account": "1234567890",
"beneficiary_ifsc": "HDFC0001234",
"amount": 5000,
"currency": "INR",
"payout_type": "imps",
"purpose": "SALARY",
},
{
"beneficiary_name": "Bob Johnson",
"beneficiary_vpa": "bob@upi",
"amount": 3000,
"currency": "INR",
"payout_type": "upi",
"purpose": "COMMISSION",
},
{
"beneficiary_name": "Carol Williams",
"beneficiary_account": "9876543210",
"beneficiary_ifsc": "ICIC0004567",
"amount": 7000,
"currency": "INR",
"payout_type": "neft",
"purpose": "PAYOUT",
},
],
"currency": "INR",
"webhook_url": "https://example.com/webhooks/batch",
})
print(f"Batch created: {batch['batch_id']}")
print(f"Total amount: {batch['total_amount']} {batch['currency']}")
print(f"Status: {batch['status']}") # "validated"
# 2. Confirm the batch to start processing
confirmed = zenpays.payouts.confirm_batch(batch["batch_id"])
print(f"Processing started: {confirmed['status']}") # "processing"
# 3. Poll for completion
def wait_for_batch(batch_id: str):
status = zenpays.payouts.get_batch(batch_id)
print(f"Progress: {status['progress_percentage']}% "
f"({status['successful_payouts']} succeeded, {status['failed_payouts']} failed)")
if status["status"] in ("completed", "partially_completed"):
print("Batch finished!")
# 4. Check individual results
result = zenpays.payouts.get_batch_payouts(batch_id)
for p in result["data"]:
print(f" {p['id']}: {p['amount']} {p['currency']} -> {p['status']}")
# 5. Check for failures
failed = zenpays.payouts.get_batch_payouts(batch_id, {"status": "failed"})
if failed["data"]:
print(f"{len(failed['data'])} payouts failed:")
for p in failed["data"]:
print(f" {p['id']}: {p.get('failure_reason')}")
return
time.sleep(5)
wait_for_batch(batch_id)
wait_for_batch(batch["batch_id"])
if __name__ == "__main__":
process_batch_payout()