Failed Conditions
Push — develop ( 3861b4...1ec521 )
by Reüel
07:46
created

Client   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 362
Duplicated Lines 0 %

Test Coverage

Coverage 34.19%

Importance

Changes 0
Metric Value
wmc 42
eloc 109
dl 0
loc 362
c 0
b 0
f 0
ccs 40
cts 117
cp 0.3419
rs 9.0399

14 Methods

Rating   Name   Duplication   Size   Complexity  
A get_error() 0 2 1
A __construct() 0 2 1
A send_request() 0 41 4
A set_mode() 0 2 1
A get_payments() 0 2 1
A get_payment() 0 6 2
A create_payment() 0 2 1
A get_issuers() 0 19 4
A get_payment_methods() 0 25 5
B get_first_valid_mandate_datetime() 0 40 7
A has_valid_mandate() 0 20 5
A create_customer() 0 24 4
A get_mandates() 0 6 2
A get_customer() 0 16 4

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
namespace Pronamic\WordPress\Pay\Gateways\Mollie;
4
5
use Pronamic\WordPress\DateTime\DateTime;
6
use Pronamic\WordPress\Pay\Core\XML\Security;
7
use WP_Error;
8
9
/**
10
 * Title: Mollie
11
 * Description:
12
 * Copyright: 2005-2019 Pronamic
13
 * Company: Pronamic
14
 *
15
 * @author  Remco Tolsma
16
 * @version 2.1.0
17
 * @since   1.0.0
18
 */
