22
33namespace Drupal \commerce_paypal \Plugin \Commerce \PaymentGateway ;
44
5+ use Drupal \commerce \TimeInterface ;
56use Drupal \commerce_order \Entity \OrderInterface ;
67use Drupal \commerce_payment \Entity \PaymentInterface ;
78use Drupal \commerce_payment \Exception \InvalidRequestException ;
89use Drupal \commerce_payment \Exception \PaymentGatewayException ;
910use Drupal \commerce_payment \PaymentMethodTypeManager ;
1011use Drupal \commerce_payment \PaymentTypeManager ;
12+ use Drupal \commerce_payment \Plugin \Commerce \PaymentGateway \OffsitePaymentGatewayBase ;
13+ use Drupal \commerce_paypal \IPNHandlerInterface ;
1114use Drupal \commerce_price \Price ;
1215use Drupal \commerce_price \RounderInterface ;
1316use Drupal \Core \Entity \EntityTypeManagerInterface ;
1417use Drupal \Core \Form \FormStateInterface ;
18+ use Drupal \Core \Logger \LoggerChannelFactoryInterface ;
1519use GuzzleHttp \ClientInterface ;
1620use Symfony \Component \HttpFoundation \Request ;
1721use Symfony \Component \DependencyInjection \ContainerInterface ;
22+ use Symfony \Component \HttpKernel \Exception \BadRequestHttpException ;
1823
1924/**
2025 * Provides the Paypal Express Checkout payment gateway.
3237 * },
3338 * )
3439 */
35- class ExpressCheckout extends PayPalIPNGatewayBase implements ExpressCheckoutInterface {
40+ class ExpressCheckout extends OffsitePaymentGatewayBase implements ExpressCheckoutInterface {
41+
42+ /**
43+ * The logger.
44+ *
45+ * @var \Drupal\Core\Logger\LoggerChannelInterface
46+ */
47+ protected $ logger ;
3648
3749 /**
3850 * The HTTP client.
@@ -42,19 +54,59 @@ class ExpressCheckout extends PayPalIPNGatewayBase implements ExpressCheckoutInt
4254 protected $ httpClient ;
4355
4456 /**
45- * The rounder.
57+ * The price rounder.
4658 *
4759 * @var \Drupal\commerce_price\RounderInterface
4860 */
4961 protected $ rounder ;
5062
5163 /**
52- * {@inheritdoc}
64+ * The time.
65+ *
66+ * @var \Drupal\commerce\TimeInterface
5367 */
54- public function __construct (array $ configuration , $ plugin_id , $ plugin_definition , EntityTypeManagerInterface $ entity_type_manager , PaymentTypeManager $ payment_type_manager , PaymentMethodTypeManager $ payment_method_type_manager , ClientInterface $ client , RounderInterface $ rounder ) {
68+ protected $ time ;
69+
70+ /**
71+ * The IPN handler.
72+ *
73+ * @var \Drupal\commerce_paypal\IPNHandlerInterface
74+ */
75+ protected $ ipnHandler ;
76+
77+ /**
78+ * Constructs a new PaymentGatewayBase object.
79+ *
80+ * @param array $configuration
81+ * A configuration array containing information about the plugin instance.
82+ * @param string $plugin_id
83+ * The plugin_id for the plugin instance.
84+ * @param mixed $plugin_definition
85+ * The plugin implementation definition.
86+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
87+ * The entity type manager.
88+ * @param \Drupal\commerce_payment\PaymentTypeManager $payment_type_manager
89+ * The payment type manager.
90+ * @param \Drupal\commerce_payment\PaymentMethodTypeManager $payment_method_type_manager
91+ * The payment method type manager.
92+ * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_channel_factory
93+ * The logger channel factory.
94+ * @param \GuzzleHttp\ClientInterface $client
95+ * The client.
96+ * @param \Drupal\commerce_price\RounderInterface $rounder
97+ * The price rounder.
98+ * @param \Drupal\commerce\TimeInterface $time
99+ * The time.
100+ * @param \Drupal\commerce_paypal\IPNHandlerInterface $ip_handler
101+ * The IPN handler.
102+ */
103+ public function __construct (array $ configuration , $ plugin_id , $ plugin_definition , EntityTypeManagerInterface $ entity_type_manager , PaymentTypeManager $ payment_type_manager , PaymentMethodTypeManager $ payment_method_type_manager , LoggerChannelFactoryInterface $ logger_channel_factory , ClientInterface $ client , RounderInterface $ rounder , TimeInterface $ time , IPNHandlerInterface $ ip_handler ) {
55104 parent ::__construct ($ configuration , $ plugin_id , $ plugin_definition , $ entity_type_manager , $ payment_type_manager , $ payment_method_type_manager );
105+ $ this ->logger = $ logger_channel_factory ->get ('commerce_paypal ' );
56106 $ this ->httpClient = $ client ;
57107 $ this ->rounder = $ rounder ;
108+ $ this ->time = $ time ;
109+ $ this ->ipnHandler = $ ip_handler ;
58110 }
59111
60112 /**
@@ -68,8 +120,10 @@ public static function create(ContainerInterface $container, array $configuratio
68120 $ container ->get ('entity_type.manager ' ),
69121 $ container ->get ('plugin.manager.commerce_payment_type ' ),
70122 $ container ->get ('plugin.manager.commerce_payment_method_type ' ),
123+ $ container ->get ('logger.factory ' ),
71124 $ container ->get ('http_client ' ),
72- $ container ->get ('commerce_price.rounder ' )
125+ $ container ->get ('commerce_price.rounder ' ),
126+ $ container ->get ('commerce.time ' )
73127 );
74128 }
75129
@@ -195,13 +249,13 @@ public function onReturn(OrderInterface $order, Request $request) {
195249 $ paypal_response = $ this ->doExpressCheckoutDetails ($ order );
196250
197251 // Nothing to do for failures for now - no payment saved.
198- // ToDo - more about the failures.
252+ // @todo - more about the failures.
199253 if ($ paypal_response ['PAYMENTINFO_0_PAYMENTSTATUS ' ] == 'Failed ' ) {
200254 throw new PaymentGatewayException ($ paypal_response ['PAYMENTINFO_0_LONGMESSAGE ' ], $ paypal_response ['PAYMENTINFO_0_ERRORCODE ' ]);
201255 }
202256
203257 $ payment_storage = $ this ->entityTypeManager ->getStorage ('commerce_payment ' );
204- $ request_time = \Drupal:: service ( ' commerce. time' ) ->getRequestTime ();
258+ $ request_time = $ this -> time ->getRequestTime ();
205259 $ payment = $ payment_storage ->create ([
206260 'state ' => 'authorization ' ,
207261 'amount ' => $ order ->getTotalPrice (),
@@ -214,7 +268,7 @@ public function onReturn(OrderInterface $order, Request $request) {
214268 ]);
215269
216270 // Process payment status received.
217- // ToDo : payment updates if needed.
271+ // @todo payment updates if needed.
218272 // If we didn't get an approval response code...
219273 switch ($ paypal_response ['PAYMENTINFO_0_PAYMENTSTATUS ' ]) {
220274 case 'Voided ' :
@@ -349,21 +403,18 @@ public function refundPayment(PaymentInterface $payment, Price $amount = NULL) {
349403 */
350404 public function onNotify (Request $ request ) {
351405 // Get IPN request data and basic processing for the IPN request.
352- $ ipn_data = $ this ->processIpnRequest ($ request );
353- if (!$ ipn_data ) {
354- return FALSE ;
355- }
406+ $ ipn_data = $ this ->ipnHandler ->process ($ request );
356407
357408 // Do not perform any processing on EC transactions here that do not have
358409 // transaction IDs, indicating they are non-payment IPNs such as those used
359410 // for subscription signup requests.
360411 if (empty ($ ipn_data ['txn_id ' ])) {
361- \Drupal:: logger ( ' commerce_paypal ' ) ->alert ('The IPN request does not have a transaction id. Ignored. ' );
412+ $ this -> logger ->alert ('The IPN request does not have a transaction id. Ignored. ' );
362413 return FALSE ;
363414 }
364415 // Exit when we don't get a payment status we recognize.
365416 if (!in_array ($ ipn_data ['payment_status ' ], ['Failed ' , 'Voided ' , 'Pending ' , 'Completed ' , 'Refunded ' ])) {
366- return FALSE ;
417+ throw new BadRequestHttpException ( ' Invalid payment status ' ) ;
367418 }
368419 // If this is a prior authorization capture IPN...
369420 if (in_array ($ ipn_data ['payment_status ' ], ['Voided ' , 'Pending ' , 'Completed ' ]) && !empty ($ ipn_data ['auth_id ' ])) {
@@ -372,7 +423,7 @@ public function onNotify(Request $request) {
372423 // If not, bail now because authorization transactions should be created
373424 // by the Express Checkout API request itself.
374425 if (!$ payment ) {
375- \Drupal:: logger ( ' commerce_paypal ' ) ->warning ('IPN for Order @order_number ignored: authorization transaction already created. ' , ['@order_number ' => $ ipn_data ['invoice ' ]]);
426+ $ this -> logger ->warning ('IPN for Order @order_number ignored: authorization transaction already created. ' , ['@order_number ' => $ ipn_data ['invoice ' ]]);
376427 return FALSE ;
377428 }
378429 $ amount = new Price ($ ipn_data ['mc_gross ' ], $ ipn_data ['mc_currency ' ]);
@@ -389,7 +440,7 @@ public function onNotify(Request $request) {
389440
390441 case 'Completed ' :
391442 $ payment ->state = 'capture_completed ' ;
392- $ payment ->setCapturedTime (REQUEST_TIME );
443+ $ payment ->setCapturedTime ($ this -> time -> getRequestTime () );
393444 break ;
394445 }
395446 // Update the remote id.
@@ -399,15 +450,14 @@ public function onNotify(Request $request) {
399450 // Get the corresponding parent transaction and refund it.
400451 $ payment = $ this ->loadPaymentByRemoteId ($ ipn_data ['parent_txn_id ' ]);
401452 if (!$ payment ) {
402- \Drupal:: logger ( ' commerce_paypal ' ) ->warning ('IPN for Order @order_number ignored: the transaction to be refunded does not exist. ' , ['@order_number ' => $ ipn_data ['invoice ' ]]);
453+ $ this -> logger ->warning ('IPN for Order @order_number ignored: the transaction to be refunded does not exist. ' , ['@order_number ' => $ ipn_data ['invoice ' ]]);
403454 return FALSE ;
404455 }
405456 elseif ($ payment ->getState () == 'capture_refunded ' ) {
406- \Drupal:: logger ( ' commerce_paypal ' ) ->warning ('IPN for Order @order_number ignored: the transaction is already refunded. ' , ['@order_number ' => $ ipn_data ['invoice ' ]]);
457+ $ this -> logger ->warning ('IPN for Order @order_number ignored: the transaction is already refunded. ' , ['@order_number ' => $ ipn_data ['invoice ' ]]);
407458 return FALSE ;
408459 }
409- $ amount_number = abs ($ ipn_data ['mc_gross ' ]);
410- $ amount = new Price ((string ) $ amount_number , $ ipn_data ['mc_currency ' ]);
460+ $ amount = new Price ((string ) $ ipn_data ['mc_gross ' ], $ ipn_data ['mc_currency ' ]);
411461 // Check if the Refund is partial or full.
412462 $ old_refunded_amount = $ payment ->getRefundedAmount ();
413463 $ new_refunded_amount = $ old_refunded_amount ->add ($ amount );
@@ -425,7 +475,7 @@ public function onNotify(Request $request) {
425475 else {
426476 // In other circumstances, exit the processing, because we handle those
427477 // cases directly during API response processing.
428- \Drupal:: logger ( ' commerce_paypal ' ) ->notice ('IPN for Order @order_number ignored: this operation was accommodated in the direct API response. ' , ['@order_number ' => $ ipn_data ['invoice ' ]]);
478+ $ this -> logger ->notice ('IPN for Order @order_number ignored: this operation was accommodated in the direct API response. ' , ['@order_number ' => $ ipn_data ['invoice ' ]]);
429479 return FALSE ;
430480 }
431481 if (isset ($ payment )) {
@@ -683,4 +733,23 @@ public function doRequest(array $nvp_data) {
683733 return $ paypal_response ;
684734 }
685735
736+ /**
737+ * Loads the payment for a given remote id.
738+ *
739+ * @param string $remote_id
740+ * The remote id property for a payment.
741+ *
742+ * @return \Drupal\commerce_payment\Entity\PaymentInterface
743+ * Payment object.
744+ *
745+ * @todo: to be replaced by Commerce core payment storage method
746+ * @see https://www.drupal.org/node/2856209
747+ */
748+ protected function loadPaymentByRemoteId ($ remote_id ) {
749+ /** @var \Drupal\commerce_payment\PaymentStorage $storage */
750+ $ storage = $ this ->entityTypeManager ->getStorage ('commerce_payment ' );
751+ $ payment_by_remote_id = $ storage ->loadByProperties (['remote_id ' => $ remote_id ]);
752+ return reset ($ payment_by_remote_id );
753+ }
754+
686755}
0 commit comments