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. } } }