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