UnitPaymentAccountService.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. <?php
  2. namespace App\Services;
  3. use App\Exceptions\AsaasException;
  4. use App\Models\UnitPaymentAccount;
  5. use App\Services\Integrations\Asaas\AsaasWebhookService;
  6. use Illuminate\Support\Facades\Log;
  7. use Illuminate\Validation\ValidationException;
  8. class UnitPaymentAccountService
  9. {
  10. public function __construct(
  11. protected AsaasWebhookService $webhookService,
  12. ) {}
  13. public function getByUnitId(int $unitId): ?UnitPaymentAccount
  14. {
  15. return UnitPaymentAccount::where('unit_id', $unitId)->first();
  16. }
  17. /**
  18. * Cria/atualiza a conta de pagamento da unidade.
  19. *
  20. * Ao informar uma chave: valida na Asaas e garante um webhook. Uma MESMA chave
  21. * pode ser compartilhada por várias unidades (mesmo dono) — nesse caso o webhook
  22. * é registrado uma única vez e reaproveitado (dedupe por hash da chave). As
  23. * finanças seguem separadas por unidade (externalReference STU_/TBR_).
  24. */
  25. public function upsertAsaasApiKey(int $unitId, ?string $apiKey): UnitPaymentAccount
  26. {
  27. $apiKey = $apiKey !== null ? trim($apiKey) : null;
  28. $hasKey = $apiKey !== null && $apiKey !== '';
  29. $model = UnitPaymentAccount::firstOrNew(['unit_id' => $unitId]);
  30. if (!$hasKey) {
  31. $this->releaseWebhook($model);
  32. $model->asaas_api_key = null;
  33. $model->asaas_api_key_hash = null;
  34. $model->asaas_webhook_id = null;
  35. $model->status = UnitPaymentAccount::STATUS_PENDING;
  36. $model->onboarded_at = null;
  37. $model->save();
  38. return $model->fresh();
  39. }
  40. $hash = hash('sha256', $apiKey);
  41. // Reenvio da mesma chave já configurada: mantém como está.
  42. if ($model->exists && $model->asaas_api_key === $apiKey && $model->asaas_webhook_id) {
  43. return $model->fresh();
  44. }
  45. try {
  46. $this->webhookService->validateKey($apiKey);
  47. } catch (AsaasException $e) {
  48. throw ValidationException::withMessages([
  49. 'asaas_api_key' => 'Chave Asaas inválida ou sem permissão. Verifique e tente novamente.',
  50. ]);
  51. }
  52. // Troca de chave: libera o webhook antigo (só remove se ninguém mais usa).
  53. $this->releaseWebhook($model);
  54. $webhookId = $this->resolveWebhookId($unitId, $apiKey, $hash);
  55. $model->asaas_api_key = $apiKey;
  56. $model->asaas_api_key_hash = $hash;
  57. $model->asaas_webhook_id = $webhookId;
  58. $model->status = UnitPaymentAccount::STATUS_ACTIVE;
  59. $model->onboarded_at = $model->onboarded_at ?? now();
  60. $model->save();
  61. return $model->fresh();
  62. }
  63. /**
  64. * Reaproveita o webhook de outra unidade que usa a MESMA chave; senão, registra.
  65. */
  66. private function resolveWebhookId(int $unitId, string $apiKey, string $hash): string
  67. {
  68. $sibling = UnitPaymentAccount::where('asaas_api_key_hash', $hash)
  69. ->where('unit_id', '!=', $unitId)
  70. ->whereNotNull('asaas_webhook_id')
  71. ->first();
  72. if ($sibling) {
  73. return $sibling->asaas_webhook_id;
  74. }
  75. try {
  76. return $this->webhookService->register($apiKey);
  77. } catch (AsaasException $e) {
  78. Log::error("Falha ao registrar webhook Asaas da unidade {$unitId}: " . $e->getMessage());
  79. throw ValidationException::withMessages([
  80. 'asaas_api_key' => 'Não foi possível registrar o webhook na Asaas. Tente novamente.',
  81. ]);
  82. }
  83. }
  84. /**
  85. * Remove o webhook atual — a menos que outra unidade ainda o utilize (chave
  86. * compartilhada). Best-effort: erros são apenas logados.
  87. */
  88. private function releaseWebhook(UnitPaymentAccount $model): void
  89. {
  90. if (!$model->asaas_api_key || !$model->asaas_webhook_id) {
  91. return;
  92. }
  93. $stillShared = UnitPaymentAccount::where('asaas_webhook_id', $model->asaas_webhook_id)
  94. ->where('unit_id', '!=', $model->unit_id)
  95. ->exists();
  96. if ($stillShared) {
  97. return;
  98. }
  99. try {
  100. $this->webhookService->delete($model->asaas_api_key, $model->asaas_webhook_id);
  101. } catch (\Throwable $e) {
  102. Log::warning("Não foi possível remover webhook Asaas da unidade {$model->unit_id}: " . $e->getMessage());
  103. }
  104. }
  105. }