Passed
Push — master ( 886ed1...78e783 )
by Remco
04:09 queued 01:35
created

Gateway   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 402
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 59
dl 0
loc 402
rs 4.5454
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A get_issuer_field() 0 9 2
A get_supported_payment_methods() 0 15 1
C get_available_payment_methods() 0 51 9
D start() 0 65 12
B __construct() 0 24 2
A get_webhook_url() 0 16 3
A get_issuers() 0 16 2
C update_status() 0 30 7
D get_customer_id_for_payment() 0 37 10
A update_wp_user_customer_id() 0 6 3
B move_customer_id_to_wp_user() 0 20 6
A get_customer_id_by_wp_user_id() 0 6 2

How to fix   Complexity   

Complex Class

Complex classes like Gateway 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 Gateway, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Pronamic\WordPress\Pay\Gateways\Mollie;
4
5
use Pronamic\WordPress\Pay\Core\Gateway as Core_Gateway;
6
use Pronamic\WordPress\Pay\Core\PaymentMethods;
7
use Pronamic\WordPress\Pay\Core\Recurring as Core_Recurring;
8
use Pronamic\WordPress\Pay\Core\Statuses as Core_Statuses;
9
use Pronamic\WordPress\Pay\Payments\Payment;
0 ignored issues
show
Bug introduced by
The type Pronamic\WordPress\Pay\Payments\Payment was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
11
/**
12
 * Title: Mollie
13
 * Description:
14
 * Copyright: Copyright (c) 2005 - 2018
15
 * Company: Pronamic
16
 *
17
 * @author  Remco Tolsma
18
 * @version 2.0.1
19
 * @since   1.1.0
20
 */
