Completed
Push — master ( 919a96...568a86 )
by wiese
73:55 queued 09:12
created

AddDonationHandler::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace WMDE\Fundraising\Frontend\App\RouteHandlers;
6
7
use Silex\Application;
8
use Symfony\Component\HttpFoundation\Request;
9
use Symfony\Component\HttpFoundation\Response;
10
use WMDE\Euro\Euro;
11
use WMDE\Fundraising\Frontend\DonationContext\Domain\Model\DonationTrackingInfo;
12
use WMDE\Fundraising\Frontend\DonationContext\UseCases\AddDonation\AddDonationRequest;
13
use WMDE\Fundraising\Frontend\DonationContext\UseCases\AddDonation\AddDonationResponse;
14
use WMDE\Fundraising\Frontend\Factories\FunFunFactory;
15
use WMDE\Fundraising\Frontend\Infrastructure\AmountParser;
16
use WMDE\Fundraising\Frontend\PaymentContext\Domain\Model\BankData;
17
use WMDE\Fundraising\Frontend\PaymentContext\Domain\Model\Iban;
18
use WMDE\Fundraising\Frontend\PaymentContext\Domain\Model\PaymentType;
19
20
/**
21
 * @license GNU GPL v2+
22
 * @author Kai Nissen < [email protected] >
23
 * @author Jeroen De Dauw < [email protected] >
24
 */
