|
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: |
|
|
|
|
|
|
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
|
|
|
} |
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.
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.