@php $primary = $setting?->primary_color ?: '#124a61'; $accent = $setting?->accent_color ?: '#2e86ab'; $companyName = $setting?->company_name ?: $invoice->team?->name; $symbol = $currencySymbol ?: ''; $code = $invoice->currency_code ?: ''; $money = function ($value) use ($symbol, $code) { $formatted = number_format((float) $value, 2); return ($symbol !== '' ? $symbol . ' ' : ($code !== '' ? $code . ' ' : '')) . $formatted; }; $logoUrl = $setting?->logoSrc(); // Where to pay, for this invoice's currency only, resolved through the same // helper the PDF uses so the page and the download always agree — including // on whether the bank details show at all, which follows the invoice's // payment method. $banking = \App\Support\DocumentBanking::for( $invoice->team_id, $invoice->currency_code, $setting, $invoice->paymentMethod ); $hasBanking = $banking['showsBanking']; $hasBalance = (float) $invoice->balance_due > 0.01 && ! in_array($invoice->status, ['VOID', 'PAID'], true); $paynowConfigured = $paynowConfigured ?? false; $canPayOnline = $hasBalance && $paynowConfigured; $isPaid = ! $hasBalance && $invoice->status !== 'VOID'; $total = (float) $invoice->total; $paid = (float) $invoice->amount_paid; $paidPct = $total > 0 ? min(100, round(($paid / $total) * 100)) : ($isPaid ? 100 : 0); $itemCount = $invoice->lineItems->count(); // Status as a customer would say it, not as the database stores it. $statusMeta = [ 'UNPAID' => ['label' => __('Unpaid'), 'icon' => 'shield-check', 'bg' => '#fff7ed', 'text' => '#c2410c'], 'PARTIAL' => ['label' => __('Partially paid'), 'icon' => 'clock', 'bg' => '#fefce8', 'text' => '#a16207'], 'PAID' => ['label' => __('Paid'), 'icon' => 'check', 'bg' => '#ecfdf5', 'text' => '#047857'], 'OVERDUE' => ['label' => __('Unpaid, overdue'), 'icon' => 'exclamation-triangle','bg' => '#fff1f2', 'text' => '#be123c'], 'VOID' => ['label' => __('Cancelled'), 'icon' => 'x-mark', 'bg' => '#f1f5f9', 'text' => '#475569'], ]; $status = $statusMeta[$invoice->isOverdue() ? 'OVERDUE' : $invoice->displayStatus()]; // Wallets Paynow can push a prompt to. Each shows its own logo from // public/images/wallets/ when the file is there, and falls back to a // wordmark in the brand colour so a missing asset never breaks the card. $walletLogo = function (string $slug): ?string { foreach (['svg', 'png', 'webp'] as $extension) { $path = "images/wallets/{$slug}.{$extension}"; if (is_file(public_path($path))) { return asset($path); } } return null; }; // Until the logo files land, each wallet is drawn as its own two-tone // wordmark, which is how all three brands actually split their colours. // Closer to the real mark than one flat colour, and it never 404s. // Each logo gets its own height. A wordmark 5x wider than it is tall and a // stacked mark only 2x wider cannot share one, sizing them alike leaves the // stacked one a smudge. Heights are optical, matched by eye at 100% zoom. $wallets = [ 'ecocash' => [ 'label' => 'EcoCash', 'hint' => __('Econet'), 'tone' => '#e5262c', 'height' => 'h-[17px]', 'parts' => [['Eco', '#0056a4'], ['Cash', '#e5262c']], ], 'onemoney' => [ 'label' => 'OneMoney', 'hint' => __('NetOne'), 'tone' => '#f58220', 'height' => 'h-[30px]', 'parts' => [['One', '#231f20'], ['Money', '#f58220']], ], 'innbucks' => [ 'label' => 'InnBucks', 'hint' => __('Any network'), 'tone' => '#12284c', 'height' => 'h-[19px]', 'parts' => [['Inn', '#12284c'], ['Bucks', '#12284c']], 'dots' => ['#f3b41b', '#8e5ea2', '#2faf8e', '#e1252b'], ], ]; foreach ($wallets as $slug => $wallet) { $wallets[$slug]['logo'] = $walletLogo($slug); } $selectedWallet = old('method', 'ecocash'); @endphp {{ $invoice->invoice_number }} · {{ $companyName }} {{-- Same company mark the rest of the app uses, so a customer's tab is recognisable while they pay. --}} {{-- The built stylesheet, not the Tailwind CDN: it carries the self-hosted icon font that needs, and keeps a payment page off third-party script hosts. Every utility used here is in the build, tailwind.config scans resources/views/**. --}} @vite(['resources/css/app.css'])
@if ($logoUrl) {{ $companyName }} @endif

{{ $companyName }}

{{ __('Invoice') }} #{{ $invoice->invoice_number }} {{ $status['label'] }}
{{ __('Download PDF') }}
@if (session('paynow_error'))
{{ session('paynow_error') }}
@endif

{{ $isPaid ? __('Amount paid') : __('Amount due') }}

{{ $isPaid ? $money($invoice->total) : $money($invoice->balance_due) }}

@if ($isPaid)

{{ __('Paid in full. Thank you for your business.') }}

@elseif ($paid > 0)

