Test Failed
Push — develop ( 5d1492...ddf0fb )
by Remco
03:56
created

Client::get_payment_methods()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 9.2876

Importance

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