Test Failed
Push — develop ( 21667b...724bc2 )
by Remco
05:38 queued 13s
created

Client::get_current_profile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
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