File: /var/www/matriculas_api_dev/app/Services/WebhookFirmaAkiService.php
<?php
namespace App\Services;
use App\Repositories\ContractRepository;
use Illuminate\Support\Facades\Log;
use Exception;
use Illuminate\Support\Facades\Http;
class WebhookFirmaAkiService
{
protected $contractRepository;
public function __construct(ContractRepository $contractRepository)
{
$this->contractRepository = $contractRepository;
}
public function process(array $payload, array $headers, object $webhookLog): array
{
Log::info("Webhook FirmaAki recibido", $payload);
$code = data_get($payload, 'code'); // Ej: SIGN, COMPLETE, REJECT, EMAIL SENT
$statusCode = data_get($payload, 'document.statusCode'); // Ej: SIGNED, REJECTED
$documentId = data_get($payload, 'document.id'); // Ej: SIGNED, REJECTED
$contractCode = data_get($payload, 'document.metadata.contractId'); // Ej: CTR-2026-0001
$reason = data_get($payload, 'data.reason') ?? data_get($payload, 'data.0.reason');
$fileName = data_get($payload, 'document.name');
$fileUrl = data_get($payload, 'document.file');
if (empty($contractCode)) {
Log::warning("Webhook FirmaAki sin contractId, payload incompleto", $payload);
return ['handled' => false, 'message' => 'Missing contractId'];
}
try {
$webhookLog->event_code = $code;
$webhookLog->integration_type = 'SIGNATURE';
$webhookLog->external_id = $documentId;
$contract = $this->contractRepository->getByCode($contractCode);
if (!$contract) {
Log::warning("Contrato no encontrado para contractCode", ['contract_id' => $contractCode]);
return ['handled' => false, 'message' => 'Contract not found'];
}
$webhookLog->internal_id = $contract->id;
switch (strtoupper($code)) {
// πΉ EMAIL SENT β solo registrar el evento
case 'EMAIL SENT':
$webhookLog->status_code = 'SUCCESS';
$webhookLog->reason = 'Contrato enviado al apoderado. Correo: ' . data_get($payload, 'email');
break;
// πΉ OPEN β se abriΓ³ el documento
case 'OPEN':
$webhookLog->status_code = 'SUCCESS';
$webhookLog->reason = 'Contrato abierto por el apoderado';
break;
// πΉ SIGN β firmado por el usuario (antes de completar)
case 'SIGN':
$webhookLog->status_code = 'SUCCESS';
$webhookLog->reason = 'Contrato firmado parcialmente';
$contract->status_signature_id = $this->contractRepository->getStatus('signed', 'signature');
$contract->updated_at = now();
$contract->save();
break;
// πΉ COMPLETE β documento cerrado (firmado o rechazado)
case 'COMPLETE':
if ($statusCode === 'SIGNED') {
$webhookLog->status_code = 'SUCCESS';
$webhookLog->reason = 'Contrato firmado exitosamente';
Log::info("Contrato firmado exitosamente", ['contract_id' => $contractCode]);
$contract->status_signature_id = $this->contractRepository->getStatus('signed', 'signature');
$contract->status_contract_id = $this->contractRepository->getStatus('finished', 'contract');
$contract->file_code = $fileName;
$contract->paid_at = now();
try {
$response = Http::timeout(30)->get($fileUrl);
if ($response->successful()) {
$pdfBase64 = base64_encode($response->body());
$contract->file_data = $pdfBase64;
Log::info("Archivo PDF descargado y convertido correctamente", [
'contract_id' => $contractCode,
'size_kb' => round(strlen($pdfBase64) / 1024, 2)
]);
} else {
Log::warning("No se pudo descargar el PDF firmado", [
'contract_id' => $contractCode,
'url' => $fileUrl,
'status' => $response->status()
]);
}
} catch (\Throwable $e) {
Log::error("Error al descargar o convertir PDF firmado", [
'contract_id' => $contractCode,
'url' => $fileUrl,
'error' => $e->getMessage()
]);
}
} elseif ($statusCode === 'REJECTED') {
$webhookLog->status_code = 'FAILED';
$webhookLog->reason = 'Contrato rechazado por el apoderado';
$contract->status_signature_id = $this->contractRepository->getStatus('failed', 'signature');
$contract->status_contract_id = $this->contractRepository->getStatus('pending_signature', 'contract');
}
$contract->updated_at = now();
$contract->save();
break;
// πΉ REJECT o CANCEL β documento cancelado o rechazado
case 'REJECT':
case 'CANCEL':
$webhookLog->status_code = 'FAILED';
$webhookLog->reason = 'Contrato cancelado por el apoderado';
$contract->status_signature_id = $this->contractRepository->getStatus('failed', 'signature');
$contract->status_contract_id = $this->contractRepository->getStatus('pending_signature', 'contract');
$contract->updated_at = now();
$contract->save();
break;
default:
Log::warning("Evento no reconocido en Webhook FirmaAki", [
'code' => $code,
'contract_id' => $contractCode,
]);
break;
}
$webhookLog->save(); // β
Guarda los valores reales
return ['handled' => true, 'contract_id' => $contractCode, 'event' => $code];
} catch (Exception $e) {
$webhookLog->status_code = 'ERROR';
$webhookLog->reason = 'Error procesando webhook FirmaAki: ' . $e->getMessage();
$webhookLog->save();
return ['handled' => false, 'error' => $e->getMessage()];
}
}
}