{{ $money($paid) }} {{ __('of') }} {{ $money($total) }} {{ __('paid') }}

@elseif ($invoice->due_date)

{{ __('Due') }} {{ $invoice->due_date->format('M j, Y') }}

@endif
@if ($canPayOnline)

{{ __('Select a payment method') }}

{{ __('Secure online payments through Paynow.') }}

{{ __('Mobile money') }}

@csrf @if ($errors->any())
{{ $errors->first() }}
@endif
@foreach ($wallets as $value => $wallet) @endforeach

{{ __('A prompt will be sent to your phone. Enter your :wallet PIN to authorise the payment.', ['wallet' => $wallets[$selectedWallet]['label'] ?? 'EcoCash']) }}

{{ __('or') }}

{{ __('Debit or credit card') }}

{{ __('Pay via Visa / Mastercard through Paynow Secure Payments.') }}

@csrf {{-- Paynow's own button: customers recognise it, and it states which cards and wallets the hosted checkout accepts. --}}

{{ __('Secured by Paynow. :company never sees your PIN or card details.', ['company' => $companyName]) }}

@endif @if ($hasBalance && ! $paynowConfigured && $banking['hasAny'])

{{ __('How to pay') }}

@if ($banking['paymentMethods']->isNotEmpty())
@foreach ($banking['paymentMethods'] as $method)

{{ $method->name }} @if ($method->instructions) {{ $method->instructions }} @endif

@endforeach
@elseif ($setting?->payment_methods)
{!! \App\Support\Html::display($setting->payment_methods) !!}
@endif @if ($hasBanking)
@include('portal.partials.banking', ['banking' => $banking, 'setting' => $setting, 'currencyCode' => $invoice->currency_code])
@endif
@endif @if ($invoice->payments->isNotEmpty())

{{ __('Payments received') }}

@foreach ($invoice->payments->sortBy('paid_at') as $payment)

{{ $payment->channel }}

{{ optional($payment->paid_at)->format('d M Y') }} @if ($payment->reference) · {{ $payment->reference }} @endif @if ((float) $payment->surcharge > 0) · +{{ $money($payment->surcharge) }} {{ __('fee') }} @endif

{{ $money($payment->amount) }}

@endforeach
@endif @if ($invoice->notes || ($hasBalance && $hasBanking && $paynowConfigured) || $setting?->terms_and_conditions)
@if ($invoice->notes)

{{ __('Notes') }}

{{ $invoice->notes }}

@endif @if ($hasBalance && $hasBanking && $paynowConfigured)

{{ __('Banking Details') }} @if ($banking['accounts']->isNotEmpty()) · {{ $invoice->currency_code }} @endif

@include('portal.partials.banking', ['banking' => $banking, 'setting' => $setting, 'currencyCode' => $invoice->currency_code])
@endif @if ($setting?->terms_and_conditions)

{{ __('Terms & Conditions') }}

{!! \App\Support\Html::display(\App\Support\DocumentTerms::forInvoice($setting, $invoice)) !!}
@endif
@endif

{{ __('Invoice summary') }}

{{ __('Invoice number') }}
{{ $invoice->invoice_number }}
{{ __('Issue date') }}
{{ $invoice->created_at?->format('M j, Y') }}
@if ($invoice->due_date)
{{ __('Due date') }}
{{ $invoice->due_date->format('M j, Y') }}
@endif
{{ __('Billed to') }}
{{ $invoice->customer?->name ?: '—' }} @if ($invoice->customer?->email) {{ $invoice->customer->email }} @endif @if ($invoice->customer?->phone) {{ $invoice->customer->phone }} @endif
{{ __('Currency') }}
{{ $code ?: '—' }}
{{ __('Subtotal') }}
{{ $money($invoice->subtotal) }}
@if ((float) $invoice->discount > 0)
{{ __('Discount') }}
- {{ $money($invoice->discount) }}
@endif
{{ __('VAT') }}
{{ $money($invoice->vat) }}
@if ((float) $invoice->surcharge > 0)
{{ \App\Models\PaymentMethod::feeLabel() }}
{{ $money($invoice->surcharge) }}
@endif
{{ __('Total') }}
{{ $money($invoice->total) }}
@if ($paid > 0 && ! $isPaid)
{{ __('Already paid') }}
- {{ $money($paid) }}
{{ __('Balance due') }}
{{ $money($invoice->balance_due) }}
@endif

{{ __('Order details') }}

{{ $itemCount }} {{ trans_choice('item|items', $itemCount) }}
@forelse ($invoice->lineItems as $line)

{{ $line->name }}

@if ($line->description)

{!! nl2br(e($line->description)) !!}

@endif

{{ rtrim(rtrim(number_format((float) $line->qty, 2), '0'), '.') }} × {{ $money($line->unit_price) }}

{{ $money($line->line_total) }}

@empty

{{ __('No items.') }}

@endforelse

{{ $companyName }}

@if ($setting?->email || $setting?->phone)

{{ __('Questions about this invoice?') }} @if ($setting?->email) {{ $setting->email }} @endif @if ($setting?->email && $setting?->phone) · @endif @if ($setting?->phone) {{ $setting->phone }} @endif

@endif
@if ($canPayOnline) @endif