Passed
Push — master ( 63237f...7c0386 )
by Reüel
06:18 queued 10s
created

Gateway::get_available_payment_methods()   B

Complexity

Conditions 11
Paths 35

Size

Total Lines 66
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 16.8164

Importance

Changes 6
Bugs 0 Features 0
Metric Value
cc 11
eloc 34
c 6
b 0
f 0
nc 35
nop 0
dl 0
loc 66
ccs 21
cts 33
cp 0.6364
crap 16.8164
rs 7.3166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Mollie gateway.
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2019 Pronamic
7
 * @license   GPL-3.0-or-later
8
 * @package   Pronamic\WordPress\Pay
9
 */
10
11
namespace Pronamic\WordPress\Pay\Gateways\Mollie;
12
13
use DateInterval;
14
use Pronamic\WordPress\DateTime\DateTime;
15
use Pronamic\WordPress\Pay\Banks\BankAccountDetails;
16
use Pronamic\WordPress\Pay\Banks\BankTransferDetails;
17
use Pronamic\WordPress\Pay\Core\Gateway as Core_Gateway;
18
use Pronamic\WordPress\Pay\Core\PaymentMethods;
19
use Pronamic\WordPress\Pay\Core\Recurring as Core_Recurring;
20
use Pronamic\WordPress\Pay\Payments\PaymentStatus;
21
use Pronamic\WordPress\Pay\Payments\Payment;
22
23
/**
24
 * Title: Mollie
25
 * Description:
26
 * Copyright: 2005-2019 Pronamic
27
 * Company: Pronamic
28
 *
29
 * @author  Remco Tolsma
30
 * @version 2.0.9
31
 * @since   1.1.0
32
 */
