Test Failed
Push — master ( 272cf6...4fb2a3 )
by Remco
21:31 queued 14:28
created

src/Client.php (2 issues)

Labels
Severity
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.1.0
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 40
	 */
55 40
	private $error;
56 40
57
	/**
58
	 * Constructs and initializes an Mollie client object
59
	 *
60
	 * @param string $api_key Mollie API key.
61
	 */
62
	public function __construct( $api_key ) {
63
		$this->api_key = $api_key;
64 40
	}
65 40
66 40
	/**
67
	 * Set mode
68
	 *
69
	 * @since 1.1.9
70
	 * @param string $mode Mode (test or live).
71
	 */
72
	public function set_mode( $mode ) {
73 5
		$this->mode = $mode;
74 5
	}
75
76
	/**
77
	 * Error
78
	 *
79
	 * @return WP_Error
80
	 */
81
	public function get_error() {
82
		return $this->error;
83
	}
84
85
	/**
86
	 * Send request with the specified action and parameters
87 10
	 *
88
	 * @param string $end_point              Requested endpoint.
89 10
	 * @param string $method                 HTTP method to use.
90
	 * @param array  $data                   Request data.
91 10
	 * @param int    $expected_response_code Expected response code.
92 10
	 *
93
	 * @return bool|object
94 10
	 */
95
	private function send_request( $end_point, $method = 'GET', array $data = array(), $expected_response_code = 200 ) {
96 10
		// Request.
97
		$url = self::API_URL . $end_point;
98 10
99
		$response = wp_remote_request(
100
			$url,
101
			array(
102
				'method'  => $method,
103 10
				'headers' => array(
104
					'Authorization' => 'Bearer ' . $this->api_key,
105 10
				),
106 10
				'body'    => $data,
107
			)
108
		);
109
110 10
		// Response code.
111
		$response_code = wp_remote_retrieve_response_code( $response );
0 ignored issues
show
It seems like $response can also be of type WP_Error; however, parameter $response of wp_remote_retrieve_response_code() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

111
		$response_code = wp_remote_retrieve_response_code( /** @scrutinizer ignore-type */ $response );
Loading history...
112 10
113
		// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
114 10
		if ( $expected_response_code != $response_code ) {
115
			$this->error = new WP_Error( 'mollie_error', 'Unexpected response code.' );
116
		}
117
118
		// Body.
119
		$body = wp_remote_retrieve_body( $response );
0 ignored issues
show
It seems like $response can also be of type WP_Error; however, parameter $response of wp_remote_retrieve_body() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

119
		$body = wp_remote_retrieve_body( /** @scrutinizer ignore-type */ $response );
Loading history...
120
121 10
		$data = json_decode( $body );
122 10
123
		if ( ! is_object( $data ) ) {
124 10
			$this->error = new WP_Error( 'mollie_error', 'Could not parse response.' );
125
126
			return false;
127
		}
128
129
		// Mollie error.
130
		if ( isset( $data->status, $data->title, $data->detail ) ) {
131
			$this->error = new \WP_Error( 'mollie_error', $data->detail, $data );
132
133
			return false;
134
		}
135
136
		return $data;
137
	}
138
139
	/**
140
	 * Create payment.
141
	 *
142
	 * @param PaymentRequest $request Payment request.
143
	 *
144
	 * @return bool|object
145
	 */
146
	public function create_payment( PaymentRequest $request ) {
147
		return $this->send_request( 'payments', 'POST', $request->get_array(), 201 );
148
	}
149
150
	/**
151 3
	 * Get payments.
152 3
	 *
153
	 * @return bool|object
154 3
	 */
155 3
	public function get_payments() {
156
		return $this->send_request( 'payments', 'GET' );
157
	}
158
159
	/**
160
	 * Get payment.
161
	 *
162
	 * @param string $payment_id Payment ID.
163
	 *
164
	 * @return bool|object
165
	 */
166
	public function get_payment( $payment_id ) {
167
		if ( empty( $payment_id ) ) {
168
			return false;
169
		}
170
171
		return $this->send_request( 'payments/' . $payment_id, 'GET' );
172
	}
173
174
	/**
175
	 * Get issuers
176
	 *
177
	 * @return array|bool
178
	 */
179
	public function get_issuers() {
180
		$response = $this->send_request( 'methods/ideal?include=issuers', 'GET' );
181 2
182 2
		if ( false === $response ) {
183
			return false;
184 2
		}
185 2
186
		$issuers = array();
187
188 2
		if ( isset( $response->issuers ) ) {
189
			foreach ( $response->issuers as $issuer ) {
190 2
				$id   = Security::filter( $issuer->id );
191 2
				$name = Security::filter( $issuer->name );
192
193
				$issuers[ $id ] = $name;
194
			}
195
		}
196
197
		return $issuers;
198
	}
