File Manager
Viewing File: TransferService.php
<?php
namespace App\Services;
use App\Models\{Settings, Withdrawal};
use App\Repositories\{WithdrawalRepository, UserRepository};
use App\Traits\CreatesNotifications;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class TransferService
{
use CreatesNotifications;
protected $withdrawalRepo;
protected $userRepo;
protected $settings;
public function __construct(
WithdrawalRepository $withdrawalRepo,
UserRepository $userRepo
) {
$this->withdrawalRepo = $withdrawalRepo;
$this->userRepo = $userRepo;
$this->settings = Settings::first();
}
/**
* Process internal transfer (instant between accounts)
*
* @param array $data
* @param int $userId
* @return array ['success' => bool, 'message' => string, 'withdrawal' => Withdrawal|null]
*/
public function processInternalTransfer(array $data, int $userId): array
{
return DB::transaction(function () use ($data, $userId) {
try {
// Find recipient
$recipient = $this->userRepo->findByAccountNumber($data['accountnumber']);
if (!$recipient) {
return ['success' => false, 'message' => 'Recipient account not found'];
}
// Debit sender
if (!$this->userRepo->debitAccount($userId, $data['to_deduct'])) {
return ['success' => false, 'message' => 'Insufficient funds'];
}
// Credit recipient
$this->userRepo->creditAccount($recipient->id, $data['amount']);
// Create debit record (sender)
$debitData = array_merge($data, [
'user' => $userId,
'type' => 'Debit',
'status' => 'Processed',
'date' => now(),
'bal' => $this->userRepo->find($userId)->account_bal
]);
$debitWithdrawal = $this->withdrawalRepo->create($debitData);
// Create credit record (recipient)
$creditData = array_merge($data, [
'user' => $recipient->id,
'type' => 'Credit',
'status' => 'Processed',
'date' => now(),
'bal' => $recipient->account_bal
]);
$this->withdrawalRepo->create($creditData);
// Send notifications
$this->notifyTransfer($userId, $data['amount'], $this->settings->currency, $data['accountname'], 'completed');
$this->notifyTransfer($recipient->id, $data['amount'], $this->settings->currency, 'Internal Transfer', 'received');
return [
'success' => true,
'message' => 'Internal transfer completed successfully',
'withdrawal' => $debitWithdrawal
];
} catch (\Exception $e) {
Log::error('Internal transfer failed', [
'user_id' => $userId,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return ['success' => false, 'message' => 'Transfer failed. Please try again.'];
}
});
}
/**
* Process local/domestic transfer (pending, requires approval)
*
* @param array $data
* @param int $userId
* @return array
*/
public function processLocalTransfer(array $data, int $userId): array
{
return DB::transaction(function () use ($data, $userId) {
try {
$toDeduct = $data['to_deduct'] ?? $data['amount'];
// Debit user
if (!$this->userRepo->debitAccount($userId, $toDeduct)) {
return ['success' => false, 'message' => 'Insufficient funds'];
}
// Create pending withdrawal
$withdrawalData = array_merge($data, [
'user' => $userId,
'type' => 'Debit',
'status' => 'Pending',
'date' => now(),
'bal' => $this->userRepo->find($userId)->account_bal,
'to_deduct' => $toDeduct
]);
$withdrawal = $this->withdrawalRepo->create($withdrawalData);
// Lock transfer action
$this->userRepo->updateTransferAction($userId, 0);
// Send notification
$this->notifyTransfer($userId, $data['amount'], $this->settings->currency, $data['accountname'], 'pending');
return [
'success' => true,
'message' => 'Local transfer initiated successfully',
'withdrawal' => $withdrawal
];
} catch (\Exception $e) {
Log::error('Local transfer failed', [
'user_id' => $userId,
'error' => $e->getMessage()
]);
return ['success' => false, 'message' => 'Transfer failed. Please try again.'];
}
});
}
/**
* Process international/wire transfer
*
* @param array $data
* @param int $userId
* @return array
*/
public function processInternationalTransfer(array $data, int $userId): array
{
return DB::transaction(function () use ($data, $userId) {
try {
$toDeduct = $data['to_deduct'] ?? $data['amount'];
if (!$this->userRepo->debitAccount($userId, $toDeduct)) {
return ['success' => false, 'message' => 'Insufficient funds'];
}
$withdrawalData = array_merge($data, [
'user' => $userId,
'type' => 'Debit',
'status' => 'Pending',
'date' => now(),
'bal' => $this->userRepo->find($userId)->account_bal,
'to_deduct' => $toDeduct,
'swiftcode' => $data['swiftcode'] ?? null,
'iban' => $data['iban'] ?? null,
'bankaddress' => $data['address'] ?? null,
'country' => $data['country'] ?? null
]);
$withdrawal = $this->withdrawalRepo->create($withdrawalData);
$this->userRepo->updateTransferAction($userId, 0);
$this->notifyTransfer($userId, $data['amount'], $this->settings->currency, $data['accountname'], 'pending');
return [
'success' => true,
'message' => 'International transfer initiated successfully',
'withdrawal' => $withdrawal
];
} catch (\Exception $e) {
Log::error('International transfer failed', [
'user_id' => $userId,
'error' => $e->getMessage()
]);
return ['success' => false, 'message' => 'Transfer failed. Please try again.'];
}
});
}
/**
* Process third-party transfer (PayPal, Skrill, etc.)
*
* @param array $data
* @param int $userId
* @return array
*/
public function processThirdPartyTransfer(array $data, int $userId): array
{
return DB::transaction(function () use ($data, $userId) {
try {
$toDeduct = $data['to_deduct'] ?? $data['amount'];
if (!$this->userRepo->debitAccount($userId, $toDeduct)) {
return ['success' => false, 'message' => 'Insufficient funds'];
}
$withdrawalData = array_merge($data, [
'user' => $userId,
'type' => 'Debit',
'status' => 'Pending',
'date' => now(),
'bal' => $this->userRepo->find($userId)->account_bal,
'to_deduct' => $toDeduct
]);
$withdrawal = $this->withdrawalRepo->create($withdrawalData);
$this->userRepo->updateTransferAction($userId, 0);
$this->notifyTransfer($userId, $data['amount'], $this->settings->currency, $data['accountname'], 'pending');
return [
'success' => true,
'message' => ucfirst(strtolower($data['bankname'])) . ' transfer initiated successfully',
'withdrawal' => $withdrawal
];
} catch (\Exception $e) {
Log::error('Third-party transfer failed', [
'user_id' => $userId,
'payment_method' => $data['bankname'] ?? 'unknown',
'error' => $e->getMessage()
]);
return ['success' => false, 'message' => 'Transfer failed. Please try again.'];
}
});
}
/**
* Process cryptocurrency transfer
*
* @param array $data
* @param int $userId
* @return array
*/
public function processCryptocurrencyTransfer(array $data, int $userId): array
{
return DB::transaction(function () use ($data, $userId) {
try {
$toDeduct = $data['to_deduct'] ?? $data['amount'];
$cryptoCoin = $data['crypto_currency'] ?? null;
if ($cryptoCoin && in_array($cryptoCoin, \App\Models\CryptoAccount::SUPPORTED_COINS)) {
// Debit from crypto balance
$cryptoAccount = \App\Models\CryptoAccount::where('user_id', $userId)->first();
if (!$cryptoAccount || !$cryptoAccount->hasSufficientBalance($cryptoCoin, $toDeduct)) {
return ['success' => false, 'message' => 'Insufficient crypto balance'];
}
DB::table('crypto_accounts')
->where('user_id', $userId)
->update([
$cryptoCoin => $cryptoAccount->$cryptoCoin - $toDeduct,
]);
} else {
// Fallback: debit from main account balance
if (!$this->userRepo->debitAccount($userId, $toDeduct)) {
return ['success' => false, 'message' => 'Insufficient funds'];
}
}
$withdrawalData = array_merge($data, [
'user' => $userId,
'type' => 'Debit',
'status' => 'Pending',
'date' => now(),
'bal' => $this->userRepo->find($userId)->account_bal,
'to_deduct' => $toDeduct,
'paydetails' => $data['details'] ?? null,
'crypto_currency' => $cryptoCoin,
]);
$withdrawal = $this->withdrawalRepo->create($withdrawalData);
$this->userRepo->updateTransferAction($userId, 0);
$coinName = $cryptoCoin ? (\App\Models\CryptoAccount::COIN_NAMES[$cryptoCoin] ?? strtoupper($cryptoCoin)) : 'Crypto';
$this->notifyTransfer($userId, $data['amount'] . ' ' . $coinName, $this->settings->currency, $data['accountname'], 'pending', 'crypto');
return [
'success' => true,
'message' => 'Cryptocurrency transfer initiated successfully',
'withdrawal' => $withdrawal
];
} catch (\Exception $e) {
Log::error('Cryptocurrency transfer failed', [
'user_id' => $userId,
'crypto_type' => $data['bankname'] ?? 'unknown',
'error' => $e->getMessage()
]);
return ['success' => false, 'message' => 'Transfer failed. Please try again.'];
}
});
}
}