19
class Client {
20
	/**
21
	 * Mollie API endpoint URL
22
	 *
23
	 * @var string
24
	 */
25
	const API_URL = 'https://api.mollie.com/v2/';
26
27
	/**
28
	 * Mollie API Key ID
29
	 *
30
	 * @var string
31
	 */
32
	private $api_key;
33
34
	/**
35
	 * Mode
36
	 *
37
	 * @since 1.1.9
38
	 * @var string
39
	 */
40
	private $mode;
41
42
	/**
43
	 * Error
44
	 *
45
	 * @var WP_Error
46
	 */
47
	private $error;
48
49
	/**
50
	 * Constructs and initializes an Mollie client object
51
	 *
52
	 * @param string $api_key Mollie API key.
53
	 */
54 40
	public function __construct( $api_key ) {
55 40
		$this->api_key = $api_key;
56 40
	}
57
58
	/**
59
	 * Set mode
60
	 *
61
	 * @since 1.1.9
62
	 * @param string $mode Mode (test or live).
63
	 */
64 40
	public function set_mode( $mode ) {
65 40
		$this->mode = $mode;
66 40
	}
67
68
	/**
69
	 * Error
70
	 *
71
	 * @return WP_Error
72
	 */
73 5
	public function get_error() {
74 5
		return $this->error;
75
	}
76
77
	/**
78
	 * Send request with the specified action and parameters
79
	 *
80
	 * @param string $end_point              Requested endpoint.
81
	 * @param string $method                 HTTP method to use.
82
	 * @param array  $data                   Request data.
83
	 * @param int    $expected_response_code Expected response code.
84
	 *
85
	 * @return bool|object
86
	 */
87 10
	private function send_request( $end_point, $method = 'GET', array $data = array(), $expected_response_code = 200 ) {
88
		// Request.
89 10
		$url = self::API_URL . $end_point;
90
91 10
		$response = wp_remote_request(
92 10
			$url,
93
			array(
94 10
				'method'  => $method,
95
				'headers' => array(
96 10
					'Authorization' => 'Bearer ' . $this->api_key,
97
				),
98 10
				'body'    => $data,
99
			)
100
		);
101
102
		// Response code.
103 10
		$response_code = wp_remote_retrieve_response_code( $response );
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type WP_Error; however, parameter $response of wp_remote_retrieve_response_code() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

103
		$response_code = wp_remote_retrieve_response_code( /** @scrutinizer ignore-type */ $response );
Loading history...
104
105 10
		if ( $expected_response_code != $response_code ) { // WPCS: loose comparison ok.
106 10
			$this->error = new WP_Error( 'mollie_error', 'Unexpected response code.' );
107
		}
108
109
		// Body.
110 10
		$body = wp_remote_retrieve_body( $response );
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type WP_Error; however, parameter $response of wp_remote_retrieve_body() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

110
		$body = wp_remote_retrieve_body( /** @scrutinizer ignore-type */ $response );
Loading history...
111
112 10
		$data = json_decode( $body );
113
114 10
		if ( ! is_object( $data ) ) {
115
			$this->error = new WP_Error( 'mollie_error', 'Could not parse response.' );
116
117
			return false;
118
		}
119
120
		// Mollie error.
121 10
		if ( isset( $data->status, $data->title, $data->detail ) ) {
122 10
			$this->error = new \WP_Error( 'mollie_error', $data->detail, $data );
123
124 10
			return false;
125
		}
126
127
		return $data;
128
	}
129
130
	/**
131
	 * Create payment.
132
	 *
133
	 * @param PaymentRequest $request Payment request.
134
	 *
135
	 * @return bool|object
136
	 */
137
	public function create_payment( PaymentRequest $request ) {
138
		return $this->send_request( 'payments', 'POST', $request->get_array(), 201 );
139
	}
140
141
	/**
142
	 * Get payments.
143
	 *
144
	 * @return bool|object
145
	 */
146
	public function get_payments() {
147
		return $this->send_request( 'payments', 'GET' );
148
	}
149
150
	/**
151
	 * Get payment.
152
	 *
153
	 * @param string $payment_id Payment ID.
154
	 *
155
	 * @return bool|object
156
	 */
157
	public function get_payment( $payment_id ) {
158
		if ( empty( $payment_id ) ) {
159
			return false;
160
		}
161
162
		return $this->send_request( 'payments/' . $payment_id, 'GET' );
163
	}
164
165
	/**
166
	 * Get issuers
167
	 *
168
	 * @return array|bool
169
	 */
170 3
	public function get_issuers() {
171 3
		$response = $this->send_request( 'methods/ideal?include=issuers', 'GET' );
172
173 3
		if ( false === $response ) {
174 3
			return false;
175
		}
176
177
		$issuers = array();
178
179
		if ( isset( $response->issuers ) ) {
180
			foreach ( $response->issuers as $issuer ) {
181
				$id   = Security::filter( $issuer->id );
182
				$name = Security::filter( $issuer->name );
183
184
				$issuers[ $id ] = $name;
185
			}
186
		}
187
188
		return $issuers;
189
	}
190
191
	/**
192
	 * Get payment methods
193
	 *
194
	 * @param string $sequence_type Sequence type.
195
	 *
196
	 * @return array|bool
197
	 */
198 2
	public function get_payment_methods( $sequence_type = '' ) {
199 2
		$data = array();
200
201 2
		if ( '' !== $sequence_type ) {
202 2
			$data['sequenceType'] = $sequence_type;
203
		}
204
205 2
		$response = $this->send_request( 'methods', 'GET', $data );
206
207 2
		if ( false === $response ) {
208 2
			return false;
209
		}
210
211
		$payment_methods = array();
212
213
		if ( isset( $response->_embedded->methods ) ) {
214
			foreach ( $response->_embedded->methods as $payment_method ) {
215
				$id   = Security::filter( $payment_method->id );
216
				$name = Security::filter( $payment_method->description );
217
218
				$payment_methods[ $id ] = $name;
219
			}
220
		}
221
222
		return $payment_methods;
223
	}
224
225
	/**
226
	 * Create customer.
227
	 *
228
	 * @since 1.1.6
229
	 *
230
	 * @param string $email Customer email address.
231
	 * @param string $name  Customer name.
232
	 *
233
	 * @return string|bool
234
	 */
235
	public function create_customer( $email, $name ) {
236
		if ( empty( $email ) ) {
237
			return false;
238
		}
239
240
		$response = $this->send_request(
241
			'customers',
242
			'POST',
243
			array(
244
				'name'  => $name,
245
				'email' => $email,
246
			),
247
			201
248
		);
249
250
		if ( false === $response ) {
251
			return false;
252
		}
253
254
		if ( ! isset( $response->id ) ) {
255
			return false;
256
		}
257
258
		return $response->id;
259
	}
260
261
	/**
262
	 * Get customer.
263
	 *
264
	 * @param string $customer_id Mollie customer ID.
265
	 *
266
	 * @since unreleased
267
	 *
268
	 * @return object|bool
269
	 */
270 5
	public function get_customer( $customer_id ) {
271 5
		if ( empty( $customer_id ) ) {
272
			return false;
273
		}
274
275 5
		$response = $this->send_request( 'customers/' . $customer_id, 'GET', array(), 200 );
276
277 5
		if ( false === $response ) {
278 5
			return false;
279
		}
280
281
		if ( is_wp_error( $this->error ) ) {
282
			return false;
283
		}
284
285
		return $response;
286
	}
287
288
	/**
289
	 * Get mandates for customer.
290
	 *
291
	 * @param string $customer_id Mollie customer ID.
292
	 *
293
	 * @return object|bool
294
	 */
295
	public function get_mandates( $customer_id ) {
296
		if ( '' === $customer_id ) {
297
			return false;
298
		}
299
300
		return $this->send_request( 'customers/' . $customer_id . '/mandates?limit=250', 'GET' );
301
	}
302
303
	/**
304
	 * Is there a valid mandate for customer?
305
	 *
306
	 * @param string      $customer_id    Mollie customer ID.
307
	 * @param string|null $payment_method Payment method to find mandates for.
308
	 *
309
	 * @return boolean
310
	 */
311
	public function has_valid_mandate( $customer_id, $payment_method = null ) {
312
		$mandates = $this->get_mandates( $customer_id );
313
314
		if ( ! $mandates ) {
315
			return false;
316
		}
317
318
		$mollie_method = Methods::transform( $payment_method );
319
320
		foreach ( $mandates->_embedded as $mandate ) {
321
			if ( $mollie_method !== $mandate->method ) {
322
				continue;
323
			}
324
325
			if ( 'valid' === $mandate->status ) {
326
				return true;
327
			}
328
		}
329
330
		return false;
331
	}
332
333
	/**
334
	 * Get formatted date and time of first valid mandate.
335
	 *
336
	 * @param string $customer_id    Mollie customer ID.
337
	 * @param string $payment_method Payment method.
338
	 *
339
	 * @return null|DateTime
340
	 */
341
	public function get_first_valid_mandate_datetime( $customer_id, $payment_method = null ) {
342
		$mandates = $this->get_mandates( $customer_id );
343
344
		if ( ! $mandates ) {
345
			return null;
346
		}
347
348
		$mollie_method = Methods::transform( $payment_method );
349
350
		foreach ( $mandates->_embedded as $mandate ) {
351
			if ( $mollie_method !== $mandate->method ) {
352
				continue;
353
			}
354
355
			if ( 'valid' !== $mandate->status ) {
356
				continue;
357
			}
358
359
			if ( ! isset( $valid_mandates ) ) {
360
				$valid_mandates = array();
361
			}
362
363
			// @codingStandardsIgnoreStart
364
			$valid_mandates[ $mandate->createdAt ] = $mandate;
365
			// @codingStandardsIgnoreEnd
366
		}
367
368
		if ( isset( $valid_mandates ) ) {
369
			ksort( $valid_mandates );
370
371
			$mandate = array_shift( $valid_mandates );
372
373
			// @codingStandardsIgnoreStart
374
			$create_date = new DateTime( $mandate->createdAt );
375
			// @codingStandardsIgnoreEnd
376
377
			return $create_date;
378
		}
379
380
		return null;
381
	}
382
}
383