'$'.number_format((float) $v, 2); $fmtDateTz = static function (?string $s): string { if (! $s) { return ''; } $dt = new \DateTime($s, new \DateTimeZone('UTC')); $tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone()); $dt->setTimezone(new \DateTimeZone($tzName ?: 'UTC')); return $dt->format('m-d-Y h:i A'); }; $txid = $data['transaction_id'] ?? $data['transactionId'] ?? $data['payment_id'] ?? $data['id'] ?? ''; $methodRaw = $data['payment_method'] ?? ($data['method'] ?? ''); $method = $methodRaw ? ucfirst((string) $methodRaw) : ''; $amountPaid = $fmtMoney($data['amount'] ?? 0); $preBalance = $fmtMoney($data['pre_balance'] ?? 0); $postBalance = $fmtMoney($data['post_balance'] ?? 0); $invoiceTotal = $fmtMoney($data['invoice_total'] ?? 0); $invoiceDiscountRaw = isset($data['invoice_discount']) ? (float) $data['invoice_discount'] : 0.0; $parentYearDiscountTotalRaw = isset($data['parent_year_discount_total']) ? (float) $data['parent_year_discount_total'] : 0.0; $emailData = [ 'title' => 'Payment Receipt', 'parentName' => $parentName, 'schoolYear' => $data['school_year'] ?? '', 'semester' => $data['semester'] ?? '', 'invoiceId' => $data['invoice_id'] ?? null, 'invoiceNumber' => $data['invoice_number'] ?? null, 'transactionId' => $txid, 'paymentDate' => $fmtDateTz($data['payment_date'] ?? null), 'method' => $method, 'paymentMethod' => $method, 'amount' => $amountPaid, 'invoiceTotal' => $invoiceTotal, 'preBalance' => $preBalance, 'postBalance' => $postBalance, 'checkNumber' => $data['check_number'] ?? null, 'installmentSeq' => (int) ($data['installment_seq'] ?? 1), 'invoiceDiscount' => $invoiceDiscountRaw > 0 ? $fmtMoney($invoiceDiscountRaw) : null, 'parentYearDiscountTotal' => $parentYearDiscountTotalRaw > 0 ? $fmtMoney($parentYearDiscountTotalRaw) : null, 'portalLink' => $this->urls->webLoginUrl(), 'students' => $studentdata, ]; $inAppTitle = 'Payment Received'; $inAppMsg = sprintf( 'We received %s for Invoice #%s (Installment #%d). New balance: %s. Check your email for details.', $emailData['amount'], (string) ($emailData['invoiceId'] ?? $emailData['invoiceNumber'] ?? ''), (int) $emailData['installmentSeq'], $emailData['postBalance'] ); $this->notifyUser((int) ($data['user_id'] ?? 0), $inAppTitle, $inAppMsg, 'payments'); $emailBody = $this->renderEmail('emails/payment_receipt', $emailData, 'Payment Receipt'); $subject = sprintf( 'Payment Receipt - Invoice #%s (%s) %s', (string) ($emailData['invoiceId'] ?? $emailData['invoiceNumber'] ?? ''), $emailData['amount'], $txid ? "[TX: {$txid}]" : '' ); return $this->sendEmail((string) ($data['email'] ?? ''), $subject, $emailBody, 'payments'); } public function extraCharge(array $data, array $studentdata = []): array { $fmtMoney = static fn ($v) => '$'.number_format((float) $v, 2); $fmtFromUtc = static function (?string $s, string $fmt = 'm-d-Y h:i A'): string { if (! $s) { return ''; } try { $dt = new \DateTime($s, new \DateTimeZone('UTC')); $tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone()); $dt->setTimezone(new \DateTimeZone($tzName ?: 'UTC')); return $dt->format($fmt); } catch (\Throwable $e) { Log::error('extraCharge date error: '.$e->getMessage()); return ''; } }; $fmtDueLocal = static function (?string $s): string { if (! $s) { return ''; } try { if (strlen($s) <= 10) { $tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone()); $dt = \DateTime::createFromFormat('Y-m-d', $s, new \DateTimeZone($tzName ?: 'UTC')); return $dt ? $dt->format('m-d-Y') : ''; } if (preg_match('/(Z|[+\-]\d{2}:\d{2})$/', $s)) { $dt = new \DateTime($s); $tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone()); $dt->setTimezone(new \DateTimeZone($tzName ?: 'UTC')); return $dt->format('m-d-Y h:i A'); } $tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone()); $dt = new \DateTime($s, new \DateTimeZone($tzName ?: 'UTC')); return $dt->format('m-d-Y h:i A'); } catch (\Throwable $e) { Log::error('extraCharge due date error: '.$e->getMessage()); return ''; } }; $parentName = trim((string) ($data['firstname'] ?? '').' '.(string) ($data['lastname'] ?? '')); $amountSigned = (float) ($data['amount_signed'] ?? 0); $amountAbs = (float) ($data['amount_abs'] ?? abs($amountSigned)); $preBalance = $fmtMoney($data['pre_balance'] ?? 0); $postBalance = $fmtMoney($data['post_balance'] ?? 0); $invoiceTotal = $fmtMoney($data['invoice_total'] ?? 0); $createdAt = $fmtFromUtc($data['created_at'] ?? null); $dueDate = $fmtDueLocal($data['due_date'] ?? null); $typeLabel = (($data['charge_type'] ?? 'add') === 'add') ? 'Added charge' : 'Deducted amount'; $amountFmt = $fmtMoney($amountSigned); $emailData = [ 'title' => 'Account Charge Update', 'parentName' => $parentName, 'schoolYear' => $data['school_year'] ?? '', 'semester' => $data['semester'] ?? '', 'invoiceId' => $data['invoice_id'] ?? null, 'invoiceNumber' => $data['invoice_number'] ?? null, 'typeLabel' => $typeLabel, 'chargeTitle' => $data['charge_title'] ?? '', 'chargeDesc' => $data['charge_desc'] ?? '', 'chargeType' => $data['charge_type'] ?? 'add', 'amountSigned' => $amountFmt, 'amountAbs' => $fmtMoney($amountAbs), 'invoiceTotal' => $invoiceTotal, 'preBalance' => $preBalance, 'postBalance' => $postBalance, 'createdAt' => $createdAt, 'dueDate' => $dueDate !== '' ? $dueDate : null, 'portalLink' => $data['portal_link'] ?? $this->urls->webLoginUrl(), 'invoiceLink' => $data['invoice_link'] ?? null, 'students' => $studentdata, ]; $inAppTitle = 'Account Updated'; $inAppMsg = sprintf( '%s: %s (%s). New balance: %s.', $typeLabel, $emailData['chargeTitle'], $emailData['amountSigned'], $emailData['postBalance'] ); $this->notifyUser((int) ($data['user_id'] ?? 0), $inAppTitle, $inAppMsg, 'billing'); $subject = sprintf( 'Charge Update - Invoice #%s (%s)', (string) ($emailData['invoiceId'] ?? $emailData['invoiceNumber'] ?? ''), $emailData['amountSigned'] ); $body = $this->renderEmail('emails/extra_charge_notice', $emailData, 'Account Charge Update'); return $this->sendEmail((string) ($data['email'] ?? ''), $subject, $body, 'billing'); } private function notifyUser(int $userId, string $title, string $message, string $topic): void { if ($userId <= 0) { return; } $this->notifier->notifyUser($userId, $title, $message, ['in_app'], $topic, 'parent'); } private function sendEmail(string $to, string $subject, string $html, string $fromKey): array { if ($to === '') { Log::warning('Payment event missing recipient email', ['subject' => $subject]); return ['ok' => false, 'message' => 'Missing recipient email.']; } $ok = $this->emailService->send($to, $subject, $html, $fromKey); if (! $ok) { Log::error('Payment event email failed', ['to' => $to, 'subject' => $subject]); } return ['ok' => $ok]; } private function renderEmail(string $viewName, array $data, string $fallbackTitle): string { Log::debug('Skipping Blade email template', ['view' => $viewName]); return '

'.e($fallbackTitle).'

'; } }