Passed
Push — master ( 63237f...7c0386 )
by Reüel
06:18 queued 10s
created

Client::get_customer()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

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