Passed
Push — develop-3.3.x ( c85e2c...2b4c5a )
by Mario
02:35
created

core::net_amount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 4
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 *
4
 * PayPal Donation extension for the phpBB Forum Software package.
5
 *
6
 * @copyright (c) 2015-2024 Skouat
7
 * @license GNU General Public License, version 2 (GPL-2.0)
8
 *
9
 */
10
11
namespace skouat\ppde\actions;
12
13
use phpbb\config\config;
14
use phpbb\event\dispatcher_interface;
15
use phpbb\language\language;
16
use phpbb\path_helper;
17
use phpbb\user;
18
19
class core
20
{
21
	/**
22
	 * Services properties declaration
23
	 */
24
	public $notification;
25
	protected $config;
26
	protected $dispatcher;
27
	protected $language;
28
	protected $php_ext;
29
	protected $ppde_entity_transaction;
30
	protected $ppde_operator_transaction;
31
	protected $transaction_data;
32
	protected $user;
33
34
	/**
35
	 * @var boolean
36
	 */
37
	private $donor_is_member = false;
38
	/**
39
	 * @var boolean
40
	 */
41
	private $is_ipn_test = false;
42
	/**
43
	 * @var array
44
	 */
45
	private $payer_data = array();
46
	/**
47
	 * phpBB root path
48
	 *
49
	 * @var string
50
	 */
51
	private $root_path;
52
	/**
53
	 * @var string
54
	 */
55
	private $ipn_suffix;
56
57
	/**
58
	 * Constructor
59
	 *
60
	 * @param config                              $config                    Config object
61
	 * @param language                            $language                  Language user object
62
	 * @param \skouat\ppde\notification\core      $notification              PPDE Notification object
63
	 * @param path_helper                         $path_helper               Path helper object
64
	 * @param \skouat\ppde\entity\transactions    $ppde_entity_transaction   Transaction entity object
65
	 * @param \skouat\ppde\operators\transactions $ppde_operator_transaction Transaction operator object
66
	 * @param dispatcher_interface                $dispatcher                Dispatcher object
67
	 * @param user                                $user                      User object
68
	 * @param string                              $php_ext                   phpEx
69
	 *
70
	 * @access public
71
	 */
72
	public function __construct(
73
		config $config,
74
		language $language,
75
		\skouat\ppde\notification\core $notification,
76
		path_helper $path_helper,
77
		\skouat\ppde\entity\transactions $ppde_entity_transaction,
78
		\skouat\ppde\operators\transactions $ppde_operator_transaction,
79
		dispatcher_interface $dispatcher,
80
		user $user,
81
		$php_ext)
82
	{
83
		$this->config = $config;
84
		$this->dispatcher = $dispatcher;
85
		$this->language = $language;
86
		$this->notification = $notification;
87
		$this->ppde_entity_transaction = $ppde_entity_transaction;
88
		$this->ppde_operator_transaction = $ppde_operator_transaction;
89
		$this->php_ext = $php_ext;
90
		$this->root_path = $path_helper->get_phpbb_root_path();
91
		$this->user = $user;
92
	}
93
94
	/**
95
	 * Sets properties related to ipn tests
96
	 *
97
	 * @param bool $ipn_test
98
	 *
99
	 * @return void
100
	 * @access public
101
	 */
102
	public function set_ipn_test_properties($ipn_test): void
103
	{
104
		$this->set_ipn_test($ipn_test);
105
		$this->set_ipn_suffix();
106
	}
107
108
	/**
109
	 * Sets the property $this->is_ipn_test
110
	 *
111
	 * @param bool $ipn_test
112
	 *
113
	 * @return void
114
	 * @access private
115
	 */
116
	private function set_ipn_test($ipn_test): void
117
	{
118
		$this->is_ipn_test = (bool) $ipn_test;
119
	}
120
121
	/**
122
	 * Sets the property $this->ipn_suffix
123
	 *
124
	 * @return void
125
	 * @access private
126
	 */
127
	private function set_ipn_suffix(): void
128
	{
129
		$this->ipn_suffix = $this->is_ipn_test ? '_ipn' : '';
130
	}
131
132
	/**
133
	 * Gets the property $this->ipn_suffix
134
	 *
135
	 * @return string
136
	 * @access private
137
	 */
138
	public function get_ipn_suffix(): string
139
	{
140
		return $this->get_ipn_test() ? $this->ipn_suffix : '';
141
	}
142
143
	/**
144
	 * @return boolean
145
	 * @access private
146
	 */
147
	public function get_ipn_test(): bool
148
	{
149
		return $this->is_ipn_test;
150
	}
151
152
	/**
153
	 * Checks if the donor is a member then gets payer_data values
154
	 *
155
	 * @return void
156
	 * @access public
157
	 */
158
159
	public function is_donor_is_member(): void
160
	{
161
		if ($this->is_donor_anonymous())
162
		{
163
			$this->donor_is_member = $this->check_donor_status_based_on_email($this->transaction_data['payer_email']);
164
165
		}
166
		else
167
		{
168
			$this->donor_is_member = $this->check_donors_status('user', $this->transaction_data['user_id']);
169
		}
170
	}
171
172
	/**
173
	 * @return boolean
174
	 */
175
	public function get_donor_is_member(): bool
176
	{
177
		return $this->donor_is_member;
178
	}
179
180
	/**
181
	 * Determine if the donor is anonymous.
182
	 *
183
	 * @return bool True if the donor is anonymous, false otherwise.
184
	 */
185
	private function is_donor_anonymous(): bool
186
	{
187
		return (int) $this->transaction_data['user_id'] === ANONYMOUS || !$this->check_donors_status('user', $this->transaction_data['user_id']);
188
	}
189
190
	/**
191
	 * Gets donor informations (user id, username, amount donated) and returns if exists
192
	 *
193
	 * @param string     $type Allowed value : 'user' or 'email'
194
	 * @param string|int $args If $type is set to 'user', $args must be a user id.
195
	 *                         If $type is set to 'email', $args must be an email address
196
	 *
197
	 * @return bool
198
	 * @access private
199
	 */
200
	private function check_donors_status($type, $args): bool
201
	{
202
		$this->payer_data = $this->ppde_operator_transaction->query_donor_user_data($type, $args);
203
204
		return (bool) count((array) $this->payer_data);
205
	}
206
207
	/**
208
	 * Checks the donor status based on email.
209
	 *
210
	 * @param string $email The email of the donor.
211
	 * @return bool Returns true if the status of the donor is active, false otherwise.
212
	 */
213
	private function check_donor_status_based_on_email($email): bool
214
	{
215
		if (empty($email))
216
		{
217
			return false;
218
		}
219
220
		return $this->check_donors_status('email', $email);
221
	}
222
223
	/**
224
	 * @return array
225
	 */
226
	public function get_payer_data(): array
227
	{
228
		return (count($this->payer_data) != 0) ? $this->payer_data : [];
229
	}
230
231
	/**
232
	 * Log the transaction to the database
233
	 *
234
	 * @param array $data Transaction data array
235
	 *
236
	 * @return void
237
	 * @access public
238
	 */
239
	public function log_to_db($data): void
240
	{
241
		$this->set_transaction_data($data);
242
		$this->validate_and_set_transaction_data();
243
		$this->ppde_entity_transaction->add_edit_data();
244
	}
245
246
	/**
247
	 * Set Transaction Data array
248
	 *
249
	 * @param array $transaction_data Array of the donation transaction.
250
	 *
251
	 * @return void
252
	 * @access public
253
	 */
254
	public function set_transaction_data(array $transaction_data): void
255
	{
256
		$this->transaction_data = $this->merge_transaction_data($transaction_data);
257
	}
258
259
	/**
260
	 * Merge transaction data if existing transaction data is not empty, else return passed transaction data
261
	 *
262
	 * @param array $transaction_data Array of the donation transaction.
263
	 *
264
	 * @return array Merged or original transaction data
265
	 * @access private
266
	 */
267
	private function merge_transaction_data(array $transaction_data): array
268
	{
269
		return !empty($this->transaction_data)
270
			? array_merge($this->transaction_data, $transaction_data)
271
			: $transaction_data;
272
	}
273
274
	private function validate_and_set_transaction_data(): void
275
	{
276
		// Handle user_id data
277
		$this->extract_user_id();
278
		$this->validate_user_id();
279
280
		// Set username in extra_data property in $entity
281
		$user_ary = $this->ppde_operator_transaction->query_donor_user_data('user', $this->transaction_data['user_id']);
282
		$this->ppde_entity_transaction->set_username($user_ary['username']);
283
284
		// Set 'net_amount' in $this->transaction_data
285
		$this->transaction_data['net_amount'] = $this->net_amount(
286
			$this->transaction_data['mc_gross'],
287
			$this->transaction_data['mc_fee']
288
		);
289
290
		$data = $this->ppde_operator_transaction->build_data_ary($this->transaction_data);
291
292
		// Load data in the entity
293
		$this->ppde_entity_transaction->set_entity_data($data);
294
		$this->ppde_entity_transaction->set_id($this->ppde_entity_transaction->transaction_exists());
295
	}
296
297
	/**
298
	 * Retrieve user_id from custom args
299
	 *
300
	 * @return void
301
	 * @access private
302
	 */
303
	private function extract_user_id(): void
304
	{
305
		[$this->transaction_data['user_id']] = explode('_', substr($this->transaction_data['custom'], 4), -1);
306
	}
307
308
	/**
309
	 * Avoid the user_id to be set to 0
310
	 *
311
	 * @return void
312
	 * @access private
313
	 */
314
	private function validate_user_id(): void
315
	{
316
		if (empty($this->transaction_data['user_id']) || !is_numeric($this->transaction_data['user_id']))
317
		{
318
			$this->transaction_data['user_id'] = ANONYMOUS;
319
		}
320
	}
321
322
	/**
323
	 * Returns the net amount of a donation
324
	 *
325
	 * @param float  $amount
326
	 * @param float  $fee
327
	 * @param string $dec_point
328
	 * @param string $thousands_sep
329
	 *
330
	 * @return string
331
	 * @access public
332
	 */
333
	public function net_amount($amount, $fee, $dec_point = '.', $thousands_sep = ''): string
334
	{
335
		return number_format((float) $amount - (float) $fee, 2, $dec_point, $thousands_sep);
336
	}
337
338
	/**
339
	 * Check we are in the ACP
340
	 *
341
	 * @return bool
342
	 * @access public
343
	 */
344
	public function is_in_admin(): bool
345
	{
346
		return defined('IN_ADMIN') && isset($this->user->data['session_admin']) && $this->user->data['session_admin'];
347
	}
348
349
	/**
350
	 * Perform actions for validated transaction
351
	 *
352
	 * @param bool $is_member
353
	 */
354
	public function do_transactions_actions($is_member): void
355
	{
356
		$this->update_overview_stats();
357
		$this->update_raised_amount();
358
359
		if ($is_member)
360
		{
361
			$this->update_donor_stats();
362
			$this->donors_group_user_add();
363
			$this->notification->notify_donor_donation_received();
364
		}
365
	}
366
367
	/**
368
	 * Updates the Overview module statistics
369
	 *
370
	 * @return void
371
	 * @access public
372
	 */
373
	public function update_overview_stats(): void
374
	{
375
		$this->config->set('ppde_anonymous_donors_count' . $this->ipn_suffix, $this->get_count_result('ppde_anonymous_donors_count' . $this->ipn_suffix));
376
		$this->config->set('ppde_known_donors_count' . $this->ipn_suffix, $this->get_count_result('ppde_known_donors_count' . $this->ipn_suffix), true);
377
		$this->config->set('ppde_transactions_count' . $this->ipn_suffix, $this->get_count_result('ppde_transactions_count' . $this->ipn_suffix), true);
378
	}
379
380
	/**
381
	 * Returns count result for updating stats
382
	 *
383
	 * @param string $config_name
384
	 *
385
	 * @return int
386
	 * @access private
387
	 */
388
	private function get_count_result($config_name): int
389
	{
390
		if (!$this->config->offsetExists($config_name))
391
		{
392
			trigger_error($this->language->lang('EXCEPTION_INVALID_CONFIG_NAME', $config_name), E_USER_WARNING);
393
		}
394
395
		return $this->ppde_operator_transaction->sql_query_count_result($config_name, $this->is_ipn_test);
396
	}
397
398
	/**
399
	 * Updates the amount of donation raised
400
	 *
401
	 * @return void
402
	 * @access public
403
	 */
404
	public function update_raised_amount(): void
405
	{
406
		$net_amount = (float) $this->net_amount($this->transaction_data['mc_gross'], $this->transaction_data['mc_fee']);
407
408
		if (!empty($this->transaction_data['settle_amount']))
409
		{
410
			$net_amount = $this->transaction_data['settle_amount'];
411
		}
412
413
		$this->config->set('ppde_raised' . $this->ipn_suffix, (float) $this->config['ppde_raised' . $this->ipn_suffix] + $net_amount);
414
	}
415
416
	/**
417
	 * Updates donor member stats
418
	 *
419
	 * @return void
420
	 * @access public
421
	 */
422
	public function update_donor_stats(): void
423
	{
424
		if ($this->donor_is_member)
425
		{
426
			$this->update_user_stats((int) $this->payer_data['user_id'], (float) $this->payer_data['user_ppde_donated_amount'] + (float) $this->transaction_data['mc_gross']);
427
		}
428
	}
429
430
	/**
431
	 * @param int   $user_id
432
	 * @param float $amount
433
	 */
434
	public function update_user_stats($user_id, $amount): void
435
	{
436
		if (!$user_id)
437
		{
438
			trigger_error($this->language->lang('EXCEPTION_INVALID_USER_ID', $user_id), E_USER_WARNING);
439
		}
440
441
		$this->ppde_operator_transaction->sql_update_user_stats($user_id, $amount);
442
	}
443
444
	/**
445
	 * Add donor to the donors group
446
	 *
447
	 * @return void
448
	 * @access public
449
	 */
450
	public function donors_group_user_add(): void
451
	{
452
		// We add the user to the donors group
453
		$can_use_autogroup = $this->can_use_autogroup();
454
		$group_id = (int) $this->config['ppde_ipn_group_id'];
455
		$payer_id = (int) $this->payer_data['user_id'];
456
		$payer_username = $this->payer_data['username'];
457
		$default_group = $this->config['ppde_ipn_group_as_default'];
458
		$payer_donated_amount = $this->payer_data['user_ppde_donated_amount'];
459
460
		/**
461
		 * Event to modify data before a user is added to the donors group
462
		 *
463
		 * @event skouat.ppde.donors_group_user_add_before
464
		 * @var bool    can_use_autogroup      Whether or not to add the user to the group
465
		 * @var int     group_id               The ID of the group to which the user will be added
466
		 * @var int     payer_id               The ID of the user who will we added to the group
467
		 * @var string  payer_username         The user name
468
		 * @var bool    default_group          Whether or not the group should be made default for the user
469
		 * @var float   payer_donated_amount   The user donated amount
470
		 * @since 1.0.3
471
		 * @changed 2.1.2 Added var $payer_donated_amount
472
		 */
473
		$vars = [
474
			'can_use_autogroup',
475
			'group_id',
476
			'payer_id',
477
			'payer_username',
478
			'default_group',
479
			'payer_donated_amount',
480
		];
481
		extract($this->dispatcher->trigger_event('skouat.ppde.donors_group_user_add_before', compact($vars)));
482
483
		if ($can_use_autogroup)
484
		{
485
			if (!function_exists('group_user_add'))
486
			{
487
				include($this->root_path . 'includes/functions_user.' . $this->php_ext);
488
			}
489
490
			// Adds the user to the donors group and set as default.
491
			group_user_add($group_id, [$payer_id], [$payer_username], get_group_name($group_id), $default_group);
492
		}
493
	}
494
495
	/**
496
	 * Checks if all required settings are meet for adding the donor to the group of donors
497
	 *
498
	 * @return bool
499
	 * @access private
500
	 */
501
	private function can_use_autogroup(): bool
502
	{
503
		return
504
			$this->autogroup_is_enabled() &&
505
			$this->donor_is_member &&
506
			$this->payment_status_is_completed() &&
507
			$this->minimum_donation_raised();
508
	}
509
510
	/**
511
	 * Checks if Autogroup could be used
512
	 *
513
	 * @return bool
514
	 * @access private
515
	 */
516
	private function autogroup_is_enabled(): bool
517
	{
518
		return $this->config['ppde_ipn_enable'] && $this->config['ppde_ipn_autogroup_enable'];
519
	}
520
521
	/**
522
	 * Checks if payment_status is completed
523
	 *
524
	 * @return bool
525
	 * @access public
526
	 */
527
	public function payment_status_is_completed(): bool
528
	{
529
		return $this->transaction_data['payment_status'] === 'Completed';
530
	}
531
532
	/**
533
	 * Checks if member's donation is upper or equal to the minimum defined
534
	 *
535
	 * @return bool
536
	 * @access public
537
	 */
538
	public function minimum_donation_raised(): bool
539
	{
540
		// Updates payer_data info before checking values
541
		$this->check_donors_status('user', $this->payer_data['user_id']);
542
543
		return (float) $this->payer_data['user_ppde_donated_amount'] >= (float) $this->config['ppde_ipn_min_before_group'];
544
	}
545
}
546