Completed
Pull Request — master (#1951)
by
unknown
63:33
created

logValidationErrors()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 12
nc 2
nop 2
dl 0
loc 15
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace WMDE\Fundraising\Frontend\App\Controllers\Membership;
6
7
use Symfony\Component\HttpFoundation\RedirectResponse;
8
use Symfony\Component\HttpFoundation\Request;
9
use Symfony\Component\HttpFoundation\Response;
10
use WMDE\Euro\Euro;
11
use WMDE\Fundraising\DonationContext\UseCases\GetDonation\GetDonationRequest;
12
use WMDE\Fundraising\Frontend\App\Routes;
13
use WMDE\Fundraising\Frontend\BucketTesting\Logging\Events\MembershipApplicationCreated;
14
use WMDE\Fundraising\Frontend\Factories\FunFunFactory;
15
use WMDE\Fundraising\Frontend\Presentation\DonationMembershipApplicationAdapter;
16
use WMDE\Fundraising\MembershipContext\Tracking\MembershipApplicationTrackingInfo;
17
use WMDE\Fundraising\MembershipContext\UseCases\ApplyForMembership\ApplicationValidationResult;
18
use WMDE\Fundraising\MembershipContext\UseCases\ApplyForMembership\ApplyForMembershipRequest;
19
use WMDE\Fundraising\MembershipContext\UseCases\ApplyForMembership\ApplyForMembershipResponse;
20
use WMDE\Fundraising\PaymentContext\Domain\Model\BankData;
21
use WMDE\Fundraising\PaymentContext\Domain\Model\Iban;
22
use WMDE\Fundraising\PaymentContext\Domain\Model\PaymentMethod;
23
24
/**
25
 * @license GPL-2.0-or-later
26
 */
27
class ApplyForMembershipController {
28
29
	public const SUBMISSION_COOKIE_NAME = 'memapp_timestamp';
30
	public const TIMESTAMP_FORMAT = 'Y-m-d H:i:s';
31
32
	private FunFunFactory $ffFactory;
33
34
	public function index( FunFunFactory $ffFactory, Request $httpRequest ): Response {
35
		$this->ffFactory = $ffFactory;
36
		if ( !$this->isSubmissionAllowed( $httpRequest ) ) {
37
			return new Response( $this->ffFactory->newSystemMessageResponse( 'membership_application_rejected_limit' ) );
38
		}
39
40
		try {
41
			$responseModel = $this->callUseCase( $httpRequest );
42
		}
43
		catch ( \InvalidArgumentException $ex ) {
44
			return $this->newFailureResponse( $httpRequest );
45
		}
46
47
		if ( !$responseModel->isSuccessful() ) {
48
			$this->logValidationErrors( $responseModel->getValidationResult(), $httpRequest );
49
			return $this->newFailureResponse( $httpRequest );
50
		}
51
52
		return $this->newHttpResponse( $responseModel );
53
	}
54
55
	private function callUseCase( Request $httpRequest ): ApplyForMembershipResponse {
56
		$applyForMembershipRequest = $this->createMembershipRequest( $httpRequest );
57
58
		$this->addFeeToRequestModel( $applyForMembershipRequest, $httpRequest );
59
60
		$applyForMembershipRequest->assertNoNullFields()->freeze();
61
62
		return $this->ffFactory->newApplyForMembershipUseCase()->applyForMembership( $applyForMembershipRequest );
63
	}
64
65
	private function createMembershipRequest( Request $httpRequest ): ApplyForMembershipRequest {
66
		$request = new ApplyForMembershipRequest();
67
68
		$request->setMembershipType( $httpRequest->request->get( 'membership_type', '' ) );
69
70
		if ( $httpRequest->request->get( 'adresstyp', '' ) === 'firma' ) {
71
			$request->markApplicantAsCompany();
72
		}
73
74
		$request->setApplicantSalutation( $httpRequest->request->get( 'anrede', '' ) );
75
		$request->setApplicantTitle( $httpRequest->request->get( 'titel', '' ) );
76
		$request->setApplicantFirstName( $httpRequest->request->get( 'vorname', '' ) );
77
		$request->setApplicantLastName( $httpRequest->request->get( 'nachname', '' ) );
78
		$request->setApplicantCompanyName( $httpRequest->request->get( 'firma', '' ) );
79
80
		$request->setApplicantStreetAddress( $this->filterAutofillCommas( $httpRequest->request->get( 'strasse', '' ) ) );
81
		$request->setApplicantPostalCode( $httpRequest->request->get( 'postcode', '' ) );
82
		$request->setApplicantCity( $httpRequest->request->get( 'ort', '' ) );
83
		$request->setApplicantCountryCode( $httpRequest->request->get( 'country', '' ) );
84
85
		$request->setApplicantEmailAddress( $httpRequest->request->get( 'email', '' ) );
86
		$request->setApplicantPhoneNumber( $httpRequest->request->get( 'phone', '' ) );
87
		$request->setApplicantDateOfBirth( $httpRequest->request->get( 'dob', '' ) );
88
89
		$request->setPaymentType( $httpRequest->request->get( 'payment_type', '' ) );
90
		$request->setPaymentIntervalInMonths( (int)$httpRequest->request->get( 'membership_fee_interval', 0 ) );
91
92
		$request->setTrackingInfo( new MembershipApplicationTrackingInfo(
93
			$httpRequest->request->get( 'templateCampaign', '' ),
94
			$httpRequest->request->get( 'templateName', '' )
95
		) );
96
97
		$request->setPiwikTrackingString( $httpRequest->attributes->get( 'trackingCode' ) );
98
99
		$request->setOptsIntoDonationReceipt( $httpRequest->request->getBoolean( 'donationReceipt', true ) );
100
101
		$request->setBankData( $this->createBakData( $httpRequest ) );
102
103
		return $request;
104
	}
105
106
	private function createBakData( Request $httpRequest ): BankData {
107
		$bankData = new BankData();
108
109
		$bankData->setBankName( $httpRequest->request->get( 'bank_name', '' ) );
110
		$bankData->setIban( new Iban( $httpRequest->request->get( 'iban', '' ) ) );
111
		$bankData->setBic( $httpRequest->request->get( 'bic', '' ) );
112
		$bankData->setAccount( $httpRequest->request->get( 'account_number', '' ) );
113
		$bankData->setBankCode( $httpRequest->request->get( 'bank_code', '' ) );
114
115
		$bankData->assertNoNullFields()->freeze();
116
117
		return $bankData;
118
	}
119
120
	private function newFailureResponse( Request $httpRequest ): Response {
121
		return new Response(
122
			$this->ffFactory->newMembershipFormViolationPresenter()->present(
123
				$this->createMembershipRequest( $httpRequest ),
124
				$httpRequest->request->get( 'showMembershipTypeOption' ) === 'true'
125
			)
126
		);
127
	}
128
129
	private function addFeeToRequestModel( ApplyForMembershipRequest $requestModel, Request $httpRequest ) {
130
		$requestModel->setPaymentAmountInEuros( Euro::newFromCents(
131
			intval( $httpRequest->request->get( 'membership_fee', '' ) )
132
		) );
133
	}
134
135
	private function isSubmissionAllowed( Request $request ): bool {
136
		$lastSubmission = $request->cookies->get( self::SUBMISSION_COOKIE_NAME, '' );
137
		if ( $lastSubmission === '' ) {
138
			return true;
139
		}
140
141
		$minNextTimestamp = \DateTime::createFromFormat( self::TIMESTAMP_FORMAT, $lastSubmission )
142
			->add( new \DateInterval( $this->ffFactory->getMembershipApplicationTimeframeLimit() ) );
143
		if ( $minNextTimestamp > new \DateTime() ) {
144
			return false;
145
		}
146
147
		return true;
148
	}
149
150
	private function newHttpResponse( ApplyForMembershipResponse $responseModel ): Response {
151
		switch ( $responseModel->getMembershipApplication()->getPayment()->getPaymentMethod()->getId() ) {
152
			case PaymentMethod::DIRECT_DEBIT:
153
				$httpResponse = $this->newDirectDebitResponse( $responseModel );
154
				break;
155
			case PaymentMethod::PAYPAL:
156
				$httpResponse = $this->newPayPalResponse( $responseModel );
157
				break;
158
			default:
159
				throw new \LogicException( 'This code should not be reached' );
160
		}
161
162
		$this->addCookie( $httpResponse );
163
164
		return $httpResponse;
165
	}
166
167
	private function newDirectDebitResponse( ApplyForMembershipResponse $responseModel ): Response {
168
		return new RedirectResponse(
169
			$this->ffFactory->getUrlGenerator()->generateAbsoluteUrl(
170
				'show-membership-confirmation',
171
				[
172
					'id' => $responseModel->getMembershipApplication()->getId(),
173
					'accessToken' => $responseModel->getAccessToken()
174
				]
175
			)
176
		);
177
	}
178
179
	private function newPayPalResponse( ApplyForMembershipResponse $responseModel ): Response {
180
		return new RedirectResponse(
181
			$this->ffFactory->newPayPalUrlGeneratorForMembershipApplications()->generateUrl(
182
				$responseModel->getMembershipApplication()->getId(),
0 ignored issues
show
Bug introduced by
It seems like $responseModel->getMembe...pApplication()->getId() can also be of type null; however, parameter $itemId of WMDE\Fundraising\Payment...r\PayPal::generateUrl() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

182
				/** @scrutinizer ignore-type */ $responseModel->getMembershipApplication()->getId(),
Loading history...
183
				$responseModel->getMembershipApplication()->getPayment()->getAmount(),
184
				$responseModel->getMembershipApplication()->getPayment()->getIntervalInMonths(),
185
				$responseModel->getUpdateToken(),
186
				$responseModel->getAccessToken()
187
			)
188
		);
189
	}
190
191
	private function addCookie( Response $httpResponse ) {
192
		$httpResponse->headers->setCookie(
193
			$this->ffFactory->getCookieBuilder()->newCookie(
194
				self::SUBMISSION_COOKIE_NAME,
195
				date( self::TIMESTAMP_FORMAT )
196
			)
197
		);
198
	}
199
200
	/**
201
	 * Safari and Chrome concatenate street autofill values (e.g. house number and street name) with a comma.
202
	 * This method removes the commas.
203
	 *
204
	 * @param string $value
205
	 * @return string
206
	 */
207
	private function filterAutofillCommas( string $value ): string {
208
		return trim( preg_replace( [ '/,/', '/\s{2,}/' ], [ ' ', ' ' ], $value ) );
209
	}
210
211
	private function logValidationErrors( ApplicationValidationResult $validationResult, Request $httpRequest ): void {
212
		$violations = $validationResult->getViolations();
213
		$formattedConstraintViolations = [];
214
		foreach ( $violations as $constraintViolationSource => $constraintViolation ) {
215
			$formattedConstraintViolations['validation_errors'][] = sprintf(
216
				'Validation for field "%s" failed: "%s"',
217
				$constraintViolationSource,
218
				$constraintViolation
219
			);
220
		}
221
		$formattedConstraintViolations['request_data'] = $httpRequest->request->all();
222
		$this->ffFactory->getValidationErrorLogger()->logViolations(
223
			'Unexpected server-side form validation errors.',
224
			array_keys( $violations ),
225
			$formattedConstraintViolations
226
		);
227
	}
228
}
229