Skip to content

Commit

Permalink
Basic webhooks implementation + mail styles
Browse files Browse the repository at this point in the history
  • Loading branch information
JanMikes committed Dec 31, 2024
1 parent eb1e6d9 commit ee31f47
Show file tree
Hide file tree
Showing 27 changed files with 530 additions and 145 deletions.
16 changes: 0 additions & 16 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,6 @@ services:
XDEBUG_MODE: debug
PHP_IDE_CONFIG: "serverName=speedpuzzling"

messenger-consumer:
image: ghcr.io/myspeedpuzzling/web-base:main
restart: unless-stopped
tty: true
command: "bash -c 'wait-for-it postgres:5432 -- sleep 5 && bin/console messenger:consume async -vv --time-limit 3600 --memory-limit 256M'"
volumes:
- .:/app
depends_on:
- postgres
- redis
- minio
environment:
XDEBUG_CONFIG: "client_host=host.docker.internal"
XDEBUG_MODE: debug
PHP_IDE_CONFIG: "serverName=speedpuzzling"

postgres:
image: postgres:16.0
environment:
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@
"symfony/web-link": "^7.0",
"symfony/webpack-encore-bundle": "^2.0",
"symfony/yaml": "^7.0",
"twig/cssinliner-extra": "^3.11",
"twig/extra-bundle": "^3.11",
"twig/cssinliner-extra": "^3.18",
"twig/extra-bundle": "^3.18",
"twig/intl-extra": "^3.8",
"twig/string-extra": "^3.7",
"twig/twig": "^3.0"
Expand Down
22 changes: 11 additions & 11 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions config/packages/twig.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@

$twig->global('get_notifications')
->value(service(GetNotifications::class));

$twig->path('%kernel.project_dir%/public/img', 'images');
$twig->path('%kernel.project_dir%/public/css', 'styles');
};
76 changes: 76 additions & 0 deletions public/css/email.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
body {
color: #111;
font-family: Arial, Tahoma, Sans-serif, serif;
background: #ebebeb;
font-size: 15px;
}

h1 {
font-weight: normal;
font-size: 26px;
}

h2 {
font-weight: normal;
font-size: 22px;
}

h3 {
font-weight: normal;
font-size: 18px;
}

p {
margin: 1em 0;
}

a {
color: #f34770;
text-decoration: underline;
}

hr {
border-bottom: 1px solid #ccc;
margin: 15px 0;
padding: 0;
height: 1px;
}


h1.newsletter {
color: #f34770;
font-size: 24px;
font-weight: normal;
}

.admin-h1 {
font-size: 24px;
font-weight: normal;
}

.logo {
width: 80px;
}

.newsletterContainer {
width: 100%;
max-width: 600px;
margin: 15px auto;
border: 1px solid #f34770;
padding: 15px;
background: #fff;
}

.border-bottom {
border-bottom: 1px solid #333;
padding-bottom: 15px;
margin-bottom: 15px;
}

.text-center {
text-align: center;
}

.text-end {
text-align: right;
}
13 changes: 0 additions & 13 deletions src/Events/SubscriptionPaymentFailed.php

This file was deleted.

13 changes: 13 additions & 0 deletions src/Message/CreateMembershipSubscription.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace SpeedPuzzling\Web\Message;

readonly final class CreateMembershipSubscription
{
public function __construct(
public string $stripeSubscriptionId,
) {
}
}
13 changes: 13 additions & 0 deletions src/Message/NotifyAboutFailedPayment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace SpeedPuzzling\Web\Message;

readonly final class NotifyAboutFailedPayment
{
public function __construct(
public string $stripeSubscriptionId,
) {
}
}
13 changes: 13 additions & 0 deletions src/Message/UpdateMembershipSubscription.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace SpeedPuzzling\Web\Message;

readonly final class UpdateMembershipSubscription
{
public function __construct(
public string $stripeSubscriptionId,
) {
}
}
72 changes: 72 additions & 0 deletions src/MessageHandler/CreateMembershipSubscriptionHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace SpeedPuzzling\Web\MessageHandler;

