Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions app/Http/Controllers/InvoiceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,26 @@ public function show(\Illuminate\Http\Request $request, \App\Models\Invoice $inv
allowFallback: $btcForUri !== null
);

$includeSelf = $request->boolean('include_self');
$issuerEmail = (string) ($invoice->user?->email ?? '');
$visibleDeliveries = $invoice->deliveries
->where('status', '!=', 'skipped')
->when(
!$includeSelf && $issuerEmail !== '',
fn ($collection) => $collection->filter(
fn ($delivery) => strcasecmp((string) $delivery->recipient, $issuerEmail) !== 0
)
)
->values();

return view('invoices.show', [
'invoice' => $invoice,
'rate' => $rate,
'paymentSummary' => $summary,
'paymentHistory' => $paymentHistory,
'reattributeDestinations' => $reattributeDestinations,
'includeSelf' => $includeSelf,
'visibleDeliveries' => $visibleDeliveries,
'gettingStartedStrip' => $request->boolean('getting_started')
? (static function () use ($gettingStartedFlow, $request, $invoice): array {
$snapshot = $gettingStartedFlow->snapshot($request->user());
Expand Down
9 changes: 0 additions & 9 deletions app/Jobs/DeliverInvoiceMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
use App\Mail\InvoiceUnderpaymentIssuerMail;
use App\Mail\InvoiceIssuerPaidNoticeMail;
use App\Models\Invoice;
use App\Mail\InvoicePartialWarningClientMail;
use App\Mail\InvoicePartialWarningIssuerMail;
use App\Models\InvoiceDelivery;
use App\Models\InvoicePayment;
use App\Services\InvoiceDeliveryService;
Expand Down Expand Up @@ -140,8 +138,6 @@ public function handle(MailAlias $mailAlias, InvoiceDeliveryService $deliveries)
'issuer_overpay_alert' => new InvoiceOverpaymentIssuerMail($invoice, $delivery),
'client_underpay_alert' => new InvoiceUnderpaymentClientMail($invoice, $delivery),
'issuer_underpay_alert' => new InvoiceUnderpaymentIssuerMail($invoice, $delivery),
'client_partial_warning' => new InvoicePartialWarningClientMail($invoice, $delivery),
'issuer_partial_warning' => new InvoicePartialWarningIssuerMail($invoice, $delivery),
default => new InvoiceReadyMail($invoice, $delivery),
};

Expand Down Expand Up @@ -288,11 +284,6 @@ private function shouldSkipDelivery(
return 'Underpayment resolved before send.';
}

$partialTypes = ['client_partial_warning', 'issuer_partial_warning'];
if (in_array($delivery->type, $partialTypes, true) && !$invoice->shouldWarnAboutPartialPayments()) {
return 'Partial-payment warning no longer applicable.';
}

$pastDueTypes = ['past_due_issuer', 'past_due_client'];
if (in_array($delivery->type, $pastDueTypes, true) && in_array($invoice->status, ['paid', 'void'], true)) {
return 'Invoice settled before past-due alert.';
Expand Down
6 changes: 6 additions & 0 deletions app/Mail/InvoiceIssuerPaidNoticeMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ public function content(): Content
markdown: 'mail.invoice-issuer-paid',
with: [
'invoice' => $this->invoice,
'settlementPayments' => $this->invoice->payments()
->whereNotNull('confirmed_at')
->whereNull('ignored_at')
->where('is_adjustment', false)
->orderBy('confirmed_at')
->get(),
],
);
}
Expand Down
2 changes: 1 addition & 1 deletion app/Mail/InvoiceOverpaymentClientMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function __construct(public Invoice $invoice, public InvoiceDelivery $del
public function envelope(): Envelope
{
return new Envelope(
subject: 'Invoice ' . ($this->invoice->number ?? $this->invoice->id) . ' was overpaid',
subject: 'Payment confirmed — Invoice ' . ($this->invoice->number ?? $this->invoice->id) . ' overpaid',
replyTo: [new Address($this->invoice->user->email, $this->invoice->user->name)],
);
}
Expand Down
2 changes: 1 addition & 1 deletion app/Mail/InvoiceOverpaymentIssuerMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function __construct(public Invoice $invoice, public InvoiceDelivery $del
public function envelope(): Envelope
{
return new Envelope(
subject: 'Client overpaid invoice ' . ($this->invoice->number ?? $this->invoice->id),
subject: 'Payment confirmed — client overpaid Invoice ' . ($this->invoice->number ?? $this->invoice->id),
);
}

Expand Down
6 changes: 6 additions & 0 deletions app/Mail/InvoicePaidReceiptMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ public function content(): Content
'delivery' => $this->delivery,
'client' => $this->invoice->client,
'publicUrl' => $this->invoice->public_url,
'settlementPayments' => $this->invoice->payments()
->whereNotNull('confirmed_at')
->whereNull('ignored_at')
->where('is_adjustment', false)
->orderBy('confirmed_at')
->get(),
],
);
}
Expand Down
43 changes: 0 additions & 43 deletions app/Mail/InvoicePartialWarningClientMail.php

This file was deleted.

41 changes: 0 additions & 41 deletions app/Mail/InvoicePartialWarningIssuerMail.php

This file was deleted.

19 changes: 18 additions & 1 deletion app/Mail/InvoicePastDueClientMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,15 @@ public function __construct(public Invoice $invoice, public InvoiceDelivery $del

public function envelope(): Envelope
{
$number = $this->invoice->number ?? $this->invoice->id;
$subject = match ($this->slot()) {
2 => "2nd past-due notice — invoice {$number} is 1 week overdue",
3 => "3rd past-due notice — invoice {$number} is 2 weeks overdue",
default => "Reminder: invoice {$number} is past due",
};

return new Envelope(
subject: 'Reminder: invoice ' . ($this->invoice->number ?? $this->invoice->id) . ' is past due',
subject: $subject,
replyTo: [new Address($this->invoice->user->email, $this->invoice->user->name)],
);
}
Expand All @@ -32,6 +39,7 @@ public function content(): Content
markdown: 'mail.invoice-past-due-client',
with: [
'invoice' => $this->invoice,
'slot' => $this->slot(),
],
);
}
Expand All @@ -40,4 +48,13 @@ public function attachments(): array
{
return [];
}

