getOrCreateQrSetting(); $thresholdMinor = $settings->auto_withdraw_amount_minor; if ($thresholdMinor === null || $thresholdMinor < 100) { return false; } try { $balanceMinor = $this->billing->balanceMinor($user->public_id); } catch (Throwable $e) { Log::warning('Auto-withdraw balance check failed', [ 'user' => $user->public_id, 'error' => $e->getMessage(), ]); return false; } if ($balanceMinor < $thresholdMinor) { return false; } if (! $this->hasPayoutAccount($user)) { return false; } if ($this->hasPendingWithdrawal($user)) { return false; } $amountMajor = round($balanceMinor / 100, 2); if ($amountMajor < 1) { return false; } try { $response = $this->identitySend('POST', '/api/identity/wallet/withdraw', [ 'user' => $user->public_id, 'amount' => $amountMajor, ]); if (! $response->successful()) { Log::warning('Auto-withdraw failed', [ 'user' => $user->public_id, 'status' => $response->status(), 'body' => $response->body(), ]); return false; } $this->notifications->withdrawalSubmitted($user, $amountMajor); return true; } catch (Throwable $e) { Log::warning('Auto-withdraw exception', [ 'user' => $user->public_id, 'error' => $e->getMessage(), ]); return false; } } public function processAll(): int { $count = 0; QrSetting::query() ->whereNotNull('auto_withdraw_amount_minor') ->where('auto_withdraw_amount_minor', '>=', 100) ->with('user') ->chunkById(50, function ($settings) use (&$count) { foreach ($settings as $setting) { $user = $setting->user; if ($user && $this->attemptForUser($user)) { $count++; } } }); return $count; } private function hasPayoutAccount(User $user): bool { $response = $this->identitySend( 'GET', '/api/identity/payout-account?user='.urlencode((string) $user->public_id), [], ); if (! $response->successful()) { return false; } return filled($response->json('data.payout_account.account_number')); } private function hasPendingWithdrawal(User $user): bool { $response = $this->identitySend( 'GET', '/api/identity/wallet/withdrawals?user='.urlencode((string) $user->public_id), [], ); if (! $response->successful()) { return false; } return collect($response->json('data', [])) ->contains(fn ($withdrawal) => in_array($withdrawal['status'] ?? '', ['pending', 'processing'], true)); } private function identitySend(string $method, string $path, array $payload): HttpResponse { return Http::baseUrl(rtrim((string) config('services.ladill_identity.url'), '/')) ->withToken((string) config('services.ladill_identity.key')) ->connectTimeout(10) ->timeout(20) ->acceptJson() ->asJson() ->send($method, $path, ['json' => $payload]); } }