Completed
Pull Request — master (#659)
by Roy
02:11
created

WC_Stripe_Privacy::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 0
dl 0
loc 16
rs 9.4285
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
		array_splice( $settings, ( count( $settings ) - 1 ), 0, $insert_setting );
48
49
		return $settings;
50
	}
51
52
	/**
53
	 * Returns a list of orders that are using one of Stripe's payment methods.
54
	 *
55
	 * @param string  $email_address
56
	 * @param int     $page
57
	 *
58
	 * @return array WP_Post
59
	 */
60
	protected function get_stripe_orders( $email_address, $page ) {
61
		$user = get_user_by( 'email', $email_address ); // Check if user has an ID in the DB to load stored personal data.
62
63
		$order_query    = array(
64
			'payment_method' => array( 'stripe', 'stripe_alipay', 'stripe_bancontact', 'stripe_eps', 'stripe_giropay', 'stripe_ideal', 'stripe_multibanco', 'stripe_p24', 'stripe_sepa', 'stripe_sofort' ),
65
			'limit'          => 10,
66
			'page'           => $page,
67
		);
68
69
		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...
70
			$order_query['customer_id'] = (int) $user->ID;
71
		} else {
72
			$order_query['billing_email'] = $email_address;
73
		}
74
75
		return wc_get_orders( $order_query );
76
	}
77
78
	/**
79
	 * Gets the message of the privacy to display.
80
	 *
81
	 */
82
	public function get_privacy_message() {
83
		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' ) );
84
	}
85
86
	/**
87
	 * Handle exporting data for Orders.
88
	 *
89
	 * @param string $email_address E-mail address to export.
90
	 * @param int    $page          Pagination of data.
91
	 *
92
	 * @return array
93
	 */
94
	public function order_data_exporter( $email_address, $page = 1 ) {
95
		$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...
96
		$data_to_export = array();
97
98
		$orders = $this->get_stripe_orders( $email_address, (int) $page );
99
100
		$done = true;
101
102 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...
103
			foreach ( $orders as $order ) {
104
				$data_to_export[] = array(
105
					'group_id'    => 'woocommerce_orders',
106
					'group_label' => __( 'Orders', 'woocommerce-gateway-stripe' ),
107
					'item_id'     => 'order-' . $order->get_id(),
108
					'data'        => array(
109
						array(
110
							'name'  => __( 'Stripe payment id', 'woocommerce-gateway-stripe' ),
111
							'value' => get_post_meta( $order->get_id(), '_stripe_source_id', true ),
112
						),
113
						array(
114
							'name'  => __( 'Stripe customer id', 'woocommerce-gateway-stripe' ),
115
							'value' => get_post_meta( $order->get_id(), '_stripe_customer_id', true ),
116
						),
117
					),
118
				);
119
			}
120
121
			$done = 10 > count( $orders );
122
		}
123
124
		return array(
125
			'data' => $data_to_export,
126
			'done' => $done,
127
		);
128
	}
129
130
	/**
131
	 * Handle exporting data for Subscriptions.
132
	 *
133
	 * @param string $email_address E-mail address to export.
134
	 * @param int    $page          Pagination of data.
135
	 *
136
	 * @return array
137
	 */
138
	public function subscriptions_data_exporter( $email_address, $page = 1 ) {
139
		$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...
140
		$page           = (int) $page;
141
		$data_to_export = array();
142
143
		$meta_query = array(
144
			'relation'    => 'AND',
145
			array(
146
				'key'     => '_payment_method',
147
				'value'   => array( 'stripe', 'stripe_alipay', 'stripe_bancontact', 'stripe_eps', 'stripe_giropay', 'stripe_ideal', 'stripe_multibanco', 'stripe_p24', 'stripe_sepa', 'stripe_sofort' ),
148
				'compare' => 'IN',
149
			),
150
			array(
151
				'key'     => '_billing_email',
152
				'value'   => $email_address,
153
				'compare' => '=',
154
			),
155
		);
156
157
		$subscription_query    = array(
158
			'posts_per_page'  => 10,
159
			'page'            => $page,
160
			'meta_query'      => $meta_query,
161
		);
162
163
		$subscriptions = wcs_get_subscriptions( $subscription_query );
164
165
		$done = true;
166
167 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...
168
			foreach ( $subscriptions as $subscription ) {
169
				$data_to_export[] = array(
170
					'group_id'    => 'woocommerce_subscriptions',
171
					'group_label' => __( 'Subscriptions', 'woocommerce-gateway-stripe' ),
172
					'item_id'     => 'subscription-' . $subscription->get_id(),
173
					'data'        => array(
174
						array(
175
							'name'  => __( 'Stripe payment id', 'woocommerce-gateway-stripe' ),
176
							'value' => get_post_meta( $subscription->get_id(), '_stripe_source_id', true ),
177
						),
178
						array(
179
							'name'  => __( 'Stripe customer id', 'woocommerce-gateway-stripe' ),
180
							'value' => get_post_meta( $subscription->get_id(), '_stripe_customer_id', true ),
181
						),
182
					),
183
				);
184
			}
185
186
			$done = 10 > count( $subscriptions );
187
		}