private function slot(): int
{
if (preg_match('/^past_due_(\d+)$/', (string) ($this->delivery->context_key ?? ''), $m)) {
return (int) $m[1];
}

return 1;
}
}
19 changes: 18 additions & 1 deletion app/Mail/InvoicePastDueIssuerMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,15 @@ public function __construct(public Invoice $invoice, public InvoiceDelivery $del

public function envelope(): Envelope
{
$number = $this->invoice->number ?? $this->invoice->id;
$subject = match ($this->slot()) {
2 => "2nd past-due notice sent — invoice {$number} 1 week overdue",
3 => "3rd past-due notice sent — invoice {$number} 2 weeks overdue",
default => "Reminder sent: invoice {$number} past due",
};

return new Envelope(
subject: 'Invoice ' . ($this->invoice->number ?? $this->invoice->id) . ' is past due',
subject: $subject,
);
}

Expand All @@ -30,6 +37,7 @@ public function content(): Content
markdown: 'mail.invoice-past-due-issuer',
with: [
'invoice' => $this->invoice,
'slot' => $this->slot(),
],
);
}
Expand All @@ -38,4 +46,13 @@ public function attachments(): array
{
return [];
}

private function slot(): int
{
if (preg_match('/^past_due_(\d+)$/', (string) ($this->delivery->context_key ?? ''), $m)) {
return (int) $m[1];
}

return 1;
}
}
2 changes: 1 addition & 1 deletion app/Mail/InvoicePaymentAcknowledgmentIssuerMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function __construct(
public function envelope(): Envelope
{
return new Envelope(
subject: 'Review detected payment for Invoice ' . ($this->invoice->number ?? $this->invoice->id),
subject: 'Review payment activity on Invoice ' . ($this->invoice->number ?? $this->invoice->id),
);
}

Expand Down
2 changes: 1 addition & 1 deletion app/Mail/InvoiceUnderpaymentClientMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function __construct(public Invoice $invoice, public InvoiceDelivery $del
public function envelope(): Envelope
{
return new Envelope(
subject: 'Invoice ' . ($this->invoice->number ?? $this->invoice->id) . ' has a balance due',
subject: 'Payment confirmed — balance still due on Invoice ' . ($this->invoice->number ?? $this->invoice->id),
replyTo: [new Address($this->invoice->user->email, $this->invoice->user->name)],
);
}
Expand Down
2 changes: 1 addition & 1 deletion app/Mail/InvoiceUnderpaymentIssuerMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function __construct(public Invoice $invoice, public InvoiceDelivery $del
public function envelope(): Envelope
{
return new Envelope(
subject: 'Client underpayment alert for invoice ' . ($this->invoice->number ?? $this->invoice->id),
subject: 'Payment confirmed — client underpaid Invoice ' . ($this->invoice->number ?? $this->invoice->id),
);
}

Expand Down
18 changes: 0 additions & 18 deletions app/Models/Invoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ class Invoice extends Model
'billing_address_override','invoice_footer_note_override','branding_heading_override',
'last_overpayment_alert_at','last_underpayment_alert_at',
'last_past_due_issuer_alert_at','last_past_due_client_alert_at',
'last_partial_warning_sent_at',
];

protected $casts = [
Expand Down Expand Up @@ -58,7 +57,6 @@ class Invoice extends Model
'last_underpayment_alert_at' => 'datetime',
'last_past_due_issuer_alert_at' => 'datetime',
'last_past_due_client_alert_at' => 'datetime',
'last_partial_warning_sent_at' => 'datetime',
];
public const SATS_PER_BTC = 100_000_000;
public const PAYMENT_SAT_TOLERANCE = 100;
Expand Down Expand Up @@ -690,22 +688,6 @@ public function getPublicUrlAttribute(): ?string
return $base . $path;
}

public function shouldWarnAboutPartialPayments(): bool
{
if (in_array($this->status, ['paid','void'])) {
return false;
}

$outstanding = $this->outstanding_sats;
if ($outstanding === null || $outstanding <= 0) {
return false;
}

$payments = $this->activePayments();

return $payments->count() >= 2;
}

public function sumPaymentsUsd(bool $confirmedOnly = false): float
{
$payments = $this->activePayments();
Expand Down
2 changes: 0 additions & 2 deletions app/Models/InvoiceDelivery.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ public function typeLabel(): string
'issuer_overpay_alert' => 'Overpayment alert (issuer)',
'client_underpay_alert' => 'Underpayment alert (client)',
'issuer_underpay_alert' => 'Underpayment alert (issuer)',
'client_partial_warning' => 'Partial payment warning (client)',
'issuer_partial_warning' => 'Partial payment warning (issuer)',
default => Str::of($this->type)->replace('_', ' ')->headline()->toString(),
};
}
Expand Down
12 changes: 4 additions & 8 deletions app/Services/InvoiceAlertService.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public function sendDetectedPaymentAcknowledgments(Invoice $invoice, InvoicePaym

public function checkPaymentThresholds(Invoice $invoice): void
{
if ($invoice->status === 'void') {
return;
}

if ($invoice->requiresClientOverpayAlert()) {
$this->maybeSendOverpayAlert($invoice);
}
Expand Down Expand Up @@ -98,14 +102,6 @@ public function skipInvalidQueuedDeliveries(Invoice $invoice, string $reasonPref
"{$reasonPrefix} Overpayment alert no longer applies."
);
}

if (! $invoice->shouldWarnAboutPartialPayments()) {
$this->skipQueuedDeliveries(
$invoice,
['client_partial_warning', 'issuer_partial_warning'],
"{$reasonPrefix} Partial-payment warning no longer applies."
);
}
}

public function sendPastDueAlerts(Invoice $invoice): void
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up(): void
{
DB::table('invoice_deliveries')
->whereIn('type', ['client_partial_warning', 'issuer_partial_warning'])
->delete();

Schema::table('invoices', function (Blueprint $table) {
$table->dropColumn('last_partial_warning_sent_at');
});
}

public function down(): void
{
Schema::table('invoices', function (Blueprint $table) {
$table->timestamp('last_partial_warning_sent_at')->nullable();
});
}
};
Loading
Loading