Failed Conditions
Push — develop ( 8049e9...12fcc4 )
by Reüel
04:48
created

DropInGateway   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 487
Duplicated Lines 0 %

Test Coverage

Coverage 3.24%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 205
c 2
b 0
f 0
dl 0
loc 487
ccs 6
cts 185
cp 0.0324
rs 9.1199
wmc 41

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A get_supported_payment_methods() 0 10 1
A start() 0 47 5
C payment_redirect() 0 135 11
A get_checkout_payment_methods_configuration() 0 20 1
A send_payment_details() 0 4 1
C update_status() 0 89 15
A create_payment() 0 76 5
A checkout_head() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like DropInGateway often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DropInGateway, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Drop-in gateway
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2020 Pronamic
7
 * @license   GPL-3.0-or-later
8
 * @package   Pronamic\WordPress\Pay\Gateways\Adyen
9
 */
10
11
namespace Pronamic\WordPress\Pay\Gateways\Adyen;
12
13
use Locale;
14
use Pronamic\WordPress\Pay\Core\Gateway as Core_Gateway;
15
use Pronamic\WordPress\Pay\Core\PaymentMethods;
16
use Pronamic\WordPress\Pay\Core\Server;
17
use Pronamic\WordPress\Pay\Core\Util as Core_Util;
18
use Pronamic\WordPress\Pay\Payments\Payment;
19
use Pronamic\WordPress\Pay\Plugin;
20
21
/**
22
 * Drop-in gateway
23
 *
24
 * @link https://github.com/adyenpayments/php/blob/master/generatepaymentform.php
25
 *
26
 * @author  Remco Tolsma
27
 * @version 1.0.5
28
 * @since   1.0.0
29
 */
