| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- <?php
- namespace App\Services\Integrations\Asaas;
- use App\Exceptions\AsaasException;
- use App\Models\Unit;
- use App\Models\UnitPaymentAccount;
- use Exception;
- use Illuminate\Support\Facades\Log;
- class AsaasAccountService
- {
- protected AsaasClient $client;
- public function __construct(AsaasClient $client)
- {
- $this->client = $client;
- }
- public function ensureSubaccount(Unit $unit): UnitPaymentAccount
- {
- $existingAccount = UnitPaymentAccount::where('unit_id', $unit->id)->first();
-
- if ($existingAccount && $existingAccount->asaas_account_id) {
- return $existingAccount;
- }
- if (empty($unit->cnpj)) {
- throw new Exception("A unidade {$unit->fantasy_name} não possui um CNPJ cadastrado. O CNPJ é obrigatório para criar a subconta.");
- }
- // Garante a chave de criptografia ANTES de chamar o Asaas. Sem APP_KEY, o
- // POST cria a subconta lá, mas o save local falha ao encriptar a apiKey
- // (cast 'encrypted') -> subconta órfã: apiKey perdida e e-mail/CNPJ travados.
- if (empty(config('app.key'))) {
- throw new Exception("APP_KEY não configurada. Abortando antes de criar a subconta no Asaas para evitar conta órfã (Unidade {$unit->id}).");
- }
- $payload = [
- 'name' => $unit->fantasy_name ?? $unit->social_reason ?? 'Unidade Ginástica do Cérebro',
- 'email' => $unit->email,
- 'cpfCnpj' => preg_replace('/[^0-9]/', '', $unit->cnpj),
- 'mobilePhone' => preg_replace('/[^0-9]/', '', $unit->cell_number ?? $unit->phone_number ?? ''),
- 'address' => $unit->street,
- 'addressNumber' => $unit->address_number ?? 'S/N',
- 'province' => $unit->neighborhood,
- 'postalCode' => preg_replace('/[^0-9]/', '', $unit->postal_code),
- 'companyType' => 'LIMITED',
- 'incomeValue' => 10000.00, // Renda/Faturamento exigido pelo Asaas
- ];
- try {
- $response = $this->client->post('/accounts', $payload);
- } catch (AsaasException $e) {
- // Colisão de e-mail (400): diagnostica se a conta é SUA (órfã, recuperável
- // só pelo painel) ou EXTERNA (e-mail em uso fora do seu parent -> trocar e-mail).
- if ($e->getCode() === 400 && str_contains($e->getMessage(), 'já está em uso')) {
- $this->logEmailCollision($unit);
- }
- throw $e;
- }
- return UnitPaymentAccount::updateOrCreate(
- ['unit_id' => $unit->id],
- [
- 'asaas_account_id' => $response['id'],
- 'asaas_wallet_id' => $response['walletId'],
- // Cast 'encrypted' do model encripta no save; e SyncStudentChargeJob
- // espera mais uma camada manual. Mantém o encryptString para casar.
- 'asaas_api_key' => \Illuminate\Support\Facades\Crypt::encryptString($response['apiKey']),
- 'status' => 'ACTIVE',
- 'onboarded_at' => now(),
- ]
- );
- }
- /**
- * Loga, no erro de e-mail em uso, se a conta colidente pertence ao seu parent
- * (subconta órfã) ou é externa — para acelerar o diagnóstico.
- */
- private function logEmailCollision(Unit $unit): void
- {
- try {
- $accounts = $this->client->get('/accounts', ['email' => $unit->email])['data'] ?? [];
- if (count($accounts) > 0) {
- Log::warning("Asaas: e-mail {$unit->email} já é uma SUBCONTA SUA (órfã). Recuperável só pelo painel/encerramento.", [
- 'unit_id' => $unit->id,
- 'asaas_account_id' => $accounts[0]['id'] ?? null,
- 'wallet_id' => $accounts[0]['walletId'] ?? null,
- ]);
- } else {
- Log::warning("Asaas: e-mail {$unit->email} está em uso em conta EXTERNA (fora do seu parent). Cadastre a unidade com outro e-mail.", [
- 'unit_id' => $unit->id,
- ]);
- }
- } catch (\Throwable $t) {
- // Diagnóstico é best-effort: nunca mascara o erro original do POST.
- }
- }
- }
|