Passed
Push — master ( 78e783...fc34de )
by Remco
05:58 queued 02:59
created

Gateway::get_issuer_field()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 8
nc 2
nop 0
1
<?php
2
3
namespace Pronamic\WordPress\Pay\Gateways\Mollie;
4
5
use Pronamic\WordPress\Pay\Core\Gateway as Core_Gateway;
6
use Pronamic\WordPress\Pay\Core\PaymentMethods;
7
use Pronamic\WordPress\Pay\Core\Recurring as Core_Recurring;
8
use Pronamic\WordPress\Pay\Core\Statuses as Core_Statuses;
9
use Pronamic\WordPress\Pay\Payments\Payment;
0 ignored issues
show
Bug introduced by
The type Pronamic\WordPress\Pay\Payments\Payment was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
11
/**
12
 * Title: Mollie
13
 * Description:
14
 * Copyright: Copyright (c) 2005 - 2018
15
 * Company: Pronamic
16
 *
17
 * @author  Remco Tolsma
18
 * @version 2.0.2
19
 * @since   1.1.0
20
 */
21
class Gateway extends Core_Gateway {
22
	/**
23
	 * Slug of this gateway
24
	 *
25
	 * @var string
26
	 */
27
	const SLUG = 'mollie';
28
29
	/**
30
	 * Meta key for customer ID.
31
	 *
32
	 * @var string
33
	 */
34
	private $meta_key_customer_id = '_pronamic_pay_mollie_customer_id';
35
36
	/**
37
	 * Constructs and initializes an Mollie gateway
38
	 *
39
	 * @param Config $config
40
	 */
41
	public function __construct( Config $config ) {
42
		parent::__construct( $config );
43
44
		$this->supports = array(
45
			'payment_status_request',
46
			'recurring_direct_debit',
47
			'recurring_credit_card',
48
			'recurring',
49
		);
50
51
		$this->set_method( Core_Gateway::METHOD_HTTP_REDIRECT );
52
		$this->set_has_feedback( true );
53
		$this->set_amount_minimum( 1.20 );
54
		$this->set_slug( self::SLUG );
55
56
		$this->client = new Client( $config->api_key );
0 ignored issues
show
Documentation Bug introduced by
It seems like new Pronamic\WordPress\P...lient($config->api_key) of type Pronamic\WordPress\Pay\Gateways\Mollie\Client is incompatible with the declared type Pronamic\WordPress\Pay\Core\Client of property $client.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
57
		$this->client->set_mode( $config->mode );
58
59
		if ( 'test' === $config->mode ) {
60
			$this->meta_key_customer_id = '_pronamic_pay_mollie_customer_id_test';
61
		}
62
63
		// Actions.
64
		add_action( 'pronamic_payment_status_update', array( $this, 'move_customer_id_to_wp_user' ), 99, 1 );
0 ignored issues
show
Bug introduced by
The function add_action was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

64
		/** @scrutinizer ignore-call */ 
65
  add_action( 'pronamic_payment_status_update', array( $this, 'move_customer_id_to_wp_user' ), 99, 1 );
Loading history...
65
	}
66
67
	/**
68
	 * Get issuers
69
	 *
70
	 * @see Pronamic_WP_Pay_Gateway::get_issuers()
71
	 */
72
	public function get_issuers() {
73
		$groups = array();
74
75
		$result = $this->client->get_issuers();
76
77
		if ( ! $result ) {
78
			$this->error = $this->client->get_error();
79
80
			return $groups;
81
		}
82
83
		$groups[] = array(
84
			'options' => $result,
85
		);
86
87
		return $groups;
88
	}
89
90
	/**
91
	 * Get available payment methods.
92
	 *
93
	 * @see Core_Gateway::get_available_payment_methods()
94
	 */
95
	public function get_available_payment_methods() {
96
		$payment_methods = array();
97
98
		// Set recurring types to get payment methods for.
99
		$recurring_types = array( null, Recurring::RECURRING, Recurring::FIRST );
100
101
		$results = array();
102
103
		foreach ( $recurring_types as $recurring_type ) {
104
			// Get active payment methods for Mollie account.
105
			$result = $this->client->get_payment_methods( $recurring_type );
106
107
			if ( ! $result ) {
108
				$this->error = $this->client->get_error();
109
110
				break;
111
			}
112
113
			if ( Recurring::FIRST === $recurring_type ) {
114
				foreach ( $result as $method => $title ) {
115
					unset( $result[ $method ] );
116
117
					// Get WordPress payment method for direct debit method.
118
					$method         = Methods::transform_gateway_method( $method );
119
					$payment_method = array_search( $method, PaymentMethods::get_recurring_methods(), true );
120
121
					if ( $payment_method ) {
122
						$results[ $payment_method ] = $title;
123
					}
124
				}
125
			}
126
127
			$results = array_merge( $results, $result );
128
		}
129
130
		// Transform to WordPress payment methods.
131
		foreach ( $results as $method => $title ) {
132
			if ( PaymentMethods::is_recurring_method( $method ) ) {
133
				$payment_method = $method;
134
			} else {
135
				$payment_method = Methods::transform_gateway_method( $method );
136
			}
137
138
			if ( $payment_method ) {
139
				$payment_methods[] = $payment_method;
140
			}
141
		}
142
143
		$payment_methods = array_unique( $payment_methods );
144
145
		return $payment_methods;
146
	}
147
148
	/**
149
	 * Get supported payment methods
150
	 *
151
	 * @see Pronamic_WP_Pay_Gateway::get_supported_payment_methods()
152
	 */
153
	public function get_supported_payment_methods() {
154
		return array(
155
			PaymentMethods::BANCONTACT,
156
			PaymentMethods::BANK_TRANSFER,
157
			PaymentMethods::BELFIUS,
158
			PaymentMethods::BITCOIN,
159
			PaymentMethods::CREDIT_CARD,
160
			PaymentMethods::DIRECT_DEBIT,
161
			PaymentMethods::DIRECT_DEBIT_BANCONTACT,
162
			PaymentMethods::DIRECT_DEBIT_IDEAL,
163
			PaymentMethods::DIRECT_DEBIT_SOFORT,
164
			PaymentMethods::IDEAL,
165
			PaymentMethods::KBC,
166
			PaymentMethods::PAYPAL,
167
			PaymentMethods::SOFORT,
168
		);
169
	}
170
171
	/**
172
	 * Get webhook URL for Mollie.
173
	 *
174
	 * @return string
175
	 */
176
	private function get_webhook_url() {
177
		$url = home_url( '/' );
0 ignored issues
show
Bug introduced by
The function home_url was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

177
		$url = /** @scrutinizer ignore-call */ home_url( '/' );
Loading history...
178
179
		$host = wp_parse_url( $url, PHP_URL_HOST );
0 ignored issues
show
Bug introduced by
The function wp_parse_url was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

179
		$host = /** @scrutinizer ignore-call */ wp_parse_url( $url, PHP_URL_HOST );
Loading history...
180
181
		if ( 'localhost' === $host ) {
182
			// Mollie doesn't allow localhost
183
			return null;
184
		} elseif ( '.dev' === substr( $host, -4 ) ) {
185
			// Mollie doesn't allow the TLD .dev
186
			return null;
187
		}
188
189
		$url = add_query_arg( 'mollie_webhook', '', $url );
0 ignored issues
show
Bug introduced by
The function add_query_arg was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

189
		$url = /** @scrutinizer ignore-call */ add_query_arg( 'mollie_webhook', '', $url );
Loading history...
190
191
		return $url;
192
	}
193
194
	/**
195
	 * Start
196
	 *
197
	 * @see Pronamic_WP_Pay_Gateway::start()
198
	 */
199
	public function start( Payment $payment ) {
200
		$request = new PaymentRequest();
201
202
		$request->amount       = $payment->get_amount()->get_amount();
203
		$request->description  = $payment->get_description();
204
		$request->redirect_url = $payment->get_return_url();
205
		$request->webhook_url  = $this->get_webhook_url();
206
		$request->locale       = LocaleHelper::transform( $payment->get_language() );
207
208
		// Customer ID.
209
		$customer_id = $this->get_customer_id_for_payment( $payment );
210
211
		if ( ! empty( $customer_id ) ) {
212
			$request->customer_id = $customer_id;
0 ignored issues
show
Documentation Bug introduced by
It seems like $customer_id can also be of type boolean. However, the property $customer_id is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
213
		}
214
215
		// Payment method.
216
		$payment_method = $payment->get_method();
217
218
		// Subscription.
219
		$subscription = $payment->get_subscription();
220
221
		if ( $subscription && PaymentMethods::is_recurring_method( $payment_method ) ) {
222
			$request->recurring_type = $payment->get_recurring() ? Recurring::RECURRING : Recurring::FIRST;
223
224
			if ( Recurring::FIRST === $request->recurring_type ) {
225
				$payment_method = PaymentMethods::get_first_payment_method( $payment_method );
226
			}
227
228
			if ( Recurring::RECURRING === $request->recurring_type ) {
229
				$payment->set_action_url( $payment->get_return_url() );
230
			}
231
		}
232
233
		// Leap of faith if the WordPress payment method could not transform to a Mollie method?
234
		$request->method = Methods::transform( $payment_method, $payment_method );
235
236
		// Issuer.
237
		if ( Methods::IDEAL === $request->method ) {
238
			// If payment method is iDEAL we set the user chosen issuer ID.
239
			$request->issuer = $payment->get_issuer();
240
		}
241
242
		// Create payment.
243
		$result = $this->client->create_payment( $request );
244
245
		if ( ! $result ) {
246
			$this->error = $this->client->get_error();
247
248
			return false;
249
		}
250
251
		// Set transaction ID.
252
		if ( isset( $result->id ) ) {
253
			$payment->set_transaction_id( $result->id );
254
		}
255
256
		// Set status
257
		if ( isset( $result->status ) ) {
258
			$payment->set_status( Statuses::transform( $result->status ) );
259
		}
260
261
		// Set action URL.
262
		if ( isset( $result->links, $result->links->paymentUrl ) ) {
263
			$payment->set_action_url( $result->links->paymentUrl );
264
		}
265
	}
266
267
	/**
268
	 * Update status of the specified payment
269
	 *
270
	 * @param Payment $payment
271
	 */
272
	public function update_status( Payment $payment ) {
273
		$mollie_payment = $this->client->get_payment( $payment->get_transaction_id() );
274
275
		if ( ! $mollie_payment ) {
276
			$payment->set_status( Core_Statuses::FAILURE );
277
278
			$this->error = $this->client->get_error();
279
280
			return;
281
		}
282
283
		$payment->set_status( Statuses::transform( $mollie_payment->status ) );
284
285
		if ( isset( $mollie_payment->details ) ) {
286
			$details = $mollie_payment->details;
287
288
			if ( isset( $details->consumerName ) ) {
289
				$payment->set_consumer_name( $details->consumerName );
290
			}
291
292
			if ( isset( $details->cardHolder ) ) {
293
				$payment->set_consumer_name( $details->cardHolder );
294
			}
295
296
			if ( isset( $details->consumerAccount ) ) {
297
				$payment->set_consumer_iban( $details->consumerAccount );
298
			}
299
300
			if ( isset( $details->consumerBic ) ) {
301
				$payment->set_consumer_bic( $details->consumerBic );
302
			}
303
		}
304
	}
305
306
	/**
307
	 * Get Mollie customer ID for payment.
308
	 *
309
	 * @param Payment $payment Payment.
310
	 *
311
	 * @return bool|string
312
	 */
313
	private function get_customer_id_for_payment( Payment $payment ) {
314
		// Get Mollie customer ID from user meta.
315
		$customer_id = $this->get_customer_id_by_wp_user_id( $payment->user_id );
316
317
		if ( Core_Recurring::FIRST === $payment->recurring_type ) {
318
			// Create new customer if the customer does not exist at Mollie.
319
			if ( empty( $customer_id ) || ! $this->client->get_customer( $customer_id ) ) {
320
				$customer_id = $this->client->create_customer( $payment->get_email(), $payment->get_customer_name() );
321
322
				$this->update_wp_user_customer_id( $payment->user_id, $customer_id );
323
			}
324
325
			// Temporarily store customer ID in subscription meta for guest users.
326
			if ( empty( $payment->user_id ) && ! empty( $customer_id ) ) {
327
				$subscription = $payment->get_subscription();
328
329
				if ( $subscription ) {
330
					$subscription->set_meta( 'mollie_customer_id', $customer_id );
331
				}
332
			}
333
		}
334
335
		// Try to move customer ID from subscription meta to user.
336
		if ( empty( $customer_id ) ) {
337
			// Move customer ID from subscription meta to user meta.
338
			$this->move_customer_id_to_wp_user( $payment );
339
340
			// Get customer ID from user meta, again.
341
			$customer_id = $this->get_customer_id_by_wp_user_id( $payment->user_id );
342
		}
343
344
		// Try to get customer ID from subscription meta.
345
		if ( empty( $customer_id ) && $payment->get_subscription() ) {
346
			$customer_id = $payment->get_subscription()->get_meta( 'mollie_customer_id' );
347
		}
348
349
		return $customer_id;
350
	}
351
352
	/**
353
	 * Get Mollie customer ID by the specified WordPress user ID.
354
	 *
355
	 * @param int $user_id WordPress user ID.
356
	 *
357
	 * @return string
358
	 */
359
	private function get_customer_id_by_wp_user_id( $user_id ) {
360
		if ( empty( $user_id ) ) {
361
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
362
		}
363
364
		return get_user_meta( $user_id, $this->meta_key_customer_id, true );
0 ignored issues
show
Bug introduced by
The function get_user_meta was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

364
		return /** @scrutinizer ignore-call */ get_user_meta( $user_id, $this->meta_key_customer_id, true );
Loading history...
365
	}
366
367
	/**
368
	 * Update Mollie customer ID meta for WordPress user.
369
	 *
370
	 * @param int    $user_id     WordPress user ID.
371
	 * @param string $customer_id Mollie Customer ID.
372
	 *
373
	 * @return bool
374
	 */
375
	private function update_wp_user_customer_id( $user_id, $customer_id ) {
376
		if ( empty( $user_id ) || empty( $customer_id ) ) {
377
			return false;
378
		}
379
380
		update_user_meta( $user_id, $this->meta_key_customer_id, $customer_id );
0 ignored issues
show
Bug introduced by
The function update_user_meta was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

380
		/** @scrutinizer ignore-call */ 
381
  update_user_meta( $user_id, $this->meta_key_customer_id, $customer_id );
Loading history...
381
	}
382
383
	/**
384
	 * Move Mollie customer ID from subscription meta to WordPress user meta.
385
	 *
386
	 * @param Payment $payment Payment.
387
	 *
388
	 * @return void
389
	 */
390
	public function move_customer_id_to_wp_user( Payment $payment ) {
391
		if ( $this->config->id !== $payment->config_id ) {
392
			return;
393
		}
394
395
		$subscription = $payment->get_subscription();
396
397
		if ( ! $subscription || empty( $subscription->user_id ) ) {
398
			return;
399
		}
400
401
		// Get customer ID from subscription meta.
402
		$customer_id = $subscription->get_meta( 'mollie_customer_id' );
403
404
		if ( ! empty( $customer_id ) && ! empty( $subscription->user_id ) ) {
405
			// Set customer ID as user meta.
406
			$this->update_wp_user_customer_id( $subscription->user_id, $customer_id );
407
408
			// Delete customer ID from subscription meta.
409
			$subscription->set_meta( 'mollie_customer_id', null );
410
		}
411
	}
412
}
413