25
class AddDonationHandler {
26
27
	private $ffFactory;
28
	private $app;
29
30
	public function __construct( FunFunFactory $ffFactory, Application $app ) {
31
		$this->ffFactory = $ffFactory;
32
		$this->app = $app;
33
	}
34
35
	public function handle( Request $request ): Response {
36
		if ( !$this->isSubmissionAllowed( $request ) ) {
37
			return new Response( $this->ffFactory->newSystemMessageResponse( 'donation_rejected_limit' ) );
38
		}
39
40
		$addDonationRequest = $this->createDonationRequest( $request );
41
		$responseModel = $this->ffFactory->newAddDonationUseCase()->addDonation( $addDonationRequest );
42
43
		if ( !$responseModel->isSuccessful() ) {
44
			return new Response(
45
				$this->ffFactory->newDonationFormViolationPresenter()->present(
46
					$responseModel->getValidationErrors(), $addDonationRequest,
47
					$this->newTrackingInfoFromRequest( $request )
48
				)
49
			);
50
		}
51
52
		$this->sendTrackingDataIfNeeded( $request, $responseModel );
53
54
		return $this->newHttpResponse( $responseModel );
55
	}
56
57
	private function sendTrackingDataIfNeeded( Request $request, AddDonationResponse $responseModel ) {
58
		if ( $request->get( 'mbt', '' ) !== '1' || $responseModel->getDonation()->getPaymentType() !== PaymentType::PAYPAL ) {
59
			return;
60
		}
61
62
		$trackingCode = explode( '/', $request->attributes->get( 'trackingCode' ) );
63
		$campaign = $trackingCode[0];
64
		$keyword = $trackingCode[1] ?? '';
65
66
		$this->ffFactory->getPageViewTracker()->trackPaypalRedirection( $campaign, $keyword, $request->getClientIp() );
67
	}
68
69
	private function newHttpResponse( AddDonationResponse $responseModel ): Response {
70
		switch( $responseModel->getDonation()->getPaymentType() ) {
71
			case PaymentType::DIRECT_DEBIT:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
72
			case PaymentType::BANK_TRANSFER:
73
				$httpResponse = $this->app->redirect(
74
					$this->app['url_generator']->generate(
75
						'show-donation-confirmation',
76
						[
77
							'id' => $responseModel->getDonation()->getId(),
78
							'accessToken' => $responseModel->getAccessToken()
79
						]
80
					),
81
					Response::HTTP_SEE_OTHER
82
				);
83
				break;
84
			case PaymentType::PAYPAL:
85
				$httpResponse = $this->app->redirect(
86
					$this->ffFactory->newPayPalUrlGeneratorForDonations()->generateUrl(
87
						$responseModel->getDonation()->getId(),
88
						$responseModel->getDonation()->getAmount(),
89
						$responseModel->getDonation()->getPaymentIntervalInMonths(),
90
						$responseModel->getUpdateToken(),
91
						$responseModel->getAccessToken()
92
					)
93
				);
94
				break;
95
			case PaymentType::SOFORT:
96
				$httpResponse = $this->app->redirect(
97
					$this->ffFactory->newSofortUrlGeneratorForDonations()->generateUrl(
98
						$responseModel->getDonation()->getId(),
99
						$responseModel->getDonation()->getPayment()->getPaymentMethod()->getUuid(),
100
						$responseModel->getDonation()->getAmount(),
101
						$responseModel->getAccessToken()
102
					)
103
				);
104
				break;
105
			case PaymentType::CREDIT_CARD:
106
				$httpResponse = new Response(
107
					$this->ffFactory->newCreditCardPaymentHtmlPresenter()->present( $responseModel )
108
				);
109
				break;
110
			default:
111
				throw new \LogicException( 'This code should not be reached' );
112
		}
113
		return $httpResponse;
114
	}
115
116
	private function createDonationRequest( Request $request ): AddDonationRequest {
117
		$donationRequest = new AddDonationRequest();
118
119
		$donationRequest->setAmount( $this->getEuroAmountFromString( $request->get( 'betrag', '' ) ) );
120
121
		$donationRequest->setPaymentType( $request->get( 'zahlweise', '' ) );
122
		$donationRequest->setInterval( intval( $request->get( 'periode', 0 ) ) );
123
124
		$donationRequest->setDonorType( $request->get( 'addressType', '' ) );
125
		$donationRequest->setDonorSalutation( $request->get( 'salutation', '' ) );
126
		$donationRequest->setDonorTitle( $request->get( 'title', '' ) );
127
		$donationRequest->setDonorCompany( $request->get( 'companyName', '' ) );
128
		$donationRequest->setDonorFirstName( $request->get( 'firstName', '' ) );
129
		$donationRequest->setDonorLastName( $request->get( 'lastName', '' ) );
130
		$donationRequest->setDonorStreetAddress( $this->filterAutofillCommas( $request->get( 'street', '' ) ) );
131
		$donationRequest->setDonorPostalCode( $request->get( 'postcode', '' ) );
132
		$donationRequest->setDonorCity( $request->get( 'city', '' ) );
133
		$donationRequest->setDonorCountryCode( $request->get( 'country', '' ) );
134
		$donationRequest->setDonorEmailAddress( $request->get( 'email', '' ) );
135
136
		if ( $request->get( 'zahlweise', '' ) === PaymentType::DIRECT_DEBIT ) {
137
			$donationRequest->setBankData( $this->getBankDataFromRequest( $request ) );
138
		}
139
140
		$donationRequest->setTracking( $request->attributes->get( 'trackingCode' ) );
141
		$donationRequest->setOptIn( $request->get( 'info', '' ) );
142
		$donationRequest->setSource( $request->attributes->get( 'trackingSource' ) );
143
		$donationRequest->setTotalImpressionCount( intval( $request->get( 'impCount', 0 ) ) );
144
		$donationRequest->setSingleBannerImpressionCount( intval( $request->get( 'bImpCount', 0 ) ) );
145
146
		return $donationRequest;
147
	}
148
149
	private function getBankDataFromRequest( Request $request ): BankData {
150
		$bankData = new BankData();
151
		$bankData->setIban( new Iban( $request->get( 'iban', '' ) ) )
152
			->setBic( $request->get( 'bic', '' ) )
153
			->setAccount( $request->get( 'konto', '' ) )
154
			->setBankCode( $request->get( 'blz', '' ) )
155
			->setBankName( $request->get( 'bankname', '' ) );
156
157
		if ( $bankData->isComplete() ) {
158
			return $bankData->freeze()->assertNoNullFields();
159
		}
160
161
		if ( $bankData->hasIban() ) {
162
			$bankData = $this->newBankDataFromIban( $bankData->getIban() );
163
		} elseif ( $bankData->hasCompleteLegacyBankData() ) {
164
			$bankData = $this->newBankDataFromAccountAndBankCode( $bankData->getAccount(), $bankData->getBankCode() );
165
		}
166
		return $bankData->freeze()->assertNoNullFields();
167
	}
168
169
	private function newBankDataFromIban( Iban $iban ): BankData {
170
		return $this->ffFactory->newBankDataConverter()->getBankDataFromIban( $iban );
171
	}
172
173
	private function newBankDataFromAccountAndBankCode( string $account, string $bankCode ): BankData {
174
		return $this->ffFactory->newBankDataConverter()->getBankDataFromAccountData( $account, $bankCode );
175
	}
176
177
	private function getEuroAmountFromString( string $amount ): Euro {
178
		$locale = 'de_DE'; // TODO: make this configurable for multilanguage support
179
		try {
180
			return Euro::newFromFloat( ( new AmountParser( $locale ) )->parseAsFloat( $amount ) );
181
		} catch ( \InvalidArgumentException $ex ) {
182
			return Euro::newFromCents( 0 );
183
		}
184
185
	}
186
187
	private function isSubmissionAllowed( Request $request ) {
188
		$lastSubmission = $request->cookies->get( ShowDonationConfirmationHandler::SUBMISSION_COOKIE_NAME, '' );
189
		if ( $lastSubmission === '' ) {
190
			return true;
191
		}
192
193
		$minNextTimestamp =
194
			\DateTime::createFromFormat( ShowDonationConfirmationHandler::TIMESTAMP_FORMAT, $lastSubmission )
195
			->add( new \DateInterval( $this->ffFactory->getDonationTimeframeLimit() ) );
196
197
		if ( $minNextTimestamp > new \DateTime() ) {
198
			return false;
199
		}
200
201
		return true;
202
	}
203
204
	private function newTrackingInfoFromRequest( Request $request ): DonationTrackingInfo {
205
		$tracking = new DonationTrackingInfo();
206
		$tracking->setSingleBannerImpressionCount( intval( $request->get( 'bImpCount', 0 ) ) );
207
		$tracking->setTotalImpressionCount( intval( $request->get( 'impCount', 0 ) ) );
208
209
		return $tracking;
210
	}
211
212
	/**
213
	 * Safari and Chrome concatenate street autofill values (e.g. house number and street name) with a comma.
214
	 * This method removes the commas.
215
	 *
216
	 * @param string $value
217
	 * @return string
218
	 */
219
	private function filterAutofillCommas( string $value ): string {
220
		return trim( preg_replace( ['/,/', '/\s{2,}/'], [' ', ' '], $value ) );
221
	}
222
}