21
class Gateway extends Core_Gateway {
22
	/**
23
	 * Slug of this gateway
24
	 *
25
	 * @var string
26
	 */
27
	const SLUG = 'mollie';
28
29
	/**
30
	 * Meta key for customer ID.
31
	 *
32
	 * @var string
33
	 */
34
	private $meta_key_customer_id = '_pronamic_pay_mollie_customer_id';
35
36
	/**
37
	 * Constructs and initializes an Mollie gateway
38
	 *
39
	 * @param Config $config
40
	 */
41
	public function __construct( Config $config ) {
42
		parent::__construct( $config );
43
44
		$this->supports = array(
45
			'payment_status_request',
46
			'recurring_direct_debit',
47
			'recurring_credit_card',
48
			'recurring',
49
		);
50
51
		$this->set_method( Core_Gateway::METHOD_HTTP_REDIRECT );
52
		$this->set_has_feedback( true );
53
		$this->set_amount_minimum( 1.20 );
54
		$this->set_slug( self::SLUG );
55
56
		$this->client = new Client( $config->api_key );
0 ignored issues
show
Documentation Bug introduced by
It seems like new Pronamic\WordPress\P...lient($config->api_key) of type Pronamic\WordPress\Pay\Gateways\Mollie\Client is incompatible with the declared type Pronamic\WordPress\Pay\Core\Client of property $client.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
57
		$this->client->set_mode( $config->mode );
58
59
		if ( 'test' === $config->mode ) {
60
			$this->meta_key_customer_id = '_pronamic_pay_mollie_customer_id_test';
61
		}
62
63
		// Actions.
64
		add_action( 'pronamic_payment_status_update', array( $this, 'move_customer_id_to_wp_user' ), 99, 1 );
0 ignored issues
show
Bug introduced by
The function add_action was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

64
		/** @scrutinizer ignore-call */ 
65
  add_action( 'pronamic_payment_status_update', array( $this, 'move_customer_id_to_wp_user' ), 99, 1 );
Loading history...
65
	}
66
67
	/**
68
	 * Get issuers
69
	 *
70
	 * @see Pronamic_WP_Pay_Gateway::get_issuers()
71
	 */
72
	public function get_issuers() {
73
		$groups = array();
74
75
		$result = $this->client->get_issuers();
76
77
		if ( ! $result ) {
78
			$this->error = $this->client->get_error();
79
80
			return $groups;
81
		}
82
83
		$groups[] = array(
84
			'options' => $result,
85
		);
86
87
		return $groups;
88
	}
89
90
	public function get_issuer_field() {
91
		if ( PaymentMethods::IDEAL === $this->get_payment_method() ) {
92
			return array(
93
				'id'       => 'pronamic_ideal_issuer_id',
94
				'name'     => 'pronamic_ideal_issuer_id',
95
				'label'    => __( 'Choose your bank', 'pronamic_ideal' ),
0 ignored issues
show
Bug introduced by
The function __ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

95
				'label'    => /** @scrutinizer ignore-call */ __( 'Choose your bank', 'pronamic_ideal' ),
Loading history...
96
				'required' => true,
97
				'type'     => 'select',
98
				'choices'  => $this->get_transient_issuers(),
99
			);
100
		}
101
	}
102
103
	/**
104
	 * Get available payment methods.
105
	 *
106
	 * @see Core_Gateway::get_available_payment_methods()
107
	 */
108
	public function get_available_payment_methods() {
109
		$payment_methods = array();
110
111
		// Set recurring types to get payment methods for.
112
		$recurring_types = array( null, Recurring::RECURRING, Recurring::FIRST );
113
114
		$results = array();
115
116
		foreach ( $recurring_types as $recurring_type ) {
117
			// Get active payment methods for Mollie account.
118
			$result = $this->client->get_payment_methods( $recurring_type );
119
120
			if ( ! $result ) {
121
				$this->error = $this->client->get_error();
122
123
				break;
124
			}
125
126
			if ( Recurring::FIRST === $recurring_type ) {
127
				foreach ( $result as $method => $title ) {
128
					unset( $result[ $method ] );
129
130
					// Get WordPress payment method for direct debit method.
131
					$method         = Methods::transform_gateway_method( $method );
132
					$payment_method = array_search( $method, PaymentMethods::get_recurring_methods(), true );
133
134
					if ( $payment_method ) {
135
						$results[ $payment_method ] = $title;
136
					}
137
				}
138
			}
139
140
			$results = array_merge( $results, $result );
141
		}
142
143
		// Transform to WordPress payment methods.
144
		foreach ( $results as $method => $title ) {
145
			if ( PaymentMethods::is_recurring_method( $method ) ) {
146
				$payment_method = $method;
147
			} else {
148
				$payment_method = Methods::transform_gateway_method( $method );
149
			}
150
151
			if ( $payment_method ) {
152
				$payment_methods[] = $payment_method;
153
			}
154
		}
155
156
		$payment_methods = array_unique( $payment_methods );
157
158
		return $payment_methods;
159
	}
160
161
	/**
162
	 * Get supported payment methods
163
	 *
164
	 * @see Pronamic_WP_Pay_Gateway::get_supported_payment_methods()
165
	 */
166
	public function get_supported_payment_methods() {
167
		return array(
168
			PaymentMethods::BANCONTACT,
169
			PaymentMethods::BANK_TRANSFER,
170
			PaymentMethods::BELFIUS,
171
			PaymentMethods::BITCOIN,
172
			PaymentMethods::CREDIT_CARD,
173
			PaymentMethods::DIRECT_DEBIT,
174
			PaymentMethods::DIRECT_DEBIT_BANCONTACT,
175
			PaymentMethods::DIRECT_DEBIT_IDEAL,
176
			PaymentMethods::DIRECT_DEBIT_SOFORT,
177
			PaymentMethods::IDEAL,
178
			PaymentMethods::KBC,
179
			PaymentMethods::PAYPAL,
180
			PaymentMethods::SOFORT,
181
		);
182
	}
183
184
	/**
185
	 * Get webhook URL for Mollie.
186
	 *
187
	 * @return string
188
	 */
189
	private function get_webhook_url() {
190
		$url = home_url( '/' );
0 ignored issues
show
Bug introduced by
The function home_url was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

190
		$url = /** @scrutinizer ignore-call */ home_url( '/' );
Loading history...
191
192
		$host = wp_parse_url( $url, PHP_URL_HOST );
0 ignored issues
show
Bug introduced by
The function wp_parse_url was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

192
		$host = /** @scrutinizer ignore-call */ wp_parse_url( $url, PHP_URL_HOST );
Loading history...
193
194
		if ( 'localhost' === $host ) {
195
			// Mollie doesn't allow localhost
196
			return null;
197
		} elseif ( '.dev' === substr( $host, -4 ) ) {
198
			// Mollie doesn't allow the TLD .dev
199
			return null;
200
		}
201
202
		$url = add_query_arg( 'mollie_webhook', '', $url );
0 ignored issues
show
Bug introduced by
The function add_query_arg was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

202
		$url = /** @scrutinizer ignore-call */ add_query_arg( 'mollie_webhook', '', $url );
Loading history...
203
204
		return $url;
205
	}
206
207
	/**
208
	 * Start
209
	 *
210
	 * @see Pronamic_WP_Pay_Gateway::start()
211
	 */
212
	public function start( Payment $payment ) {
213
		$request = new PaymentRequest();
214
215
		$request->amount       = $payment->get_amount()->get_amount();
216
		$request->description  = $payment->get_description();
217
		$request->redirect_url = $payment->get_return_url();
218
		$request->webhook_url  = $this->get_webhook_url();
219
		$request->locale       = LocaleHelper::transform( $payment->get_language() );
220
221
		// Issuer.
222
		if ( Methods::IDEAL === $request->method ) {
223
			// If payment method is iDEAL we set the user chosen issuer ID.
224
			$request->issuer = $payment->get_issuer();
225
		}
226
227
		// Customer ID.
228
		$customer_id = $this->get_customer_id_for_payment( $payment );
229
230
		if ( ! empty( $customer_id ) ) {
231
			$request->customer_id = $customer_id;
0 ignored issues
show
Documentation Bug introduced by
It seems like $customer_id can also be of type boolean. However, the property $customer_id is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
232
		}
233
234
		// Payment method.
235
		$payment_method = $payment->get_method();
236
237
		// Subscription.
238
		$subscription = $payment->get_subscription();
239
240
		if ( $subscription && PaymentMethods::is_recurring_method( $payment_method ) ) {
241
			$request->recurring_type = $payment->get_recurring() ? Recurring::RECURRING : Recurring::FIRST;
242
243
			if ( Recurring::FIRST === $request->recurring_type ) {
244
				$payment_method = PaymentMethods::get_first_payment_method( $payment_method );
245
			}
246
247
			if ( Recurring::RECURRING === $request->recurring_type ) {
248
				$payment->set_action_url( $payment->get_return_url() );
249
			}
250
		}
251
252
		// Leap of faith if the WordPress payment method could not transform to a Mollie method?
253
		$request->method = Methods::transform( $payment_method, $payment_method );
254
255
		// Create payment.
256
		$result = $this->client->create_payment( $request );
257
258
		if ( ! $result ) {
259
			$this->error = $this->client->get_error();
260
261
			return false;
262
		}
263
264
		// Set transaction ID.
265
		if ( isset( $result->id ) ) {
266
			$payment->set_transaction_id( $result->id );
267
		}
268
269
		// Set status
270
		if ( isset( $result->status ) ) {
271
			$payment->set_status( Statuses::transform( $result->status ) );
272
		}
273
274
		// Set action URL.
275
		if ( isset( $result->links, $result->links->paymentUrl ) ) {
276
			$payment->set_action_url( $result->links->paymentUrl );
277
		}
278
	}
279
280
	/**
281
	 * Update status of the specified payment
282
	 *
283
	 * @param Payment $payment
284
	 */
285
	public function update_status( Payment $payment ) {
286
		$mollie_payment = $this->client->get_payment( $payment->get_transaction_id() );
287
288
		if ( ! $mollie_payment ) {
289
			$payment->set_status( Core_Statuses::FAILURE );
290
291
			$this->error = $this->client->get_error();
292
293
			return;
294
		}
295
296
		$payment->set_status( Statuses::transform( $mollie_payment->status ) );
297
298
		if ( isset( $mollie_payment->details ) ) {
299
			$details = $mollie_payment->details;
300
301
			if ( isset( $details->consumerName ) ) {
302
				$payment->set_consumer_name( $details->consumerName );
303
			}
304
305
			if ( isset( $details->cardHolder ) ) {
306
				$payment->set_consumer_name( $details->cardHolder );
307
			}
308
309
			if ( isset( $details->consumerAccount ) ) {
310
				$payment->set_consumer_iban( $details->consumerAccount );
311
			}
312
313
			if ( isset( $details->consumerBic ) ) {
314
				$payment->set_consumer_bic( $details->consumerBic );
315
			}
316
		}
317
	}
318
319
	/**
320
	 * Get Mollie customer ID for payment.
321
	 *
322
	 * @param Payment $payment Payment.
323
	 *
324
	 * @return bool|string
325
	 */
326
	private function get_customer_id_for_payment( Payment $payment ) {
327
		// Get Mollie customer ID from user meta.
328
		$customer_id = $this->get_customer_id_by_wp_user_id( $payment->user_id );
329
330
		if ( Core_Recurring::FIRST === $payment->recurring_type ) {
331
			// Create new customer if the customer does not exist at Mollie.
332
			if ( empty( $customer_id ) || ! $this->client->get_customer( $customer_id ) ) {
333
				$customer_id = $this->client->create_customer( $payment->get_email(), $payment->get_customer_name() );
334
335
				$this->update_wp_user_customer_id( $payment->user_id, $customer_id );
336
			}
337
338
			// Temporarily store customer ID in subscription meta for guest users.
339
			if ( empty( $payment->user_id ) && ! empty( $customer_id ) ) {
340
				$subscription = $payment->get_subscription();
341
342
				if ( $subscription ) {
343
					$subscription->set_meta( 'mollie_customer_id', $customer_id );
344
				}
345
			}
346
		}
347
348
		// Try to move customer ID from subscription meta to user.
349
		if ( empty( $customer_id ) ) {
350
			// Move customer ID from subscription meta to user meta.
351
			$this->move_customer_id_to_wp_user( $payment );
352
353
			// Get customer ID from user meta, again.
354
			$customer_id = $this->get_customer_id_by_wp_user_id( $payment->user_id );
355
		}
356
357
		// Try to get customer ID from subscription meta.
358
		if ( empty( $customer_id ) && $payment->get_subscription() ) {
359
			$customer_id = $payment->get_subscription()->get_meta( 'mollie_customer_id' );
360
		}
361
362
		return $customer_id;
363
	}
364
365
	/**
366
	 * Get Mollie customer ID by the specified WordPress user ID.
367
	 *
368
	 * @param int $user_id WordPress user ID.
369
	 *
370
	 * @return string
371
	 */
372
	private function get_customer_id_by_wp_user_id( $user_id ) {
373
		if ( empty( $user_id ) ) {
374
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
375
		}
376
377
		return get_user_meta( $user_id, $this->meta_key_customer_id, true );
0 ignored issues
show
Bug introduced by
The function get_user_meta was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

377
		return /** @scrutinizer ignore-call */ get_user_meta( $user_id, $this->meta_key_customer_id, true );
Loading history...
378
	}
379
380
	/**
381
	 * Update Mollie customer ID meta for WordPress user.
382
	 *
383
	 * @param int    $user_id     WordPress user ID.
384
	 * @param string $customer_id Mollie Customer ID.
385
	 *
386
	 * @return bool
387
	 */
388
	private function update_wp_user_customer_id( $user_id, $customer_id ) {
389
		if ( empty( $user_id ) || empty( $customer_id ) ) {
390
			return false;
391
		}
392
393
		update_user_meta( $user_id, $this->meta_key_customer_id, $customer_id );
0 ignored issues
show
Bug introduced by
The function update_user_meta was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

393
		/** @scrutinizer ignore-call */ 
394
  update_user_meta( $user_id, $this->meta_key_customer_id, $customer_id );
Loading history...
394
	}
395
396
	/**
397
	 * Move Mollie customer ID from subscription meta to WordPress user meta.
398
	 *
399
	 * @param Payment $payment Payment.
400
	 *
401
	 * @return void
402
	 */
403
	public function move_customer_id_to_wp_user( Payment $payment ) {
404
		if ( $this->config->id !== $payment->config_id ) {
405
			return;
406
		}
407
408
		$subscription = $payment->get_subscription();
409
410
		if ( ! $subscription || empty( $subscription->user_id ) ) {
411
			return;
412
		}
413
414
		// Get customer ID from subscription meta.
415
		$customer_id = $subscription->get_meta( 'mollie_customer_id' );
416
417
		if ( ! empty( $customer_id ) && ! empty( $subscription->user_id ) ) {
418
			// Set customer ID as user meta.
419
			$this->update_wp_user_customer_id( $subscription->user_id, $customer_id );
420
421
			// Delete customer ID from subscription meta.
422
			$subscription->set_meta( 'mollie_customer_id', null );
423
		}
424
	}
425
}
426