199
200
	/**
201
	 * Get payment methods
202
	 *
203
	 * @param string $sequence_type Sequence type.
204
	 *
205
	 * @return array|bool
206
	 */
207
	public function get_payment_methods( $sequence_type = '' ) {
208
		$data = array();
209
210
		if ( '' !== $sequence_type ) {
211
			$data['sequenceType'] = $sequence_type;
212
		}
213
214
		$response = $this->send_request( 'methods', 'GET', $data );
215
216
		if ( false === $response ) {
217
			return false;
218
		}
219
220
		$payment_methods = array();
221
222
		if ( isset( $response->_embedded->methods ) ) {
223
			foreach ( $response->_embedded->methods as $payment_method ) {
224
				$id   = Security::filter( $payment_method->id );
225
				$name = Security::filter( $payment_method->description );
226
227
				$payment_methods[ $id ] = $name;
228
			}
229
		}
230
231
		return $payment_methods;
232
	}
233
234
	/**
235
	 * Create customer.
236
	 *
237
	 * @since 1.1.6
238
	 *
239
	 * @param string $email Customer email address.
240
	 * @param string $name  Customer name.
241
	 *
242
	 * @return string|bool
243
	 */
244
	public function create_customer( $email, $name ) {
245
		if ( empty( $email ) ) {
246
			return false;
247
		}
248
249
		$response = $this->send_request(
250
			'customers',
251
			'POST',
252
			array(
253 5
				'name'  => $name,
254 5
				'email' => $email,
255
			),
256
			201
257
		);
258 5
259
		if ( false === $response ) {
260 5
			return false;
261 5
		}
262
263
		if ( ! isset( $response->id ) ) {
264
			return false;
265
		}
266
267
		return $response->id;
268
	}
269
270
	/**
271
	 * Get customer.
272
	 *
273
	 * @param string $customer_id Mollie customer ID.
274
	 *
275
	 * @since unreleased
276
	 *
277
	 * @return object|bool
278
	 */
279
	public function get_customer( $customer_id ) {
280
		if ( empty( $customer_id ) ) {
281
			return false;
282
		}
283
284
		$response = $this->send_request( 'customers/' . $customer_id, 'GET', array(), 200 );
285
286
		if ( false === $response ) {
287
			return false;
288
		}
289
290
		if ( is_wp_error( $this->error ) ) {
291
			return false;
292
		}
293
294
		return $response;
295
	}
296
297
	/**
298
	 * Get mandates for customer.
299
	 *
300
	 * @param string $customer_id Mollie customer ID.
301
	 *
302
	 * @return object|bool
303
	 */
304
	public function get_mandates( $customer_id ) {
305
		if ( '' === $customer_id ) {
306
			return false;
307
		}
308
309
		return $this->send_request( 'customers/' . $customer_id . '/mandates?limit=250', 'GET' );
310
	}
311
312
	/**
313
	 * Is there a valid mandate for customer?
314
	 *
315
	 * @param string      $customer_id    Mollie customer ID.
316
	 * @param string|null $payment_method Payment method to find mandates for.
317
	 *
318
	 * @return boolean
319
	 */
320
	public function has_valid_mandate( $customer_id, $payment_method = null ) {
321
		$mandates = $this->get_mandates( $customer_id );
322
323
		if ( ! $mandates ) {
324
			return false;
325
		}
326
327
		$mollie_method = Methods::transform( $payment_method );
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
	 */
350
	public function get_first_valid_mandate_datetime( $customer_id, $payment_method = null ) {
351
		$mandates = $this->get_mandates( $customer_id );
352
353
		if ( ! $mandates ) {
354
			return null;
355
		}
356
357
		$mollie_method = Methods::transform( $payment_method );
358
359
		foreach ( $mandates->_embedded as $mandate ) {
360
			if ( $mollie_method !== $mandate->method ) {
361
				continue;
362
			}
363
364
			if ( 'valid' !== $mandate->status ) {
365
				continue;
366
			}
367
368
			if ( ! isset( $valid_mandates ) ) {
369
				$valid_mandates = array();
370
			}
371
372
			// @codingStandardsIgnoreStart
373
			$valid_mandates[ $mandate->createdAt ] = $mandate;
374
			// @codingStandardsIgnoreEnd
375
		}
376
377
		if ( isset( $valid_mandates ) ) {
378
			ksort( $valid_mandates );
379
380
			$mandate = array_shift( $valid_mandates );
381
382
			// @codingStandardsIgnoreStart
383
			$create_date = new DateTime( $mandate->createdAt );
384
			// @codingStandardsIgnoreEnd
385
386
			return $create_date;
387
		}
388
389
		return null;
390
	}
391
}
392