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

src/Integration.php (4 issues)

1
<?php
2
/**
3
 * Mollie integration.
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\Pay\Core\PaymentMethods;
14
use Pronamic\WordPress\Pay\AbstractGatewayIntegration;
0 ignored issues
show
The type Pronamic\WordPress\Pay\AbstractGatewayIntegration 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...
15
use Pronamic\WordPress\Pay\Payments\Payment;
16
use WP_User;
17
18
/**
19
 * Title: Mollie integration
20
 * Description:
21
 * Copyright: 2005-2020 Pronamic
22
 * Company: Pronamic
23
 *
24
 * @author  Remco Tolsma
25
 * @version 2.0.9
26
 * @since   1.0.0
27
 */
28
class Integration extends AbstractGatewayIntegration {
29
	/**
30
	 * Register URL.
31
	 *
32
	 * @var string
33
	 */
34
	public $register_url;
35
36
	/**
37
	 * Construct and intialize Mollie integration.
38
	 *
39
	 * @param array $args Arguments.
40
	 */
41
	public function __construct( $args = array() ) {
42
		$args = wp_parse_args(
43
			$args,
44
			array(
45
				'id'            => 'mollie',
46
				'name'          => 'Mollie',
47
				'url'           => 'http://www.mollie.com/en/',
48
				'product_url'   => \__( 'https://www.mollie.com/en/pricing', 'pronamic_ideal' ),
49
				'dashboard_url' => 'https://www.mollie.com/dashboard/',
50
				'provider'      => 'mollie',
51
				'supports'      => array(
52
					'payment_status_request',
53
					'recurring_direct_debit',
54
					'recurring_credit_card',
55
					'recurring',
56
					'webhook',
57
					'webhook_log',
58
					'webhook_no_config',
59
				),
60
			)
61
		);
62
63
		parent::__construct( $args );
64
65
		// Actions.
66
		$function = array( __NAMESPACE__ . '\Listener', 'listen' );
67
68
		if ( ! has_action( 'wp_loaded', $function ) ) {
69
			add_action( 'wp_loaded', $function );
70
		}
71
72
		if ( is_admin() ) {
73
			$function = array( __CLASS__, 'user_profile' );
74
75
			if ( ! has_action( 'show_user_profile', $function ) ) {
76
				add_action( 'show_user_profile', $function );
77
			}
78
79
			if ( ! has_action( 'edit_user_profile', $function ) ) {
80
				add_action( 'edit_user_profile', $function );
81
			}
82
		}
83
84
		// Filters.
85
		$function = array( $this, 'next_payment_delivery_date' );
86
87
		if ( ! \has_filter( 'pronamic_pay_subscription_next_payment_delivery_date', $function ) ) {
88
			\add_filter( 'pronamic_pay_subscription_next_payment_delivery_date', $function, 10, 2 );
89
		}
90
91
		$function = array( $this, 'maybe_handle_oauth_authorization' );
92
93
		if ( ! \has_action( 'init', $function ) ) {
94
			\add_filter( 'init', $function );
95
		}
96
97
		add_filter( 'pronamic_payment_provider_url_mollie', array( $this, 'payment_provider_url' ), 10, 2 );
98
99
		// Tables.
100
		$this->register_tables();
101
102
		// Upgrades.
103
		$upgrades = $this->get_upgrades();
104
105
		$upgrades->add( new Upgrade300() );
106
107
		/**
108
		 * CLI.
109
		 *
110
		 * @link https://github.com/woocommerce/woocommerce/blob/3.9.0/includes/class-woocommerce.php#L453-L455
111
		 */
112
		if ( defined( 'WP_CLI' ) && WP_CLI ) {
0 ignored issues
show
The constant Pronamic\WordPress\Pay\Gateways\Mollie\WP_CLI was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
113
			$this->cli = new CLI();
0 ignored issues
show
Bug Best Practice introduced by
The property cli does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
114
		}
115
	}
116
117
	/**
118
	 * Register tables.
119
	 *
120
	 * @link https://github.com/WordPress/WordPress/blob/5.3/wp-includes/wp-db.php#L894-L937
121
	 */
122
	private function register_tables() {
123
		global $wpdb;
124
125
		/**
126
		 * Tables.
127
		 */
128
		$wpdb->pronamic_pay_mollie_organizations  = $wpdb->base_prefix . 'pronamic_pay_mollie_organizations';
129
		$wpdb->pronamic_pay_mollie_profiles       = $wpdb->base_prefix . 'pronamic_pay_mollie_profiles';
130
		$wpdb->pronamic_pay_mollie_customers      = $wpdb->base_prefix . 'pronamic_pay_mollie_customers';
131
		$wpdb->pronamic_pay_mollie_customer_users = $wpdb->base_prefix . 'pronamic_pay_mollie_customer_users';
132
	}
133
134
	/**
135
	 * Get settings fields.
136
	 *
137
	 * @param int|null $config_id Config ID.
138
	 * @return array<int, array<string, array<int, string>|int|string|true>>
139
	 */
140
	public function get_settings_fields( $config_id = null ) {
141
		$fields = array();
142
143
		$api_key = \get_post_meta( $config_id, '_pronamic_gateway_mollie_api_key', true );
144
145
		/*
146
		 * Mollie Connect.
147
		 */
148
		$fields[] = array(
149
			'section'  => 'general',
150
			'type'     => 'html',
151
			'callback' => function( $field ) use ( $config_id ) {
152
				$this->field_mollie_connect( $field, $config_id );
153
			},
154
		);
155
156
		if ( ! empty( $api_key ) ) {
157
			// API Key.
158
			$fields[] = array(
159
				'section'  => 'general',
160
				'filter'   => FILTER_SANITIZE_STRING,
161
				'methods'  => array( 'mollie-deprecated' ),
162
				'meta_key' => '_pronamic_gateway_mollie_api_key',
163
				'title'    => _x( 'API Key', 'mollie', 'pronamic_ideal' ),
164
				'type'     => 'text',
165
				'classes'  => array( 'regular-text', 'code' ),
166
				'tooltip'  => __( 'API key as mentioned in the payment provider dashboard', 'pronamic_ideal' ),
167
			);
168
		}
169
170
		// Due date days.
171
		$fields[] = array(
172
			'section'     => 'advanced',
173
			'filter'      => \FILTER_SANITIZE_NUMBER_INT,
174
			'meta_key'    => '_pronamic_gateway_mollie_due_date_days',
175
			'title'       => _x( 'Due date days', 'mollie', 'pronamic_ideal' ),
176
			'type'        => 'number',
177
			'min'         => 1,
178
			'max'         => 100,
179
			'classes'     => array( 'regular-text' ),
180
			'tooltip'     => __( 'Number of days after which a bank transfer payment expires.', 'pronamic_ideal' ),
181
			'description' => sprintf(
182
				/* translators: 1: <code>1</code>, 2: <code>100</code>, 3: <code>12</code> */
183
				__( 'Minimum %1$s and maximum %2$s days. Default: %3$s days.', 'pronamic_ideal' ),
184
				sprintf( '<code>%s</code>', '1' ),
185
				sprintf( '<code>%s</code>', '100' ),
186
				sprintf( '<code>%s</code>', '12' )
187
			),
188
		);
189
190
		// Webhook.
191
		$fields[] = array(
192
			'section'  => 'feedback',
193
			'title'    => __( 'Webhook URL', 'pronamic_ideal' ),
194
			'type'     => 'text',
195
			'classes'  => array( 'large-text', 'code' ),
196
			'value'    => add_query_arg( 'mollie_webhook', '', home_url( '/' ) ),
197
			'readonly' => true,
198
			'tooltip'  => __( 'The Webhook URL as sent with each transaction to receive automatic payment status updates on.', 'pronamic_ideal' ),
199
		);
200
201
		return $fields;
202
	}
203
204
	/**
205
	 * Field mollie connect.
206
	 *
207
	 * @param array $field     Setting field.
208
	 * @param int   $config_id Config ID.
209
	 * @return void
210
	 */
211
	public function field_mollie_connect( $field, $config_id ) {
212
		// Authorize URL.
213
		$state = array(
214
			'redirect_uri' => \home_url(),
215
			'post_id'      => $config_id,
216
		);
217
218
		$authorize_url = \add_query_arg(
219
			array(
220
				'state' => \base64_encode( \wp_json_encode( $state ) ),
0 ignored issues
show
It seems like wp_json_encode($state) can also be of type false; however, parameter $data of base64_encode() does only seem to accept string, 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

220
				'state' => \base64_encode( /** @scrutinizer ignore-type */ \wp_json_encode( $state ) ),
Loading history...
221
			),
222
			Connect::WP_PAY_MOLLIE_CONNECT_API_URL . 'oauth2/authorize/'
223
		);
224
225
		// Check connection.
226
		$connect = new Connect( $config_id );
227
228
		$connect->set_refresh_token( $this->get_meta( $config_id, 'mollie_refresh_token' ) );
229
		$connect->set_access_token( $this->get_meta( $config_id, 'mollie_access_token' ) );
230
		$connect->set_access_token_valid_until( $this->get_meta( $config_id, 'mollie_access_token_valid_until' ) );
231
232
		if ( $connect->is_access_token_valid() ) {
233
			printf(
234
				/* translators: %s: reconnect HTML link */
235
				\esc_html__( 'Connected with Mollie (%s)', 'pronamic_ideal' ),
236
				sprintf(
237
					'<a href="%1$s" title="%2$s">%2$s</a>',
238
					\esc_url( $authorize_url ),
239
					\esc_html__( 'reconnect', 'pronamiic_ideal' )
240
				)
241
			);
242
243
			return;
244
		}
245
246
		?>
247
248
		<p>
249
			<a href="<?php echo \esc_url( $authorize_url ); ?>" title="<?php echo \esc_attr_x( 'Connect via Mollie', 'mollie', 'pronamic_ideal' ); ?>">
250
				<?php
251
252
				printf(
253
					'<img src="%s" alt="%s" />',
254
					\esc_url( \plugins_url( '../images/mollie_connect.svg', __FILE__ ) ),
255
					\esc_html_x( 'Connect via Mollie', 'mollie', 'pronamic_ideal' )
256
				);
257
258
				?>
259
			</a>
260
		</p>
261
262
		<p>
263
			<em><?php esc_html_e( 'Click the Connect via Mollie button to authorize a Mollie account.', 'pronamic_ideal' ); ?></em>
264
		</p>
265
266
		<?php
267
	}
268
269
	/**
270
	 * Save post.
271
	 *
272
	 * @link https://developer.wordpress.org/reference/functions/get_post_meta/
273
	 * @param int $post_id Post ID.
274
	 * @return void
275
	 */
276
	public function save_post( $post_id ) {
277
		$api_key = get_post_meta( $post_id, '_pronamic_gateway_mollie_api_key', true );
278
279
		if ( ! is_string( $api_key ) ) {
280
			return;
281
		}
282
283
		$api_key_prefix = substr( $api_key, 0, 4 );
284
285
		switch ( $api_key_prefix ) {
286
			case 'live':
287
				update_post_meta( $post_id, '_pronamic_gateway_mode', Gateway::MODE_LIVE );
288
289
				return;
290
			case 'test':
291
				update_post_meta( $post_id, '_pronamic_gateway_mode', Gateway::MODE_TEST );
292
293
				return;
294
		}
295
	}
296
297
	/**
298
	 * User profile.
299
	 *
300
	 * @since 1.1.6
301
	 * @link https://github.com/WordPress/WordPress/blob/4.5.2/wp-admin/user-edit.php#L578-L600
302
	 * @param WP_User $user WordPress user.
303
	 * @return void
304
	 */
305
	public static function user_profile( $user ) {
306
		include __DIR__ . '/../views/html-admin-user-profile.php';
307
	}
308
309
	/**
310
	 * Payment provider URL.
311
	 *
312
	 * @param string|null $url     Payment provider URL.
313
	 * @param Payment     $payment Payment.
314
	 * @return string|null
315
	 */
316
	public function payment_provider_url( $url, Payment $payment ) {
317
		$transaction_id = $payment->get_transaction_id();
318
319
		if ( null === $transaction_id ) {
320
			return $url;
321
		}
322
323
		return sprintf(
324
			'https://www.mollie.com/dashboard/payments/%s',
325
			$transaction_id
326
		);
327
	}
328
	/**
329
	 * Get configuration by post ID.
330
	 *
331
	 * @param int $post_id Post ID.
332
	 * @return Config
333
	 */
334
	public function get_config( $post_id ) {
335
		$config = new Config();
336
337
		$config->id                       = intval( $post_id );
338
		$config->api_key                  = $this->get_meta( $post_id, 'mollie_api_key' );
339
		$config->mode                     = $this->get_meta( $post_id, 'mode' );
340
		$config->due_date_days            = $this->get_meta( $post_id, 'mollie_due_date_days' );
341
		$config->refresh_token            = $this->get_meta( $post_id, 'mollie_refresh_token' );
342
		$config->access_token             = $this->get_meta( $post_id, 'mollie_access_token' );
343
		$config->access_token_valid_until = $this->get_meta( $post_id, 'mollie_access_token_valid_until' );
344
345
		return $config;
346
	}
347
348
	/**
349
	 * Get gateway.
350
	 *
351
	 * @param int $post_id Post ID.
352
	 * @return Gateway
353
	 */
354
	public function get_gateway( $post_id ) {
355
		return new Gateway( $this->get_config( $post_id ) );
356
	}
357
358
	/**
359
	 * Next payment delivery date.
360
	 *
361
	 * @param \DateTime $next_payment_delivery_date Next payment delivery date.
362
	 * @param Payment   $payment                    Payment.
363
	 * @return \DateTime
364
	 */
365
	public function next_payment_delivery_date( \DateTime $next_payment_delivery_date, Payment $payment ) {
366
		$config_id = $payment->get_config_id();
367
368
		if ( null === $config_id ) {
369
			return $next_payment_delivery_date;
370
		}
371
372
		// Check gateway.
373
		$gateway_id = \get_post_meta( $config_id, '_pronamic_gateway_id', true );
374
375
		if ( 'mollie' !== $gateway_id ) {
376
			return $next_payment_delivery_date;
377
		}
378
379
		// Check direct debit payment method.
380
		$method = $payment->get_method();
381
382
		if ( null === $method ) {
383
			return $next_payment_delivery_date;
384
		}
385
386
		if ( ! PaymentMethods::is_direct_debit_method( $method ) ) {
387
			return $next_payment_delivery_date;
388
		}
389
390
		// Check subscription.
391
		$subscription = $payment->get_subscription();
392
393
		if ( null === $subscription ) {
394
			return $next_payment_delivery_date;
395
		}
396
397
		// Base delivery date on next payment date.
398
		$next_payment_date = $subscription->get_next_payment_date();
399
400
		if ( null === $next_payment_date ) {
401
			return $next_payment_delivery_date;
402
		}
403
404
		$next_payment_delivery_date = clone $next_payment_date;
405
406
		// Textual representation of the day of the week, Sunday through Saturday.
407
		$day_of_week = $next_payment_delivery_date->format( 'l' );
408
409
		/*
410
		 * Subtract days from next payment date for earlier delivery.
411
		 *
412
		 * @link https://help.mollie.com/hc/en-us/articles/115000785649-When-are-direct-debit-payments-processed-and-paid-out-
413
		 * @link https://help.mollie.com/hc/en-us/articles/115002540294-What-are-the-payment-methods-processing-times-
414
		 */
415
		switch ( $day_of_week ) {
416
			case 'Monday':
417
				$next_payment_delivery_date->modify( '-3 days' );
418
419
				break;
420
			case 'Saturday':
421
				$next_payment_delivery_date->modify( '-2 days' );
422
423
				break;
424
			case 'Sunday':
425
				$next_payment_delivery_date->modify( '-3 days' );
426
427
				break;
428
			default:
429
				$next_payment_delivery_date->modify( '-1 day' );
430
431
				break;
432
		}
433
434
		$next_payment_delivery_date->setTime( 0, 0, 0 );
435
436
		return $next_payment_delivery_date;
437
	}
438
439
	/**
440
	 * Maybe handle Mollie oAuth authorization.
441
	 *
442
	 * @return void
443
	 */
444
	public function maybe_handle_oauth_authorization() {
445
		if ( ! filter_has_var( \INPUT_GET, 'code' ) ) {
446
			return;
447
		}
448
449
		if ( ! filter_has_var( \INPUT_GET, 'state' ) ) {
450
			return;
451
		}
452
453
		$code  = filter_input( \INPUT_GET, 'code', \FILTER_SANITIZE_STRING );
454
		$state = filter_input( \INPUT_GET, 'state', \FILTER_SANITIZE_STRING );
455
456
		if ( empty( $code ) || empty( $state ) ) {
457
			return;
458
		}
459
460
		$state_data = json_decode( base64_decode( $state ) );
461
462
		if ( ! is_object( $state_data ) ) {
463
			return;
464
		}
465
466
		if ( ! isset( $state_data->post_id ) ) {
467
			return;
468
		}
469
470
		if ( \filter_has_var( \INPUT_GET, 'error' ) && \filter_has_var( \INPUT_GET, 'error_description' ) ) {
471
			die( \esc_html( \filter_input( \INPUT_GET, 'error_description' ) ) );
472
		}
473
474
		$connect = new Connect( $state_data->post_id );
475
476
		$connect->handle_authorization( $code, $state_data );
477
	}
478
}
479