Test Failed
Push — feature/mollie-connect ( e02179 )
by Reüel
07:33
created

Client::set_api_key()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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