core::minimum_donation_raised()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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