diff --git a/assets/images/email/background.jpeg b/assets/images/email/background.jpeg new file mode 100644 index 0000000..c66306a Binary files /dev/null and b/assets/images/email/background.jpeg differ diff --git a/assets/images/email/header.jpeg b/assets/images/email/header.jpeg new file mode 100644 index 0000000..d006d1a Binary files /dev/null and b/assets/images/email/header.jpeg differ diff --git a/composer.json b/composer.json index 5c6700a..776a62b 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,7 @@ "php": ">=8.2", "ext-ctype": "*", "ext-iconv": "*", + "chillerlan/php-qrcode": "^5.0", "doctrine/annotations": "^2.0", "doctrine/dbal": "^3", "doctrine/doctrine-bundle": "^2.13", diff --git a/composer.lock b/composer.lock index e377fcd..4ba9056 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,167 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6e7803d436b49242a4a3816fdc776d3a", + "content-hash": "26b10019eec6bd118285cb25e8daf878", "packages": [ + { + "name": "chillerlan/php-qrcode", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-qrcode.git", + "reference": "42e215640e9ebdd857570c9e4e52245d1ee51de2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/42e215640e9ebdd857570c9e4e52245d1ee51de2", + "reference": "42e215640e9ebdd857570c9e4e52245d1ee51de2", + "shasum": "" + }, + "require": { + "chillerlan/php-settings-container": "^2.1.6 || ^3.2.1", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "chillerlan/php-authenticator": "^4.3.1 || ^5.2.1", + "ext-fileinfo": "*", + "phan/phan": "^5.4.5", + "phpcompatibility/php-compatibility": "10.x-dev", + "phpmd/phpmd": "^2.15", + "phpunit/phpunit": "^9.6", + "setasign/fpdf": "^1.8.2", + "slevomat/coding-standard": "^8.15", + "squizlabs/php_codesniffer": "^3.11" + }, + "suggest": { + "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", + "setasign/fpdf": "Required to use the QR FPDF output.", + "simple-icons/simple-icons": "SVG icons that you can use to embed as logos in the QR Code" + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\QRCode\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT", + "Apache-2.0" + ], + "authors": [ + { + "name": "Kazuhiko Arase", + "homepage": "https://github.com/kazuhikoarase/qrcode-generator" + }, + { + "name": "ZXing Authors", + "homepage": "https://github.com/zxing/zxing" + }, + { + "name": "Ashot Khanamiryan", + "homepage": "https://github.com/khanamiryan/php-qrcode-detector-decoder" + }, + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + }, + { + "name": "Contributors", + "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" + } + ], + "description": "A QR Code generator and reader with a user-friendly API. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-qrcode", + "keywords": [ + "phpqrcode", + "qr", + "qr code", + "qr-reader", + "qrcode", + "qrcode-generator", + "qrcode-reader" + ], + "support": { + "docs": "https://php-qrcode.readthedocs.io", + "issues": "https://github.com/chillerlan/php-qrcode/issues", + "source": "https://github.com/chillerlan/php-qrcode" + }, + "funding": [ + { + "url": "https://ko-fi.com/codemasher", + "type": "Ko-Fi" + } + ], + "time": "2024-11-21T16:12:34+00:00" + }, + { + "name": "chillerlan/php-settings-container", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-settings-container.git", + "reference": "95ed3e9676a1d47cab2e3174d19b43f5dbf52681" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/95ed3e9676a1d47cab2e3174d19b43f5dbf52681", + "reference": "95ed3e9676a1d47cab2e3174d19b43f5dbf52681", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^8.1" + }, + "require-dev": { + "phpmd/phpmd": "^2.15", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-deprecation-rules": "^1.2", + "phpunit/phpunit": "^10.5", + "squizlabs/php_codesniffer": "^3.10" + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\Settings\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + } + ], + "description": "A container class for immutable settings objects. Not a DI container.", + "homepage": "https://github.com/chillerlan/php-settings-container", + "keywords": [ + "Settings", + "configuration", + "container", + "helper" + ], + "support": { + "issues": "https://github.com/chillerlan/php-settings-container/issues", + "source": "https://github.com/chillerlan/php-settings-container" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2024-07-16T11:13:48+00:00" + }, { "name": "composer/semver", "version": "3.4.3", diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index 3f795d9..a6da352 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -1,5 +1,7 @@ twig: file_name_pattern: '*.twig' + paths: + '%kernel.project_dir%/assets/images/email': images when@test: twig: diff --git a/src/Controller/SuccessController.php b/src/Controller/SuccessController.php new file mode 100644 index 0000000..ebb7804 --- /dev/null +++ b/src/Controller/SuccessController.php @@ -0,0 +1,20 @@ +completePayment((string)$request->query->get('session_id')); + + return $this->render('ticket/success.html.twig'); + } +} \ No newline at end of file diff --git a/src/Controller/TestController.php b/src/Controller/TestController.php new file mode 100644 index 0000000..c3c15a8 --- /dev/null +++ b/src/Controller/TestController.php @@ -0,0 +1,24 @@ +dumpFile('test.pdf', $content); + + return $this->render('test.html.twig', ['qr' => (new QRCode())->render('https://www.google.com')]); + } +} diff --git a/src/Controller/TicketController.php b/src/Controller/TicketController.php index 0a30335..50bf39e 100644 --- a/src/Controller/TicketController.php +++ b/src/Controller/TicketController.php @@ -33,7 +33,7 @@ final class TicketController extends AbstractController return $this->json(['id' => $this->service->handleTicketData($ticketData)->id]); } - #[Route(path: '/success', name: 'app_success', methods: Request::METHOD_GET)] + #[Route(path: '/success', name: 'app_order_success', methods: Request::METHOD_GET)] public function success(Request $request): Response { $sessionId = $request->query->get('session_id'); @@ -50,7 +50,7 @@ final class TicketController extends AbstractController return $this->redirectToRoute('app_ticket'); } - return $this->render('ticket/success.html.twig'); + return $this->redirectToRoute('app_success_page'); } #[Route(path: '/cancelled', name: 'app_cancelled', methods: Request::METHOD_GET)] diff --git a/src/Service/TicketEmailService.php b/src/Service/TicketEmailService.php new file mode 100644 index 0000000..b9db500 --- /dev/null +++ b/src/Service/TicketEmailService.php @@ -0,0 +1,64 @@ +htmlTemplate('email/order.html.twig') + ->subject('Abiball Ticket') + ->from(new Address($this->senderMail, 'Noreply')) + ->to(new Address($payment->getCustomer()?->getEmail(), $payment->getCustomer()?->getFirstname() . ' ' . $payment->getCustomer()?->getLastname())) + ->context([ + 'payment' => $payment, + ]); + + $i = 0; + foreach ($payment->getCustomer()?->getTickets() as $ticket) { + ++$i; + $mail->addPart(new DataPart($this->generateTicket($ticket), "ticket-$i.pdf", 'application/pdf')); + } + + $this->mailer->send($mail); + } + + private function generateTicket(Ticket $ticket): string + { + return (new DompdfWrapper(new DompdfFactory()))->getPdf($this->twig->render('test.html.twig', ['qr' => (new QRCode())->render($this->generateUrl($ticket))])); + } + + private function generateUrl(Ticket $ticket): string + { + return $this->urlGenerator->generate('admin', [ + 'crudAction' => 'detail', + 'crudControllerFqcn' => TicketCrudController::class, + 'entityId' => $ticket->getCustomer()?->getId() + ], UrlGeneratorInterface::ABSOLUTE_URL); + } +} \ No newline at end of file diff --git a/src/Service/TicketService.php b/src/Service/TicketService.php index 3570e73..3fe3a66 100644 --- a/src/Service/TicketService.php +++ b/src/Service/TicketService.php @@ -21,6 +21,7 @@ class TicketService private readonly UrlGeneratorInterface $generator, private readonly EntityManagerInterface $em, private readonly PaymentRepository $paymentRepository, + private readonly TicketEmailService $emailService, #[Autowire(env: 'STRIPE_SECRET_KEY')] string $stripeKey ) { @@ -36,18 +37,6 @@ class TicketService return $session; } - public function saveTicketData(TicketFormData $data, string $sessionId): void - { - $payment = (new Payment()) - ->setSessionId($sessionId) - ->setCompleted(false) - ->setCustomer($this->createEntityFromData($data)); - - $this->em->persist($payment); - $this->em->flush(); - } - - public function completePayment(string $sessionId): bool { if (!$payment = $this->paymentRepository->findOneBy(['sessionId' => $sessionId])) { @@ -57,9 +46,23 @@ class TicketService $payment->setCompleted(true); $this->em->flush(); + $this->emailService->sendSuccessEmail($payment); + return true; } + + private function saveTicketData(TicketFormData $data, string $sessionId): void + { + $payment = (new Payment()) + ->setSessionId($sessionId) + ->setCompleted(false) + ->setCustomer($this->createEntityFromData($data)); + + $this->em->persist($payment); + $this->em->flush(); + } + private function getLineItems(array $tickets): array { $lineItems = []; @@ -87,7 +90,7 @@ class TicketService 'line_items' => $lineItems, 'mode' => 'payment', 'customer_email' => $email, - 'success_url' => $this->generator->generate('app_success', [], 0) . '?session_id={CHECKOUT_SESSION_ID}', + 'success_url' => $this->generator->generate('app_order_success', [], 0) . '?session_id={CHECKOUT_SESSION_ID}', 'cancel_url' => $this->generator->generate('app_cancelled', [], 0), ]); } diff --git a/templates/email/order.html.twig b/templates/email/order.html.twig new file mode 100644 index 0000000..2597141 --- /dev/null +++ b/templates/email/order.html.twig @@ -0,0 +1,87 @@ + + +
+ + + +
+ ![]() |
+
+ ![]() |
+
+
+
+ Du bist eingeladen!++ Sei dabei bei einem Abend voller Feierlichkeiten, Tanz und unvergesslicher Momente beim Abiball der Freien Waldorfschule Bremen + +
+
+ Veranstaltungsdetails+Datum: [Datum] +Uhrzeit: [Uhrzeit] +Adresse: [Adresse] + |
+