188
189
		return array(
190
			'data' => $data_to_export,
191
			'done' => $done,
192
		);
193
	}
194
195
	/**
196
	 * Finds and exports customer data by email address.
197
	 *
198
	 * @param string $email_address The user email address.
199
	 * @param int    $page  Page.
200
	 * @return array An array of personal data in name value pairs
201
	 */
202
	public function customer_data_exporter( $email_address, $page ) {
203
		$user           = get_user_by( 'email', $email_address ); // Check if user has an ID in the DB to load stored personal data.
204
		$data_to_export = array();
205
206
		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...
207
			$stripe_user = new WC_Stripe_Customer( $user->ID );
208
209
			$data_to_export[] = array(
210
				'group_id'    => 'woocommerce_customer',
211
				'group_label' => __( 'Customer Data', 'woocommerce-gateway-stripe' ),
212
				'item_id'     => 'user',
213
				'data'        => array(
214
					array(
215
						'name'  => __( 'Stripe payment id', 'woocommerce-gateway-stripe' ),
216
						'value' => get_user_meta( $user->ID, '_stripe_source_id', true ),
217
					),
218
					array(
219
						'name'  => __( 'Stripe customer id', 'woocommerce-gateway-stripe' ),
220
						'value' => $stripe_user->get_id(),
221
					),
222
				),
223
			);
224
		}
225
226
		return array(
227
			'data' => $data_to_export,
228
			'done' => true,
229
		);
230
	}
231
232
	/**
233
	 * Finds and erases customer data by email address.
234
	 *
235
	 * @param string $email_address The user email address.
236
	 * @param int    $page  Page.
237
	 * @return array An array of personal data in name value pairs
238
	 */
239
	public function customer_data_eraser( $email_address, $page ) {
240
		$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...
241
		$user               = get_user_by( 'email', $email_address ); // Check if user has an ID in the DB to load stored personal data.
242
		$stripe_customer_id = '';
243
		$stripe_source_id   = '';
244
245
		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...
246
			$stripe_customer_id = get_user_meta( $user->ID, '_stripe_customer_id', true );
247
			$stripe_source_id   = get_user_meta( $user->ID, '_stripe_source_id', true );
248
		}
249
250
		$items_removed  = false;
251
		$messages       = array();
252
253
		if ( ! empty( $stripe_customer_id ) || ! empty( $stripe_source_id ) ) {
254
			$items_removed = true;
255
			delete_user_meta( $user->ID, '_stripe_customer_id' );
256
			delete_user_meta( $user->ID, '_stripe_source_id' );
257
			$messages[] = __( 'Stripe User Data Erased.', 'woocommerce-gateway-stripe' );
258
		}
259
260
		return array(
261
			'items_removed'  => $items_removed,
262
			'items_retained' => false,
263
			'messages'       => $messages,
264
			'done'           => true,
265
		);
266
	}
267
268
	/**
269
	 * Finds and erases order data by email address.
270
	 *
271
	 * @param string $email_address The user email address.
272
	 * @param int    $page  Page.
273
	 * @return array An array of personal data in name value pairs
274
	 */
275
	public function order_data_eraser( $email_address, $page ) {
276
		$orders = $this->get_stripe_orders( $email_address, (int) $page );
277
278
		$items_removed  = false;
279
		$items_retained = false;
280
		$messages       = array();
281
282
		foreach ( (array) $orders as $order ) {
283
			$order = wc_get_order( $order->get_id() );
284
285
			list( $removed, $retained, $msgs ) = $this->maybe_handle_order( $order );
286
			$items_removed  |= $removed;
287
			$items_retained |= $retained;
288
			$messages        = array_merge( $messages, $msgs );
289
290
			list( $removed, $retained, $msgs ) = $this->maybe_handle_subscription( $order );
291
			$items_removed  |= $removed;
292
			$items_retained |= $retained;
293
			$messages        = array_merge( $messages, $msgs );
294
		}
295
296
		// Tell core if we have more orders to work on still
297
		$done = count( $orders ) < 10;
298
299
		return array(
300
			'items_removed'  => $items_removed,
301
			'items_retained' => $items_retained,
302
			'messages'       => $messages,
303
			'done'           => $done,
304
		);
305
	}
306
307
	/**
308
	 * Handle eraser of data tied to Subscriptions
309
	 *
310
	 * @param WC_Order $order
311
	 * @return array
312
	 */
