Skip to content

Commit

Permalink
Merge pull request #244 from wmde/donation-mailer-interface-oop
Browse files Browse the repository at this point in the history
Improve DonationNotifier implementation
  • Loading branch information
gbirke authored Apr 4, 2024
2 parents 2c9a42b + 95178dc commit 25473d5
Show file tree
Hide file tree
Showing 19 changed files with 259 additions and 354 deletions.
2 changes: 1 addition & 1 deletion .scrutinizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ filter:
build:
image: default-bionic
environment:
php: 8.1.4
php: 8.2
nodes:
analysis:
tests:
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
"config": {
"discard-changes": true,
"allow-plugins": {
"composer/package-versions-deprecated": true
"composer/package-versions-deprecated": true,
"dealerdirect/phpcodesniffer-composer-installer": true
}
},
"extra": {
Expand Down
4 changes: 3 additions & 1 deletion src/Domain/Model/DonorName.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ interface DonorName {
public function getFullName(): string;

/**
* @return string[]
* Get name components for usage in templates
*
* @return array<string,string>
*/
public function toArray(): array;

Expand Down
12 changes: 12 additions & 0 deletions src/Infrastructure/AdminNotificationInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare( strict_types=1 );

namespace WMDE\Fundraising\DonationContext\Infrastructure;

use WMDE\EmailAddress\EmailAddress;
use WMDE\Fundraising\DonationContext\Infrastructure\DonationNotifier\TemplateArgumentsAdmin;

interface AdminNotificationInterface {
public function sendMail( EmailAddress $recipient, TemplateArgumentsAdmin $templateArguments ): void;
}
18 changes: 18 additions & 0 deletions src/Infrastructure/DonationNotifier/TemplateArgumentsAdmin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
declare( strict_types=1 );

namespace WMDE\Fundraising\DonationContext\Infrastructure\DonationNotifier;

class TemplateArgumentsAdmin {
/**
* @param int $donationId
* @param array<string,bool> $moderationFlags Name and state of {@see \WMDE\Fundraising\DonationContext\Domain\Model\ModerationIdentifier}
* @param float $amount
*/
public function __construct(
public readonly int $donationId,
public readonly array $moderationFlags,
public readonly float $amount,
) {
}
}
30 changes: 30 additions & 0 deletions src/Infrastructure/DonationNotifier/TemplateArgumentsDonation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
declare( strict_types=1 );

namespace WMDE\Fundraising\DonationContext\Infrastructure\DonationNotifier;

class TemplateArgumentsDonation {
/**
* @param int $id
* @param float $amount
* @param int $amountInCents
* @param int $interval
* @param string $paymentType
* @param bool $needsModeration
* @param array<string,boolean> $moderationFlags Name and state of {@see \WMDE\Fundraising\DonationContext\Domain\Model\ModerationIdentifier}
* @param string $bankTransferCode
* @param bool $receiptOptIn
*/
public function __construct(
public readonly int $id,
public readonly float $amount,
public readonly int $amountInCents,
public readonly int $interval,
public readonly string $paymentType,
public readonly bool $needsModeration,
public readonly array $moderationFlags,
public readonly string $bankTransferCode,
public readonly bool $receiptOptIn,
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
declare( strict_types=1 );

namespace WMDE\Fundraising\DonationContext\Infrastructure\DonationNotifier;

class TemplateArgumentsDonationConfirmation {
/**
* @param array<string,string> $recipient Output of Name::toArray()
* @param TemplateArgumentsDonation $donation
*/
public function __construct(
public readonly array $recipient,
public readonly TemplateArgumentsDonation $donation
) {
}
}
13 changes: 13 additions & 0 deletions src/Infrastructure/DonorNotificationInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare( strict_types = 1 );

namespace WMDE\Fundraising\DonationContext\Infrastructure;

use WMDE\EmailAddress\EmailAddress;
use WMDE\Fundraising\DonationContext\Infrastructure\DonationNotifier\TemplateArgumentsDonationConfirmation;

interface DonorNotificationInterface {

public function sendMail( EmailAddress $recipient, TemplateArgumentsDonationConfirmation $templateArguments ): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,24 @@
use WMDE\Fundraising\DonationContext\Domain\Model\Donation;
use WMDE\Fundraising\DonationContext\Domain\Model\ModerationIdentifier;
use WMDE\Fundraising\DonationContext\Domain\Model\ModerationReason;
use WMDE\Fundraising\DonationContext\Infrastructure\DonationNotifier\TemplateArgumentsAdmin;
use WMDE\Fundraising\DonationContext\Infrastructure\DonationNotifier\TemplateArgumentsDonation;
use WMDE\Fundraising\DonationContext\Infrastructure\DonationNotifier\TemplateArgumentsDonationConfirmation;
use WMDE\Fundraising\DonationContext\UseCases\DonationNotifier;
use WMDE\Fundraising\PaymentContext\UseCases\GetPayment\GetPaymentUseCase;

class DonationMailer implements DonationNotifier {
/**
* This class transforms the Donation and its Payment objects into TemplateArgument transfer objects.
*
* It also implements the following notification strategy:
* - anonymous donors don't get a confirmation
* - admins only get a notification if the donation's moderation reasons contain an "amount too high" reason
*/
class TemplateDonationNotifier implements DonationNotifier {

public function __construct(
private readonly TemplateMailerInterface $confirmationMailer,
private readonly TemplateMailerInterface $adminMailer,
private readonly DonorNotificationInterface $confirmationMailer,
private readonly AdminNotificationInterface $adminMailer,
private readonly GetPaymentUseCase $getPaymentService,
private readonly string $adminEmailAddress
) {
Expand All @@ -28,31 +38,26 @@ public function sendConfirmationFor( Donation $donation ): void {
}
$this->confirmationMailer->sendMail(
new EmailAddress( $donation->getDonor()->getEmailAddress() ),
$this->getTemplateArguments( $donation )
$this->getTemplateArgumentsForDonorConfirmation( $donation )
);
}

/**
* @param Donation $donation
*
* @return array{recipient:array<string,string>,donation:array<string,mixed>}
*/
private function getTemplateArguments( Donation $donation ): array {
private function getTemplateArgumentsForDonorConfirmation( Donation $donation ): TemplateArgumentsDonationConfirmation {
$paymentInfo = $this->getPaymentService->getPaymentDataArray( $donation->getPaymentId() );
return [
'recipient' => $donation->getDonor()->getName()->toArray(),
'donation' => [
'id' => $donation->getId(),
'amount' => Euro::newFromCents( (int)$paymentInfo['amount'] )->getEuroFloat(),
'amountInCents' => $paymentInfo['amount'],
'interval' => $paymentInfo['interval'],
'paymentType' => $paymentInfo['paymentType'],
'needsModeration' => $donation->isMarkedForModeration(),
'moderationFlags' => $this->getModerationFlags( ...$donation->getModerationReasons() ),
'bankTransferCode' => $paymentInfo['paymentReferenceCode'] ?? '',
'receiptOptIn' => $donation->getDonor()->wantsReceipt(),
]
];
return new TemplateArgumentsDonationConfirmation(
$donation->getDonor()->getName()->toArray(),
new TemplateArgumentsDonation(
id: $donation->getId(),
amount: Euro::newFromCents( (int)$paymentInfo['amount'] )->getEuroFloat(),
amountInCents: intval( $paymentInfo['amount'] ),
interval: intval( $paymentInfo['interval'] ),
paymentType: strval( $paymentInfo['paymentType'] ),
needsModeration: $donation->isMarkedForModeration(),
moderationFlags: $this->getModerationFlags( ...$donation->getModerationReasons() ),
bankTransferCode: strval( $paymentInfo['paymentReferenceCode'] ?? '' ),
receiptOptIn: $donation->getDonor()->wantsReceipt(),
)
);
}

/**
Expand Down Expand Up @@ -82,17 +87,13 @@ public function sendModerationNotificationToAdmin( Donation $donation ): void {
);
}

/**
* @param Donation $donation
* @return array{id:int,moderationFlags:array<string,boolean>,amount:float}
*/
private function getAdminTemplateArguments( Donation $donation ): array {
private function getAdminTemplateArguments( Donation $donation ): TemplateArgumentsAdmin {
$paymentInfo = $this->getPaymentService->getPaymentDataArray( $donation->getPaymentId() );
return [
'id' => $donation->getId(),
'moderationFlags' => $this->getModerationFlags( ...$donation->getModerationReasons() ),
'amount' => Euro::newFromCents( (int)$paymentInfo['amount'] )->getEuroFloat()
];
return new TemplateArgumentsAdmin(
donationId: $donation->getId(),
moderationFlags: $this->getModerationFlags( ...$donation->getModerationReasons() ),
amount: Euro::newFromCents( (int)$paymentInfo['amount'] )->getEuroFloat()
);
}

}
16 changes: 0 additions & 16 deletions src/Infrastructure/TemplateMailerInterface.php

This file was deleted.

12 changes: 2 additions & 10 deletions src/UseCases/CancelDonation/CancelDonationRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,18 @@

class CancelDonationRequest {

private int $donationId;
private ?string $authorizedUser;

public function __construct( int $donationId, ?string $authorizedUser = null ) {
$this->donationId = $donationId;
$this->authorizedUser = $authorizedUser;
public function __construct( private readonly int $donationId, private readonly string $authorizedUser ) {
}

public function getDonationId(): int {
return $this->donationId;
}

public function isAuthorizedRequest(): bool {
return $this->authorizedUser !== null;
return true;
}

public function getUserName(): string {
if ( $this->authorizedUser == null ) {
throw new \LogicException( "Tried to access user name of unauthorized user. Call isAuthorizedRequest() first!" );
}
return $this->authorizedUser;
}

Expand Down
56 changes: 8 additions & 48 deletions src/UseCases/CancelDonation/CancelDonationUseCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,24 @@

namespace WMDE\Fundraising\DonationContext\UseCases\CancelDonation;

use WMDE\EmailAddress\EmailAddress;
use WMDE\Fundraising\DonationContext\Domain\Model\Donation;
use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationRepository;
use WMDE\Fundraising\DonationContext\Domain\Repositories\GetDonationException;
use WMDE\Fundraising\DonationContext\Domain\Repositories\StoreDonationException;
use WMDE\Fundraising\DonationContext\Infrastructure\DonationAuthorizationChecker;
use WMDE\Fundraising\DonationContext\Infrastructure\DonationEventLogger;
use WMDE\Fundraising\DonationContext\Infrastructure\TemplateMailerInterface;
use WMDE\Fundraising\PaymentContext\UseCases\CancelPayment\CancelPaymentUseCase;
use WMDE\Fundraising\PaymentContext\UseCases\CancelPayment\FailureResponse;

class CancelDonationUseCase {

private const LOG_MESSAGE_DONATION_STATUS_CHANGE = 'frontend: storno';
private const LOG_MESSAGE_DONATION_STATUS_CHANGE_BY_ADMIN = 'cancelled by user: %s';

public function __construct(
private DonationRepository $donationRepository,
private TemplateMailerInterface $mailer,
private DonationAuthorizationChecker $authorizationService,
private DonationEventLogger $donationLogger,
private CancelPaymentUseCase $cancelPaymentUseCase ) {
private readonly DonationRepository $donationRepository,
private readonly DonationAuthorizationChecker $authorizationService,
private readonly DonationEventLogger $donationLogger,
private readonly CancelPaymentUseCase $cancelPaymentUseCase
) {
}

public function cancelDonation( CancelDonationRequest $cancellationRequest ): CancelDonationResponse {
Expand Down Expand Up @@ -58,31 +54,20 @@ public function cancelDonation( CancelDonationRequest $cancellationRequest ): Ca

$this->donationLogger->log( $donation->getId(), $this->getLogMessage( $cancellationRequest ) );

try {
$this->sendConfirmationEmail( $cancellationRequest, $donation );
} catch ( \RuntimeException $ex ) {
return new CancelDonationResponse(
$cancellationRequest->getDonationId(),
CancelDonationResponse::MAIL_DELIVERY_FAILED
);
}

return $this->newSuccessResponse( $cancellationRequest );
}

public function getLogMessage( CancelDonationRequest $cancellationRequest ): string {
if ( $cancellationRequest->isAuthorizedRequest() ) {
return sprintf( self::LOG_MESSAGE_DONATION_STATUS_CHANGE_BY_ADMIN, $cancellationRequest->getUserName() );
}
return self::LOG_MESSAGE_DONATION_STATUS_CHANGE;
return sprintf( self::LOG_MESSAGE_DONATION_STATUS_CHANGE_BY_ADMIN, $cancellationRequest->getUserName() );
}

public function requestIsAllowedToModifyDonation( CancelDonationRequest $cancellationRequest ): bool {
if ( $cancellationRequest->isAuthorizedRequest() ) {
return $this->authorizationService->systemCanModifyDonation( $cancellationRequest->getDonationId() );

}
return $this->authorizationService->userCanModifyDonation( $cancellationRequest->getDonationId() );
// Users on the frontend are no longer allowed to cancel donations
return false;
}

private function newFailureResponse( CancelDonationRequest $cancellationRequest ): CancelDonationResponse {
Expand All @@ -99,29 +84,4 @@ private function newSuccessResponse( CancelDonationRequest $cancellationRequest
);
}

private function sendConfirmationEmail( CancelDonationRequest $cancellationRequest, Donation $donation ): void {
if ( $cancellationRequest->isAuthorizedRequest() ) {
return;
}
if ( !$donation->getDonor()->hasEmailAddress() ) {
return;
}
$this->mailer->sendMail(
new EmailAddress( $donation->getDonor()->getEmailAddress() ),
$this->getConfirmationMailTemplateArguments( $donation )
);
}

/**
* @param Donation $donation
*
* @return array<string,mixed>
*/
private function getConfirmationMailTemplateArguments( Donation $donation ): array {
return [
'donationId' => $donation->getId(),
'recipient' => $donation->getDonor()->getName()->toArray(),
];
}

}
Loading

0 comments on commit 25473d5

Please sign in to comment.