first()?->value; if ($state === null) { return ['level' => 1, 'since' => null]; } if (!is_array($state) || !in_array($state['level'] ?? null, [1, 2, 3], true)) { return null; } return $state; } catch (\Throwable $e) { return null; } } public static function save(int $level): void { if (!in_array($level, [1, 2, 3], true)) { throw new \InvalidArgumentException('Invalid automatic mail level'); } Cache::lock('automatic-mail-policy:save', 10)->block(5, function () use ($level) { DB::transaction(function () use ($level) { $previous = self::state(); if ($previous === null) { throw new \RuntimeException('automatic_mail_policy_unavailable'); } Setting::createOrUpdate('automatic_mail_policy', [ 'level' => $level, 'since' => $level === 2 ? (($previous['level'] === 2 ? $previous['since'] : null) ?? now()->timestamp) : null, ]); }); }); Cache::store('redis')->forget(\App\Support\Setting::CACHE_KEY); } public static function purpose(array $params): string { // Compatibility for older queued jobs. Never infer notify: it also serves replies/tests. return $params['purpose'] ?? match ($params['template_name'] ?? '') { 'verify' => 'verification', 'mailLogin' => 'login_link', 'remindTraffic' => 'traffic_reminder', 'remindExpire' => 'expiry_reminder', default => 'other', }; } public static function decision(string $purpose, array $context = []): ?array { if (!in_array($purpose, ['traffic_reminder', 'expiry_reminder', 'automatic_broadcast', 'service_expired'], true)) { return null; // Includes manual broadcast, all responses and explicitly classified admin tests. } $state = self::state(); if ($state === null) { return ['status' => 'pending', 'error' => 'automatic_mail_policy_unavailable', 'retryable' => true]; } $allowed = $purpose === 'service_expired' ? $state['level'] === 2 : $state['level'] === 1; if (!$allowed) { return self::blocked('automatic_mail_level_blocked'); } if ($purpose === 'service_expired') { $due = (int) ($context['subscription_expired_at'] ?? 0); try { $user = User::find($context['user_id'] ?? 0); $reminders = Setting::where('name', 'remind_mail_enable')->first()?->value; } catch (\Throwable $e) { return ['status' => 'pending', 'error' => 'subscription_expiry_check_unavailable', 'retryable' => true]; } if (!$state['since'] || $due <= $state['since'] || $due > now()->timestamp || !$reminders || !$user || !$user->remind_expire || $user->banned || !$user->plan_id || (int) $user->expired_at !== $due) { return self::blocked('subscription_expiry_no_longer_valid'); } } return null; } private static function blocked(string $error): array { return ['status' => 'policy_blocked', 'error' => $error, 'retryable' => false]; } }