use DateTimeImmutable;
use Psr\Clock\ClockInterface;
use Ramsey\Uuid\Uuid;
use SpeedPuzzling\Web\Entity\Membership;
use SpeedPuzzling\Web\Exceptions\MembershipNotFound;
use SpeedPuzzling\Web\Exceptions\PlayerNotFound;
use SpeedPuzzling\Web\Message\CreateMembershipSubscription;
use SpeedPuzzling\Web\Message\UpdateMembershipSubscription;
use SpeedPuzzling\Web\Repository\MembershipRepository;
use SpeedPuzzling\Web\Repository\PlayerRepository;
use Stripe\StripeClient;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
use Symfony\Component\Messenger\MessageBusInterface;

#[AsMessageHandler]
readonly final class CreateMembershipSubscriptionHandler
{
public function __construct(
private StripeClient $stripeClient,
private PlayerRepository $playerRepository,
private MembershipRepository $membershipRepository,
private ClockInterface $clock,
private MessageBusInterface $messageBus,
) {
}

/**
* @throws PlayerNotFound
*/
public function __invoke(CreateMembershipSubscription $message): void
{
$subscriptionId = $message->stripeSubscriptionId;
$subscription = $this->stripeClient->subscriptions->retrieve($subscriptionId);
$billingPeriodEnd = DateTimeImmutable::createFromFormat('U', (string) $subscription->current_period_end);
assert($billingPeriodEnd instanceof DateTimeImmutable);

$customerId = $subscription->customer;
assert(is_string($customerId));

$customer = $this->stripeClient->customers->retrieve($customerId);
$playerId = $customer->metadata->player_id ?? null;

if (!is_string($playerId)) {
// Can not continue without playerId
return;
}

try {
$this->membershipRepository->getByPlayerId($playerId);
$this->messageBus->dispatch(
new UpdateMembershipSubscription($subscriptionId),
);
} catch (MembershipNotFound) {
$player = $this->playerRepository->get($playerId);
$membership = new Membership(
Uuid::uuid7(),
$player,
$this->clock->now(),
$subscriptionId,
$billingPeriodEnd,
);

$this->membershipRepository->save($membership);
}
}
}
61 changes: 61 additions & 0 deletions src/MessageHandler/NotifyWhenMembershipStarted.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace SpeedPuzzling\Web\MessageHandler;

use SpeedPuzzling\Web\Events\MembershipStarted;
use SpeedPuzzling\Web\Exceptions\MembershipNotFound;
use SpeedPuzzling\Web\Repository\MembershipRepository;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler]
readonly final class NotifyWhenMembershipStarted
{
public function __construct(
private MembershipRepository $membershipRepository,
private MailerInterface $mailer,
) {
}

/**
* @throws MembershipNotFound
*/
public function __invoke(MembershipStarted $message): void
{
$membership = $this->membershipRepository->get($message->membershipId->toString());
$player = $membership->player;

if ($player->email === null) {
return;
}

if ($membership->billingPeriodEndsAt === null) {
$email = (new TemplatedEmail())
->to($player->email)
->locale('en') // TODO: take locale from user object
->subject('Congratulations to your MySpeedPuzzling membership!')
->htmlTemplate('emails/membership_granted.html.twig')
->context([
'membershipExpiresAt' => $membership->endsAt?->format('d.m.Y'),
]);

$this->mailer->send($email);
}

if ($membership->billingPeriodEndsAt !== null) {
$email = (new TemplatedEmail())
->to($player->email)
->locale('en') // TODO: take locale from user object
->subject('Congratulations to your MySpeedPuzzling membership!')
->htmlTemplate('emails/membership_subscribed.html.twig')
->context([
'nextBillingPeriod' => $membership->billingPeriodEndsAt->format('d.m.Y H:i'),
]);

$this->mailer->send($email);
}
}
}
Loading

0 comments on commit ee31f47

Please sign in to comment.