Transfers
The TransferResource resource allows you to automate sending money to your customers. This is useful for payouts, payroll, and withdrawals.
The Transfer Process
You can transfer money in four easy steps:
- Create a transfer recipient: A beneficiary on your integration that you can send money to.
- Generate a transfer reference: A unique identifier to track and reconcile the transfer.
- Initiate a transfer: Send the money using the recipient code and reference.
- Listen for transfer status: Use webhooks to confirm the final status (success, failed, reversed).
Initiate Transfer
Send money to a customer.
Method Signature
paystack()->transfer()->initiate(array $payload): arrayParameters
| Parameter | Type | Description |
|---|---|---|
source | string | Source of funds (default: balance). |
amount | int | Amount in kobo/pesewas. |
recipient | string | Recipient code (e.g., RCP_...). |
reference | string | Unique transfer reference. |
reason | string | (Optional) Reason for transfer. |
currency | string | (Optional) Currency (default: NGN). |
Transfer Reference
A transfer reference is a unique identifier that lets you track, manage and reconcile each transfer request.
- Format: Lowercase letters (a-z), digits (0-9), underscore (_), and dash (-).
- Length: 16 to 50 characters.
- Recommendation: Use a v4 UUID or a unique string prefixed with your app identifier.
Example
$transfer = paystack()->transfer()->initiate([
'source' => 'balance',
'amount' => 500000, // 5,000 NGN
'recipient' => 'RCP_gd9vgag7n5lr5ix',
'reference' => 'your_unique_ref_' . time(),
'reason' => 'Monthly Payout'
]);Finalize Transfer
Finalize an initiated transfer (required if OTP is enabled).
Method Signature
paystack()->transfer()->finalize(string $transferCode, string $otp): arrayExample
// After initiating, if status is 'otp', collect OTP from user
$result = paystack()->transfer()->finalize('TRF_vsyqdmlzble3uii', '123456');Bulk Transfer
Batch multiple transfers in a single request. Note: You must disable "Transfers OTP" in your Paystack Dashboard to use this.
Method Signature
paystack()->transfer()->bulk(array $transfers, string $source = 'balance', string $currency = 'NGN'): arrayExample
$transfers = [
[
'amount' => 20000,
'recipient' => 'RCP_gd9vgag7n5lr5ix',
'reference' => 'ref_001',
'reason' => 'Staff Bonus'
],
[
'amount' => 30000,
'recipient' => 'RCP_zpk2tgagu6lgb4g',
'reference' => 'ref_002',
'reason' => 'Vendor Payment'
]
];
$response = paystack()->transfer()->bulk($transfers);List Transfers
List all transfers made on your integration.
Method Signature
paystack()->transfer()->list(array $filters = []): arrayExample
$transfers = paystack()->transfer()->list(['perPage' => 20, 'page' => 1]);Fetch Transfer
Get details of a single transfer.
Method Signature
paystack()->transfer()->fetch(string|int $idOrCode): arrayExample
$details = paystack()->transfer()->fetch('TRF_v5tip3zx8nna9o78');Verify Transfer
Verify the status of a transfer using its reference.
Method Signature
paystack()->transfer()->verify(string $reference): arrayExample
$status = paystack()->transfer()->verify('your_unique_ref_123');Use Cases
1. Automated Payout System
A typical flow for an automated payout system where OTP is disabled for seamless transactions.
// 1. Ensure recipient exists
$recipientCode = $user->recipient_code;
// 2. Initiate Transfer
try {
$response = paystack()->transfer()->initiate([
'amount' => 100000, // 1,000.00
'recipient' => $recipientCode,
'reference' => 'payout_' . uniqid(),
'reason' => 'Withdrawal Request'
]);
if ($response['data']['status'] === 'success' || $response['data']['status'] === 'pending') {
// Mark withdrawal as processing in your DB
}
} catch (\Exception $e) {
// Handle insufficient balance or other errors
}2. Handling OTP Transfers
If your integration requires OTP (e.g., for security on manual triggers), you need a two-step process.
// Step 1: Initiate
$response = paystack()->transfer()->initiate([
'amount' => 50000,
'recipient' => 'RCP_xxxx',
'reference' => 'ref_otp_test'
]);
if ($response['data']['status'] === 'otp') {
// Redirect user to a page to enter the OTP sent to their phone/email
$transferCode = $response['data']['transfer_code'];
return view('enter_otp', compact('transferCode'));
}
// Step 2: Finalize (in your controller handling the OTP form)
public function submitOtp(Request $request) {
$result = paystack()->transfer()->finalize(
$request->transfer_code,
$request->otp
);
return response()->json($result);
}