Test Failed
Push — develop ( 7dfe94...172541 )
by Remco
04:09
created

Client::create_payment()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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