RApiPaymentHelper::saveNewPaymentLog()   A
last analyzed

Complexity

Conditions 6
Paths 7

Size

Total Lines 31
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
c 0
b 0
f 0
dl 0
loc 31
rs 9.2222
cc 6
nc 7
nop 2
1
<?php
2
/**
3
 * @package     Redcore
4
 * @subpackage  Api
5
 *
6
 * @copyright   Copyright (C) 2008 - 2021 redWEB.dk. All rights reserved.
7
 * @license     GNU General Public License version 2 or later, see LICENSE.
8
 */
9
10
defined('JPATH_BASE') or die;
11
12
use Joomla\Utilities\ArrayHelper;
13
14
/**
15
 * Helper class for Payment calls
16
 *
17
 * @package     Redcore
18
 * @subpackage  Api
19
 * @since       1.5
20
 */
21
class RApiPaymentHelper
22
{
23
	/**
24
	 * Payments container
25
	 * @var array
26
	 */
27
	protected static $payments = array();
28
29
	/**
30
	 * Extension payments container
31
	 * @var array
32
	 */
33
	protected static $extensionPayments = array();
34
35
	/**
36
	 * Plugin parameters container
37
	 * @var array
38
	 */
39
	protected static $pluginParams = array();
40
41
	/**
42
	 * Extension helper classes container
43
	 * @var array
44
	 */
45
	protected static $extensionHelperClasses = array();
46
47
	/**
48
	 * Gets Payment parameters
49
	 * If owner name config is not found it will use extension config, and if extension config is not found it will use default plugin config
50
	 *
51
	 * @param   string  $paymentName    Payment Name
52
	 * @param   string  $extensionName  Extension Name
53
	 * @param   string  $ownerName      Owner Name
54
	 *
55
	 * @return  object
56
	 */
57
	public static function getPaymentParams($paymentName = '', $extensionName = '', $ownerName = '')
58
	{
59
		if (isset(self::$pluginParams[$paymentName][$extensionName][$ownerName]))
60
		{
61
			return self::$pluginParams[$paymentName][$extensionName][$ownerName];
62
		}
63
64
		// This query will make a fallback
65
		// If owner name config is not found it will use extension config, and if extension config is not found it will use default plugin config
66
		$db = JFactory::getDbo();
67
		$query = $db->getQuery(true)
68
			->select('pc1.*, p.*, COALESCE(pc2.params, COALESCE(pc1.params, p.params)) as params, COALESCE(pc2.state, COALESCE(pc1.state, p.state)) as state')
69
			->select('CONCAT("plg_redpayment_", p.element) as plugin_path_name')
70
			->select('COALESCE(pc2.extension_name, COALESCE(pc1.extension_name, ' . $db->q('') . ')) as extension_name')
71
			->select('COALESCE(pc2.owner_name, COALESCE(pc1.owner_name, ' . $db->q('') . ')) as owner_name')
72
			->select('COALESCE(pc2.state, COALESCE(pc1.state, p.enabled)) as state')
73
			->select('p.params AS original_params')
74
			->from($db->qn('#__extensions', 'p'))
75
			->where($db->qn('p.type') . '= ' . $db->q('plugin'))
76
			->where($db->qn('p.folder') . '= ' . $db->q('redpayment'))
77
			->where($db->qn('p.element') . ' = ' . $db->q($paymentName))
78
			->leftJoin(
79
				$db->qn('#__redcore_payment_configuration', 'pc1') . ' ON pc1.payment_name = p.element AND pc1.extension_name = ' . $db->q($extensionName)
80
				. ' AND pc1.owner_name = ' . $db->q('')
81
			)
82
			->leftJoin(
83
				$db->qn('#__redcore_payment_configuration', 'pc2') . ' ON pc2.payment_name = p.element AND pc2.extension_name = ' . $db->q($extensionName)
84
				. ' AND pc2.owner_name = ' . $db->q($ownerName)
85
			);
86
87
		$db->setQuery($query);
88
		$item = $db->loadObject();
89
90
		$registry = new Joomla\Registry\Registry;
91
		$registry->loadString($item->original_params);
92
		$item->original_params = $registry;
93
		$originalParams = clone $registry;
94
95
		$registry = new Joomla\Registry\Registry;
96
		$registry->loadString($item->params);
97
		$originalParams->merge($registry);
98
		$item->params = $originalParams;
99
100
		self::$pluginParams[$paymentName][$extensionName][$ownerName] = $item;
101
102
		return self::$pluginParams[$paymentName][$extensionName][$ownerName];
103
	}
104
105
	/**
106
	 * Gets Payment data
107
	 *
108
	 * @param   int  $paymentId  Payment Id
109
	 *
110
	 * @return  object
111
	 */
112
	public static function getPaymentById($paymentId)
113
	{
114
		if (empty($paymentId))
115
		{
116
			return null;
117
		}
118
119
		if (isset(self::$payments[$paymentId]))
120
		{
121
			return self::$payments[$paymentId];
122
		}
123
124
		$db = JFactory::getDbo();
125
126
		$query = $db->getQuery(true)
127
			->select('p.*')
128
			->from($db->qn('#__redcore_payments', 'p'))
129
			->where('p.id = ' . (int) $paymentId);
130
		$db->setQuery($query);
131
		$item = $db->loadObject();
132
		self::$payments[$paymentId] = $item;
133
134
		if ($item && !empty($item->extension_name))
135
		{
136
			self::$extensionPayments[$item->extension_name][$item->order_id] = $paymentId;
137
		}
138
139
		return self::$payments[$paymentId];
140
	}
141
142
	/**
143
	 * Gets Payment data
144
	 *
145
	 * @param   string  $extensionName  Extension name (ex: com_content)
146
	 * @param   string  $orderId        Extension order Id
147
	 *
148
	 * @return  object
149
	 */
150
	public static function getPaymentByExtensionId($extensionName, $orderId)
151
	{
152
		if (empty($extensionName) || empty($orderId))
153
		{
154
			return null;
155
		}
156
157
		if (isset(self::$extensionPayments[$extensionName][$orderId]))
158
		{
159
			return self::getPaymentById(self::$extensionPayments[$extensionName][$orderId]);
160
		}
161
162
		$db = JFactory::getDbo();
163
164
		$query = $db->getQuery(true)
165
			->select('p.*')
166
			->from($db->qn('#__redcore_payments', 'p'))
167
			->where('p.order_id = ' . $db->q($orderId))
168
			->where('p.extension_name = ' . $db->q($extensionName));
169
		$db->setQuery($query);
170
		$payment = $db->loadObject();
171
172
		if ($payment && $payment->id)
173
		{
174
			self::$payments[$payment->id] = $payment;
175
			self::$extensionPayments[$extensionName][$orderId] = $payment->id;
176
177
			return self::getPaymentById(self::$extensionPayments[$extensionName][$orderId]);
178
		}
179
180
		return null;
181
	}
182
183
	/**
184
	 * Prepare Payment data for chart
185
	 *
186
	 * @param   array   $data       Data used for chart definition
187
	 * @param   string  $chartType  Chart types: Line, Bar, Radar, PolarArea, Pie, Doughnut
188
	 *
189
	 * @return  string
190
	 *
191
	 * @since   1.5
192
	 */
193
	public static function prepareChartData($data, $chartType = 'Line')
194
	{
195
		$chartType = RHtmlRchart::getChartType($chartType);
196
		$chartData = array();
197
		$amounts = $data['amounts'];
198
		$labels  = $data['labels'];
199
200
		switch ($chartType)
201
		{
202
			case 'PolarArea':
203
			case 'Pie':
204
			case 'Doughnut':
205
206
				foreach ($amounts as $extensionName => $amount)
207
				{
208
					$dataValues = 0;
209
					$color = implode(',', RHtmlRchart::getColorFromHash($extensionName));
210
					$strokeColor = implode(',', RHtmlRchart::getColorFromHash($extensionName, 'redcore'));
211
212
					foreach ($amount as $value)
213
					{
214
						$dataValues += $value['sum'];
215
					}
216
217
					$dataSet = new stdClass;
218
					$dataSet->value = $dataValues;
219
					$dataSet->color = 'rgba(' . $color . ',0.5)';
220
					$dataSet->highlight = 'rgba(' . $strokeColor . ',1)';
221
					$dataSet->label = $extensionName;
222
223
					$chartData[] = $dataSet;
224
				}
225
226
				break;
227
228
			case 'Line':
229
			case 'Radar':
230
			case 'Bar':
231
			default:
232
				$chartData['labels'] = $labels;
233
				$chartData['datasets'] = array();
234
235
				if (empty($amounts))
236
				{
237
					// Needed for proper chart display
238
					$chartData['datasets'] = array(array());
239
				}
240
				else
241
				{
242
					foreach ($amounts as $extensionName => $amount)
243
					{
244
						$dataValues = array();
245
						$color = implode(',', RHtmlRchart::getColorFromHash($extensionName));
246
						$strokeColor = implode(',', RHtmlRchart::getColorFromHash($extensionName, 'redcore'));
247
248
						foreach ($chartData['labels'] as $label)
249
						{
250
							$dataValues[] = !isset($amount[$label]) ? 0 : $amount[$label];
251
						}
252
253
						$dataSet = array(
254
							'label' => $extensionName,
255
							'fillColor' => 'rgba(' . $color . ',0.2)',
256
							'strokeColor' => 'rgba(' . $strokeColor . ',1)',
257
							'data' => $dataValues,
258
						);
259
260
						if ($chartType == 'Bar')
261
						{
262
							$dataSet['highlightFill'] = 'rgba(' . $color . ',0.75)';
263
							$dataSet['highlightStroke'] = 'rgba(' . $color . ',1)';
264
						}
265
						else
266
						{
267
							$dataSet['pointColor'] = 'rgba(' . $color . ',1)';
268
							$dataSet['pointStrokeColor'] = '#fff';
269
							$dataSet['pointHighlightFill'] = '#fff';
270
							$dataSet['pointHighlightStroke'] = 'rgba(' . $color . ',1)';
271
						}
272
273
						$chartData['datasets'][] = $dataSet;
274
					}
275
				}
276
277
				break;
278
		}
279
280
		return $chartData;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $chartData returns the type array|stdClass[] which is incompatible with the documented return type string.
Loading history...
281
	}
282
283
	/**
284
	 * Prepare Payment data for chart
285
	 *
286
	 * @param   array   $filters   Filters for chart data
287
	 * @param   int     $interval  Chart interval points
288
	 * @param   string  $sortItem  On which field should it sort the values
289
	 *
290
	 * @return  mixed
291
	 *
292
	 * @since   1.5
293
	 */
294
	public static function getChartData($filters = array(), $interval = 7, $sortItem = 'all')
295
	{
296
		$db = JFactory::getDbo();
297
298
		$db->setQuery(self::getChartDataQuery($filters));
299
		$data = $db->loadObjectList();
300
		$chartData = array();
301
		$chartData['amounts'] = array();
302
		$chartData['labels'] = array();
303
		$chartData['currency'] = 'USD';
304
305
		// Is status is not set then we are in the statuses view graph and we are not looking only confirmed payments
306
		$dateField = !isset($filters['status']) ? 'created_date' : 'confirmed_date';
307
308
		if (empty($filters['start_date']))
309
		{
310
			if (!empty($filters['end_date']))
311
			{
312
				$startDate = date('Y-m-d', strtotime($filters['end_date'] . ' -7 weeks'));
313
			}
314
			else
315
			{
316
				$startDate = date('Y-m-d', strtotime('today -7 weeks'));
317
			}
318
		}
319
		else
320
		{
321
			$startDate = $filters['start_date'];
322
		}
323
324
		if (empty($filters['end_date']))
325
		{
326
			$endDate = date('Y-m-d', strtotime($startDate . ' +7 weeks'));
327
		}
328
		else
329
		{
330
			$endDate = $filters['end_date'];
331
		}
332
333
		$startDateNumber = strtotime($startDate);
334
		$endDateNumber = strtotime($endDate);
335
		$checkPoints = array();
336
		$chartData['days'] = round(($endDateNumber - $startDateNumber) / 86400);
337
		$point = round($chartData['days'] / $interval);
338
339
		for ($i = $interval; $i >= 1; $i--)
340
		{
341
			$startDateNumber = strtotime($endDate . ' -' . ($i * $point) . ' days');
342
			$endDateNumber = strtotime($endDate . ' -' . (($i - 1) * $point) . ' days');
343
344
			$startDatelabel = date('Y-m-d', $startDateNumber);
345
			$endDateLabel = date('Y-m-d', $endDateNumber);
346
			$chartData['labels'][] = $startDatelabel;
347
348
			$checkPoints[] = array(
349
				'startDate' => $startDatelabel,
350
				'endDate' => $endDateLabel,
351
			);
352
		}
353
354
		foreach ($data as $key => $item)
355
		{
356
			$sortName = $sortItem == 'all' ? 'all' : $item->{$sortItem};
357
358
			if (!isset($chartData['amounts'][$sortName]))
359
			{
360
				$chartData['amounts'][$sortName] = array();
361
			}
362
363
			$createdDate = explode('-', $item->{$dateField});
364
			$itemYear = $createdDate[0];
365
			$itemMonth = $createdDate[1];
366
			$itemDay = explode(' ', $createdDate[2]);
367
			$itemDay = $itemDay[0];
368
369
			if (!isset($chartData['amounts'][$sortName]['sum'][$itemYear]['val'][$itemMonth]['val'][$itemDay]))
370
			{
371
				$chartData['amounts'][$sortName]['sum'][$itemYear]['sum'] = 0;
372
				$chartData['amounts'][$sortName]['sum'][$itemYear]['count'] = 0;
373
				$chartData['amounts'][$sortName]['sum'][$itemYear]['val'][$itemMonth]['sum'] = 0;
374
				$chartData['amounts'][$sortName]['sum'][$itemYear]['val'][$itemMonth]['count'] = 0;
375
				$chartData['amounts'][$sortName]['sum'][$itemYear]['val'][$itemMonth]['val'][$itemDay]['sum'] = 0;
376
				$chartData['amounts'][$sortName]['sum'][$itemYear]['val'][$itemMonth]['val'][$itemDay]['count'] = 0;
377
				$chartData['amounts'][$sortName]['sum'][$itemYear]['val'][$itemMonth]['val'][$itemDay]['val'] = array();
378
			}
379
380
			$chartData['amounts'][$sortName]['sum'][$itemYear]['val'][$itemMonth]['val'][$itemDay]['val'][] = $item->amount_paid;
381
			$chartData['currency'] = $item->currency;
382
383
			foreach ($checkPoints as $checkPoint)
384
			{
385
				if ($item->{$dateField} > $checkPoint['startDate'] && $item->{$dateField} < $checkPoint['endDate'])
386
				{
387
					if (!isset($chartData['amounts'][$sortName][$checkPoint['startDate']]))
388
					{
389
						$chartData['amounts'][$sortName][$checkPoint['startDate']] = 0;
390
					}
391
392
					$chartData['amounts'][$sortName][$checkPoint['startDate']] += $item->amount_paid;
393
				}
394
			}
395
396
			unset($data[$key]);
397
		}
398
399
		$currentYear = date('Y');
400
401
		foreach ($chartData['amounts'] as $extensionName => $options)
402
		{
403
			$sum = 0;
404
			$count = 0;
405
			$maxCount = 0;
406
			$maxSum = 0;
407
408
			foreach ($options['sum'] as $year => $months)
409
			{
410
				foreach ($months['val'] as $month => $days)
411
				{
412
					foreach ($days['val'] as $day => $values)
413
					{
414
						$daySum = array_sum($values['val']);
415
						$dayCount = count($values['val']);
416
						$chartData['amounts'][$extensionName]['sum'][$year]['sum'] += $daySum;
417
						$chartData['amounts'][$extensionName]['sum'][$year]['count'] += $dayCount;
418
						$chartData['amounts'][$extensionName]['sum'][$year]['val'][$month]['sum'] += $daySum;
419
						$chartData['amounts'][$extensionName]['sum'][$year]['val'][$month]['count'] += $dayCount;
420
						$chartData['amounts'][$extensionName]['sum'][$year]['val'][$month]['val'][$day]['sum'] += $daySum;
421
						$chartData['amounts'][$extensionName]['sum'][$year]['val'][$month]['val'][$day]['count'] = $dayCount;
422
						$chartData['amounts'][$extensionName]['sum'][$year]['val'][$month]['val'][$day]['average'] = round($daySum / $dayCount, 2);
423
						$sum += $daySum;
424
						$count += $dayCount;
425
426
						if ($maxCount < $dayCount)
427
						{
428
							$maxCount = $dayCount;
429
						}
430
431
						if ($maxSum < $daySum)
432
						{
433
							$maxSum = $daySum;
434
						}
435
					}
436
437
					$daysInMonth = date('t', strtotime($year . '-' . $month . '-01'));
438
					$chartData['amounts'][$extensionName]['sum'][$year]['val'][$month]['averageCount']
439
									= round($chartData['amounts'][$extensionName]['sum'][$year]['val'][$month]['count'] / $daysInMonth, 2);
440
					$chartData['amounts'][$extensionName]['sum'][$year]['val'][$month]['averageSum']
441
									= round($chartData['amounts'][$extensionName]['sum'][$year]['val'][$month]['sum'] / $daysInMonth, 2);
442
				}
443
444
				// If this is current year then it is not over yet we need to get only up to current date
445
				if ($currentYear == $year)
446
				{
447
					$daysInYear = round((strtotime(date('Y-m-d')) - strtotime($year . '-01-01')) / 86400);
448
				}
449
				else
450
				{
451
					$daysInYear = date('z', strtotime($year . '-12-31')) + 1;
452
				}
453
454
				$chartData['amounts'][$extensionName]['sum'][$year]['averageCount']
455
								= round($chartData['amounts'][$extensionName]['sum'][$year]['count'] / $daysInYear, 2);
456
				$chartData['amounts'][$extensionName]['sum'][$year]['averageSum']
457
								= round($chartData['amounts'][$extensionName]['sum'][$year]['sum'] / $daysInYear, 2);
458
			}
459
460
			$chartData['amounts'][$extensionName]['sum']['sum'] = $sum;
461
			$chartData['amounts'][$extensionName]['sum']['count'] = $count;
462
			$chartData['amounts'][$extensionName]['sum']['maxCount'] = $maxCount;
463
			$chartData['amounts'][$extensionName]['sum']['maxSum'] = $maxSum;
464
			$chartData['amounts'][$extensionName]['sum']['averageCount'] = $chartData['days'] > 0 ? round($count / $chartData['days'], 2) : 0;
465
			$chartData['amounts'][$extensionName]['sum']['averageSum'] = $chartData['days'] > 0 ? round($sum / $chartData['days'], 2) : 0;
466
		}
467
468
		return $chartData;
469
	}
470
471
	/**
472
	 * Create Payments query for chart
473
	 *
474
	 * @param   array  $filters  Filters for chart data
475
	 *
476
	 * @return  mixed
477
	 *
478
	 * @since   1.5
479
	 */
480
	public static function getChartDataQuery($filters = array())
481
	{
482
		$db = JFactory::getDbo();
483
484
		$query = $db->getQuery(true)
485
			->select('p.*')
486
			->from($db->qn('#__redcore_payments', 'p'));
487
488
		if (RBootstrap::getConfig('payment_enable_chart_sandbox_payments', 1) == 0)
489
		{
490
			$query->where('p.sandbox = 0');
491
		}
492
493
		// Is status is not set then we are in the statuses view graph and we are not looking only confirmed payments
494
		$dateField = !isset($filters['status']) ? 'created_date' : 'confirmed_date';
495
496
		// Filter search
497
		if (!empty($filters['search_payments']))
498
		{
499
			$search = $db->quote('%' . $db->escape($filters['search_payments'], true) . '%');
500
			$query->where('(p.order_name LIKE ' . $search . ')');
501
		}
502
503
		if (!empty($filters['payment_name']))
504
		{
505
			$paymentName = $db->quote($filters['payment_name']);
506
			$query->where('p.payment_name = ' . $paymentName);
0 ignored issues
show
Bug introduced by
Are you sure $paymentName of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

506
			$query->where('p.payment_name = ' . /** @scrutinizer ignore-type */ $paymentName);
Loading history...
507
		}
508
509
		if (!empty($filters['extension_name']))
510
		{
511
			$extensionName = $db->quote($filters['extension_name']);
512
			$query->where('p.extension_name = ' . $extensionName);
0 ignored issues
show
Bug introduced by
Are you sure $extensionName of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

512
			$query->where('p.extension_name = ' . /** @scrutinizer ignore-type */ $extensionName);
Loading history...
513
		}
514
515
		if (!empty($filters['owner_name']))
516
		{
517
			$ownerName = $db->quote($filters['owner_name']);
518
			$query->where('p.owner_name = ' . $ownerName);
0 ignored issues
show
Bug introduced by
Are you sure $ownerName of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

518
			$query->where('p.owner_name = ' . /** @scrutinizer ignore-type */ $ownerName);
Loading history...
519
		}
520
521
		elseif (!empty($filters['start_date']))
522
		{
523
			$filters['start_date'] = date('Y-m-d H:i:s', strtotime($filters['start_date']));
524
			$startDate = $db->quote($filters['start_date']);
525
			$query->where('p.' . $dateField . ' >= ' . $startDate);
0 ignored issues
show
Bug introduced by
Are you sure $startDate of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

525
			$query->where('p.' . $dateField . ' >= ' . /** @scrutinizer ignore-type */ $startDate);
Loading history...
526
		}
527
528
		if (!empty($filters['end_date']))
529
		{
530
			$filters['end_date'] = date('Y-m-d H:i:s', strtotime($filters['end_date']));
531
			$endDate = $db->quote($filters['end_date']);
532
			$query->where('p.' . $dateField . ' <= ' . $endDate);
0 ignored issues
show
Bug introduced by
Are you sure $endDate of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

532
			$query->where('p.' . $dateField . ' <= ' . /** @scrutinizer ignore-type */ $endDate);
Loading history...
533
		}
534
535
		if (!empty($filters['status']))
536
		{
537
			$status = $db->quote($filters['status']);
538
			$query->where('p.status = ' . $status);
0 ignored issues
show
Bug introduced by
Are you sure $status of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

538
			$query->where('p.status = ' . /** @scrutinizer ignore-type */ $status);
Loading history...
539
		}
540
541
		// Ordering
542
		$order = !empty($filters['ordering']) ? $filters['ordering'] : 'p.payment_name';
543
		$direction = !empty($filters['direction']) ? $filters['direction'] : 'ASC';
544
		$query->order($db->escape($order) . ' ' . $db->escape($direction));
545
546
		return $query;
547
	}
548
549
	/**
550
	 * Update payment status
551
	 *
552
	 * @param   int  $paymentId     Id of the payment
553
	 * @param   int  $retryCounter  Id of the payment
554
	 *
555
	 * @return  bool
556
	 *
557
	 * @since   1.5
558
	 */
559
	public static function updatePaymentCounter($paymentId, $retryCounter)
560
	{
561
		$paymentOriginal = self::getPaymentById($paymentId);
562
		$paymentOriginal->retry_counter = $retryCounter;
563
564
		return self::updatePaymentData($paymentOriginal);
565
	}
566
567
	/**
568
	 * Update payment status
569
	 *
570
	 * @param   mixed  $paymentData  Payment data
571
	 *
572
	 * @return  bool
573
	 */
574
	public static function updatePaymentData($paymentData)
575
	{
576
		if (is_object($paymentData))
577
		{
578
			$paymentData = ArrayHelper::fromObject($paymentData);
579
		}
580
581
		// If there is no payment Id, we are checking if that payment data is saved under another row
582
		if (empty($paymentData['id']))
583
		{
584
			$oldPayment = self::getPaymentByExtensionId($paymentData['extension_name'], $paymentData['order_id']);
585
586
			// We add all relevant data to the object
587
			if ($oldPayment)
0 ignored issues
show
introduced by
$oldPayment is of type object, thus it always evaluated to true.
Loading history...
588
			{
589
				$paymentData['id'] = $oldPayment->id;
590
			}
591
		}
592
		else
593
		{
594
			$oldPayment = self::getPaymentById($paymentData['id']);
595
		}
596
597
		if ($oldPayment)
0 ignored issues
show
introduced by
$oldPayment is of type object, thus it always evaluated to true.
Loading history...
598
		{
599
			// We are in different payment
600
			if (isset($paymentData['payment_name']) && !empty($oldPayment->payment_name) && $oldPayment->payment_name != $paymentData['payment_name'])
601
			{
602
				// We check for status in old Payment data, if it is confirmed, then this order is already processed and should not be processed again
603
				if (in_array($oldPayment->status, array(RApiPaymentStatus::getStatusCompleted(), RApiPaymentStatus::getStatusCanceled_Reversal())))
604
				{
605
					return false;
606
				}
607
			}
608
		}
609
610
		// Set status to created if not set
611
		if (empty($paymentData['status']))
612
		{
613
			$paymentData['status'] = RApiPaymentStatus::getStatusCreated();
614
		}
615
616
		/** @var RedcoreModelPayment $model */
617
		$model = RModelAdmin::getAdminInstance('Payment', array(), 'com_redcore');
618
619
		if (!$model->save($paymentData))
620
		{
621
			return false;
622
		}
623
624
		$paymentId = (isset($paymentData['id']) ? $paymentData['id'] : (int) $model->getState('payment.id'));
625
626
		if (!empty($paymentData['id']))
627
		{
628
			// We unset the object so we can load a fresh one on next request
629
			unset(self::$payments[$paymentData['id']]);
630
		}
631
632
		return $paymentId;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $paymentId also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
633
	}
634
635
	/**
636
	 * Update payment status
637
	 *
638
	 * @param   int  $paymentId  Id of the payment
639
	 *
640
	 * @return  mixed
641
	 *
642
	 * @since   1.5
643
	 */
644
	public static function updatePaymentStatus($paymentId)
645
	{
646
		if (empty($paymentId))
647
		{
648
			return false;
649
		}
650
651
		$db = JFactory::getDbo();
652
653
		$query = $db->getQuery(true)
654
			->select('pl.*')
655
			->from($db->qn('#__redcore_payment_log', 'pl'))
656
			->where('pl.payment_id = ' . (int) $paymentId)
657
			->order('pl.created_date ASC');
658
659
		$db->setQuery($query);
660
		$paymentLogs = $db->loadObjectList();
661
662
		if ($paymentLogs)
663
		{
664
			$paymentOriginal = self::getPaymentById($paymentId);
665
			$payment = ArrayHelper::fromObject($paymentOriginal);
666
			$customerNote = array();
667
			$amountPaid = 0;
668
			$currency = '';
669
			$status = RApiPaymentStatus::getStatusCreated();
670
671
			foreach ($paymentLogs as $paymentLog)
672
			{
673
				if ($paymentLog->status == RApiPaymentStatus::getStatusCompleted())
674
				{
675
					if (!empty($currency) && $currency != $paymentLog->currency)
676
					{
677
						// We have a problem. Two different confirmed payments but in different currencies.
678
						// We will only set latest payment data
679
						$amountPaid = $paymentLog->amount;
680
						$currency = $paymentLog->currency;
681
					}
682
					else
683
					{
684
						if ($payment['transaction_id'] != $paymentLog->transaction_id || $amountPaid == 0)
685
						{
686
							$amountPaid += $paymentLog->amount;
687
						}
688
689
						if (!empty($paymentLog->currency))
690
						{
691
							$currency = $paymentLog->currency;
692
						}
693
					}
694
695
					$payment['coupon_code'] = $paymentLog->coupon_code;
696
					$payment['confirmed_date'] = $paymentLog->created_date;
697
				}
698
699
				if (!empty($paymentLog->transaction_id))
700
				{
701
					$payment['transaction_id'] = $paymentLog->transaction_id;
702
				}
703
704
				// We will take customer note from every log but not duplicates
705
				if (!empty($paymentLog->customer_note) && !in_array($paymentLog->customer_note, $customerNote))
706
				{
707
					$customerNote[] = $paymentLog->customer_note;
708
				}
709
710
				$status = RApiPaymentStatus::changeStatus($status, $paymentLog->status);
711
712
				// Handle statuses that subtract paid amount
713
				if (in_array($status, array(RApiPaymentStatus::getStatusRefunded(), RApiPaymentStatus::getStatusReversed())))
714
				{
715
					$amountPaid -= $paymentLog->amount;
716
717
					if ($amountPaid < 0)
718
					{
719
						$amountPaid = 0;
720
					}
721
				}
722
			}
723
724
			$payment['amount_paid'] = $amountPaid;
725
			$payment['customer_note'] = implode("\r\n", $customerNote);
726
727
			if ($status == RApiPaymentStatus::getStatusCompleted() && $payment['amount_paid'] != $payment['amount_total'])
728
			{
729
				// If not ePay partial payment capture
730
				if (self::isInstantCapture((object) $payment) || $payment['amount_original'] <= $payment['amount_total'])
731
				{
732
					$status = RApiPaymentStatus::getStatusPending();
733
				}
734
			}
735
736
			$payment['status'] = $status;
737
738
			if (empty($payment['currency']))
739
			{
740
				$payment['currency'] = $currency;
741
			}
742
743
			// Currency should not be numeric
744
			if (!empty($payment['currency']) && is_numeric($payment['currency']))
745
			{
746
				$payment['currency'] = RHelperCurrency::getIsoCode($payment['currency']);
747
			}
748
749
			if (!self::updatePaymentData($payment))
750
			{
751
				return false;
752
			}
753
754
			// If we changed status we need to call extension helper file to trigger its update method
755
			if ($paymentOriginal->status != $payment['status'])
756
			{
757
				$paymentNew = self::getPaymentById($paymentId);
758
759
				self::triggerExtensionHelperMethod($paymentNew->extension_name, 'paymentStatusChanged', $paymentOriginal, $paymentNew);
760
			}
761
		}
762
763
		return true;
764
	}
765
766
	/**
767
	 * Check if ePay is using  instant capture
768
	 *
769
	 * @param   object	$payment	Payment object
770
	 *
771
	 * @return  boolean
772
	 */
773
	public static function isInstantCapture($payment)
774
	{
775
		$plugin 		= RApiPaymentHelper::getPaymentParams($payment->payment_name, $payment->extension_name, $payment->owner_name);
776
		$instantcapture = false;
777
778
		if ($plugin && strcmp($plugin->element, 'epay') === 0)
779
		{
780
			$instantcapture = (boolean) $plugin->params->get('instantcapture', 0);
781
		}
782
783
		return $instantcapture;
784
	}
785
786
	/**
787
	 * Display payment
788
	 *
789
	 * @param   string  $paymentName    Payment name
790
	 * @param   string  $extensionName  Extension name
791
	 * @param   string  $ownerName      Owner name
792
	 * @param   array   $data           Data for the payment form
793
	 *
794
	 * @return  string|false
795
	 *
796
	 * @since   1.5
797
	 */
798
	public static function displayPayment($paymentName, $extensionName, $ownerName = '', $data = array())
799
	{
800
		JPluginHelper::importPlugin('redpayment');
801
		$app = JFactory::getApplication();
802
		$html = '';
803
		$app->triggerEvent('onRedpaymentDisplayPayment', array($paymentName, $extensionName, $ownerName, $data, &$html));
804
805
		return $html;
806
	}
807
808
	/**
809
	 * Create new payment
810
	 *
811
	 * @param   string  $paymentName    Payment name
812
	 * @param   string  $extensionName  Extension name
813
	 * @param   string  $ownerName      Owner name
814
	 * @param   array   $data           Data for the payment form
815
	 *
816
	 * @return  string|false
817
	 *
818
	 * @since   1.5
819
	 */
820
	public static function createNewPayment($paymentName, $extensionName, $ownerName, $data = array())
821
	{
822
		JPluginHelper::importPlugin('redpayment');
823
		$app = JFactory::getApplication();
824
		$paymentId = null;
825
		$app->triggerEvent('onRedpaymentCreatePayment', array($paymentName, $extensionName, $ownerName, $data, &$paymentId));
826
827
		return $paymentId;
828
	}
829
830
	/**
831
	 * List payments
832
	 *
833
	 * @param   string  $extensionName  Extension name
834
	 * @param   string  $ownerName      Owner name
835
	 * @param   string  $listType       List type can be between radio and dropdown (if parameter not set then default redcore plugin option is used)
836
	 * @param   string  $name           Name of the field
837
	 * @param   string  $value          Selected value of the field
838
	 * @param   string  $id             Id of the field
839
	 * @param   string  $attributes     Attributes for the field
840
	 *
841
	 * @return  string|false
842
	 *
843
	 * @since   1.5
844
	 */
845
	public static function listPayments($extensionName = null, $ownerName = null, $listType = null, $name = '', $value = '', $id = '', $attributes = '')
846
	{
847
		JPluginHelper::importPlugin('redpayment');
848
		$app = JFactory::getApplication();
849
850
		if (empty($listType))
851
		{
852
			$listType = RBootstrap::getConfig('payment_list_payments_type', 'radio');
853
		}
854
855
		if (empty($extensionName))
856
		{
857
			$extensionName = $app->input->get->getString('option', '');
858
		}
859
860
		$payments = array();
861
862
		$app->triggerEvent('onRedpaymentListPayments', array($extensionName, $ownerName, &$payments));
863
864
		return RLayoutHelper::render(
865
			'redpayment.list.' . strtolower($listType),
866
			array(
867
				'options' => array(
868
					'payments' => $payments,
869
					'extensionName' => $extensionName,
870
					'ownerName' => $ownerName,
871
					'name' => $name,
872
					'value' => $value,
873
					'id' => $id,
874
					'attributes' => $attributes,
875
					'selectSingleOption' => true,
876
				)
877
			)
878
		);
879
	}
880
881
	/**
882
	 * Check payment status
883
	 *
884
	 * @param   int  $paymentId  Id of the payment
885
	 *
886
	 * @return  array|false
887
	 *
888
	 * @since   1.5
889
	 */
890
	public static function checkPayment($paymentId)
891
	{
892
		JPluginHelper::importPlugin('redpayment');
893
		$status = array();
894
895
		if ($item = self::getPaymentById($paymentId))
896
		{
897
			JFactory::getApplication()->triggerEvent('onRedpaymentCheckPayment', array($item->payment_name, $paymentId, &$status));
898
		}
899
900
		return $status;
901
	}
902
903
	/**
904
	 * Refund payment
905
	 *
906
	 * @param   int  $paymentId  Id of the payment
907
	 *
908
	 * @return  bool
909
	 *
910
	 * @since   1.5
911
	 */
912
	public static function refundPayment($paymentId)
913
	{
914
		JPluginHelper::importPlugin('redpayment');
915
		$isRefunded = null;
916
917
		if ($item = self::getPaymentById($paymentId))
918
		{
919
			JFactory::getApplication()->triggerEvent(
920
				'onRedpaymentRefundPayment', array($item->payment_name, $item->extension_name, $item->owner_name, $item, &$isRefunded)
921
			);
922
		}
923
924
		return $isRefunded;
925
	}
926
927
	/**
928
	 * Capture payment
929
	 *
930
	 * @param   int  $paymentId  Id of the payment
931
	 *
932
	 * @return  bool
933
	 *
934
	 * @since   1.5
935
	 */
936
	public static function capturePayment($paymentId)
937
	{
938
		JPluginHelper::importPlugin('redpayment');
939
		$isCaptured = null;
940
941
		if ($item = self::getPaymentById($paymentId))
942
		{
943
			if ((float) $item->amount_total > (float) $item->amount_original)
944
			{
945
				$item->amount_total = $item->amount_original;
946
			}
947
948
			$item->amount_paid = $item->amount_total;
949
950
			JFactory::getApplication()->triggerEvent(
951
				'onRedpaymentCapturePayment', array($item->payment_name, $item->extension_name, $item->owner_name, $item, &$isCaptured)
952
			);
953
		}
954
955
		return $isCaptured;
956
	}
957
958
	/**
959
	 * Delete payment
960
	 *
961
	 * @param   int  $paymentId  Id of the payment
962
	 *
963
	 * @return  bool
964
	 *
965
	 * @since   1.5
966
	 */
967
	public static function deletePayment($paymentId)
968
	{
969
		JPluginHelper::importPlugin('redpayment');
970
		$isDeleted = null;
971
972
		if ($item = self::getPaymentById($paymentId))
973
		{
974
			JFactory::getApplication()->triggerEvent(
975
				'onRedpaymentDeletePayment', array($item->payment_name, $item->extension_name, $item->owner_name, $item, &$isDeleted)
976
			);
977
		}
978
979
		return $isDeleted;
980
	}
981
982
	/**
983
	 * Logs the relevant data from payment gateway to file
984
	 *
985
	 * @param   string   $paymentName    Payment name
986
	 * @param   string   $extensionName  Extension name
987
	 * @param   mixed    $data           Request data
988
	 * @param   boolean  $isValid        Is Valid payment
989
	 * @param   string   $statusText     Status text
990
	 *
991
	 * @return  void
992
	 */
993
	public static function logToFile($paymentName, $extensionName, $data, $isValid = true, $statusText = '')
994
	{
995
		if (RBootstrap::getConfig('payment_enable_file_logger', 0))
996
		{
997
			return;
998
		}
999
1000
		JLoader::import('joomla.filesystem.file');
1001
		$config = JFactory::getConfig();
1002
		$logpath = $config->get('log_path');
1003
1004
		$logFilename = $logpath . '/redpayment/' . $paymentName . '/' . $extensionName . '/'
1005
			. date('Y-m-') . strtolower($paymentName) . '-' . strtolower($extensionName) . '_log';
1006
		$logFile = $logFilename . '.php';
1007
1008
		if (JFile::exists($logFile))
1009
		{
1010
			// If file is over 1MB we break it in new file
1011
			if (@filesize($logFile) > 1048576)
1012
			{
1013
				$i = 1;
1014
1015
				while (true)
1016
				{
1017
					$newFilename = $logFilename . '-' . $i . '.php';
1018
1019
					if (!JFile::exists($newFilename))
1020
					{
1021
						// Copy old file contents to a new location
1022
						JFile::copy($logFile, $newFilename);
1023
						JFile::delete($logFile);
1024
1025
						// We start our logger from start
1026
						$dummy = "<?php die(); ?>\n";
1027
						JFile::write($logFile, $dummy);
1028
1029
						break;
1030
					}
1031
1032
					$i++;
1033
				}
1034
			}
1035
		}
1036
		else
1037
		{
1038
			// New log file in a month
1039
			$dummy = "<?php die(); ?>\n";
1040
			JFile::write($logFile, $dummy);
1041
		}
1042
1043
		// Current file contents
1044
		$logData = @file_get_contents($logFile);
1045
1046
		if ($logData === false)
1047
		{
1048
			$logData = '';
1049
		}
1050
1051
		$logData .= "\n" . str_repeat('=', 20);
1052
		$logData .= $isValid ? ' VALID ' . $paymentName . ' ' : ' INVALID ' . $paymentName . ' *** FRAUD ATTEMPT OR INVALID NOTIFICATION *** ';
1053
		$logData .= str_repeat('=', 20);
1054
1055
		if (!empty($statusText))
1056
		{
1057
			$logData .= "\n" . str_repeat('=', 20);
1058
			$logData .= "\n" . $statusText;
1059
			$logData .= "\n" . str_repeat('=', 20);
1060
		}
1061
1062
		$logData .= "\nDatetime : " . gmdate('Y-m-d H:i:s') . " GMT\n\n";
1063
1064
		if (is_array($data))
1065
		{
1066
			foreach ($data as $key => $value)
1067
			{
1068
				$logData .= str_pad($key, 30, ' ') . $value . "\n";
1069
			}
1070
		}
1071
		elseif (is_object($data))
1072
		{
1073
			$logData .= (json_encode($data)) . "\n";
1074
		}
1075
		else
1076
		{
1077
			$logData .= $data . "\n";
1078
		}
1079
1080
		$logData .= "\n";
1081
1082
		JFile::write($logFile, $logData);
1083
	}
1084
1085
	/**
1086
	 * Generate Payment Log depending on the status
1087
	 *
1088
	 * @param   string        $status   Status string
1089
	 * @param   array|object  $data     Data from gateway
1090
	 * @param   string        $message  Message Text
1091
	 *
1092
	 * @return array
1093
	 */
1094
	public static function generatePaymentLog($status, $data, $message = null)
1095
	{
1096
		if (is_object($data))
1097
		{
1098
			$data = ArrayHelper::fromObject($data);
1099
		}
1100
1101
		$paymentLog = array();
1102
1103
		$paymentLog['payment_id'] = !empty($data['payment_id']) ? $data['payment_id'] : @$data['id'];
1104
		$paymentLog['ip_address'] = $_SERVER['REMOTE_ADDR'];
1105
		$paymentLog['referrer'] = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null;
1106
		$paymentLog['transaction_id'] = !empty($data['transaction_id']) ? $data['transaction_id'] : '';
1107
		$paymentLog['status'] = RApiPaymentStatus::getStatus($status);
1108
		$paymentLog['message_uri'] = JUri::getInstance()->toString();
1109
		$paymentLog['message_post'] = json_encode($data);
1110
		$paymentLog['message_text'] = !is_null($message) ?
1111
			$message : JText::sprintf('LIB_REDCORE_PAYMENT_LOG_DEFAULT_MESSAGE', $data['extension_name'], $data['payment_name']);
1112
		$paymentLog['coupon_code'] = !empty($data['coupon_code']) ? $data['coupon_code'] : '';
1113
1114
		return $paymentLog;
1115
	}
1116
1117
	/**
1118
	 * Generate Payment Log depending on the status
1119
	 *
1120
	 * @param   array  $paymentLog           Data for payment log storage
1121
	 * @param   bool   $updatePaymentStatus  Update Payment Status
1122
	 *
1123
	 * @return bool
1124
	 */
1125
	public static function saveNewPaymentLog($paymentLog, $updatePaymentStatus = true)
1126
	{
1127
		if (empty($paymentLog['payment_id']))
1128
		{
1129
			return false;
1130
		}
1131
1132
		// Forcing default set of statuses
1133
		$paymentLog['status'] = RApiPaymentStatus::getStatus($paymentLog['status']);
1134
1135
		// Currency should not be numeric
1136
		if (!empty($paymentLog['currency']) && is_numeric($paymentLog['currency']))
1137
		{
1138
			$paymentLog['currency'] = RHelperCurrency::getIsoCode($paymentLog['currency']);
1139
		}
1140
1141
		/** @var RedcoreModelPayment_Log $logModel */
1142
		$logModel = RModelAdmin::getAdminInstance('Payment_Log', array(), 'com_redcore');
1143
1144
		// Avoid ghost id from URL
1145
		$paymentLog['id'] = 0;
1146
1147
		if ($logModel->save($paymentLog))
1148
		{
1149
			if ($updatePaymentStatus)
1150
			{
1151
				self::updatePaymentStatus($paymentLog['payment_id']);
1152
			}
1153
		}
1154
1155
		return true;
1156
	}
1157
1158
	/**
1159
	 * Calls method from extension helper file if exists
1160
	 *
1161
	 * @param   string  $extensionName  Extension name
1162
	 * @param   string  $functionName   Function name
1163
	 *
1164
	 * @return  mixed It will return Extension helper class function result or false if it does not exists
1165
	 */
1166
	public static function triggerExtensionHelperMethod($extensionName, $functionName)
1167
	{
1168
		$apiHelperClass = self::getExtensionHelperObject($extensionName);
1169
		$args = func_get_args();
1170
1171
		// Remove extension and function name from arguments
1172
		array_shift($args);
1173
		array_shift($args);
1174
1175
		// PHP 5.3 workaround
1176
		$temp = array();
1177
1178
		foreach ($args as &$arg)
1179
		{
1180
			$temp[] = &$arg;
1181
		}
1182
1183
		// Checks if that method exists in helper file and executes it
1184
		if (method_exists($apiHelperClass, $functionName))
1185
		{
1186
			return call_user_func_array(array($apiHelperClass, $functionName), $temp);
1187
		}
1188
1189
		return false;
1190
	}
1191
1192
	/**
1193
	 * Gets instance of extension helper object if exists
1194
	 *
1195
	 * @param   string  $extensionName  Extension name
1196
	 *
1197
	 * @return  mixed It will return Extension helper class or false if it does not exists
1198
	 */
1199
	public static function getExtensionHelperObject($extensionName)
1200
	{
1201
		if (!$extensionName)
1202
		{
1203
			return false;
1204
		}
1205
1206
		if (isset(self::$extensionHelperClasses[$extensionName]))
1207
		{
1208
			return self::$extensionHelperClasses[$extensionName];
1209
		}
1210
1211
		// Helper file location to search inside of the extension
1212
		$helperFile = JPath::clean(JPATH_ADMINISTRATOR . '/components/' . strtolower($extensionName) . '/helpers/redpayment/extension_helper.php');
1213
1214
		if (file_exists($helperFile))
1215
		{
1216
			require_once $helperFile;
1217
		}
1218
1219
		$helperClassName = 'RApiPaymentExtensionHelper' . ucfirst(strtolower($extensionName));
1220
1221
		if (class_exists($helperClassName))
1222
		{
1223
			self::$extensionHelperClasses[$extensionName] = new $helperClassName;
1224
		}
1225
		else
1226
		{
1227
			self::$extensionHelperClasses[$extensionName] = null;
1228
		}
1229
1230
		return self::$extensionHelperClasses[$extensionName];
1231
	}
1232
1233
	/**
1234
	 * Gets last payment log object
1235
	 *
1236
	 * @param   int  $paymentId  Id of the payment
1237
	 *
1238
	 * @return  object
1239
	 *
1240
	 * @since   1.5
1241
	 */
1242
	public static function getLastPaymentLog($paymentId)
1243
	{
1244
		$db = JFactory::getDbo();
1245
1246
		$query = $db->getQuery(true)
1247
			->select('pl.*')
1248
			->from($db->qn('#__redcore_payment_log', 'pl'))
1249
			->where('pl.payment_id = ' . (int) $paymentId)
1250
			->order('pl.created_date DESC');
1251
1252
		$db->setQuery($query, 0, 1);
1253
1254
		return $db->loadObject();
1255
	}
1256
}
1257