Files
alrahma_sunday_school_api/app/Services/Payments/PaypalPaymentSyncService.php
T
2026-06-11 11:46:12 -04:00

175 lines
5.8 KiB
PHP

<?php
namespace App\Services\Payments;
use App\Models\Configuration;
use App\Models\Enrollment;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\PayPalPayment;
use App\Models\Student;
use App\Models\User;
use App\Services\EmailService;
use Illuminate\Support\Facades\Log;
class PaypalPaymentSyncService
{
public function __construct(private EmailService $emailService) {}
public function sync(bool $dryRun = false, bool $reportOnly = false): array
{
$mode = $reportOnly ? 'REPORT-ONLY' : ($dryRun ? 'DRY-RUN' : 'LIVE');
$semester = (string) (Configuration::getConfig('semester') ?? '');
$schoolYear = (string) (Configuration::getConfig('school_year') ?? '');
$entries = PayPalPayment::query()
->where('status', 'COMPLETED')
->where('synced', 0)
->where('sync_attempts', '<', 3)
->whereNotNull('transaction_id')
->get();
$synced = 0;
$failed = [];
foreach ($entries as $entry) {
if (! $reportOnly) {
$entry->sync_attempts = (int) $entry->sync_attempts + 1;
$entry->save();
}
$user = User::query()->where('school_id', $entry->parent_school_id)->first();
if (! $user) {
$failed[] = (string) $entry->transaction_id;
Log::error('PayPal sync: no user for parent_school_id', ['parent_school_id' => $entry->parent_school_id]);
continue;
}
$invoice = Invoice::query()
->where('parent_id', $user->id)
->where('school_year', $schoolYear)
->latest('created_at')
->first();
if (! $reportOnly && ! $dryRun) {
if ($invoice) {
$ok = $this->applyPayment($invoice, $entry->amount, (string) $entry->transaction_id, $schoolYear, $semester, $entry->created_at);
if (! $ok) {
$failed[] = (string) $entry->transaction_id;
continue;
}
} else {
Payment::query()->create([
'parent_id' => $user->id,
'invoice_id' => 0,
'total_amount' => $entry->amount,
'paid_amount' => $entry->amount,
'balance' => 0.00,
'number_of_installments' => 1,
'transaction_id' => $entry->transaction_id,
'payment_method' => 'PayPal',
'payment_date' => optional($entry->created_at)->format('Y-m-d'),
'school_year' => $schoolYear,
'semester' => $semester,
'status' => 'Completed',
'updated_by' => null,
]);
}
$entry->synced = 1;
$entry->save();
}
$synced++;
}
$this->sendReport($mode, $synced, $failed);
return [
'mode' => $mode,
'processed' => $synced,
'failed' => $failed,
];
}
private function applyPayment(Invoice $invoice, float $amount, string $transactionId, string $schoolYear, string $semester, $createdAt): bool
{
$newPaid = (float) $invoice->paid_amount + $amount;
$newBalance = (float) $invoice->balance - $amount;
$invoice->paid_amount = $newPaid;
$invoice->balance = $newBalance;
if ($newBalance <= 0) {
$invoice->status = 'Paid';
}
if (! $invoice->save()) {
return false;
}
Payment::query()->create([
'parent_id' => $invoice->parent_id,
'invoice_id' => $invoice->id,
'total_amount' => $invoice->total_amount,
'paid_amount' => $amount,
'balance' => $newBalance,
'number_of_installments' => 1,
'transaction_id' => $transactionId,
'payment_method' => 'PayPal',
'payment_date' => $createdAt ? date('Y-m-d', strtotime((string) $createdAt)) : date('Y-m-d'),
'status' => $newBalance <= 0 ? 'Full' : 'Partial',
'check_file' => null,
'updated_by' => null,
'school_year' => $schoolYear,
'semester' => $semester,
]);
$this->updateEnrollmentStatusIfPaid($invoice->id, $schoolYear);
return true;
}
private function updateEnrollmentStatusIfPaid(int $invoiceId, string $schoolYear): void
{
$invoice = Invoice::query()->find($invoiceId);
if (! $invoice || (float) $invoice->balance > 0) {
return;
}
$students = Student::query()
->where('parent_id', $invoice->parent_id)
->where('school_year', $schoolYear)
->get();
foreach ($students as $student) {
Enrollment::query()
->where('student_id', $student->id)
->update(['enrollment_status' => 'enrolled']);
}
}
private function sendReport(string $mode, int $processed, array $failed): void
{
if ($processed === 0 && empty($failed)) {
Log::info("[{$mode}] No PayPal sync updates.");
return;
}
$subject = "[{$mode}] PayPal Sync Report - ".now()->format('Y-m-d H:i');
$body = "PayPal Sync Mode: {$mode}\n\n";
$body .= "{$processed} PayPal payments processed.\n\n";
if (! empty($failed)) {
$body .= count($failed)." failed transactions:\n".implode("\n", $failed);
} else {
$body .= "No failed transactions.\n";
}
$this->emailService->send('support@alrahmaisgl.org', $subject, nl2br($body), 'finance');
}
}