WC_Stripe_Privacy::account_settings()   A
last analyzed

Complexity

Conditions 5
Paths 6

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 6
nop 1
dl 0
loc 28
rs 9.1608
c 0
b 0
f 0
1
<?php
2
if ( ! class_exists( 'WC_Abstract_Privacy' ) ) {
3
	return;
4
}
5
6
class WC_Stripe_Privacy extends WC_Abstract_Privacy {
7
	/**
8
	 * Constructor
9
	 *
10
	 */
11
	public function __construct() {
12
		parent::__construct( __( 'Stripe', 'woocommerce-gateway-stripe' ) );
13
14
		$this->add_exporter( 'woocommerce-gateway-stripe-order-data', __( 'WooCommerce Stripe Order Data', 'woocommerce-gateway-stripe' ), array( $this, 'order_data_exporter' ) );
15
16
		if ( function_exists( 'wcs_get_subscriptions' ) ) {
17
			$this->add_exporter( 'woocommerce-gateway-stripe-subscriptions-data', __( 'WooCommerce Stripe Subscriptions Data', 'woocommerce-gateway-stripe' ), array( $this, 'subscriptions_data_exporter' ) );
18
		}
19
20
		$this->add_exporter( 'woocommerce-gateway-stripe-customer-data', __( 'WooCommerce Stripe Customer Data', 'woocommerce-gateway-stripe' ), array( $this, 'customer_data_exporter' ) );
21
22
		$this->add_eraser( 'woocommerce-gateway-stripe-customer-data', __( 'WooCommerce Stripe Customer Data', 'woocommerce-gateway-stripe' ), array( $this, 'customer_data_eraser' ) );
23
		$this->add_eraser( 'woocommerce-gateway-stripe-order-data', __( 'WooCommerce Stripe Data', 'woocommerce-gateway-stripe' ), array( $this, 'order_data_eraser' ) );
24
25
		add_filter( 'woocommerce_get_settings_account', array( $this, 'account_settings' ) );
26
	}
27
28
	/**
29
	 * Add retention settings to account tab.
30
	 *
31
	 * @param array $settings
32
	 * @return array $settings Updated
33
	 */
34
	public function account_settings( $settings ) {
35
		$insert_setting = array(
36
			array(
37
				'title'       => __( 'Retain Stripe Data', 'woocommerce-gateway-stripe' ),
38
				'desc_tip'    => __( 'Retains any Stripe data such as Stripe customer ID, source ID.', 'woocommerce-gateway-stripe' ),
39
				'id'          => 'woocommerce_gateway_stripe_retention',
40
				'type'        => 'relative_date_selector',
41
				'placeholder' => __( 'N/A', 'woocommerce-gateway-stripe' ),
42
				'default'     => '',
43
				'autoload'    => false,
44
			),
45
		);
46
47
		$index = null;
48
49
		foreach ( $settings as $key => $value) {
50
			if ( 'sectionend' === $value[ 'type' ] && 'personal_data_retention' === $value[ 'id' ] ) {
51
				$index = $key;
52
				break;
53
			}
54
		}
55
56
		if ( ! is_null( $index ) ) {
57
			array_splice( $settings, $index, 0, $insert_setting );
58
		}
59
60
		return $settings;
61
	}
62
63
	/**
64
	 * Returns a list of orders that are using one of Stripe's payment methods.
65
	 *
66
	 * @param string  $email_address
67
	 * @param int     $page
68
	 *
69
	 * @return array WP_Post
70
	 */
71
	protected function get_stripe_orders( $email_address, $page ) {
72
		$user = get_user_by( 'email', $email_address ); // Check if user has an ID in the DB to load stored personal data.
73
74
		$order_query = array(
75
			'payment_method' => array( 'stripe', 'stripe_alipay', 'stripe_bancontact', 'stripe_eps', 'stripe_giropay', 'stripe_ideal', 'stripe_multibanco', 'stripe_p24', 'stripe_sepa', 'stripe_sofort' ),
76
			'limit'          => 10,
77
			'page'           => $page,
78
		);
79
80
		if ( $user instanceof WP_User ) {
0 ignored issues
show
Bug introduced by
The class WP_User does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
81
			$order_query['customer_id'] = (int) $user->ID;
82
		} else {
83
			$order_query['billing_email'] = $email_address;
84
		}
85
86
		return wc_get_orders( $order_query );
87
	}
88
89
	/**
90
	 * Gets the message of the privacy to display.
91
	 *
92
	 */
93
	public function get_privacy_message() {
94
		/* translators: %s URL to docs */
95
		return wpautop( sprintf( __( 'By using this extension, you may be storing personal data or sharing data with an external service. <a href="%s" target="_blank">Learn more about how this works, including what you may want to include in your privacy policy.</a>', 'woocommerce-gateway-stripe' ), 'https://docs.woocommerce.com/document/privacy-payments/#woocommerce-gateway-stripe' ) );
96
	}
97
98
	/**
99
	 * Handle exporting data for Orders.
100
	 *
101
	 * @param string $email_address E-mail address to export.
102
	 * @param int    $page          Pagination of data.
103
	 *
104
	 * @return array
105
	 */
106
	public function order_data_exporter( $email_address, $page = 1 ) {
107
		$done           = false;
0 ignored issues
show
Unused Code introduced by
$done is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
108
		$data_to_export = array();
109
110
		$orders = $this->get_stripe_orders( $email_address, (int) $page );
111
112
		$done = true;
113
114 View Code Duplication
		if ( 0 < count( $orders ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
115
			foreach ( $orders as $order ) {
116
				$data_to_export[] = array(
117
					'group_id'    => 'woocommerce_orders',
118
					'group_label' => __( 'Orders', 'woocommerce-gateway-stripe' ),
119
					'item_id'     => 'order-' . $order->get_id(),
120
					'data'        => array(
121
						array(
122
							'name'  => __( 'Stripe payment id', 'woocommerce-gateway-stripe' ),
123
							'value' => get_post_meta( $order->get_id(), '_stripe_source_id', true ),
124
						),
125
						array(
126
							'name'  => __( 'Stripe customer id', 'woocommerce-gateway-stripe' ),
127
							'value' => get_post_meta( $order->get_id(), '_stripe_customer_id', true ),
128
						),
129
					),
130
				);
131
			}
132
133
			$done = 10 > count( $orders );
134
		}
135
136
		return array(
137
			'data' => $data_to_export,
138
			'done' => $done,
139
		);
140
	}
141
142
	/**
143
	 * Handle exporting data for Subscriptions.
144
	 *
145
	 * @param string $email_address E-mail address to export.
146
	 * @param int    $page          Pagination of data.
147
	 *
148
	 * @return array
149
	 */
150
	public function subscriptions_data_exporter( $email_address, $page = 1 ) {
151
		$done           = false;
0 ignored issues
show
Unused Code introduced by
$done is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
152
		$page           = (int) $page;
153
		$data_to_export = array();
154
155
		$meta_query = array(
156
			'relation' => 'AND',
157
			array(
158
				'key'     => '_payment_method',
159
				'value'   => array( 'stripe', 'stripe_alipay', 'stripe_bancontact', 'stripe_eps', 'stripe_giropay', 'stripe_ideal', 'stripe_multibanco', 'stripe_p24', 'stripe_sepa', 'stripe_sofort' ),
160
				'compare' => 'IN',
161
			),
162
			array(
163
				'key'     => '_billing_email',
164
				'value'   => $email_address,
165
				'compare' => '=',
166
			),
167
		);
168
169
		$subscription_query = array(
170
			'posts_per_page' => 10,
171
			'page'           => $page,
172
			'meta_query'     => $meta_query,
173
		);
174
175
		$subscriptions = wcs_get_subscriptions( $subscription_query );
176
177
		$done = true;
178
179 View Code Duplication
		if ( 0 < count( $subscriptions ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
180
			foreach ( $subscriptions as $subscription ) {
181
				$data_to_export[] = array(
182
					'group_id'    => 'woocommerce_subscriptions',
183
					'group_label' => __( 'Subscriptions', 'woocommerce-gateway-stripe' ),
184
					'item_id'     => 'subscription-' . $subscription->get_id(),
185
					'data'        => array(
186
						array(
187
							'name'  => __( 'Stripe payment id', 'woocommerce-gateway-stripe' ),
188
							'value' => get_post_meta( $subscription->get_id(), '_stripe_source_id', true ),
189
						),
190
						array(
191
							'name'  => __( 'Stripe customer id', 'woocommerce-gateway-stripe' ),
192
							'value' => get_post_meta( $subscription->get_id(), '_stripe_customer_id', true ),
193
						),
194
					),
195
				);
196
			}
197
198
			$done = 10 > count( $subscriptions );
199
		}
200
201
		return array(
202
			'data' => $data_to_export,
203
			'done' => $done,
204
		);
205
	}
206
207
	/**
208
	 * Finds and exports customer data by email address.
209
	 *
210
	 * @param string $email_address The user email address.
211
	 * @param int    $page  Page.
212
	 * @return array An array of personal data in name value pairs
213
	 */
214
	public function customer_data_exporter( $email_address, $page ) {
215
		$user           = get_user_by( 'email', $email_address ); // Check if user has an ID in the DB to load stored personal data.
216
		$data_to_export = array();
217
218
		if ( $user instanceof WP_User ) {
0 ignored issues
show
Bug introduced by
The class WP_User does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
219
			$stripe_user = new WC_Stripe_Customer( $user->ID );
220
221
			$data_to_export[] = array(
222
				'group_id'    => 'woocommerce_customer',
223
				'group_label' => __( 'Customer Data', 'woocommerce-gateway-stripe' ),
224
				'item_id'     => 'user',
225
				'data'        => array(
226
					array(
227
						'name'  => __( 'Stripe payment id', 'woocommerce-gateway-stripe' ),
228
						'value' => get_user_option( '_stripe_source_id', $user->ID ),
229
					),
230
					array(
231
						'name'  => __( 'Stripe customer id', 'woocommerce-gateway-stripe' ),
232
						'value' => $stripe_user->get_id(),
233
					),
234
				),
235
			);
236
		}
237
238
		return array(
239
			'data' => $data_to_export,
240
			'done' => true,
241
		);
242
	}
243
244
	/**
245
	 * Finds and erases customer data by email address.
246
	 *
247
	 * @param string $email_address The user email address.
248
	 * @param int    $page  Page.
249
	 * @return array An array of personal data in name value pairs
250
	 */
251
	public function customer_data_eraser( $email_address, $page ) {
252
		$page               = (int) $page;
0 ignored issues
show
Unused Code introduced by
$page is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
253
		$user               = get_user_by( 'email', $email_address ); // Check if user has an ID in the DB to load stored personal data.
254
		$stripe_customer_id = '';
255
		$stripe_source_id   = '';
256
257
		if ( $user instanceof WP_User ) {
0 ignored issues
show
Bug introduced by
The class WP_User does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
258
			$stripe_customer_id = get_user_option( '_stripe_customer_id', $user->ID );
259
			$stripe_source_id   = get_user_option( '_stripe_source_id', $user->ID );
260
		}
261
262
		$items_removed = false;
263
		$messages      = array();
264
265
		if ( ! empty( $stripe_customer_id ) || ! empty( $stripe_source_id ) ) {
266
			$items_removed = true;
267
			delete_user_option( $user->ID, '_stripe_customer_id' );
268
			delete_user_option( $user->ID, '_stripe_source_id' );
269
			$messages[] = __( 'Stripe User Data Erased.', 'woocommerce-gateway-stripe' );
270
		}
271
272
		return array(
273
			'items_removed'  => $items_removed,
274
			'items_retained' => false,
275
			'messages'       => $messages,
276
			'done'           => true,
277
		);
278
	}
279
280
	/**
281
	 * Finds and erases order data by email address.
282
	 *
283
	 * @param string $email_address The user email address.
284
	 * @param int    $page  Page.
285
	 * @return array An array of personal data in name value pairs
286
	 */
287
	public function order_data_eraser( $email_address, $page ) {
288
		$orders = $this->get_stripe_orders( $email_address, (int) $page );
289
290
		$items_removed  = false;
291
		$items_retained = false;
292
		$messages       = array();
293
294
		foreach ( (array) $orders as $order ) {
295
			$order = wc_get_order( $order->get_id() );
296
297
			list( $removed, $retained, $msgs ) = $this->maybe_handle_order( $order );
298
			$items_removed                    |= $removed;
299
			$items_retained                   |= $retained;
300
			$messages                          = array_merge( $messages, $msgs );
301
302
			list( $removed, $retained, $msgs ) = $this->maybe_handle_subscription( $order );
303
			$items_removed                    |= $removed;
304
			$items_retained                   |= $retained;
305
			$messages                          = array_merge( $messages, $msgs );
306
		}
307
308
		// Tell core if we have more orders to work on still
309
		$done = count( $orders ) < 10;
310
311
		return array(
312
			'items_removed'  => $items_removed,
313
			'items_retained' => $items_retained,
314
			'messages'       => $messages,
315
			'done'           => $done,
316
		);
317
	}
318
319
	/**
320
	 * Handle eraser of data tied to Subscriptions
321
	 *
322
	 * @param WC_Order $order
323
	 * @return array
324
	 */
325
	protected function maybe_handle_subscription( $order ) {
326
		if ( ! class_exists( 'WC_Subscriptions' ) ) {
327
			return array( false, false, array() );
328
		}
329
330
		if ( ! wcs_order_contains_subscription( $order ) ) {
331
			return array( false, false, array() );
332
		}
333
334
		$subscription    = current( wcs_get_subscriptions_for_order( $order->get_id() ) );
335
		$subscription_id = $subscription->get_id();
336
337
		$stripe_source_id = get_post_meta( $subscription_id, '_stripe_source_id', true );
338
339
		if ( empty( $stripe_source_id ) ) {
340
			return array( false, false, array() );
341
		}
342
343 View Code Duplication
		if ( ! $this->is_retention_expired( $order->get_date_created()->getTimestamp() ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
344
			/* translators: %d Order ID */
345
			return array( false, true, array( sprintf( __( 'Order ID %d is less than set retention days. Personal data retained. (Stripe)', 'woocommerce-gateway-stripe' ), $order->get_id() ) ) );
346
		}
347
348
		if ( $subscription->has_status( apply_filters( 'wc_stripe_privacy_eraser_subs_statuses', array( 'on-hold', 'active' ) ) ) ) {
349
			/* translators: %d Order ID */
350
			return array( false, true, array( sprintf( __( 'Order ID %d contains an active Subscription. Personal data retained. (Stripe)', 'woocommerce-gateway-stripe' ), $order->get_id() ) ) );
351
		}
352
353
		$renewal_orders = WC_Subscriptions_Renewal_Order::get_renewal_orders( $order->get_id() );
354
355
		foreach ( $renewal_orders as $renewal_order_id ) {
356
			delete_post_meta( $renewal_order_id, '_stripe_source_id' );
357
			delete_post_meta( $renewal_order_id, '_stripe_refund_id' );
358
			delete_post_meta( $renewal_order_id, '_stripe_customer_id' );
359
		}
360
361
		delete_post_meta( $subscription_id, '_stripe_source_id' );
362
		delete_post_meta( $subscription_id, '_stripe_refund_id' );
363
		delete_post_meta( $subscription_id, '_stripe_customer_id' );
364
365
		return array( true, false, array( __( 'Stripe Subscription Data Erased.', 'woocommerce-gateway-stripe' ) ) );
366
	}
367
368
	/**
369
	 * Handle eraser of data tied to Orders
370
	 *
371
	 * @param WC_Order $order
372
	 * @return array
373
	 */
374
	protected function maybe_handle_order( $order ) {
375
		$order_id           = $order->get_id();
376
		$stripe_source_id   = get_post_meta( $order_id, '_stripe_source_id', true );
377
		$stripe_refund_id   = get_post_meta( $order_id, '_stripe_refund_id', true );
378
		$stripe_customer_id = get_post_meta( $order_id, '_stripe_customer_id', true );
379
380 View Code Duplication
		if ( ! $this->is_retention_expired( $order->get_date_created()->getTimestamp() ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
381
			/* translators: %d Order ID */
382
			return array( false, true, array( sprintf( __( 'Order ID %d is less than set retention days. Personal data retained. (Stripe)', 'woocommerce-gateway-stripe' ), $order->get_id() ) ) );
383
		}
384
385
		if ( empty( $stripe_source_id ) && empty( $stripe_refund_id ) && empty( $stripe_customer_id ) ) {
386
			return array( false, false, array() );
387
		}
388
389
		delete_post_meta( $order_id, '_stripe_source_id' );
390
		delete_post_meta( $order_id, '_stripe_refund_id' );
391
		delete_post_meta( $order_id, '_stripe_customer_id' );
392
393
		return array( true, false, array( __( 'Stripe personal data erased.', 'woocommerce-gateway-stripe' ) ) );
394
	}
395
396
	/**
397
	 * Checks if create date is passed retention duration.
398
	 *
399
	 */
400
	public function is_retention_expired( $created_date ) {
401
		$retention  = wc_parse_relative_date_option( get_option( 'woocommerce_gateway_stripe_retention' ) );
402
		$is_expired = false;
403
		$time_span  = time() - strtotime( $created_date );
404
		if ( empty( $retention ) || empty( $created_date ) ) {
405
			return false;
406
		}
407
		switch ( $retention['unit'] ) {
408
			case 'days':
409
				$retention = $retention['number'] * DAY_IN_SECONDS;
410
				if ( $time_span > $retention ) {
411
					$is_expired = true;
412
				}
413
				break;
414
			case 'weeks':
415
				$retention = $retention['number'] * WEEK_IN_SECONDS;
416
				if ( $time_span > $retention ) {
417
					$is_expired = true;
418
				}
419
				break;
420
			case 'months':
421
				$retention = $retention['number'] * MONTH_IN_SECONDS;
422
				if ( $time_span > $retention ) {
423
					$is_expired = true;
424
				}
425
				break;
426
			case 'years':
427
				$retention = $retention['number'] * YEAR_IN_SECONDS;
428
				if ( $time_span > $retention ) {
429
					$is_expired = true;
430
				}
431
				break;
432
		}
433
		return $is_expired;
434
	}
435
}
436
437
new WC_Stripe_Privacy();
438