<?php
namespace App\Controller\Webhook;
use App\Enum\DocumentTypeEnum;
use App\Enum\SignatoryEnum;
use App\Service\SegmentAPI;
use App\Traits\SentryNotifyTrait;
use Doctrine\ORM\EntityManagerInterface;
use Evo\Domain\Core\SignatureServiceInterface;
use Evo\Infrastructure\MappingORM\Document;
use Evo\Infrastructure\MappingORM\LegalRepresentative;
use Evo\Infrastructure\MappingORM\Organization;
use Evo\Infrastructure\MappingORM\ProcessYousign;
use Evo\Infrastructure\MappingORM\Signature;
use Evo\Infrastructure\MappingORM\User;
use Evo\Infrastructure\PdfGenerator\PathGenerator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Symfony\Component\Routing\Annotation\Route;
class YousignController extends AbstractController
{
use SentryNotifyTrait;
private SignatureServiceInterface $yousignAPI;
private HubInterface $hub;
private SegmentAPI $segmentAPI;
public function __construct(SignatureServiceInterface $yousignAPI, HubInterface $hub, SegmentAPI $segmentAPI)
{
$this->yousignAPI = $yousignAPI;
$this->hub = $hub;
$this->segmentAPI = $segmentAPI;
}
/**
* @Route("/document", name="app_webhook_yousign", methods={"POST"}))
*/
public function webhook(EntityManagerInterface $em, Request $request): JsonResponse
{
$signatureRepository = $em->getRepository(Signature::class);
$legalRepresentativeRepository = $em->getRepository(LegalRepresentative::class);
$userRepository = $em->getRepository(User::class);
$signatureCondition = [];
$signatory = null;
$yousignPayload = json_decode(file_get_contents('php://input'), true);
if ('test' === getenv('APP_ENV')) {
$yousignPayload = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
}
if (!isset($yousignPayload['data']['signature_request']['external_id'])) {
return $this->json(['message' => 'signature_request not found'], Response::HTTP_NOT_FOUND);
}
$metadata = $this->yousignAPI->getMetadataFromSignatureRequest($yousignPayload['data']['signature_request']['id']);
$externalIds = explode('-', $yousignPayload['data']['signature_request']['external_id']);
$organizationId = $metadata['organizationId'] ?? $externalIds[0];
$organization = $em->getRepository(Organization::class)->find($organizationId);
if (null === $externalIds[0] || !$organization) {
$this->sendSentryMessage('[WEBHOOK YOUSIGN]:: Problem with the signature of document : organization not found', 'warning');
return $this->json(['message' => 'organization not found'], Response::HTTP_BAD_REQUEST);
}
$documents = $organization->getDocuments();
// get document contract from $documents with array_filter
$document = array_values(array_filter($documents, static function (Document $document) {
return DocumentTypeEnum::DOMICILIATION_CONTRACT === $document->getType();
}));
/** @var Document $document */
$document = $document[0];
if (!$document) {
return $this->json(['message' => 'document not found'], Response::HTTP_OK);
}
$documentId = $document->getId();
if (SignatoryEnum::BOTH === $document->getSignedBy()) {
return $this->json(['message' => 'document already signed by client and admin'], Response::HTTP_OK);
}
$signers = $yousignPayload['data']['signature_request']['signers'];
if ($signers[0]['status'] !== $signers[1]['status'] &&
(
('signed' === $signers[0]['status'] && 'notified' === $signers[1]['status']) ||
('notified' === $signers[0]['status'] && 'signed' === $signers[1]['status'])
)) {
if (SignatoryEnum::SIGNED_CLIENT === $document->getSignedBy()) {
return $this->json(['message' => 'document already signed by client'], Response::HTTP_OK);
}
$type = 'legalRepresentative';
$legalRepresentativeId = $externalIds[1];
$lr = $legalRepresentativeRepository->find($legalRepresentativeId);
if (!isset($externalIds[1]) || !$lr) {
$this->sendSentryMessage(sprintf('[WEBHOOK YOUSIGN]:: Problem with the signature of document # %d of type %s : legal representative not found', $documentId, $type), 'error');
return $this->json(['message' => 'legal representative not found'], Response::HTTP_OK);
}
$signatureCondition = ['processYousign' => $organization->getProcessYousign(), 'legalRepresentative' => $lr->getId()];
$signatory = SignatoryEnum::SIGNED_CLIENT;
} elseif ($signers[0]['status'] === $signers[1]['status'] && 'signed' === $signers[0]['status']) {
$type = 'user';
$signer = $yousignPayload['data']['signer']['info'];
$user = $userRepository->findOneBy(['email' => $signer['email']]);
if (null === $user) {
return $this->json(['message' => 'user not found'], Response::HTTP_OK);
}
$signatureCondition = ['processYousign' => $organization->getProcessYousign(), 'admin' => $user->getId()];
$signatory = SignatoryEnum::BOTH;
}
if (!isset($type) || !$signatureCondition) {
return $this->json(['message' => 'type not found'], Response::HTTP_OK);
}
$signature = $signatureRepository->findOneBy($signatureCondition);
if (!$signature) {
// create new signature
$signature = (new Signature())
->setDocument($document)
->setLegalRepresentative($lr ?? null)
->setProcessYousign($organization->getProcessYousign())
->setCreatedAt(new \DateTime());
}
$signature->setSignatureDate(new \DateTime());
$em->persist($signature);
$em->flush();
if (!$organization->getProcessYousign()) {
return $this->json(['message' => 'Process Yousign not found'], Response::HTTP_NOT_FOUND);
}
/**
* Download signed document and send this to AWS.
*
* @var ProcessYousign $processYousign
*/
$processYousign = $organization->getProcessYousign();
try {
$this->yousignAPI->downloadFile($processYousign, $document);
$document->setAwsPath(PathGenerator::generateFilename($document));
$document->setYousignLink(null);
$document->setSignedBy($signatory);
$em->persist($document);
$em->flush();
if (SignatoryEnum::SIGNED_CLIENT === $signatory) {
$this->segmentAPI->trackNewDocument($document);
}
if (SignatoryEnum::BOTH === $signatory) {
try {
$update = new Update(
'http://api.digidom.pro/signal/kyc/finished',
json_encode(
[
'organizationId' => $document->getId(),
'status' => 'signed_by_both',
],
JSON_THROW_ON_ERROR
)
);
$this->hub->publish($update);
} catch (\Exception $e) {
$this->sendSentryMessage(sprintf('[WEBHOOK YOUSIGN]:: %s', $e->getMessage()), 'error');
}
}
return $this->json(['message' => sprintf('Webhook executed for %s', $type)], Response::HTTP_OK);
} catch (\Exception $e) {
$error = sprintf('[WEBHOOK YOUSIGN]:: %s', $e->getMessage());
$this->sendSentryMessage($error, 'error');
return $this->json(
['message' => $error],
is_int($e->getCode()) ? $e->getCode() : Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
}