where('codigo', $tenantCode)->firstOrFail(); $user = User::query()->findOrFail($userId); $this->mailService ->forTenant($tenantCode) ->send( $user->email, "Bienvenido a {$tenant->nombre}", view('mail.notifications.welcome', compact('tenant', 'user'))->render(), ); } public function sendPasswordResetCode( int $attemptId, string $tenantCode, string $channel = PasswordResetRequested::CHANNEL_STOREFRONT, ): void { $tenant = Tenant::query() ->with('websiteType') ->where('codigo', $tenantCode) ->firstOrFail(); $attempt = ResetPasswordAttempt::query() ->with('user') ->findOrFail($attemptId); if ($attempt->status !== ResetPasswordAttempt::STATUS_PENDING) { Log::warning('Password reset email was skipped because the attempt is no longer pending.', [ 'attempt_id' => $attemptId, 'tenant_code' => $tenantCode, 'attempt_status' => $attempt->status, ]); return; } $recoveryDomain = match ($channel) { PasswordResetRequested::CHANNEL_ADMINAPP => $tenant->websiteType?->dominio, PasswordResetRequested::CHANNEL_SCANNER => $tenant->websiteType?->scanner_domain, default => $tenant->dominio, }; $recoveryQuery = ['email' => $attempt->user->email]; if ( $channel === PasswordResetRequested::CHANNEL_SCANNER && $attempt->reason === ResetPasswordAttempt::REASON_STAFF_CREATED ) { $recoveryQuery['code'] = $attempt->codigo; } $recoveryUrl = $recoveryDomain === null ? null : 'https://'.$recoveryDomain.'/recuperar-contrasena/codigo?'.http_build_query($recoveryQuery); $this->mailService ->forTenant($tenantCode) ->send( $attempt->user->email, "Código para recuperar tu contraseña - {$tenant->nombre}", view('mail.notifications.password-reset', compact('tenant', 'attempt', 'recoveryUrl'))->render(), ); } public function sendPurchasePaid(int $purchaseId): void { $purchase = Purchase::query() ->with(['tenant', 'user', 'items']) ->findOrFail($purchaseId); $this->mailService ->forTenant($purchase->tenant_codigo) ->send( $this->recipientFor($purchase), "Pago confirmado - Compra #{$purchase->getKey()}", view('mail.notifications.purchase-paid', compact('purchase'))->render(), ); } /** @param array $ticketIds */ public function sendTicketsAvailable(int $purchaseId, array $ticketIds): void { $purchase = Purchase::query()->with(['tenant', 'user'])->findOrFail($purchaseId); /** @var Collection $tickets */ $tickets = Ticket::query() ->where('tenant_code', $purchase->tenant_codigo) ->where('user_id', $purchase->user_id) ->whereKey($ticketIds) ->get(); if ($tickets->isEmpty()) { return; } $this->mailService ->forTenant($purchase->tenant_codigo) ->send( $this->recipientFor($purchase), 'Tus tickets ya están disponibles', view('mail.notifications.tickets-available', compact('purchase', 'tickets'))->render(), ); } private function recipientFor(Purchase $purchase): string { return (string) ($purchase->email ?: $purchase->user?->email); } }