|
@@ -22,8 +22,10 @@ public function getByUnitId(int $unitId): ?UnitPaymentAccount
|
|
|
/**
|
|
/**
|
|
|
* Cria/atualiza a conta de pagamento da unidade.
|
|
* Cria/atualiza a conta de pagamento da unidade.
|
|
|
*
|
|
*
|
|
|
- * Ao informar uma chave: valida na Asaas e registra o webhook (síncrono e
|
|
|
|
|
- * estrito — chave inválida bloqueia). Ao limpar: remove o webhook (best-effort).
|
|
|
|
|
|
|
+ * Ao informar uma chave: valida na Asaas e garante um webhook. Uma MESMA chave
|
|
|
|
|
+ * pode ser compartilhada por várias unidades (mesmo dono) — nesse caso o webhook
|
|
|
|
|
+ * é registrado uma única vez e reaproveitado (dedupe por hash da chave). As
|
|
|
|
|
+ * finanças seguem separadas por unidade (externalReference STU_/TBR_).
|
|
|
*/
|
|
*/
|
|
|
public function upsertAsaasApiKey(int $unitId, ?string $apiKey): UnitPaymentAccount
|
|
public function upsertAsaasApiKey(int $unitId, ?string $apiKey): UnitPaymentAccount
|
|
|
{
|
|
{
|
|
@@ -33,17 +35,20 @@ public function upsertAsaasApiKey(int $unitId, ?string $apiKey): UnitPaymentAcco
|
|
|
$model = UnitPaymentAccount::firstOrNew(['unit_id' => $unitId]);
|
|
$model = UnitPaymentAccount::firstOrNew(['unit_id' => $unitId]);
|
|
|
|
|
|
|
|
if (!$hasKey) {
|
|
if (!$hasKey) {
|
|
|
- $this->removeWebhook($model);
|
|
|
|
|
|
|
+ $this->releaseWebhook($model);
|
|
|
|
|
|
|
|
- $model->asaas_api_key = null;
|
|
|
|
|
- $model->asaas_webhook_id = null;
|
|
|
|
|
- $model->status = UnitPaymentAccount::STATUS_PENDING;
|
|
|
|
|
- $model->onboarded_at = null;
|
|
|
|
|
|
|
+ $model->asaas_api_key = null;
|
|
|
|
|
+ $model->asaas_api_key_hash = null;
|
|
|
|
|
+ $model->asaas_webhook_id = null;
|
|
|
|
|
+ $model->status = UnitPaymentAccount::STATUS_PENDING;
|
|
|
|
|
+ $model->onboarded_at = null;
|
|
|
$model->save();
|
|
$model->save();
|
|
|
|
|
|
|
|
return $model->fresh();
|
|
return $model->fresh();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ $hash = hash('sha256', $apiKey);
|
|
|
|
|
+
|
|
|
// Reenvio da mesma chave já configurada: mantém como está.
|
|
// Reenvio da mesma chave já configurada: mantém como está.
|
|
|
if ($model->exists && $model->asaas_api_key === $apiKey && $model->asaas_webhook_id) {
|
|
if ($model->exists && $model->asaas_api_key === $apiKey && $model->asaas_webhook_id) {
|
|
|
return $model->fresh();
|
|
return $model->fresh();
|
|
@@ -57,36 +62,63 @@ public function upsertAsaasApiKey(int $unitId, ?string $apiKey): UnitPaymentAcco
|
|
|
]);
|
|
]);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Troca de chave: remove o webhook antigo antes de registrar o novo.
|
|
|
|
|
- $this->removeWebhook($model);
|
|
|
|
|
|
|
+ // Troca de chave: libera o webhook antigo (só remove se ninguém mais usa).
|
|
|
|
|
+ $this->releaseWebhook($model);
|
|
|
|
|
+
|
|
|
|
|
+ $webhookId = $this->resolveWebhookId($unitId, $apiKey, $hash);
|
|
|
|
|
+
|
|
|
|
|
+ $model->asaas_api_key = $apiKey;
|
|
|
|
|
+ $model->asaas_api_key_hash = $hash;
|
|
|
|
|
+ $model->asaas_webhook_id = $webhookId;
|
|
|
|
|
+ $model->status = UnitPaymentAccount::STATUS_ACTIVE;
|
|
|
|
|
+ $model->onboarded_at = $model->onboarded_at ?? now();
|
|
|
|
|
+ $model->save();
|
|
|
|
|
+
|
|
|
|
|
+ return $model->fresh();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Reaproveita o webhook de outra unidade que usa a MESMA chave; senão, registra.
|
|
|
|
|
+ */
|
|
|
|
|
+ private function resolveWebhookId(int $unitId, string $apiKey, string $hash): string
|
|
|
|
|
+ {
|
|
|
|
|
+ $sibling = UnitPaymentAccount::where('asaas_api_key_hash', $hash)
|
|
|
|
|
+ ->where('unit_id', '!=', $unitId)
|
|
|
|
|
+ ->whereNotNull('asaas_webhook_id')
|
|
|
|
|
+ ->first();
|
|
|
|
|
+
|
|
|
|
|
+ if ($sibling) {
|
|
|
|
|
+ return $sibling->asaas_webhook_id;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- $webhookId = $this->webhookService->register($apiKey);
|
|
|
|
|
|
|
+ return $this->webhookService->register($apiKey);
|
|
|
} catch (AsaasException $e) {
|
|
} catch (AsaasException $e) {
|
|
|
Log::error("Falha ao registrar webhook Asaas da unidade {$unitId}: " . $e->getMessage());
|
|
Log::error("Falha ao registrar webhook Asaas da unidade {$unitId}: " . $e->getMessage());
|
|
|
throw ValidationException::withMessages([
|
|
throw ValidationException::withMessages([
|
|
|
'asaas_api_key' => 'Não foi possível registrar o webhook na Asaas. Tente novamente.',
|
|
'asaas_api_key' => 'Não foi possível registrar o webhook na Asaas. Tente novamente.',
|
|
|
]);
|
|
]);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- $model->asaas_api_key = $apiKey;
|
|
|
|
|
- $model->asaas_webhook_id = $webhookId;
|
|
|
|
|
- $model->status = UnitPaymentAccount::STATUS_ACTIVE;
|
|
|
|
|
- $model->onboarded_at = $model->onboarded_at ?? now();
|
|
|
|
|
- $model->save();
|
|
|
|
|
-
|
|
|
|
|
- return $model->fresh();
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Remove o webhook atual da conta (ignora erros — best-effort).
|
|
|
|
|
|
|
+ * Remove o webhook atual — a menos que outra unidade ainda o utilize (chave
|
|
|
|
|
+ * compartilhada). Best-effort: erros são apenas logados.
|
|
|
*/
|
|
*/
|
|
|
- private function removeWebhook(UnitPaymentAccount $model): void
|
|
|
|
|
|
|
+ private function releaseWebhook(UnitPaymentAccount $model): void
|
|
|
{
|
|
{
|
|
|
if (!$model->asaas_api_key || !$model->asaas_webhook_id) {
|
|
if (!$model->asaas_api_key || !$model->asaas_webhook_id) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ $stillShared = UnitPaymentAccount::where('asaas_webhook_id', $model->asaas_webhook_id)
|
|
|
|
|
+ ->where('unit_id', '!=', $model->unit_id)
|
|
|
|
|
+ ->exists();
|
|
|
|
|
+
|
|
|
|
|
+ if ($stillShared) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
$this->webhookService->delete($model->asaas_api_key, $model->asaas_webhook_id);
|
|
$this->webhookService->delete($model->asaas_api_key, $model->asaas_webhook_id);
|
|
|
} catch (\Throwable $e) {
|
|
} catch (\Throwable $e) {
|