Files
ladill-transfer/app/Services/Transfer/TransferRecipientMailService.php
T
isaaccladandCursor 7c4673adb6
Deploy Ladill Transfer / deploy (push) Successful in 44s
Add recipient email and milestone notifications on transfer create.
Recipients can be emailed the download link immediately and at optional
reminders (7/3/1 days before unavailable, or when grace period starts).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 12:38:58 +00:00

100 lines
2.9 KiB
PHP

<?php
namespace App\Services\Transfer;
use App\Models\Transfer;
use App\Notifications\TransferRecipientNotification;
use Carbon\CarbonInterface;
use Illuminate\Support\Facades\Notification;
/** Sends milestone emails to transfer recipients (share link + reminders). */
class TransferRecipientMailService
{
/** @return list<string> */
public function allowedMilestones(): array
{
return array_keys((array) config('transfer.recipient_email_milestones', []));
}
/** @return array<string, string> */
public function milestoneOptions(): array
{
return (array) config('transfer.recipient_email_milestones', []);
}
/** @param list<string> $milestones */
public function normalizeMilestones(array $milestones): array
{
$allowed = $this->allowedMilestones();
return array_values(array_unique(array_filter(
$milestones,
static fn ($milestone) => in_array((string) $milestone, $allowed, true),
)));
}
public function notifyRecipient(Transfer $transfer, string $milestone): bool
{
$email = trim((string) $transfer->recipient_email);
if ($email === '' || ! $transfer->wantsRecipientMilestone($milestone)) {
return false;
}
if ($transfer->recipientMilestoneSent($milestone)) {
return false;
}
Notification::route('mail', $email)->notify(
new TransferRecipientNotification($transfer->loadMissing(['files', 'qrCode', 'user']), $milestone),
);
$transfer->markRecipientMilestoneSent($milestone);
return true;
}
public function processDueMilestones(): int
{
$sent = 0;
Transfer::query()
->accessible()
->whereNotNull('recipient_email')
->whereNotNull('recipient_email_milestones')
->with(['files', 'qrCode', 'user'])
->chunkById(100, function ($transfers) use (&$sent) {
foreach ($transfers as $transfer) {
foreach ($this->dueDayMilestones($transfer) as $milestone) {
if ($this->notifyRecipient($transfer, $milestone)) {
$sent++;
}
}
}
});
return $sent;
}
/** @return list<string> */
private function dueDayMilestones(Transfer $transfer): array
{
if (! $transfer->paid_until instanceof CarbonInterface || $transfer->paid_until->isPast()) {
return [];
}
$daysRemaining = (int) now()->startOfDay()->diffInDays(
$transfer->paid_until->copy()->startOfDay(),
false,
);
$due = [];
foreach (['7', '3', '1'] as $milestone) {
if ($daysRemaining === (int) $milestone && $transfer->wantsRecipientMilestone($milestone)) {
$due[] = $milestone;
}
}
return $due;
}
}