Failed Conditions
Push — develop ( c6b40f...3e2161 )
by Reüel
03:40
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
	 */
55
	private $error;
56
57
	/**
58
	 * Constructs and initializes an Mollie client object
59
	 *
60
	 * @param string $api_key Mollie API key.
61
	 */
62 40
	public function __construct( $api_key ) {
63 40
		$this->api_key = $api_key;
64 40
	}
65
66
	/**
67
	 * Set mode
68
	 *
69
	 * @since 1.1.9
70
	 * @param string $mode Mode (test or live).
71
	 */
72 40
	public function set_mode( $mode ) {
73 40
		$this->mode = $mode;
74 40
	}
75
76
	/**
77
	 * Error
78
	 *
79
	 * @return WP_Error
80
	 */
81 5
	public function get_error() {
82 5
		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
	 *
93
	 * @return bool|object
94
	 */
95 10
	private function send_request( $end_point, $method = 'GET', array $data = array(), $expected_response_code = 200 ) {
96
		// Request.
97 10
		$url = self::API_URL . $end_point;
98
99 10
		$response = wp_remote_request(
100 10
			$url,
101
			array(
102 10
				'method'  => $method,
103
				'headers' => array(
104 10
					'Authorization' => 'Bearer ' . $this->api_key,
105
				),
106 10
				'body'    => $data,
107
			)
108
		);
109
110
		// Response code.
111 10
		$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
113 10
		if ( $expected_response_code != $response_code ) { // WPCS: loose comparison ok.
114 10
			$this->error = new WP_Error( 'mollie_error', 'Unexpected response code.' );
115
		}
116
117
		// Body.
118 10
		$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

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