Failed Conditions
Push — master ( b549f2...090668 )
by Remco
10:21 queued 03:46
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 20
CRAP Score 5.2

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
ccs 20
cts 25
cp 0.8
crap 5.2
rs 9.1768

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