33
class Gateway extends Core_Gateway {
34
	/**
35
	 * Client.
36
	 *
37
	 * @var Client
38
	 */
39
	protected $client;
40
41
	/**
42
	 * Meta key for customer ID.
43
	 *
44
	 * @var string
45
	 */
46
	private $meta_key_customer_id = '_pronamic_pay_mollie_customer_id';
47
48
	/**
49
	 * Constructs and initializes an Mollie gateway
50
	 *
51
	 * @param Config $config Config.
52
	 */
53 39
	public function __construct( Config $config ) {
54 39
		parent::__construct( $config );
55
56 39
		$this->set_method( self::METHOD_HTTP_REDIRECT );
57
58
		// Supported features.
59 39
		$this->supports = array(
60
			'payment_status_request',
61
			'recurring_direct_debit',
62
			'recurring_credit_card',
63
			'recurring',
64
			'webhook',
65
			'webhook_log',
66
			'webhook_no_config',
67
		);
68
69
		// Client.
70 39
		$this->client = new Client( \strval( $config->api_key ) );
71 39
		$this->client->set_mode( $config->mode );
72
73
		// Mollie customer ID meta key.
74 39
		if ( self::MODE_TEST === $config->mode ) {
75 38
			$this->meta_key_customer_id = '_pronamic_pay_mollie_customer_id_test';
76
		}
77
78
		// Actions.
79 39
		add_action( 'pronamic_payment_status_update', array( $this, 'copy_customer_id_to_wp_user' ), 99, 1 );
80 39
	}
81
82
	/**
83
	 * Get issuers
84
	 *
85
	 * @see Core_Gateway::get_issuers()
86
	 */
87 3
	public function get_issuers() {
88 3
		$groups = array();
89
90
		try {
91 3
			$result = $this->client->get_issuers();
92
93
			$groups[] = array(
94
				'options' => $result,
95
			);
96 3
		} catch ( Error $e ) {
97
			// Catch Mollie error.
98 3
			$error = new \WP_Error(
99 3
				'mollie_error',
100 3
				sprintf( '%1$s (%2$s) - %3$s', $e->get_title(), $e->getCode(), $e->get_detail() )
101
			);
102
103 3
			$this->set_error( $error );
104
		} catch ( \Exception $e ) {
105
			// Catch exceptions.
106
			$error = new \WP_Error( 'mollie_error', $e->getMessage() );
107
108
			$this->set_error( $error );
109
		}
110
111 3
		return $groups;
112
	}
113
114
	/**
115
	 * Get available payment methods.
116
	 *
117
	 * @see Core_Gateway::get_available_payment_methods()
118
	 */
119 2
	public function get_available_payment_methods() {
120 2
		$payment_methods = array();
121
122
		// Set sequence types to get payment methods for.
123 2
		$sequence_types = array( Sequence::ONE_OFF, Sequence::RECURRING, Sequence::FIRST );
124
125 2
		$results = array();
126
127 2
		foreach ( $sequence_types as $sequence_type ) {
128
			// Get active payment methods for Mollie account.
129
			try {
130 2
				$result = $this->client->get_payment_methods( $sequence_type );
131 2
			} catch ( Error $e ) {
132
				// Catch Mollie error.
133
				$error = new \WP_Error(
134
					'mollie_error',
135
					sprintf( '%1$s (%2$s) - %3$s', $e->get_title(), $e->getCode(), $e->get_detail() )
136
				);
137
138
				$this->set_error( $error );
139
140
				break;
141 2
			} catch ( \Exception $e ) {
142
				// Catch exceptions.
143 2
				$error = new \WP_Error( 'mollie_error', $e->getMessage() );
144
145 2
				$this->set_error( $error );
146
147 2
				break;
148
			}
149
150 2
			if ( Sequence::FIRST === $sequence_type ) {
151
				foreach ( $result as $method => $title ) {
152
					unset( $result[ $method ] );
153
154
					// Get WordPress payment method for direct debit method.
155
					$method         = Methods::transform_gateway_method( $method );
156
					$payment_method = array_search( $method, PaymentMethods::get_recurring_methods(), true );
157
158
					if ( $payment_method ) {
159
						$results[ $payment_method ] = $title;
160
					}
161
				}
162
			}
163
164 2
			if ( is_array( $result ) ) {
165 2
				$results = array_merge( $results, $result );
166
			}
167
		}
168
169
		// Transform to WordPress payment methods.
170 2
		foreach ( $results as $method => $title ) {
171 2
			if ( PaymentMethods::is_recurring_method( $method ) ) {
172
				$payment_method = $method;
173
			} else {
174 2
				$payment_method = Methods::transform_gateway_method( $method );
175
			}
176
177 2
			if ( $payment_method ) {
178 2
				$payment_methods[] = $payment_method;
179
			}
180
		}
181
182 2
		$payment_methods = array_unique( $payment_methods );
183
184 2
		return $payment_methods;
185
	}
186
187
	/**
188
	 * Get supported payment methods
189
	 *
190
	 * @see Pronamic_WP_Pay_Gateway::get_supported_payment_methods()
191
	 */
192 2
	public function get_supported_payment_methods() {
193
		return array(
194 2
			PaymentMethods::BANCONTACT,
195
			PaymentMethods::BANK_TRANSFER,
196
			PaymentMethods::BELFIUS,
197
			PaymentMethods::CREDIT_CARD,
198
			PaymentMethods::DIRECT_DEBIT,
199
			PaymentMethods::DIRECT_DEBIT_BANCONTACT,
200
			PaymentMethods::DIRECT_DEBIT_IDEAL,
201
			PaymentMethods::DIRECT_DEBIT_SOFORT,
202
			PaymentMethods::EPS,
203
			PaymentMethods::GIROPAY,
204
			PaymentMethods::IDEAL,
205
			PaymentMethods::KBC,
206
			PaymentMethods::PAYPAL,
207
			PaymentMethods::SOFORT,
208
		);
209
	}
210
211
	/**
212
	 * Get webhook URL for Mollie.
213
	 *
214
	 * @return string|null
215
	 */
216 4
	public function get_webhook_url() {
217 4
		$url = home_url( '/' );
218
219 4
		$host = wp_parse_url( $url, PHP_URL_HOST );
220
221 4
		if ( is_array( $host ) ) {
222
			// Parsing failure.
223
			$host = '';
224
		}
225
226 4
		if ( 'localhost' === $host ) {
227
			// Mollie doesn't allow localhost.
228 1
			return null;
229 3
		} elseif ( '.dev' === substr( $host, -4 ) ) {
230
			// Mollie doesn't allow the .dev TLD.
231 1
			return null;
232 2
		} elseif ( '.local' === substr( $host, -6 ) ) {
233
			// Mollie doesn't allow the .local TLD.
234 1
			return null;
235 1
		} elseif ( '.test' === substr( $host, -5 ) ) {
236
			// Mollie doesn't allow the .test TLD.
237
			return null;
238
		}
239
240 1
		$url = add_query_arg( 'mollie_webhook', '', $url );
241
242 1
		return $url;
243
	}
244
245
	/**
246
	 * Start
247
	 *
248
	 * @see Pronamic_WP_Pay_Gateway::start()
249
	 *
250
	 * @param Payment $payment Payment.
251
	 */
252
	public function start( Payment $payment ) {
253
		$request               = new PaymentRequest();
254
		$request->amount       = AmountTransformer::transform( $payment->get_total_amount() );
255
		$request->description  = \strval( $payment->get_description() );
256
		$request->redirect_url = $payment->get_return_url();
257
		$request->webhook_url  = $this->get_webhook_url();
258
259
		// Locale.
260
		if ( null !== $payment->get_customer() ) {
261
			$request->locale = LocaleHelper::transform( $payment->get_customer()->get_locale() );
262
		}
263
264
		// Customer ID.
265
		$customer_id = $this->get_customer_id_for_payment( $payment );
266
267
		if ( is_string( $customer_id ) && ! empty( $customer_id ) ) {
268
			$request->customer_id = $customer_id;
269
		}
270
271
		// Payment method.
272
		$payment_method = $payment->get_method();
273
274
		// Recurring payment method.
275
		$is_recurring_method = ( $payment->get_subscription() && PaymentMethods::is_recurring_method( $payment_method ) );
276
277
		if ( false === $is_recurring_method ) {
278
			// Always use 'direct debit mandate via iDEAL/Bancontact/Sofort' payment methods as recurring method.
279
			$is_recurring_method = PaymentMethods::is_direct_debit_method( $payment_method );
280
		}
281
282
		if ( $is_recurring_method ) {
283
			$request->sequence_type = $payment->get_recurring() ? Sequence::RECURRING : Sequence::FIRST;
284
285
			if ( Sequence::FIRST === $request->sequence_type ) {
286
				$payment_method = PaymentMethods::get_first_payment_method( $payment_method );
287
			}
288
289
			if ( Sequence::RECURRING === $request->sequence_type ) {
290
				$payment->set_action_url( $payment->get_return_url() );
291
			}
292
		}
293
294
		// Leap of faith if the WordPress payment method could not transform to a Mollie method?
295
		$request->method = Methods::transform( $payment_method, $payment_method );
296
297
		// Issuer.
298
		if ( Methods::IDEAL === $request->method ) {
299
			$request->issuer = $payment->get_issuer();
300
		}
301
302
		// Due date.
303
		try {
304
			$due_date = new DateTime( sprintf( '+%s days', $this->config->due_date_days ) );
305
		} catch ( \Exception $e ) {
306
			$due_date = null;
307
		}
308
309
		$request->set_due_date( $due_date );
310
311
		// Create payment.
312
		$result = $this->client->create_payment( $request );
313
314
		// Set transaction ID.
315
		if ( isset( $result->id ) ) {
316
			$payment->set_transaction_id( $result->id );
317
		}
318
319
		// Set expiry date.
320
		/* phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase */
321
		if ( isset( $result->expiresAt ) ) {
322
			try {
323
				$expires_at = new DateTime( $result->expiresAt );
324
			} catch ( \Exception $e ) {
325
				$expires_at = null;
326
			}
327
328
			$payment->set_expiry_date( $expires_at );
329
		}
330
		/* phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase */
331
332
		// Set status.
333
		if ( isset( $result->status ) ) {
334
			$payment->set_status( Statuses::transform( $result->status ) );
335
		}
336
337
		// Set bank transfer recipient details.
338
		if ( isset( $result->details ) ) {
339
			$bank_transfer_recipient_details = $payment->get_bank_transfer_recipient_details();
340
341
			if ( null === $bank_transfer_recipient_details ) {
342
				$bank_transfer_recipient_details = new BankTransferDetails();
343
344
				$payment->set_bank_transfer_recipient_details( $bank_transfer_recipient_details );
345
			}
346
347
			$bank_details = $bank_transfer_recipient_details->get_bank_account();
348
349
			if ( null === $bank_details ) {
350
				$bank_details = new BankAccountDetails();
351
352
				$bank_transfer_recipient_details->set_bank_account( $bank_details );
353
			}
354
355
			$details = $result->details;
356
357
			/*
358
			 * @codingStandardsIgnoreStart
359
			 *
360
			 * Ignore coding standards because of sniff WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
361
			 */
362
			if ( isset( $details->bankName ) ) {
363
				/**
364
				 * Set `bankName` as bank details name, as result "Stichting Mollie Payments"
365
				 * is not the name of a bank, but the account holder name.
366
				 */
367
				$bank_details->set_name( $details->bankName );
368
			}
369
370
			if ( isset( $details->bankAccount ) ) {
371
				$bank_details->set_iban( $details->bankAccount );
372
			}
373
374
			if ( isset( $details->bankBic ) ) {
375
				$bank_details->set_bic( $details->bankBic );
376
			}
377
378
			if ( isset( $details->transferReference ) ) {
379
				$bank_transfer_recipient_details->set_reference( $details->transferReference );
380
			}
381
			// @codingStandardsIgnoreEnd
382
		}
383
384
		// Set action URL.
385
		if ( isset( $result->_links ) ) {
386
			if ( isset( $result->_links->checkout->href ) ) {
387
				$payment->set_action_url( $result->_links->checkout->href );
388
			}
389
		}
390
	}
391
392
	/**
393
	 * Update status of the specified payment
394
	 *
395
	 * @param Payment $payment Payment.
396
	 *
397
	 * @return void
398
	 */
399
	public function update_status( Payment $payment ) {
400
		$transaction_id = $payment->get_transaction_id();
401
402
		if ( null === $transaction_id ) {
403
			return;
404
		}
405
406
		$mollie_payment = $this->client->get_payment( $transaction_id );
407
408
		if ( isset( $mollie_payment->status ) ) {
409
			$payment->set_status( Statuses::transform( $mollie_payment->status ) );
410
		}
411
412
		if ( isset( $mollie_payment->details ) ) {
413
			$consumer_bank_details = $payment->get_consumer_bank_details();
414
415
			if ( null === $consumer_bank_details ) {
416
				$consumer_bank_details = new BankAccountDetails();
417
418
				$payment->set_consumer_bank_details( $consumer_bank_details );
419
			}
420
421
			$details = $mollie_payment->details;
422
423
			/*
424
			 * @codingStandardsIgnoreStart
425
			 *
426
			 * Ignore coding standards because of sniff WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
427
			 */
428
			if ( isset( $details->consumerName ) ) {
429
				$consumer_bank_details->set_name( $details->consumerName );
430
			}
431
432
			if ( isset( $details->cardHolder ) ) {
433
				$consumer_bank_details->set_name( $details->cardHolder );
434
			}
435
436
			if ( isset( $details->cardNumber ) ) {
437
				// The last four digits of the card number.
438
				$consumer_bank_details->set_account_number( $details->cardNumber );
439
			}
440
441
			if ( isset( $details->cardCountryCode ) ) {
442
				// The ISO 3166-1 alpha-2 country code of the country the card was issued in.
443
				$consumer_bank_details->set_country( $details->cardCountryCode );
444
			}
445
446
			if ( isset( $details->consumerAccount ) ) {
447
				switch ( $mollie_payment->method ) {
448
					case Methods::BELFIUS:
449
					case Methods::DIRECT_DEBIT:
450
					case Methods::IDEAL:
451
					case Methods::KBC:
452
					case Methods::SOFORT:
453
						$consumer_bank_details->set_iban( $details->consumerAccount );
454
455
						break;
456
					case Methods::BANCONTACT:
457
					case Methods::BANKTRANSFER:
458
					case Methods::PAYPAL:
459
					default:
460
						$consumer_bank_details->set_account_number( $details->consumerAccount );
461
462
						break;
463
				}
464
			}
465
466
			if ( isset( $details->consumerBic ) ) {
467
				$consumer_bank_details->set_bic( $details->consumerBic );
468
			}
469
			// @codingStandardsIgnoreEnd
470
		}
471
	}
472
473
	/**
474
	 * Get Mollie customer ID for payment.
475
	 *
476
	 * @param Payment $payment Payment.
477
	 *
478
	 * @return bool|string
479
	 */
480 10
	public function get_customer_id_for_payment( Payment $payment ) {
481
		// Get WordPress user ID from payment customer.
482 10
		$user_id = ( null === $payment->get_customer() ? null : $payment->get_customer()->get_user_id() );
483
484
		// Get Mollie customer ID from user meta.
485 10
		$customer_id = $this->get_customer_id_by_wp_user_id( $user_id );
486
487 10
		$subscription = $payment->get_subscription();
488
489
		// Get customer ID from subscription meta.
490 10
		if ( $subscription ) {
491 10
			$subscription_customer_id = $subscription->get_meta( 'mollie_customer_id' );
492
493
			// Try to get (legacy) customer ID from first payment.
494 10
			if ( empty( $subscription_customer_id ) && $subscription->get_first_payment() ) {
495 7
				$first_payment = $subscription->get_first_payment();
496
497 7
				$subscription_customer_id = $first_payment->get_meta( 'mollie_customer_id' );
498
			}
499
500 10
			if ( ! empty( $subscription_customer_id ) ) {
501 4
				$customer_id = $subscription_customer_id;
502
			}
503
		}
504
505
		// Create new customer if the customer does not exist at Mollie.
506 10
		if ( ( empty( $customer_id ) || ! $this->client->get_customer( $customer_id ) ) && Core_Recurring::RECURRING !== $payment->recurring_type ) {
507
			$customer_name = null;
508
509
			if ( null !== $payment->get_customer() && null !== $payment->get_customer()->get_name() ) {
510
				$customer_name = strval( $payment->get_customer()->get_name() );
511
			}
512
513
			$customer_id = $this->client->create_customer( $payment->get_email(), $customer_name );
514
515
			$this->update_wp_user_customer_id( $user_id, $customer_id );
516
		}
517
518
		// Store customer ID in subscription meta.
519 10
		if ( $subscription && empty( $subscription_customer_id ) && ! empty( $customer_id ) ) {
520
			$subscription->set_meta( 'mollie_customer_id', $customer_id );
521
		}
522
523
		// Copy customer ID from subscription to user meta.
524 10
		$this->copy_customer_id_to_wp_user( $payment );
525
526 10
		return $customer_id;
527
	}
528
529
	/**
530
	 * Get Mollie customer ID by the specified WordPress user ID.
531
	 *
532
	 * @param int $user_id WordPress user ID.
533
	 *
534
	 * @return string|bool
535
	 */
536 27
	public function get_customer_id_by_wp_user_id( $user_id ) {
537 27
		if ( empty( $user_id ) ) {
538 11
			return false;
539
		}
540
541 16
		return get_user_meta( $user_id, $this->meta_key_customer_id, true );
542
	}
543
544
	/**
545
	 * Update Mollie customer ID meta for WordPress user.
546
	 *
547
	 * @param int    $user_id     WordPress user ID.
548
	 * @param string $customer_id Mollie Customer ID.
549
	 *
550
	 * @return bool
551
	 */
552 15
	private function update_wp_user_customer_id( $user_id, $customer_id ) {
553 15
		if ( empty( $user_id ) || is_bool( $user_id ) ) {
554 3
			return false;
555
		}
556
557 12
		if ( ! is_string( $customer_id ) || empty( $customer_id ) || 1 === strlen( $customer_id ) ) {
0 ignored issues
show
introduced by
The condition is_string($customer_id) is always true.
Loading history...
558 11
			return false;
559
		}
560
561 4
		update_user_meta( $user_id, $this->meta_key_customer_id, $customer_id );
562 4
	}
563
564
	/**
565
	 * Copy Mollie customer ID from subscription meta to WordPress user meta.
566
	 *
567
	 * @param Payment $payment Payment.
568
	 *
569
	 * @return void
570
	 */
571 27
	public function copy_customer_id_to_wp_user( Payment $payment ) {
572 27
		if ( $this->config->id !== $payment->config_id ) {
573 1
			return;
574
		}
575
576 26
		$subscription = $payment->get_subscription();
577
578 26
		if ( ! $subscription || null === $subscription->get_customer() || empty( $subscription->get_customer()->get_user_id() ) ) {
579 11
			return;
580
		}
581
582 15
		$user_id = $subscription->get_customer()->get_user_id();
583
584
		// Get customer ID from subscription meta.
585 15
		$customer_id = $subscription->get_meta( 'mollie_customer_id' );
586
587 15
		$user_customer_id = $this->get_customer_id_by_wp_user_id( $user_id );
588
589 15
		if ( empty( $user_customer_id ) ) {
590
			// Set customer ID as user meta.
591 15
			$this->update_wp_user_customer_id( $user_id, (string) $customer_id );
592
		}
593 15
	}
594
}
595