313
	protected function maybe_handle_subscription( $order ) {
314
		if ( ! class_exists( 'WC_Subscriptions' ) ) {
315
			return array( false, false, array() );
316
		}
317
318
		if ( ! wcs_order_contains_subscription( $order ) ) {
319
			return array( false, false, array() );
320
		}
321
322
		$subscription    = current( wcs_get_subscriptions_for_order( $order->get_id() ) );
323
		$subscription_id = $subscription->get_id();
324
325
		$stripe_source_id = get_post_meta( $subscription_id, '_stripe_source_id', true );
326
327
		if ( empty( $stripe_source_id ) ) {
328
			return array( false, false, array() );
329
		}
330
331 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...
332
			return array( false, true, array( sprintf( __( 'Order ID %d is less than set retention days. Personal data retained. (Stripe)' ), $order->get_id() ) ) );
333
		}
334
335
		if ( $subscription->has_status( apply_filters( 'wc_stripe_privacy_eraser_subs_statuses', array( 'on-hold', 'active' ) ) ) ) {
336
			return array( false, true, array( sprintf( __( 'Order ID %d contains an active Subscription. Personal data retained. (Stripe)' ), $order->get_id() ) ) );
337
		}
338
339
		$renewal_orders = WC_Subscriptions_Renewal_Order::get_renewal_orders( $order->get_id() );
340
341
		foreach ( $renewal_orders as $renewal_order_id ) {
342
			delete_post_meta( $renewal_order_id, '_stripe_source_id' );
343
			delete_post_meta( $renewal_order_id, '_stripe_refund_id' );
344
			delete_post_meta( $renewal_order_id, '_stripe_customer_id' );
345
		}
346
347
		delete_post_meta( $subscription_id, '_stripe_source_id' );
348
		delete_post_meta( $subscription_id, '_stripe_refund_id' );
349
		delete_post_meta( $subscription_id, '_stripe_customer_id' );
350
351
		return array( true, false, array( __( 'Stripe Subscription Data Erased.', 'woocommerce-gateway-stripe' ) ) );
352
	}
353
354
	/**
355
	 * Handle eraser of data tied to Orders
356
	 *
357
	 * @param WC_Order $order
358
	 * @return array
359
	 */
360
	protected function maybe_handle_order( $order ) {
361
		$order_id           = $order->get_id();
362
		$stripe_source_id   = get_post_meta( $order_id, '_stripe_source_id', true );
363
		$stripe_refund_id   = get_post_meta( $order_id, '_stripe_refund_id', true );
364
		$stripe_customer_id = get_post_meta( $order_id, '_stripe_customer_id', true );
365
366 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...
367
			return array( false, true, array( sprintf( __( 'Order ID %d is less than set retention days. Personal data retained. (Stripe)' ), $order->get_id() ) ) );
368
		}
369
370
		if ( empty( $stripe_source_id ) && empty( $stripe_refund_id ) && empty( $stripe_customer_id ) ) {
371
			return array( false, false, array() );
372
		}
373
374
		delete_post_meta( $order_id, '_stripe_source_id' );
375
		delete_post_meta( $order_id, '_stripe_refund_id' );
376
		delete_post_meta( $order_id, '_stripe_customer_id' );
377
378
		return array( true, false, array( __( 'Stripe personal data erased.', 'woocommerce-gateway-stripe' ) ) );
379
	}
380
381
	/**
382
	 * Checks if create date is passed retention duration.
383
	 *
384
	 */
385
	public function is_retention_expired( $created_date ) {
386
		$retention  = wc_parse_relative_date_option( get_option( 'woocommerce_gateway_stripe_retention' ) );
387
		$is_expired = false;
388
		$time_span  = time() - strtotime( $created_date );
389
		if ( empty( $retention ) || empty( $created_date ) ) {
390
			return false;
391
		}
392
		switch ( $retention['unit'] ) {
393
			case 'days':
394
				$retention = $retention['number'] * DAY_IN_SECONDS;
395
				if ( $time_span > $retention ) {
396
					$is_expired = true;
397
				}
398
				break;
399
			case 'weeks':
400
				$retention = $retention['number'] * WEEK_IN_SECONDS;
401
				if ( $time_span > $retention ) {
402
					$is_expired = true;
403
				}
404
				break;
405
			case 'months':
406
				$retention = $retention['number'] * MONTH_IN_SECONDS;
407
				if ( $time_span > $retention ) {
408
					$is_expired = true;
409
				}
410
				break;
411
			case 'years':
412
				$retention = $retention['number'] * YEAR_IN_SECONDS;
413
				if ( $time_span > $retention ) {
414
					$is_expired = true;
415
				}
416
				break;
417
		}
418
		return $is_expired;
419
	}
420
}
421
422
new WC_Stripe_Privacy();
423