30
class DropInGateway extends AbstractGateway {
31
	/**
32
	 * Web SDK version.
33
	 *
34
	 * @link https://docs.adyen.com/developers/checkout/web-sdk/release-notes-web-sdk
35
	 *
36
	 * @var string
37
	 */
38
	const SDK_VERSION = '3.4.0';
39
40
	/**
41
	 * Constructs and initializes an Adyen gateway.
42
	 *
43
	 * @param Config $config Config.
44
	 */
45 1
	public function __construct( Config $config ) {
46 1
		parent::__construct( $config );
47
48
		// Supported features.
49 1
		$this->supports = array(
50
			'payment_status_request',
51
			'webhook_log',
52
			'webhook',
53
		);
54 1
	}
55
56
	/**
57
	 * Get supported payment methods
58
	 *
59
	 * @return array<string>
60
	 * @see Core_Gateway::get_supported_payment_methods()
61
	 */
62 1
	public function get_supported_payment_methods() {
63
		return array(
64 1
			PaymentMethods::ALIPAY,
65
			PaymentMethods::BANCONTACT,
66
			PaymentMethods::CREDIT_CARD,
67
			PaymentMethods::DIRECT_DEBIT,
68
			PaymentMethods::EPS,
69
			PaymentMethods::GIROPAY,
70
			PaymentMethods::IDEAL,
71
			PaymentMethods::SOFORT,
72
		);
73
	}
74
75
	/**
76
	 * Start.
77
	 *
78
	 * @param Payment $payment Payment.
79
	 *
80
	 * @return void
81
	 * @see Plugin::start()
82
	 */
83
	public function start( Payment $payment ) {
84
		$payment->set_meta( 'adyen_sdk_version', self::SDK_VERSION );
85
		$payment->set_action_url( $payment->get_pay_redirect_url() );
86
87
		/*
88
		 * API Integration
89
		 *
90
		 * @link https://docs.adyen.com/api-explorer/#/PaymentSetupAndVerificationService/v41/payments
91
		 */
92
		$api_integration_payment_method_types = array(
93
			PaymentMethodType::ALIPAY,
94
			PaymentMethodType::IDEAL,
95
			PaymentMethodType::DIRECT_EBANKING,
96
		);
97
98
		// Return early if API integration is not being used.
99
		$payment_method_type = PaymentMethodType::transform( $payment->get_method() );
100
101
		if ( ! in_array( $payment_method_type, $api_integration_payment_method_types, true ) ) {
102
			return;
103
		}
104
105
		// Payment method.
106
		$payment_method = array(
107
			'type' => $payment_method_type,
108
		);
109
110
		if ( PaymentMethodType::IDEAL === $payment_method_type ) {
111
			$payment_method['issuer'] = (string) $payment->get_issuer();
112
		}
113
114
		$payment_method = new PaymentMethod( (object) $payment_method );
115
116
		// Create payment.
117
		$payment_response = $this->create_payment( $payment, $payment_method );
118
119
		if ( $payment_response instanceof \WP_Error ) {
120
			$this->error = $payment_response;
121
122
			return;
123
		}
124
125
		// Set payment action URL.
126
		$redirect = $payment_response->get_redirect();
127
128
		if ( null !== $redirect ) {
129
			$payment->set_action_url( $redirect->get_url() );
130
		}
131
	}
132
133
	/**
134
	 * Payment redirect.
135
	 *
136
	 * @param Payment $payment Payment.
137
	 *
138
	 * @return void
139
	 */
140
	public function payment_redirect( Payment $payment ) {
141
		// Check payment ID.
142
		$payment_id = $payment->get_id();
143
144
		if ( null === $payment_id ) {
145
			return;
146
		}
147
148
		$payment_response = $payment->get_meta( 'adyen_payment_response' );
149
150
		// Only show drop-in checkout page if payment method does not redirect.
151
		if ( is_string( $payment_response ) && '' !== $payment_response ) {
152
			$payment_response = \json_decode( $payment_response );
153
154
			$payment_response = PaymentResponse::from_object( $payment_response );
155
156
			$redirect = $payment_response->get_redirect();
157
158
			if ( null !== $redirect ) {
159
				\wp_redirect( $redirect->get_url() );
160
			}
161
		}
162
163
		$url_script = sprintf(
164
			'https://checkoutshopper-%s.adyen.com/checkoutshopper/sdk/%s/adyen.js',
165
			( self::MODE_TEST === $payment->get_mode() ? 'test' : 'live' ),
166
			self::SDK_VERSION
167
		);
168
169
		// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Version is part of URL.
170
		wp_register_script(
171
			'pronamic-pay-adyen-checkout',
172
			$url_script,
173
			array(),
174
			self::SDK_VERSION,
175
			false
176
		);
177
178
		wp_register_script(
179
			'pronamic-pay-adyen-checkout-drop-in',
180
			plugins_url( '../js/dist/checkout-drop-in.js', __FILE__ ),
181
			array( 'pronamic-pay-adyen-checkout' ),
182
			\pronamic_pay_plugin()->get_version(),
183
			true
184
		);
185
186
		$url_stylesheet = sprintf(
187
			'https://checkoutshopper-%s.adyen.com/checkoutshopper/sdk/%s/adyen.css',
188
			( self::MODE_TEST === $payment->get_mode() ? 'test' : 'live' ),
189
			self::SDK_VERSION
190
		);
191
192
		// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Version is part of URL.
193
		wp_register_style(
194
			'pronamic-pay-adyen-checkout',
195
			$url_stylesheet,
196
			array(),
197
			null
198
		);
199
200
		/**
201
		 * Payment methods.
202
		 */
203
		$request = new PaymentMethodsRequest( $this->config->get_merchant_account() );
204
205
		if ( null !== $payment->get_method() ) {
206
			// Payment method type.
207
			$payment_method_type = PaymentMethodType::transform( $payment->get_method() );
208
209
			if ( null !== $payment_method_type ) {
210
				$request->set_allowed_payment_methods( array( $payment_method_type ) );
211
			}
212
		}
213
214
		$locale = Util::get_payment_locale( $payment );
215
216
		$country_code = Locale::getRegion( $locale );
217
218
		$request->set_country_code( $country_code );
219
		$request->set_amount( AmountTransformer::transform( $payment->get_total_amount() ) );
220
221
		try {
222
			$payment_methods = $this->client->get_payment_methods( $request );
223
		} catch ( \Exception $e ) {
224
			Plugin::render_exception( $e );
225
226
			exit;
1 ignored issue
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
227
		}
228
229
		/**
230
		 * Adyen checkout configuration.
231
		 *
232
		 * @link https://docs.adyen.com/checkout/drop-in-web
233
		 * @link https://docs.adyen.com/checkout/components-web
234
		 */
235
		$configuration = (object) array(
236
			'locale'                      => Util::get_payment_locale( $payment ),
237
			'environment'                 => ( self::MODE_TEST === $payment->get_mode() ? 'test' : 'live' ),
238
			'originKey'                   => $this->config->origin_key,
239
			'paymentMethodsResponse'      => $payment_methods->get_original_object(),
240
			'paymentMethodsConfiguration' => $this->get_checkout_payment_methods_configuration( $payment ),
241
			'amount'                      => AmountTransformer::transform( $payment->get_total_amount() )->get_json(),
242
		);
243
244
		/**
245
		 * Filters the Adyen checkout configuration.
246
		 *
247
		 * @param object $configuration Adyen checkout configuration.
248
		 * @since 1.2.0
249
		 */
250
		$configuration = apply_filters( 'pronamic_pay_adyen_checkout_configuration', $configuration );
251
252
		wp_localize_script(
253
			'pronamic-pay-adyen-checkout',
254
			'pronamicPayAdyenCheckout',
255
			array(
256
				'paymentsUrl'        => rest_url( Integration::REST_ROUTE_NAMESPACE . '/payments/' . $payment_id ),
257
				'paymentsDetailsUrl' => rest_url( Integration::REST_ROUTE_NAMESPACE . '/payments/details/' ),
258
				'paymentReturnUrl'   => $payment->get_return_url(),
259
				'configuration'      => $configuration,
260
				'paymentAuthorised'  => __( 'Payment completed successfully.', 'pronamic_ideal' ),
261
				'paymentReceived'    => __( 'The order has been received and we are waiting for the payment to clear.', 'pronamic_ideal' ),
262
				'paymentRefused'     => __( 'The payment has been refused. Please try again using a different method or card.', 'pronamic_ideal' ),
263
			)
264
		);
265
266
		// Add checkout head action.
267
		add_action( 'pronamic_pay_adyen_checkout_head', array( $this, 'checkout_head' ) );
268
269
		// No cache.
270
		Core_Util::no_cache();
271
272
		require __DIR__ . '/../views/checkout-drop-in.php';
273
274
		exit;
1 ignored issue
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
275
	}
276
277
	/**
278
	 * Checkout head.
279
	 *
280
	 * @return void
281
	 */
282
	public function checkout_head() {
283
		wp_print_styles( 'pronamic-pay-redirect' );
284
285
		wp_print_scripts( 'pronamic-pay-adyen-checkout' );
286
287
		wp_print_styles( 'pronamic-pay-adyen-checkout' );
288
	}
289
290
	/**
291
	 * Update status of the specified payment.
292
	 *
293
	 * @param Payment $payment Payment.
294
	 *
295
	 * @return void
296
	 */
297
	public function update_status( Payment $payment ) {
298
		// Process payload on return.
299
		if ( filter_has_var( INPUT_GET, 'payload' ) ) {
300
			$payload = filter_input( INPUT_GET, 'payload', FILTER_SANITIZE_STRING );
301
302
			$payment_result_request = new PaymentResultRequest( $payload );
303
304
			try {
305
				$payment_result_response = $this->client->get_payment_result( $payment_result_request );
306
307
				PaymentResultHelper::update_payment( $payment, $payment_result_response );
308
			} catch ( \Exception $e ) {
309
				$note = sprintf(
310
				/* translators: %s: exception message */
311
					__( 'Error getting payment result: %s', 'pronamic_ideal' ),
312
					$e->getMessage()
313
				);
314
315
				$payment->add_note( $note );
316
			}
317
318
			return;
319
		}
320
321
		// Retrieve status from payment details.
322
		$payment_response = $payment->get_meta( 'adyen_payment_response' );
323
324
		if ( is_string( $payment_response ) && '' !== $payment_response ) {
325
			$payment_response = \json_decode( $payment_response );
326
327
			$payment_response = PaymentResponse::from_object( $payment_response );
328
329
			$details_result = $payment->get_meta( 'adyen_details_result' );
330
331
			// JSON decode details result meta.
332
			if ( is_string( $details_result ) && '' !== $details_result ) {
333
				$details_result = \json_decode( $details_result );
334
			}
335
336
			// Set details result meta from GET or POST request parameters.
337
			if ( '' === $details_result ) {
338
				$details_result = array();
339
340
				$details = $payment_response->get_details();
341
342
				if ( null !== $details ) {
343
					$input_type = ( 'POST' === Server::get( 'REQUEST_METHOD' ) ? INPUT_POST : INPUT_GET );
344
345
					foreach ( $details as $detail ) {
346
						$key = (string) $detail->get_key();
347
348
						$details_result[ $key ] = \filter_input( $input_type, $key, FILTER_SANITIZE_STRING );
349
					}
350
351
					$details_result = Util::filter_null( $details_result );
352
				}
353
354
				if ( ! empty( $details_result ) ) {
355
					$payment->set_meta( 'adyen_details_result', \wp_json_encode( (object) $details_result ) );
356
				}
357
			}
358
359
			$payment_data = $payment_response->get_payment_data();
360
361
			// Do not attempt to retrieve status without any request data,
362
			// payment status already updated when additional details were submitted (i.e. cards).
363
			if ( empty( $details_result ) && empty( $payment_data ) ) {
364
				return;
365
			}
366
367
			// Update payment status from payment details.
368
			$payment_details_request = new PaymentDetailsRequest();
369
370
			$payment_details_request->set_details( (object) $details_result );
371
372
			$payment_details_request->set_payment_data( $payment_data );
373
374
			try {
375
				$payment_details_response = $this->client->request_payment_details( $payment_details_request );
376
377
				PaymentResponseHelper::update_payment( $payment, $payment_details_response );
378
			} catch ( \Exception $e ) {
379
				$note = sprintf(
380
					/* translators: %s: exception message */
381
					__( 'Error getting payment details: %s', 'pronamic_ideal' ),
382
					$e->getMessage()
383
				);
384
385
				$payment->add_note( $note );
386
			}
387
		}
388
	}
389
390
	/**
391
	 * Create payment.
392
	 *
393
	 * @param Payment       $payment        Payment.
394
	 * @param PaymentMethod $payment_method Payment method.
395
	 *
396
	 * @return \WP_Error|PaymentResponse
397
	 */
398
	public function create_payment( Payment $payment, PaymentMethod $payment_method ) {
399
		// Amount.
400
		try {
401
			$amount = AmountTransformer::transform( $payment->get_total_amount() );
402
		} catch ( \InvalidArgumentException $e ) {
403
			return new \WP_Error( 'adyen_error', $e->getMessage() );
404
		}
405
406
		// Payment request.
407
		$payment_request = new PaymentRequest(
408
			$amount,
409
			$this->config->get_merchant_account(),
410
			strval( $payment->get_id() ),
411
			$payment->get_return_url(),
412
			$payment_method
413
		);
414
415
		/**
416
		 * Application info.
417
		 *
418
		 * @link https://docs.adyen.com/api-explorer/#/PaymentSetupAndVerificationService/v51/payments__reqParam_applicationInfo
419
		 * @link https://docs.adyen.com/development-resources/building-adyen-solutions
420
		 */
421
		$application_info = new ApplicationInfo();
422
423
		$application_info->merchant_application = (object) array(
424
			'name'    => 'Pronamic Pay',
425
			'version' => \pronamic_pay_plugin()->get_version(),
426
		);
427
428
		$application_info->external_platform = (object) array(
429
			'integrator' => 'Pronamic',
430
			'name'       => 'WordPress',
431
			'version'    => \get_bloginfo( 'version' ),
432
		);
433
434
		$payment_request->set_application_info( $application_info );
435
436
		// Set country code.
437
		$locale = Util::get_payment_locale( $payment );
438
439
		$country_code = \Locale::getRegion( $locale );
440
441
		$billing_address = $payment->get_billing_address();
442
443
		if ( null !== $billing_address ) {
444
			$country = $billing_address->get_country_code();
445
446
			if ( ! empty( $country ) ) {
447
				$country_code = $country;
448
			}
449
		}
450
451
		$payment_request->set_country_code( $country_code );
452
453
		// Complement payment request.
454
		PaymentRequestHelper::complement( $payment, $payment_request );
455
456
		// Create payment.
457
		try {
458
			$payment_response = $this->client->create_payment( $payment_request );
459
		} catch ( \Exception $e ) {
460
			return new \WP_Error( 'adyen_error', $e->getMessage() );
461
		}
462
463
		/*
464
		 * Store payment response for later requests to `/payments/details`.
465
		 *
466
		 * @link https://docs.adyen.com/api-explorer/#/PaymentSetupAndVerificationService/v51/payments/details
467
		 */
468
		$payment->set_meta( 'adyen_payment_response', $payment_response->get_json() );
469
470
		// Update payment status based on response.
471
		PaymentResponseHelper::update_payment( $payment, $payment_response );
472
473
		return $payment_response;
474
	}
475
476
	/**
477
	 * Send payment details.
478
	 *
479
	 * @param PaymentDetailsRequest $payment_details_request Payment details request.
480
	 *
481
	 * @return PaymentResponse
482
	 * @throws \Exception Throws error if request fails.
483
	 */
484
	public function send_payment_details( PaymentDetailsRequest $payment_details_request ) {
485
		$payment_response = $this->client->request_payment_details( $payment_details_request );
486
487
		return $payment_response;
488
	}
489
490
	/**
491
	 * Get checkout payment methods configuration.
492
	 *
493
	 * @param Payment $payment Payment.
494
	 *
495
	 * @return object
496
	 */
497
	public function get_checkout_payment_methods_configuration( Payment $payment ) {
1 ignored issue
show
Unused Code introduced by
The parameter $payment is not used and could be removed. ( Ignorable by Annotation )

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

497
	public function get_checkout_payment_methods_configuration( /** @scrutinizer ignore-unused */ Payment $payment ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
498
		$configuration = array();
499
500
		// Cards.
501
		$configuration['card'] = array(
502
			'enableStoreDetails' => true,
503
			'hasHolderName'      => true,
504
			'holderNameRequired' => true,
505
			'hideCVC'            => false,
506
			'name'               => __( 'Credit or debit card', 'pronamic_ideal' ),
507
		);
508
509
		// Boleto Bancário.
510
		$configuration['boletobancario '] = array(
511
			'personalDetailsRequired' => true,
512
			'billingAddressRequired'  => true,
513
			'showEmailAddress'        => true,
514
		);
515
516
		return (object) $configuration;
517
	}
518
}
519