Failed Conditions
Push — develop ( 9a2c77...02c31b )
by Remco
12:58 queued 06:52
created

Client::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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