```
✨ feat(contrat/paiement): Ajoute la confirmation de paiement avec signature automatique.
```
This commit is contained in:
@@ -45,3 +45,11 @@ vich_uploader:
|
||||
uri_prefix: /pdf/devis_audit
|
||||
upload_destination: '%kernel.project_dir%/public/pdf/devis_audit'
|
||||
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer
|
||||
payment_confirmed:
|
||||
uri_prefix: /pdf/payment_confirmed
|
||||
upload_destination: '%kernel.project_dir%/public/pdf/payment_confirmed'
|
||||
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer
|
||||
payment_confirmed_signed:
|
||||
uri_prefix: /pdf/payment_confirmed_signed
|
||||
upload_destination: '%kernel.project_dir%/public/pdf/payment_confirmed_signed'
|
||||
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer
|
||||
|
||||
41
migrations/Version20260123085450.php
Normal file
41
migrations/Version20260123085450.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20260123085450 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE contrats_payments ADD payment_file_name VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE contrats_payments ADD payment_file_size INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE contrats_payments ADD payment_signed_file_name VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE contrats_payments ADD payment_signed_file_size INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE contrats_payments ADD update_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL');
|
||||
$this->addSql('COMMENT ON COLUMN contrats_payments.update_at IS \'(DC2Type:datetime_immutable)\'');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE contrats_payments DROP payment_file_name');
|
||||
$this->addSql('ALTER TABLE contrats_payments DROP payment_file_size');
|
||||
$this->addSql('ALTER TABLE contrats_payments DROP payment_signed_file_name');
|
||||
$this->addSql('ALTER TABLE contrats_payments DROP payment_signed_file_size');
|
||||
$this->addSql('ALTER TABLE contrats_payments DROP update_at');
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ use App\Repository\ContratsRepository;
|
||||
use App\Repository\CustomerAddressRepository;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Service\Mailer\Mailer;
|
||||
use App\Service\Pdf\PlPdf;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordConfirmEvent;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordEvent;
|
||||
use App\Service\Signature\Client;
|
||||
@@ -24,14 +25,18 @@ use Symfony\Bundle\SecurityBundle\Security;
|
||||
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
|
||||
|
||||
|
||||
class ContratController extends AbstractController
|
||||
@@ -126,7 +131,7 @@ class ContratController extends AbstractController
|
||||
}
|
||||
|
||||
#[Route('/reservation/gestion-contrat/{num}', name: 'gestion_contrat_view')]
|
||||
public function gestionContratView(string $num,\App\Service\Stripe\Client $stripeClient,Client $client,Mailer $mailer,EntityManagerInterface $entityManager, Request $request, ContratsRepository $contratsRepository): Response
|
||||
public function gestionContratView(string $num,UploaderHelper $uploaderHelper,KernelInterface $kernel,\App\Service\Stripe\Client $stripeClient,Client $client,Mailer $mailer,EntityManagerInterface $entityManager, Request $request, ContratsRepository $contratsRepository): Response
|
||||
{
|
||||
$contrat = $contratsRepository->findOneBy(['numReservation' => $num]);
|
||||
|
||||
@@ -296,6 +301,37 @@ class ContratController extends AbstractController
|
||||
return new RedirectResponse($result['url']);
|
||||
}
|
||||
|
||||
if($request->query->get('idPaymentPdf')) {
|
||||
$pl = $entityManager->getRepository(ContratsPayments::class)->find($request->query->get('idPaymentPdf'));
|
||||
if($pl instanceof ContratsPayments && $pl->getState() == "complete") {
|
||||
if ($pl->getPaymentSignedFileName() == null) {
|
||||
$pdf = new PlPdf($kernel, $pl, $contrat);
|
||||
$pdf->generate();
|
||||
$content = $pdf->Output('S');
|
||||
$tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf';
|
||||
file_put_contents($tmpSigned, $content);
|
||||
|
||||
// On utilise UploadedFile pour simuler un upload propre pour VichUploader
|
||||
$pl->setPaymentFile(new UploadedFile($tmpSigned, "confirmed-" . $pl->getId() . ".pdf", "application/pdf", null, true));
|
||||
$pl->setUpdateAt(new \DateTimeImmutable('now'));
|
||||
$entityManager->persist($pl);
|
||||
$entityManager->flush();
|
||||
$data = $client->autoSignConfirmedPayment($pl);
|
||||
// 1. Gestion du PDF SIGNÉ
|
||||
$tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf';
|
||||
$signedContent = file_get_contents($data);
|
||||
file_put_contents($tmpSigned, $signedContent);
|
||||
|
||||
// On utilise UploadedFile pour simuler un upload propre pour VichUploader
|
||||
$pl->setPaymentSignedFile(new UploadedFile($tmpSigned, "confirmed-certificate-" . $pl->getId() . ".pdf", "application/pdf", null, true));
|
||||
$pl->setUpdateAt(new \DateTimeImmutable('now'));
|
||||
$entityManager->persist($pl);
|
||||
$entityManager->flush();
|
||||
}
|
||||
$path= $kernel->getProjectDir() . "/public".$uploaderHelper->asset($pl,'paymentSignedFile');
|
||||
return $this->file($path, 'confirmation-paiement.pdf', ResponseHeaderBag::DISPOSITION_INLINE);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('reservation/contrat/view.twig', [
|
||||
'contrat' => $contrat,
|
||||
|
||||
@@ -6,19 +6,23 @@ use App\Entity\ContratsPayments;
|
||||
use App\Entity\Devis;
|
||||
use App\Entity\Product;
|
||||
use App\Entity\ProductReserve;
|
||||
use App\Kernel;
|
||||
use App\Repository\ProductRepository;
|
||||
use App\Service\Mailer\Mailer;
|
||||
use App\Service\Pdf\PlPdf;
|
||||
use App\Service\Stripe\Client;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class Webhooks extends AbstractController
|
||||
{
|
||||
#[Route(path: '/webhooks/payment-intent', name: 'webhooks_payment', options: ['sitemap' => false], methods: ['POST'])]
|
||||
public function payment(Mailer $mailer, ProductRepository $productRepository, Request $request, Client $client, EntityManagerInterface $entityManager): Response
|
||||
public function payment(Mailer $mailer,KernelInterface $kernel,\App\Service\Signature\Client $sig, ProductRepository $productRepository, Request $request, Client $client, EntityManagerInterface $entityManager): Response
|
||||
{
|
||||
// 1. Vérification de la signature
|
||||
if ($client->checkWebhooks($request, 'payment')) {
|
||||
@@ -34,7 +38,6 @@ class Webhooks extends AbstractController
|
||||
|
||||
if ($paymentId) {
|
||||
$pl = $entityManager->getRepository(ContratsPayments::class)->findOneBy(['paymentId' => $paymentId]);
|
||||
|
||||
if ($pl instanceof ContratsPayments && $pl->getState() !== "complete") {
|
||||
|
||||
// MAJ du paiement
|
||||
@@ -65,6 +68,27 @@ class Webhooks extends AbstractController
|
||||
}
|
||||
}
|
||||
|
||||
$pdf = new PlPdf($kernel, $pl, $contrat);
|
||||
$pdf->generate();
|
||||
$content = $pdf->Output('S');
|
||||
$tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf';
|
||||
file_put_contents($tmpSigned, $content);
|
||||
|
||||
// On utilise UploadedFile pour simuler un upload propre pour VichUploader
|
||||
$pl->setPaymentFile(new UploadedFile($tmpSigned, "confirmed-" . $pl->getId() . ".pdf", "application/pdf", null, true));
|
||||
$pl->setUpdateAt(new \DateTimeImmutable('now'));
|
||||
$entityManager->persist($pl);
|
||||
$entityManager->flush();
|
||||
$data = $sig->autoSignConfirmedPayment($pl);
|
||||
// 1. Gestion du PDF SIGNÉ
|
||||
$tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf';
|
||||
$signedContent = file_get_contents($data);
|
||||
file_put_contents($tmpSigned, $signedContent);
|
||||
|
||||
// On utilise UploadedFile pour simuler un upload propre pour VichUploader
|
||||
$pl->setPaymentSignedFile(new UploadedFile($tmpSigned, "confirmed-certificate-" . $pl->getId() . ".pdf", "application/pdf", null, true));
|
||||
$pl->setUpdateAt(new \DateTimeImmutable('now'));
|
||||
$entityManager->persist($pl);
|
||||
$entityManager->flush();
|
||||
|
||||
// --- ENVOI DES EMAILS ---
|
||||
|
||||
@@ -5,8 +5,12 @@ namespace App\Entity;
|
||||
use App\Repository\ContratsPaymentsRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
use Vich\UploaderBundle\Mapping\Attribute\Uploadable;
|
||||
use Vich\UploaderBundle\Mapping\Attribute\UploadableField;
|
||||
|
||||
#[ORM\Entity(repositoryClass: ContratsPaymentsRepository::class)]
|
||||
#[Uploadable]
|
||||
class ContratsPayments
|
||||
{
|
||||
#[ORM\Id]
|
||||
@@ -38,6 +42,24 @@ class ContratsPayments
|
||||
#[ORM\Column(type: Types::ARRAY,nullable: true)]
|
||||
private array $card = [];
|
||||
|
||||
|
||||
#[UploadableField(mapping: 'payment_confirmed', fileNameProperty: 'paymentFileName', size: 'paymentFileSize')]
|
||||
private ?File $paymentFile = null;
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?string $paymentFileName = null;
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?int $paymentFileSize = null;
|
||||
|
||||
#[UploadableField(mapping: 'payment_confirmed_signed', fileNameProperty: 'paymentSignedFileName', size: 'paymentSignedFileSize')]
|
||||
private ?File $paymentSignedFile = null;
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?string $paymentSignedFileName = null;
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?int $paymentSignedFileSize = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?\DateTimeImmutable $updateAt = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@@ -138,4 +160,117 @@ class ContratsPayments
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeImmutable|null
|
||||
*/
|
||||
public function getUpdateAt(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->updateAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return File|null
|
||||
*/
|
||||
public function getPaymentFile(): ?File
|
||||
{
|
||||
return $this->paymentFile;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPaymentFileName(): ?string
|
||||
{
|
||||
return $this->paymentFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getPaymentFileSize(): ?int
|
||||
{
|
||||
return $this->paymentFileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return File|null
|
||||
*/
|
||||
public function getPaymentSignedFile(): ?File
|
||||
{
|
||||
return $this->paymentSignedFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPaymentSignedFileName(): ?string
|
||||
{
|
||||
return $this->paymentSignedFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getPaymentSignedFileSize(): ?int
|
||||
{
|
||||
return $this->paymentSignedFileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTimeImmutable|null $updateAt
|
||||
*/
|
||||
public function setUpdateAt(?\DateTimeImmutable $updateAt): void
|
||||
{
|
||||
$this->updateAt = $updateAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param File|null $paymentFile
|
||||
*/
|
||||
public function setPaymentFile(?File $paymentFile): void
|
||||
{
|
||||
$this->paymentFile = $paymentFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $paymentFileName
|
||||
*/
|
||||
public function setPaymentFileName(?string $paymentFileName): void
|
||||
{
|
||||
$this->paymentFileName = $paymentFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $paymentFileSize
|
||||
*/
|
||||
public function setPaymentFileSize(?int $paymentFileSize): void
|
||||
{
|
||||
$this->paymentFileSize = $paymentFileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param File|null $paymentSignedFile
|
||||
*/
|
||||
public function setPaymentSignedFile(?File $paymentSignedFile): void
|
||||
{
|
||||
$this->paymentSignedFile = $paymentSignedFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $paymentSignedFileName
|
||||
*/
|
||||
public function setPaymentSignedFileName(?string $paymentSignedFileName): void
|
||||
{
|
||||
$this->paymentSignedFileName = $paymentSignedFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $paymentSignedFileSize
|
||||
*/
|
||||
public function setPaymentSignedFileSize(?int $paymentSignedFileSize): void
|
||||
{
|
||||
$this->paymentSignedFileSize = $paymentSignedFileSize;
|
||||
}
|
||||
}
|
||||
|
||||
195
src/Service/Pdf/PlPdf.php
Normal file
195
src/Service/Pdf/PlPdf.php
Normal file
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Pdf;
|
||||
|
||||
use App\Entity\Contrats;
|
||||
use App\Entity\ContratsPayments;
|
||||
use App\Entity\Devis;
|
||||
use Fpdf\Fpdf;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
class PlPdf extends Fpdf
|
||||
{
|
||||
private ContratsPayments $contratsPayments;
|
||||
private string $logo;
|
||||
private bool $isExtraPage = false;
|
||||
private Contrats $contrat;
|
||||
|
||||
public function __construct(KernelInterface $kernel, ContratsPayments $contratsPayments,Contrats $contrats, $orientation = 'P', $unit = 'mm', $size = 'A4')
|
||||
{
|
||||
parent::__construct($orientation, $unit, $size);
|
||||
$this->contratsPayments = $contratsPayments;
|
||||
$this->contrat = $contrats;
|
||||
$this->logo = $kernel->getProjectDir()."/public/provider/images/favicon.png";
|
||||
|
||||
$this->AliasNbPages();
|
||||
$this->SetAutoPageBreak(true, 35);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit l'UTF-8 en Windows-1252 pour FPDF et gère l'Euro
|
||||
*/
|
||||
private function clean(?string $text): string
|
||||
{
|
||||
if (!$text) return '';
|
||||
$text = iconv('UTF-8', 'windows-1252//TRANSLIT//IGNORE', $text);
|
||||
return str_replace('€', chr(128), $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper pour afficher le symbole Euro proprement
|
||||
*/
|
||||
private function euro(): string
|
||||
{
|
||||
return ' ' . chr(128);
|
||||
}
|
||||
|
||||
public function Header()
|
||||
{
|
||||
if ($this->page > 0 && !$this->isExtraPage) {
|
||||
// --- LOGO ET INFOS SOCIÉTÉ ---
|
||||
$this->SetY(10);
|
||||
if (file_exists($this->logo)) {
|
||||
$this->Image($this->logo, 10, 10, 15); // Un peu plus grand pour le reçu
|
||||
$this->SetX(28);
|
||||
}
|
||||
|
||||
$this->SetFont('Arial', 'B', 14);
|
||||
$this->SetTextColor(37, 99, 235); // Bleu Ludikevent
|
||||
$this->Cell(100, 7, $this->clean('Lilian SEGARD - Ludik Event'), 0, 0, 'L');
|
||||
|
||||
// BADGE "REÇU" À DROITE
|
||||
$this->SetFont('Arial', 'B', 11);
|
||||
$this->SetFillColor(37, 99, 235);
|
||||
$this->SetTextColor(255, 255, 255);
|
||||
$this->SetX(150);
|
||||
$this->Cell(50, 8, $this->clean('REÇU DE PAIEMENT'), 0, 1, 'C', true);
|
||||
|
||||
// COORDONNÉES SOUS LE NOM
|
||||
$this->SetY(17);
|
||||
$this->SetX(28);
|
||||
$this->SetFont('Arial', '', 8);
|
||||
$this->SetTextColor(80, 80, 80);
|
||||
$this->Cell(0, 4, $this->clean('SIRET : 930 488 408 00012 | 6 Rue du Château, 02800 Danizy'), 0, 1, 'L');
|
||||
$this->SetX(28);
|
||||
$this->Cell(0, 4, $this->clean('Tél. : 06 14 17 24 47 | contact@ludikevent.fr'), 0, 1, 'L');
|
||||
|
||||
$this->Ln(12);
|
||||
|
||||
// --- TITRE DU DOCUMENT ET RÉFÉRENCE ---
|
||||
$this->SetFont('Arial', 'B', 15);
|
||||
$this->SetTextColor(17, 24, 39); // Gris très foncé/Noir
|
||||
$this->Cell(0, 10, $this->clean('Confirmation de règlement'), 0, 1, 'L');
|
||||
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->SetTextColor(107, 114, 128); // Gris intermédiaire
|
||||
$this->Cell(0, 5, $this->clean('Référence Réservation : #' . $this->contrat->getNumReservation()), 0, 1, 'L');
|
||||
|
||||
// LIGNE DE SÉPARATION DESIGN
|
||||
$this->Ln(2);
|
||||
$this->SetDrawColor(37, 99, 235);
|
||||
$this->SetLineWidth(0.8);
|
||||
$this->Line(10, $this->GetY(), 40, $this->GetY()); // Petite ligne bleue
|
||||
$this->SetDrawColor(229, 231, 235); // Gris clair
|
||||
$this->SetLineWidth(0.2);
|
||||
$this->Line(40, $this->GetY(), 200, $this->GetY()); // Ligne grise qui continue
|
||||
|
||||
$this->Ln(5);
|
||||
}
|
||||
}
|
||||
public function generate(): string
|
||||
{
|
||||
$this->AddPage();
|
||||
$this->renderContent();
|
||||
return $this->Output('S');
|
||||
}
|
||||
|
||||
private function renderContent(): void
|
||||
{
|
||||
// --- 1. BLOC INFO PAIEMENT (Le "Reçu") ---
|
||||
$this->SetY(55);
|
||||
$this->SetFont('Arial', 'B', 11);
|
||||
$this->SetFillColor(245, 245, 255); // Bleu très léger
|
||||
$this->SetTextColor(37, 99, 235);
|
||||
$this->Cell(0, 10, $this->clean(" DÉTAILS DU RÈGLEMENT"), 0, 1, 'L', true);
|
||||
|
||||
$this->Ln(4);
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->SetTextColor(50, 50, 50);
|
||||
|
||||
// Tableau à deux colonnes pour les infos
|
||||
$startY = $this->GetY();
|
||||
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(50, 8, $this->clean("Montant réglé :"), 0, 0);
|
||||
$this->SetFont('Arial', 'B', 12); $this->SetTextColor(22, 163, 74); // Vert succès
|
||||
$this->Cell(0, 8, number_format($this->contratsPayments->getAmount(), 2) . $this->euro(), 0, 1);
|
||||
|
||||
$this->SetTextColor(50, 50, 50);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(50, 8, $this->clean("Date du paiement :"), 0, 0);
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell(0, 8, $this->contratsPayments->getValidateAt()->format('d/m/Y H:i'), 0, 1);
|
||||
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(50, 8, $this->clean("Mode de règlement :"), 0, 0);
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell(0, 8, $this->clean($this->contratsPayments->getCard()['type'] ?? 'Carte Bancaire'), 0, 1);
|
||||
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(50, 8, $this->clean("Référence transaction :"), 0, 0);
|
||||
$this->SetFont('Arial', 'I', 9);
|
||||
$this->Cell(0, 8, $this->clean($this->contratsPayments->getPaymentId() ?? 'N/A'), 0, 1);
|
||||
|
||||
$this->Ln(10);
|
||||
|
||||
// --- 2. BLOC CLIENT ---
|
||||
$this->SetFont('Arial', 'B', 11);
|
||||
$this->SetFillColor(250, 250, 250);
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
$this->Cell(0, 10, $this->clean(" INFORMATIONS CLIENT"), 0, 1, 'L', true);
|
||||
|
||||
$this->Ln(2);
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$customer = $this->contrat->getCustomer();
|
||||
$this->MultiCell(0, 6, $this->clean(
|
||||
$customer->getSurname() . " " . $customer->getName() . "\n" .
|
||||
$customer->getEmail() . "\n" .
|
||||
"Réservation N° " . $this->contrat->getNumReservation()
|
||||
), 0, 'L');
|
||||
|
||||
$this->Ln(15);
|
||||
|
||||
// --- 3. MESSAGE DE CONFIRMATION ---
|
||||
$this->SetDrawColor(22, 163, 74);
|
||||
$this->SetFillColor(240, 253, 244);
|
||||
$this->SetLineWidth(0.2);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->SetTextColor(21, 128, 61);
|
||||
|
||||
$text = "Ce document confirme que votre paiement a bien été reçu et validé par Ludik Event.\nNous vous remercions de votre confiance.";
|
||||
$this->MultiCell(0, 10, $this->clean($text), 1, 'C', true);
|
||||
|
||||
// --- 4. SIGNATURE / CACHET ---
|
||||
$this->SetY(-60);
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
$this->Cell(0, 5, $this->clean('{{Sign;type=signature;role=Ludikevent;height=120;width=236}}'), 0, 1, 'C');
|
||||
}
|
||||
public function Footer(): void
|
||||
{
|
||||
// Positionnement à 1,5 cm du bas
|
||||
$this->SetY(-15);
|
||||
$this->SetFont('Arial', 'I', 7);
|
||||
$this->SetTextColor(150, 150, 150);
|
||||
|
||||
// Texte gauche : Identification du devis
|
||||
$this->Cell(0, 10, $this->clean('Confirmation de paiement Ludik Event - Lilian SEGARD - SIRET 93048840800012'), 0, 0, 'L');
|
||||
|
||||
|
||||
// Numérotation des pages à droite
|
||||
$this->SetTextColor(150, 150, 150);
|
||||
$this->SetFont('Arial', 'I', 8);
|
||||
$this->Cell(0, 10, 'Page ' . $this->PageNo() . '/{nb}', 0, 0, 'R');
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Service\Signature;
|
||||
|
||||
use App\Entity\Contrats;
|
||||
use App\Entity\ContratsPayments;
|
||||
use App\Entity\CustomerOrder;
|
||||
use App\Entity\Devis;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -269,4 +270,33 @@ class Client
|
||||
$submiter = $this->getSubmiter($signId);
|
||||
return $submiter['uuid']; // numéro de signature;
|
||||
}
|
||||
|
||||
public function autoSignConfirmedPayment(ContratsPayments $contratsPayments) {
|
||||
$relativeFileUrl = $this->storage->resolveUri($contratsPayments, 'paymentFile');
|
||||
$fileUrl = $this->baseUrl . $relativeFileUrl;
|
||||
$submission = $this->docuseal->createSubmissionFromPdf([
|
||||
'name' => 'Confirmaton de paiement N°' . $contratsPayments->getPaymentId(), // Correction : getNum()
|
||||
'send_email' => true,
|
||||
'documents' => [
|
||||
[
|
||||
'name' => 'confirmation_paiement_' . $contratsPayments->getId() . '.pdf', // Correction : getNum()
|
||||
'file' => $fileUrl,
|
||||
],
|
||||
],
|
||||
'submitters' => [
|
||||
[
|
||||
'role' => 'Ludikevent',
|
||||
'email' => 'contact@ludikevent.fr',
|
||||
'completed' => true,
|
||||
'fields' => [
|
||||
['name'=>'Sign','default_value'=>$this->logoBase64()]
|
||||
]
|
||||
],
|
||||
],
|
||||
]);
|
||||
$sub = $this->docuseal->getSubmission($submission['id']);
|
||||
sleep(5);
|
||||
return $sub['documents'][0]['url'];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,7 +394,6 @@ class Client
|
||||
*/
|
||||
public function checkWebhooks(\Symfony\Component\HttpFoundation\Request $request, string $configName): bool
|
||||
{
|
||||
return true;
|
||||
// 1. Récupération de la config (Secret de signature)
|
||||
$config = $this->em->getRepository(StripeConfig::class)->findOneBy(['name' => $configName]);
|
||||
|
||||
|
||||
@@ -362,6 +362,7 @@
|
||||
<th class="px-8 py-4 text-[9px] font-black uppercase text-slate-400 tracking-widest">Type / Objet</th>
|
||||
<th class="px-8 py-4 text-[9px] font-black uppercase text-slate-400 tracking-widest">Méthode</th>
|
||||
<th class="px-8 py-4 text-[9px] font-black uppercase text-slate-400 tracking-widest text-right">Montant</th>
|
||||
<th class="px-8 py-4 text-[9px] font-black uppercase text-slate-400 tracking-widest text-right">Confirmation de paiement</th>
|
||||
<th class="px-8 py-4 text-[9px] font-black uppercase text-slate-400 tracking-widest text-center">Statut</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -396,6 +397,24 @@
|
||||
<td class="px-8 py-5 text-right font-black text-slate-900 italic text-sm">
|
||||
{{ pay.amount|number_format(2, ',', ' ') }}€
|
||||
</td>
|
||||
<td class="px-8 py-5 text-right">
|
||||
<div class="flex justify-end">
|
||||
<a target="_blank" href="{{ path('gestion_contrat_view', {num: contrat.numReservation, idPaymentPdf: pay.id}) }}"
|
||||
class="group relative flex items-center justify-center w-10 h-10 bg-blue-50 hover:bg-blue-600 text-blue-600 hover:text-white rounded-full transition-all duration-300 shadow-sm"
|
||||
title="Télécharger le reçu">
|
||||
|
||||
{# Icône de téléchargement épurée #}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 stroke-[2.5] transition-transform group-hover:scale-110" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
|
||||
</svg>
|
||||
|
||||
{# Tooltip (optionnel) #}
|
||||
<span class="absolute -top-8 scale-0 group-hover:scale-100 transition-all bg-slate-800 text-white text-[10px] px-2 py-1 rounded font-bold uppercase tracking-tighter">
|
||||
PDF
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-8 py-5 text-center">
|
||||
<div class="inline-flex items-center gap-1.5 text-green-600">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7"></path></svg>
|
||||
|
||||
Reference in New Issue
Block a user