Files
ladill-woo-manager/app/Services/Mini/AutoWithdrawService.php
T
isaaccladandCursor ffe0b9d877
Deploy Ladill Woo Manager / deploy (push) Failing after 2s
Scaffold Ladill Woo Manager for WooCommerce order fulfillment.
Standalone app with SSO shell, WordPress plugin connect flow, webhook ingest,
fulfillment inbox, and plugin activation API — no payment processing.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 22:39:38 +00:00

149 lines
4.2 KiB
PHP

<?php
namespace App\Services\Mini;
use App\Models\QrSetting;
use App\Models\User;
use App\Services\Billing\BillingClient;
use App\Services\Notifications\MiniNotificationService;
use Illuminate\Http\Client\Response as HttpResponse;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Throwable;
class AutoWithdrawService
{
public function __construct(
private BillingClient $billing,
private MiniNotificationService $notifications,
) {}
public function attemptForUser(User $user): bool
{
$settings = $user->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]);
}
}