Test Failed
Push — develop ( b6437d...f2e903 )
by Reüel
03:57
created

Client::send_request()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 52
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 5.0042

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 5
eloc 27
c 5
b 0
f 0
nc 5
nop 3
dl 0
loc 52
rs 9.1768
ccs 17
cts 18
cp 0.9444
crap 5.0042

How to fix   Long Method   

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 client.
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2020 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 Pronamic\WordPress\DateTime\DateTime;
14
use Pronamic\WordPress\Pay\Banks\BankAccountDetails;
15
use Pronamic\WordPress\Pay\Core\XML\Security;
16
17
/**
18
 * Title: Mollie
19
 * Description:
20
 * Copyright: 2005-2020 Pronamic
21
 * Company: Pronamic
22
 *
23
 * @author  Remco Tolsma
24
 * @version 2.0.9
25
 * @since   1.0.0
26
 */
27
class Client {
28
	/**
29
	 * Mollie API endpoint URL
30
	 *
31
	 * @var string
32
	 */
33
	const API_URL = 'https://api.mollie.com/v2/';
34
35
	/**
36
	 * Mollie API Key ID
37
	 *
38
	 * @var string
39
	 */
40
	private $api_key;
41
42
	/**
43
	 * Constructs and initializes an Mollie client object
44
	 *
45
	 * @param string $api_key Mollie API key.
46
	 */
47
	public function __construct( $api_key ) {
48
		$this->api_key = $api_key;
49
	}
50
51
	/**
52
	 * Send request with the specified action and parameters
53
	 *
54 39
	 * @param string                            $url    URL.
55 39
	 * @param string                            $method HTTP method to use.
56 39
	 * @param array<string, string|object|null> $data   Request data.
57
	 * @return object
58
	 * @throws Error Throws Error when Mollie error occurs.
59
	 * @throws \Exception Throws exception when error occurs.
60
	 */
61
	public function send_request( $url, $method = 'GET', array $data = array() ) {
62
		// Request.
63
		$response = wp_remote_request(
64
			$url,
65 39
			array(
66 39
				'method'  => $method,
67 39
				'headers' => array(
68
					'Authorization' => 'Bearer ' . $this->api_key,
69
				),
70
				'body'    => $data,
71
			)
72
		);
73
74
		if ( $response instanceof \WP_Error ) {
75
			throw new \Exception( $response->get_error_message() );
76
		}
77
78
		// Body.
79 9
		$body = wp_remote_retrieve_body( $response );
80
81 9
		$data = json_decode( $body );
82
83 9
		// JSON error.
84 9
		$json_error = \json_last_error();
85
86 9
		if ( \JSON_ERROR_NONE !== $json_error ) {
87
			throw new \Exception(
88 9
				\sprintf( 'JSON: %s', \json_last_error_msg() ),
89
				$json_error
90 9
			);
91
		}
92
93
		// Object.
94 9
		if ( ! \is_object( $data ) ) {
95
			$code = \wp_remote_retrieve_response_code( $response );
96
97
			throw new \Exception(
98
				\sprintf( 'Could not JSON decode Mollie response to an object (HTTP Status Code: %s).', $code ),
99 9
				\intval( $code )
100
			);
101 9
		}
102
103
		// Mollie error from JSON response.
104 9
		if ( isset( $data->status, $data->title, $data->detail ) ) {
105
			throw new Error(
106 9
				$data->status,
107 2
				$data->title,
108 2
				$data->detail
109
			);
110
		}
111
112
		return $data;
113
	}
114 9
115
	/**
116
	 * Get URL.
117
	 *
118
	 * @return string $endpoint URL endpoint.
119
	 */
120
	public function get_url( $endpoint ) {
121
		$url = self::API_URL . $endpoint;
122
123
		return $url;
124 9
	}
125 3
126 3
	/**
127 3
	 * Send request to endpoint.
128 3
	 *
129
	 * @param string                            $url    URL.
130
	 * @param string                            $method HTTP method to use.
131
	 * @param array<string, string|object|null> $data   Request data.
132 6
	 * @return object
133
	 */
134
	public function send_request_to_endpoint( $endpoint, $method = 'GET', array $data = array() ) {
135
		return $this->send_request( $this->get_url( $endpoint ), $method, $data );
136
	}
137
138
	/**
139
	 * Get profile.
140
	 *
141
	 * @param string $profile Mollie profile ID.
142
	 * @return object
143
	 * @throws Error
144
	 */
145
	public function get_profile( $profile ) {
146
		return $this->send_request_to_endpoint( 'profiles/' . $profile, 'GET' );
147
	}
148
149
	/**
150
	 * Get current profile.
151
	 *
152
	 * @return object
153
	 * @throws Error
154
	 */
155
	public function get_current_profile() {
156
		return $this->get_profile( 'me' );
157
	}
158
159
	/**
160
	 * Create payment.
161
	 *
162
	 * @param PaymentRequest $request Payment request.
163
	 * @return object
164
	 */
165
	public function create_payment( PaymentRequest $request ) {
166
		return $this->send_request_to_endpoint( 'payments', 'POST', $request->get_array() );
167
	}
168
169
	/**
170
	 * Get payments.
171
	 *
172
	 * @return bool|object
173
	 */
174
	public function get_payments() {
175 3
		return $this->send_request_to_endpoint( 'payments', 'GET' );
176 3
	}
177
178
	/**
179
	 * Get payment.
180
	 *
181
	 * @param string $payment_id Payment ID.
182
	 *
183
	 * @return object
184
	 * @throws \InvalidArgumentException Throws exception on empty payment ID argument.
185
	 */
186
	public function get_payment( $payment_id ) {
187
		if ( empty( $payment_id ) ) {
188
			throw new \InvalidArgumentException( 'Mollie payment ID can not be empty string.' );
189
		}
190
191
		return $this->send_request_to_endpoint( 'payments/' . $payment_id, 'GET' );
192
	}
193
194
	/**
195
	 * Get issuers
196
	 *
197
	 * @return array<string>
198
	 */
199
	public function get_issuers() {
200 2
		$response = $this->send_request_to_endpoint( 'methods/ideal?include=issuers', 'GET' );
201 2
202
		$issuers = array();
203 2
204 2
		if ( isset( $response->issuers ) ) {
205
			foreach ( $response->issuers as $issuer ) {
206
				$id   = Security::filter( $issuer->id );
207 2
				$name = Security::filter( $issuer->name );
208
209 2
				$issuers[ $id ] = $name;
210
			}
211 2
		}
212
213
		return $issuers;
214
	}
215 2
216 2
	/**
217 2
	 * Get payment methods
218 2
	 *
219
	 * @param string $sequence_type Sequence type.
220 2
	 *
221
	 * @return array<string>
222
	 * @throws \Exception Throws exception for methods on failed request or invalid response.
223
	 */
224 2
	public function get_payment_methods( $sequence_type = '' ) {
225
		$data = array();
226
227
		if ( '' !== $sequence_type ) {
228
			$data['sequenceType'] = $sequence_type;
229
		}
230
231
		$response = $this->send_request_to_endpoint( 'methods', 'GET', $data );
232
233
		$payment_methods = array();
234
235
		if ( ! isset( $response->_embedded ) ) {
236
			throw new \Exception( 'No embedded data in Mollie response.' );
237
		}
238
239
		if ( isset( $response->_embedded->methods ) ) {
240
			foreach ( $response->_embedded->methods as $payment_method ) {
241
				$id   = Security::filter( $payment_method->id );
242
				$name = Security::filter( $payment_method->description );
243
244
				$payment_methods[ $id ] = $name;
245
			}
246
		}
247
248
		return $payment_methods;
249
	}
250
251
	/**
252
	 * Create customer.
253
	 *
254
	 * @param Customer $customer Customer.
255
	 * @return Customer
256
	 * @throws Error Throws Error when Mollie error occurs.
257
	 * @since 1.1.6
258
	 */
259
	public function create_customer( Customer $customer ) {
260
		$response = $this->send_request_to_endpoint(
261
			'customers',
262
			'POST',
263
			$customer->get_array()
264
		);
265
266 4
		$customer->set_id( $response->id );
267 4
268
		return $customer;
269
	}
270
271
	/**
272 4
	 * Get customer.
273
	 *
274
	 * @param string $customer_id Mollie customer ID.
275
	 *
276
	 * @return null|object
277
	 * @throws \InvalidArgumentException Throws exception on empty customer ID argument.
278
	 * @throws Error Throws Error when Mollie error occurs.
279
	 */
280
	public function get_customer( $customer_id ) {
281
		if ( empty( $customer_id ) ) {
282
			throw new \InvalidArgumentException( 'Mollie customer ID can not be empty string.' );
283
		}
284
285
		try {
286
			return $this->send_request_to_endpoint( 'customers/' . $customer_id, 'GET' );
287
		} catch ( Error $error ) {
288
			if ( 404 === $error->get_status() ) {
289
				return null;
290
			}
291
292
			throw $error;
293
		}
294
	}
295
296
	/**
297
	 * Create mandate.
298
	 *
299
	 * @param Customer $customer Customer.
300
	 * @return object
301
	 * @throws Error Throws Error when Mollie error occurs.
302
	 * @since unreleased
303
	 */
304
	public function create_mandate( $customer_id, BankAccountDetails $consumer_bank_details ) {
305
		$response = $this->send_request_to_endpoint(
306
			'customers/' . $customer_id . '/mandates',
307
			'POST',
308
			array(
309
				'method'          => Methods::DIRECT_DEBIT,
310
				'consumerName'    => $consumer_bank_details->get_name(),
311
				'consumerAccount' => $consumer_bank_details->get_iban(),
312
			)
313
		);
314
315
		return $response;
316
	}
317
318
	/**
319
	 * Get mandates for customer.
320
	 *
321
	 * @param string $customer_id Mollie customer ID.
322
	 * @return object
323
	 * @throws \InvalidArgumentException Throws exception on empty customer ID argument.
324
	 */
325
	public function get_mandates( $customer_id ) {
326
		if ( '' === $customer_id ) {
327
			throw new \InvalidArgumentException( 'Mollie customer ID can not be empty string.' );
328
		}
329
330
		return $this->send_request_to_endpoint( 'customers/' . $customer_id . '/mandates?limit=250', 'GET' );
331
	}
332
333
	/**
334
	 * Is there a valid mandate for customer?
335
	 *
336
	 * @param string      $customer_id    Mollie customer ID.
337
	 * @param string|null $payment_method Payment method to find mandates for.
338
	 *
339
	 * @return boolean
340
	 * @throws \Exception Throws exception for mandates on failed request or invalid response.
341
	 */
342
	public function has_valid_mandate( $customer_id, $payment_method = null, $search = null ) {
343
		$mandates = $this->get_mandates( $customer_id );
344
345
		$mollie_method = Methods::transform( $payment_method );
346
347
		if ( ! isset( $mandates->_embedded ) ) {
348
			throw new \Exception( 'No embedded data in Mollie response.' );
349
		}
350
351
		foreach ( $mandates->_embedded->mandates as $mandate ) {
352
			if ( null !== $mollie_method && $mollie_method !== $mandate->method ) {
353
				continue;
354
			}
355
356
			// Search consumer account or card number.
357
			if ( null !== $search ) {
358
				switch ( $mollie_method ) {
359
					case Methods::DIRECT_DEBIT:
360
					case Methods::PAYPAL:
361
						if ( $search !== $mandate->details->consumerAccount ) {
362
							continue 2;
363
						}
364
365
						break;
366
					case Methods::CREDITCARD:
367
						if ( $search !== $mandate->details->cardNumber ) {
368
							continue 2;
369
						}
370
371
						break;
372
				}
373
			}
374
375
			if ( 'valid' === $mandate->status ) {
376
				return $mandate->id;
377
			}
378
		}
379
380
		return false;
381
	}
382
383
	/**
384
	 * Get formatted date and time of first valid mandate.
385
	 *
386
	 * @param string $customer_id    Mollie customer ID.
387
	 * @param string $payment_method Payment method.
388
	 *
389
	 * @return null|DateTime
390
	 * @throws \Exception Throws exception for mandates on failed request or invalid response.
391
	 */
392
	public function get_first_valid_mandate_datetime( $customer_id, $payment_method = null ) {
393
		$mandates = $this->get_mandates( $customer_id );
394
395
		$mollie_method = Methods::transform( $payment_method );
396
397
		if ( ! isset( $mandates->_embedded ) ) {
398
			throw new \Exception( 'No embedded data in Mollie response.' );
399
		}
400
401
		foreach ( $mandates->_embedded->mandates as $mandate ) {
402
			if ( $mollie_method !== $mandate->method ) {
403
				continue;
404
			}
405
406
			if ( 'valid' !== $mandate->status ) {
407
				continue;
408
			}
409
410
			if ( ! isset( $valid_mandates ) ) {
411
				$valid_mandates = array();
412
			}
413
414
			// @codingStandardsIgnoreStart
415
			$valid_mandates[ $mandate->createdAt ] = $mandate;
416
			// @codingStandardsIgnoreEnd
417
		}
418
419
		if ( isset( $valid_mandates ) ) {
420
			ksort( $valid_mandates );
421
422
			$mandate = array_shift( $valid_mandates );
423
424
			// @codingStandardsIgnoreStart
425
			$create_date = new DateTime( $mandate->createdAt );
426
			// @codingStandardsIgnoreEnd
427
428
			return $create_date;
429
		}
430
431
		return null;
432
	}
433
}
434