|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
declare( strict_types = 1 ); |
|
4
|
|
|
|
|
5
|
|
|
namespace WMDE\Fundraising\Frontend\App\RouteHandlers; |
|
6
|
|
|
|
|
7
|
|
|
use Symfony\Component\HttpFoundation\ParameterBag; |
|
8
|
|
|
use Symfony\Component\HttpFoundation\Request; |
|
9
|
|
|
use Symfony\Component\HttpFoundation\Response; |
|
10
|
|
|
use WMDE\Euro\Euro; |
|
11
|
|
|
use WMDE\Fundraising\Frontend\DonationContext\UseCases\HandlePayPalPaymentNotification\PayPalNotificationRequest; |
|
12
|
|
|
use WMDE\Fundraising\Frontend\Factories\FunFunFactory; |
|
13
|
|
|
use WMDE\Fundraising\Frontend\Infrastructure\PayPalPaymentNotificationVerifierException; |
|
14
|
|
|
|
|
15
|
|
|
/** |
|
16
|
|
|
* @license GNU GPL v2+ |
|
17
|
|
|
* @author Kai Nissen < [email protected] > |
|
18
|
|
|
*/ |
|
19
|
|
|
class PayPalNotificationHandler { |
|
20
|
|
|
|
|
21
|
|
|
private $ffFactory; |
|
22
|
|
|
|
|
23
|
|
|
public function __construct( FunFunFactory $ffFactory ) { |
|
24
|
|
|
$this->ffFactory = $ffFactory; |
|
25
|
|
|
} |
|
26
|
|
|
|
|
27
|
|
|
public function handle( Request $request ): Response { |
|
28
|
|
|
$post = $request->request; |
|
29
|
|
|
|
|
30
|
|
|
try { |
|
31
|
|
|
$this->ffFactory->getPayPalPaymentNotificationVerifier()->verify( $post->all() ); |
|
32
|
|
|
} catch ( PayPalPaymentNotificationVerifierException $e ) { |
|
33
|
|
|
// TODO: let PayPal resend IPN? |
|
34
|
|
|
|
|
35
|
|
|
return $this->createErrorResponse( $e ); |
|
36
|
|
|
} |
|
37
|
|
|
|
|
38
|
|
|
// TODO: check txn_type |
|
39
|
|
|
|
|
40
|
|
|
$useCase = $this->ffFactory->newHandlePayPalPaymentNotificationUseCase( $this->getUpdateToken( $post ) ); |
|
41
|
|
|
|
|
42
|
|
|
$useCase->handleNotification( $this->newUseCaseRequestFromPost( $post ) ); |
|
43
|
|
|
|
|
44
|
|
|
return new Response( '', Response::HTTP_OK ); # PayPal expects an empty response |
|
45
|
|
|
} |
|
46
|
|
|
|
|
47
|
|
|
private function getUpdateToken( ParameterBag $postRequest ): string { |
|
48
|
|
|
return $this->getValueFromCustomVars( $postRequest->get( 'custom', '' ), 'utoken' ); |
|
49
|
|
|
} |
|
50
|
|
|
|
|
51
|
|
|
private function getValueFromCustomVars( string $customVars, string $key ): string { |
|
52
|
|
|
$vars = json_decode( $customVars, true ); |
|
53
|
|
|
return !empty( $vars[$key] ) ? $vars[$key] : ''; |
|
54
|
|
|
} |
|
55
|
|
|
|
|
56
|
|
|
private function newUseCaseRequestFromPost( ParameterBag $postRequest ): PayPalNotificationRequest { |
|
57
|
|
|
return ( new PayPalNotificationRequest() ) |
|
58
|
|
|
->setTransactionType( $postRequest->get( 'txn_type', '' ) ) |
|
59
|
|
|
->setTransactionId( $postRequest->get( 'txn_id', '' ) ) |
|
60
|
|
|
->setPayerId( $postRequest->get( 'payer_id', '' ) ) |
|
61
|
|
|
->setSubscriberId( $postRequest->get( 'subscr_id', '' ) ) |
|
62
|
|
|
->setPayerEmail( $postRequest->get( 'payer_email', '' ) ) |
|
63
|
|
|
->setPayerStatus( $postRequest->get( 'payer_status', '' ) ) |
|
64
|
|
|
->setPayerFirstName( $postRequest->get( 'first_name', '' ) ) |
|
65
|
|
|
->setPayerLastName( $postRequest->get( 'last_name', '' ) ) |
|
66
|
|
|
->setPayerAddressName( $postRequest->get( 'address_name', '' ) ) |
|
67
|
|
|
->setPayerAddressStreet( $postRequest->get( 'address_street', '' ) ) |
|
68
|
|
|
->setPayerAddressPostalCode( $postRequest->get( 'address_zip', '' ) ) |
|
69
|
|
|
->setPayerAddressCity( $postRequest->get( 'address_city', '' ) ) |
|
70
|
|
|
->setPayerAddressCountryCode( $postRequest->get( 'address_country_code', '' ) ) |
|
71
|
|
|
->setPayerAddressStatus( $postRequest->get( 'address_status', '' ) ) |
|
72
|
|
|
->setDonationId( (int)$postRequest->get( 'item_number', 0 ) ) |
|
73
|
|
|
->setCurrencyCode( $postRequest->get( 'mc_currency', '' ) ) |
|
74
|
|
|
->setTransactionFee( Euro::newFromString( $postRequest->get( 'mc_fee', '0' ) ) ) |
|
75
|
|
|
->setAmountGross( Euro::newFromString( $postRequest->get( 'mc_gross', '0' ) ) ) |
|
76
|
|
|
->setSettleAmount( Euro::newFromString( $postRequest->get( 'settle_amount', '0' ) ) ) |
|
77
|
|
|
->setPaymentTimestamp( $postRequest->get( 'payment_date', '' ) ) |
|
78
|
|
|
->setPaymentStatus( $postRequest->get( 'payment_status', '' ) ) |
|
79
|
|
|
->setPaymentType( $postRequest->get( 'payment_type', '' ) ); |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
|
|
private function createErrorResponse( PayPalPaymentNotificationVerifierException $e ): Response { |
|
83
|
|
|
switch ( $e->getCode() ) { |
|
84
|
|
|
case PayPalPaymentNotificationVerifierException::ERROR_UNSUPPORTED_STATUS; |
|
|
|
|
|
|
85
|
|
|
// Just ignore IPN notices with a status we don't handle |
|
86
|
|
|
return new Response( '', Response::HTTP_OK ); |
|
87
|
|
|
case PayPalPaymentNotificationVerifierException::ERROR_WRONG_RECEIVER: |
|
88
|
|
|
return new Response( $e->getMessage(), Response::HTTP_FORBIDDEN ); |
|
89
|
|
|
case PayPalPaymentNotificationVerifierException::ERROR_VERIFICATION_FAILED: |
|
90
|
|
|
return new Response( $e->getMessage(), Response::HTTP_FORBIDDEN ); |
|
91
|
|
|
case PayPalPaymentNotificationVerifierException::ERROR_UNSUPPORTED_CURRENCY: |
|
92
|
|
|
return new Response( $e->getMessage(), Response::HTTP_NOT_ACCEPTABLE ); |
|
93
|
|
|
default: |
|
94
|
|
|
return new Response( $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR ); |
|
95
|
|
|
} |
|
96
|
|
|
} |
|
97
|
|
|
|
|
98
|
|
|
} |
|
99
|
|
|
|
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.