Completed
Pull Request — master (#664)
by Devin
19:01
created

Give_Payment::get_fees()   B

Complexity

Conditions 7
Paths 2

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 9
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 19
rs 8.2222
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 26 and the first side effect is on line 17.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * Payments
4
 *
5
 * This class is for working with payments in Give.
6
 *
7
 * @package     Give
8
 * @subpackage  Classes/Payment
9
 * @copyright   Copyright (c) 2016, WordImpress
10
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
11
 * @since       1.5
12
 */
13
14
15
// Exit if accessed directly
16
if ( ! defined( 'ABSPATH' ) ) {
17
	exit;
18
}
19
20
21
/**
22
 * Give_Payment Class
23
 *
24
 * @since 1.5
25
 */
26
final class Give_Payment {
27
28
	/**
29
	 * The Payment we are working with
30
	 *
31
	 * @var int
32
	 * @access private
33
	 * @since 1.0
34
	 */
35
36
	/**
37
	 * The Payment ID
38
	 *
39
	 * @since  1.5
40
	 * @var    integer
41
	 */
42
	public $ID = 0;
43
	protected $_ID = 0;
44
45
	/**
46
	 * Identify if the payment is a new one or existing
47
	 *
48
	 * @since  1.5
49
	 * @var boolean
50
	 */
51
	protected $new = false;
52
53
	/**
54
	 * The Payment number (for use with sequential payments)
55
	 *
56
	 * @since  1.5
57
	 * @var string
58
	 */
59
	protected $number = '';
60
61
	/**
62
	 * The Gateway mode the payment was made in
63
	 *
64
	 * @since  1.5
65
	 * @var string
66
	 */
67
	protected $mode = 'live';
68
69
	/**
70
	 * The Unique Payment Key
71
	 *
72
	 * @since  1.5
73
	 * @var string
74
	 */
75
	protected $key = '';
76
77
	/**
78
	 * The Donation Form Title
79
	 *
80
	 * @since  1.5
81
	 * @var string
82
	 */
83
	protected $form_title = 0;
84
85
	/**
86
	 * The Donation Form ID
87
	 *
88
	 * @since  1.5
89
	 * @var string
90
	 */
91
	protected $form_id = 0;
92
93
	/**
94
	 * The Donation Form Price ID
95
	 *
96
	 * @since  1.5
97
	 * @var string
98
	 */
99
	protected $price_id = 0;
100
101
	/**
102
	 * The total amount the payment is for
103
	 * Includes donation amount and fees
104
	 *
105
	 * @since  1.5
106
	 * @var float
107
	 */
108
	protected $total = 0.00;
109
110
	/**
111
	 * The Subtotal fo the payment before fees
112
	 *
113
	 * @since  1.5
114
	 * @var float
115
	 */
116
	protected $subtotal = 0;
117
118
	/**
119
	 * Array of global fees for this payment
120
	 *
121
	 * @since  1.5
122
	 * @var array
123
	 */
124
	protected $fees = array();
125
126
	/**
127
	 * The sum of the fee amounts
128
	 *
129
	 * @since  1.5
130
	 * @var float
131
	 */
132
	protected $fees_total = 0;
133
134
	/**
135
	 * The date the payment was created
136
	 *
137
	 * @since  1.5
138
	 * @var string
139
	 */
140
	protected $date = '';
141
	protected $post_date = '';
142
143
	/**
144
	 * The date the payment was marked as 'complete'
145
	 *
146
	 * @since  1.5
147
	 * @var string
148
	 */
149
	protected $completed_date = '';
150
151
	/**
152
	 * The status of the payment
153
	 *
154
	 * @since  1.5
155
	 * @var string
156
	 */
157
	protected $status = 'pending';
158
	protected $post_status = 'pending'; // Same as $status but here for backwards compat
159
160
	/**
161
	 * When updating, the old status prior to the change
162
	 *
163
	 * @since  1.5
164
	 * @var string
165
	 */
166
	protected $old_status = '';
167
168
	/**
169
	 * The display name of the current payment status
170
	 *
171
	 * @since  1.5
172
	 * @var string
173
	 */
174
	protected $status_nicename = '';
175
176
	/**
177
	 * The customer ID that made the payment
178
	 *
179
	 * @since  1.5
180
	 * @var integer
181
	 */
182
	protected $customer_id = null;
183
184
	/**
185
	 * The User ID (if logged in) that made the payment
186
	 *
187
	 * @since  1.5
188
	 * @var integer
189
	 */
190
	protected $user_id = 0;
191
192
	/**
193
	 * The first name of the payee
194
	 *
195
	 * @since  1.5
196
	 * @var string
197
	 */
198
	protected $first_name = '';
199
200
	/**
201
	 * The last name of the payee
202
	 *
203
	 * @since  1.5
204
	 * @var string
205
	 */
206
	protected $last_name = '';
207
208
	/**
209
	 * The email used for the payment
210
	 *
211
	 * @since  1.5
212
	 * @var string
213
	 */
214
	protected $email = '';
215
216
	/**
217
	 * Legacy (not to be accessed) array of user information
218
	 *
219
	 * @since  1.5
220
	 * @var array
221
	 */
222
	private $user_info = array();
223
224
	/**
225
	 * Legacy (not to be accessed) payment meta array
226
	 *
227
	 * @since  1.5
228
	 * @var array
229
	 */
230
	private $payment_meta = array();
231
232
	/**
233
	 * The physical address used for the payment if provided
234
	 *
235
	 * @since  1.5
236
	 * @var array
237
	 */
238
	protected $address = array();
239
240
	/**
241
	 * The transaction ID returned by the gateway
242
	 *
243
	 * @since  1.5
244
	 * @var string
245
	 */
246
	protected $transaction_id = '';
247
248
	/**
249
	 * IP Address payment was made from
250
	 *
251
	 * @since  1.5
252
	 * @var string
253
	 */
254
	protected $ip = '';
255
256
	/**
257
	 * The gateway used to process the payment
258
	 *
259
	 * @since  1.5
260
	 * @var string
261
	 */
262
	protected $gateway = '';
263
264
	/**
265
	 * The the payment was made with
266
	 *
267
	 * @since  1.5
268
	 * @var string
269
	 */
270
	protected $currency = '';
271
272
	/**
273
	 * Array of items that have changed since the last save() was run
274
	 * This is for internal use, to allow fewer update_payment_meta calls to be run
275
	 *
276
	 * @since  1.5
277
	 * @var array
278
	 */
279
	private $pending;
280
281
	/**
282
	 * The parent payment (if applicable)
283
	 *
284
	 * @since  1.5
285
	 * @var integer
286
	 */
287
	protected $parent_payment = 0;
288
289
	/**
290
	 * Setup the Give Payments class
291
	 *
292
	 * @since 1.0
293
	 *
294
	 * @param int $payment_id A given payment
295
	 *
296
	 * @return mixed void|false
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
297
	 */
298
	public function __construct( $payment_id = false ) {
299
300
		if ( empty( $payment_id ) ) {
301
			return false;
302
		}
303
304
		$this->setup_payment( $payment_id );
305
	}
306
307
	/**
308
	 * Magic GET function
309
	 *
310
	 * @since  1.5
311
	 *
312
	 * @param  string $key The property
313
	 *
314
	 * @return mixed        The value
315
	 */
316
	public function __get( $key ) {
317
318
		if ( method_exists( $this, 'get_' . $key ) ) {
319
320
			$value = call_user_func( array( $this, 'get_' . $key ) );
321
322
		} else {
323
324
			$value = $this->$key;
325
326
		}
327
328
		return $value;
329
	}
330
331
	/**
332
	 * Magic SET function
333
	 *
334
	 * Sets up the pending array for the save method
335
	 *
336
	 * @since  1.5
337
	 *
338
	 * @param string $key The property name
339
	 * @param mixed $value The value of the property
340
	 */
341
	public function __set( $key, $value ) {
342
		$ignore = array( '_ID' );
343
344
		if ( $key === 'status' ) {
345
			$this->old_status = $this->status;
346
		}
347
348
		if ( ! in_array( $key, $ignore ) ) {
349
			$this->pending[ $key ] = $value;
350
		}
351
352
		if ( '_ID' !== $key ) {
353
			$this->$key = $value;
354
		}
355
	}
356
357
	/**
358
	 * Magic ISSET function, which allows empty checks on protected elements
359
	 *
360
	 * @since  1.5
361
	 *
362
	 * @param  string $name The attribute to get
363
	 *
364
	 * @return boolean       If the item is set or not
365
	 */
366
	public function __isset( $name ) {
367
		if ( property_exists( $this, $name ) ) {
368
			return false === empty( $this->$name );
369
		} else {
370
			return null;
371
		}
372
	}
373
374
	/**
375
	 * Setup payment properties
376
	 *
377
	 * @since  1.5
378
	 *
379
	 * @param  int $payment_id The payment ID
380
	 *
381
	 * @return bool            If the setup was successful or not
382
	 */
383
	private function setup_payment( $payment_id ) {
384
		$this->pending = array();
385
386
		if ( empty( $payment_id ) ) {
387
			return false;
388
		}
389
390
		$payment = get_post( $payment_id );
391
392
		if ( ! $payment || is_wp_error( $payment ) ) {
393
			return false;
394
		}
395
396
		if ( 'give_payment' !== $payment->post_type ) {
397
			return false;
398
		}
399
400
		// Allow extensions to perform actions before the payment is loaded
401
		do_action( 'give_pre_setup_payment', $this, $payment_id );
402
403
		// Primary Identifier
404
		$this->ID = absint( $payment_id );
405
406
		// Protected ID that can never be changed
407
		$this->_ID = absint( $payment_id );
408
409
		// We have a payment, get the generic payment_meta item to reduce calls to it
410
		$this->payment_meta = $this->get_meta();
411
412
		// Status and Dates
413
		$this->date           = $payment->post_date;
414
		$this->post_date      = $payment->post_date;
415
		$this->completed_date = $this->setup_completed_date();
416
		$this->status         = $payment->post_status;
417
		$this->post_status    = $this->status;
418
		$this->mode           = $this->setup_mode();
419
		$this->parent_payment = $payment->post_parent;
420
421
		$all_payment_statuses  = give_get_payment_statuses();
422
		$this->status_nicename = array_key_exists( $this->status, $all_payment_statuses ) ? $all_payment_statuses[ $this->status ] : ucfirst( $this->status );
423
424
		// Items
425
		$this->fees = $this->setup_fees();
426
427
		// Currency Based
428
		$this->total      = $this->setup_total();
429
		$this->fees_total = $this->setup_fees_total();
430
		$this->subtotal   = $this->setup_subtotal();
431
		$this->currency   = $this->setup_currency();
432
433
		// Gateway based
434
		$this->gateway        = $this->setup_gateway();
435
		$this->transaction_id = $this->setup_transaction_id();
436
437
		// User based
438
		$this->ip          = $this->setup_ip();
439
		$this->customer_id = $this->setup_customer_id();
440
		$this->user_id     = $this->setup_user_id();
441
		$this->email       = $this->setup_email();
442
		$this->user_info   = $this->setup_user_info();
443
		$this->address     = $this->setup_address();
444
		$this->first_name  = $this->user_info['first_name'];
445
		$this->last_name   = $this->user_info['last_name'];
446
447
		// Other Identifiers
448
		$this->form_title = $this->setup_form_title();
449
		$this->form_id    = $this->setup_form_id();
450
		$this->price_id   = $this->setup_price_id();
451
		$this->key        = $this->setup_payment_key();
452
		$this->number     = $this->setup_payment_number();
453
454
		// Allow extensions to add items to this object via hook
455
		do_action( 'give_setup_payment', $this, $payment_id );
456
457
		return true;
458
	}
459
460
	/**
461
	 * Create the base of a payment.
462
	 *
463
	 * @since  1.5
464
	 * @return int|bool False on failure, the payment ID on success.
465
	 */
466
	private function insert_payment() {
467
468
		// Construct the payment title
469
		$payment_title = '';
470
		if ( ! empty( $this->first_name ) && ! empty( $this->last_name ) ) {
471
			$payment_title = $this->first_name . ' ' . $this->last_name;
472
		} else if ( ! empty( $this->first_name ) && empty( $this->last_name ) ) {
473
			$payment_title = $this->first_name;
474
		} else if ( ! empty( $this->email ) && is_email( $this->email ) ) {
475
			$payment_title = $this->email;
476
		}
477
478
		//Set Key
479
		if ( empty( $this->key ) ) {
480
481
			$auth_key             = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
482
			$this->key            = strtolower( md5( $this->email . date( 'Y-m-d H:i:s' ) . $auth_key . uniqid( 'give', true ) ) );  // Unique key
483
			$this->pending['key'] = $this->key;
484
		}
485
486
		//Set IP
487
		if ( empty( $this->ip ) ) {
488
489
			$this->ip            = give_get_ip();
490
			$this->pending['ip'] = $this->ip;
491
492
		}
493
494
		$payment_data = array(
495
			'price'        => $this->total,
496
			'date'         => $this->date,
497
			'user_email'   => $this->email,
498
			'purchase_key' => $this->key,
499
			'form_title'   => $this->form_title,
500
			'form_id'      => $this->form_id,
501
			'price_id'     => $this->price_id,
502
			'currency'     => $this->currency,
503
			'user_info'    => array(
504
				'id'         => $this->user_id,
505
				'email'      => $this->email,
506
				'first_name' => $this->first_name,
507
				'last_name'  => $this->last_name,
508
				'address'    => $this->address,
509
			),
510
			'status'       => $this->status,
511
			'fees'         => $this->fees,
512
		);
513
514
		$args = apply_filters( 'give_insert_payment_args', array(
515
			'post_title'    => $payment_title,
516
			'post_status'   => $this->status,
517
			'post_type'     => 'give_payment',
518
			'post_date'     => ! empty( $this->date ) ? $this->date : null,
519
			'post_date_gmt' => ! empty( $this->date ) ? get_gmt_from_date( $this->date ) : null,
520
			'post_parent'   => $this->parent_payment,
521
		), $payment_data );
522
523
		// Create a blank payment
524
		$payment_id = wp_insert_post( $args );
525
526
		if ( ! empty( $payment_id ) ) {
527
528
			$this->ID  = $payment_id;
529
			$this->_ID = $payment_id;
530
531
			$customer = new stdClass;
532
533
			if ( did_action( 'give_pre_process_purchase' ) && is_user_logged_in() ) {
534
				$customer = new Give_Customer( get_current_user_id(), true );
535
			}
536
537
			if ( empty( $customer->id ) ) {
538
				$customer = new Give_Customer( $this->email );
539
			}
540
541
			if ( empty( $customer->id ) ) {
542
543
				$customer_data = array(
544
					'name'    => ! is_email( $payment_title ) ? $this->first_name . ' ' . $this->last_name : '',
545
					'email'   => $this->email,
546
					'user_id' => $this->user_id,
547
				);
548
549
				$customer->create( $customer_data );
0 ignored issues
show
Bug introduced by
The method create does only exist in Give_Customer, but not in stdClass.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
550
551
			}
552
553
			$this->customer_id            = $customer->id;
554
			$this->pending['customer_id'] = $this->customer_id;
555
			$customer->attach_payment( $this->ID, false );
0 ignored issues
show
Bug introduced by
The method attach_payment does only exist in Give_Customer, but not in stdClass.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
556
557
			$this->payment_meta = apply_filters( 'give_payment_meta', $this->payment_meta, $payment_data );
558
			if ( ! empty( $this->payment_meta['fees'] ) ) {
559
				$this->fees = array_merge( $this->fees, $this->payment_meta['fees'] );
560
				foreach ( $this->fees as $fee ) {
561
					$this->increase_fees( $fee['amount'] );
562
				}
563
			}
564
565
			$this->update_meta( '_give_payment_meta', $this->payment_meta );
566
			$this->new = true;
567
		}
568
569
		return $this->ID;
570
571
	}
572
573
	/**
574
	 * Save
575
	 *
576
	 * @description: Once items have been set, an update is needed to save them to the database.
577
	 *
578
	 * @return bool  True of the save occurred, false if it failed or wasn't needed
579
	 */
580
	public function save() {
581
582
		$saved = false;
583
584
		//Must have an ID 
585
		if ( empty( $this->ID ) ) {
586
587
			$payment_id = $this->insert_payment();
588
589
			if ( false === $payment_id ) {
590
				$saved = false;
591
			} else {
592
				$this->ID = $payment_id;
593
			}
594
595
		}
596
597
		//Set ID if not matching
598
		if ( $this->ID !== $this->_ID ) {
599
			$this->ID = $this->_ID;
600
		}
601
602
		// If we have something pending, let's save it
603
		if ( ! empty( $this->pending ) ) {
604
605
			$total_increase = 0;
606
			$total_decrease = 0;
607
608
			foreach ( $this->pending as $key => $value ) {
609
610
				switch ( $key ) {
611
612
					case 'donations':
613
						// Update totals for pending donations
614
						foreach ( $this->pending[ $key ] as $item ) {
615
616
							$quantity = isset( $item['quantity'] ) ? $item['quantity'] : 1;
617
							$price_id = isset( $item['price_id'] ) ? $item['price_id'] : 0;
618
619
							switch ( $item['action'] ) {
620
621
								case 'add':
622
623
									$price = $item['price'];
624
625
									if ( 'publish' === $this->status || 'complete' === $this->status || 'revoked' === $this->status ) {
626
627
										// Add sales logs
628
										$log_date = date_i18n( 'Y-m-d G:i:s', current_time( 'timestamp' ) );
629
630
										$y = 0;
631
										while ( $y < $quantity ) {
632
633
											give_record_sale_in_log( $item['id'], $this->ID, $price_id, $log_date );
634
											$y ++;
635
										}
636
637
										$form = new Give_Donate_Form( $item['id'] );
638
										$form->increase_sales( $quantity );
639
										$form->increase_earnings( $price );
640
641
										$total_increase += $price;
642
									}
643
									break;
644
645
								case 'remove':
646
									$log_args = array(
647
										'post_type'   => 'give_log',
648
										'post_parent' => $item['id'],
649
										'numberposts' => $quantity,
650
										'meta_query'  => array(
651
											array(
652
												'key'     => '_give_log_payment_id',
653
												'value'   => $this->ID,
654
												'compare' => '=',
655
											),
656
											array(
657
												'key'     => '_give_log_price_id',
658
												'value'   => $price_id,
659
												'compare' => '='
660
											)
661
										)
662
									);
663
664
									$found_logs = get_posts( $log_args );
665
									foreach ( $found_logs as $log ) {
666
										wp_delete_post( $log->ID, true );
667
									}
668
669
									if ( 'publish' === $this->status || 'complete' === $this->status || 'revoked' === $this->status ) {
670
										$form = new Give_Donate_Form( $item['id'] );
671
										$form->decrease_sales( $quantity );
672
										$form->decrease_earnings( $item['amount'] );
673
674
										$total_decrease += $item['amount'];
675
									}
676
									break;
677
678
							}
679
680
						}
681
						break;
682
683
					case 'fees':
684
685
						if ( 'publish' !== $this->status && 'complete' !== $this->status && 'revoked' !== $this->status ) {
686
							break;
687
						}
688
689
						if ( empty( $this->pending[ $key ] ) ) {
690
							break;
691
						}
692
693
						foreach ( $this->pending[ $key ] as $fee ) {
694
695
							switch ( $fee['action'] ) {
696
697
								case 'add':
698
									$total_increase += $fee['amount'];
699
									break;
700
701
								case 'remove':
702
									$total_decrease += $fee['amount'];
703
									break;
704
705
							}
706
707
						}
708
709
						break;
710
711
					case 'status':
712
						$this->update_status( $this->status );
713
						break;
714
715
					case 'gateway':
716
						$this->update_meta( '_give_payment_gateway', $this->gateway );
717
						break;
718
719
					case 'mode':
720
						$this->update_meta( '_give_payment_mode', $this->mode );
721
						break;
722
723
					case 'transaction_id':
724
						$this->update_meta( '_give_payment_transaction_id', $this->transaction_id );
725
						break;
726
727
					case 'ip':
728
						$this->update_meta( '_give_payment_user_ip', $this->ip );
729
						break;
730
731
					case 'customer_id':
732
						$this->update_meta( '_give_payment_customer_id', $this->customer_id );
733
						break;
734
735
					case 'user_id':
736
						$this->update_meta( '_give_payment_user_id', $this->user_id );
737
						break;
738
739
					case 'form_title':
740
						$this->update_meta( '_give_payment_form_title', $this->form_title );
741
						break;
742
743
					case 'form_id':
744
						$this->update_meta( '_give_payment_form_id', $this->form_id );
745
						break;
746
747
					case 'price_id':
748
						$this->update_meta( '_give_payment_price_id', $this->price_id );
749
						break;
750
751
					case 'first_name':
752
						$this->user_info['first_name'] = $this->first_name;
753
						break;
754
755
					case 'last_name':
756
						$this->user_info['last_name'] = $this->last_name;
757
						break;
758
759
					case 'address':
760
						$this->user_info['address'] = $this->address;
761
						break;
762
763
					case 'email':
764
						$this->update_meta( '_give_payment_user_email', $this->email );
765
						break;
766
767
					case 'key':
768
						$this->update_meta( '_give_payment_purchase_key', $this->key );
769
						break;
770
771
					case 'number':
772
						$this->update_meta( '_give_payment_number', $this->number );
773
						break;
774
775
					case 'date':
776
						$args = array(
777
							'ID'        => $this->ID,
778
							'post_date' => $this->date,
779
							'edit_date' => true,
780
						);
781
782
						wp_update_post( $args );
783
						break;
784
785
					case 'completed_date':
786
						$this->update_meta( '_give_completed_date', $this->completed_date );
787
						break;
788
789
					case 'parent_payment':
790
						$args = array(
791
							'ID'          => $this->ID,
792
							'post_parent' => $this->parent_payment,
793
						);
794
795
						wp_update_post( $args );
796
						break;
797
798
					default:
799
						do_action( 'give_payment_save', $this, $key );
800
						break;
801
				}
802
			}
803
804
			if ( 'pending' !== $this->status ) {
805
806
				$customer = new Give_Customer( $this->customer_id );
807
808
				$total_change = $total_increase - $total_decrease;
809
				if ( $total_change < 0 ) {
810
811
					$total_change = - ( $total_change );
812
					// Decrease the customer's purchase stats
813
					$customer->decrease_value( $total_change );
814
					give_decrease_total_earnings( $total_change );
815
816
				} else if ( $total_change > 0 ) {
817
818
					// Increase the customer's purchase stats
819
					$customer->increase_value( $total_change );
820
					give_increase_total_earnings( $total_change );
821
822
				}
823
824
			}
825
826
			$this->update_meta( '_give_payment_total', $this->total );
827
828
			$new_meta = array(
829
				'form_title' => $this->form_title,
830
				'form_id'    => $this->form_id,
831
				'price_id'   => $this->price_id,
832
				'fees'       => $this->fees,
833
				'currency'   => $this->currency,
834
				'user_info'  => $this->user_info,
835
			);
836
837
			$meta        = $this->get_meta();
838
			$merged_meta = array_merge( $meta, $new_meta );
839
840
			// Only save the payment meta if it's changed
841
			if ( md5( serialize( $meta ) ) !== md5( serialize( $merged_meta ) ) ) {
842
				$updated = $this->update_meta( '_give_payment_meta', $merged_meta );
843
				if ( false !== $updated ) {
844
					$saved = true;
845
				}
846
			}
847
848
			$this->pending = array();
849
			$saved         = true;
850
		}
851
852
		if ( true === $saved ) {
853
			$this->setup_payment( $this->ID );
854
		}
855
856
		return $saved;
857
	}
858
859
	/**
860
	 * Add a donation to a given payment
861
	 *
862
	 * @since 1.5
863
	 *
864
	 * @param int $form_id The donation form to add
865
	 * @param array $args Other arguments to pass to the function
866
	 * @param array $options List of donation options
867
	 *
868
	 * @return bool True when successful, false otherwise
869
	 */
870
	public function add_donation( $form_id = 0, $args = array(), $options = array() ) {
871
872
		$donation = new Give_Donate_Form( $form_id );
873
874
		// Bail if this post isn't a give donation form
875
		if ( ! $donation || $donation->post_type !== 'give_forms' ) {
0 ignored issues
show
Documentation introduced by
The property post_type does not exist on object<Give_Donate_Form>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
876
			return false;
877
		}
878
879
		// Set some defaults
880
		$defaults = array(
881
			'price'    => false,
882
			'price_id' => false,
883
			'fees'     => array(),
884
		);
885
886
		$args = wp_parse_args( apply_filters( 'give_payment_add_donation_args', $args, $donation->ID ), $defaults );
887
888
		// Allow overriding the price
889
		if ( false !== $args['price'] ) {
890
			$item_price = $args['price'];
891
		} else {
892
893
			// Deal with variable pricing
894
			if ( give_has_variable_prices( $donation->ID ) ) {
895
				$prices     = maybe_unserialize( get_post_meta( $form_id, '_give_donation_levels', true ) );
896
				$item_price = '';
897
				//Loop through prices
898
				foreach ( $prices as $price ) {
899
					//Find a match between price_id and level_id
900
					//First verify array keys exists THEN make the match
901
					if ( ( isset( $args['price_id'] ) && isset( $price['_give_id']['level_id'] ) )
902
					     && $args['price_id'] == $price['_give_id']['level_id']
903
					) {
904
						$item_price = $price['_give_amount'];
905
					}
906
				}
907
				//Fallback to the lowest price point
908
				if ( $item_price == '' ) {
909
					$item_price       = give_get_lowest_price_option( $donation->ID );
910
					$args['price_id'] = give_get_lowest_price_id( $donation->ID );
911
				}
912
			} else {
913
				//Simple form price
914
				$item_price = give_get_form_price( $donation->ID );
915
			}
916
917
		}
918
919
		// Sanitizing the price here so we don't have a dozen calls later
920
		$item_price = give_sanitize_amount( $item_price );
921
		$total      = round( $item_price, give_currency_decimal_filter() );
922
923
		//Add Options
924
		$default_options = array();
925
		if ( false !== $args['price_id'] ) {
926
			$default_options['price_id'] = (int) $args['price_id'];
927
		}
928
		$options = wp_parse_args( $options, $default_options );
929
930
		// Do not allow totals to go negative
931
		if ( $total < 0 ) {
932
			$total = 0;
933
		}
934
935
		$donation = array(
936
			'name'     => $donation->post_title,
937
			'id'       => $donation->ID,
938
			'price'    => round( $total, give_currency_decimal_filter() ),
939
			'subtotal' => round( $total, give_currency_decimal_filter() ),
940
			'fees'     => $args['fees'],
941
			'price_id' => $args['price_id'],
942
			'action'   => 'add',
943
			'options'  => $options
944
		);
945
946
		$this->pending['donations'][] = $donation;
947
948
		$this->increase_subtotal( $total );
949
950
		return true;
951
952
	}
953
954
	/**
955
	 * Remove a donation from the payment
956
	 *
957
	 * @since  1.5
958
	 *
959
	 * @param  int $form_id The form ID to remove
960
	 * @param  array $args Arguments to pass to identify (quantity, amount, price_id)
961
	 *
962
	 * @return bool If the item was removed or not
963
	 */
964
	public function remove_donation( $form_id, $args = array() ) {
965
966
		// Set some defaults
967
		$defaults = array(
968
			'quantity' => 1,
969
			'price'    => false,
970
			'price_id' => false,
971
		);
972
		$args     = wp_parse_args( $args, $defaults );
973
974
		$form = new Give_Donate_Form( $form_id );
975
976
		// Bail if this post isn't a valid give donation form
977
		if ( ! $form || $form->post_type !== 'give_forms' ) {
0 ignored issues
show
Documentation introduced by
The property post_type does not exist on object<Give_Donate_Form>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
978
			return false;
979
		}
980
981
		$pending_args             = $args;
982
		$pending_args['id']       = $form_id;
983
		$pending_args['amount']   = $this->total;
984
		$pending_args['price_id'] = false !== $args['price_id'] ? (int) $args['price_id'] : false;
985
		$pending_args['quantity'] = $args['quantity'];
986
		$pending_args['action']   = 'remove';
987
988
		$this->pending['donations'][] = $pending_args;
989
990
		$this->decrease_subtotal( $this->total );
991
992
		return true;
993
	}
994
995
	/**
996
	 * Add a fee to a given payment
997
	 *
998
	 * @since  1.5
999
	 *
1000
	 * @param  array $args Array of arguments for the fee to add
1001
	 * @param bool $global
1002
	 *
1003
	 * @return bool If the fee was added
1004
	 */
1005
	public function add_fee( $args, $global = true ) {
0 ignored issues
show
Unused Code introduced by
The parameter $global is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1006
1007
		$default_args = array(
1008
			'label'    => '',
1009
			'amount'   => 0,
1010
			'type'     => 'fee',
1011
			'id'       => '',
1012
			'price_id' => 0,
1013
		);
1014
1015
		$fee          = wp_parse_args( $args, $default_args );
1016
		$this->fees[] = $fee;
1017
1018
1019
		$added_fee               = $fee;
1020
		$added_fee['action']     = 'add';
1021
		$this->pending['fees'][] = $added_fee;
1022
		reset( $this->fees );
1023
1024
		$this->increase_fees( $fee['amount'] );
1025
1026
		return true;
1027
	}
1028
1029
	/**
1030
	 * Remove a fee from the payment
1031
	 *
1032
	 * @since  1.5
1033
	 *
1034
	 * @param  int $key The array key index to remove
1035
	 *
1036
	 * @return bool     If the fee was removed successfully
1037
	 */
1038
	public function remove_fee( $key ) {
1039
		$removed = false;
1040
1041
		if ( is_numeric( $key ) ) {
1042
			$removed = $this->remove_fee_by( 'index', $key );
1043
		}
1044
1045
		return $removed;
1046
	}
1047
1048
	/**
1049
	 * Remove a fee by the defined attributed
1050
	 *
1051
	 * @since  1.5
1052
	 *
1053
	 * @param  string $key The key to remove by
1054
	 * @param  int|string $value The value to search for
1055
	 * @param  boolean $global False - removes the first value it fines, True - removes all matches
1056
	 *
1057
	 * @return boolean             If the item is removed
1058
	 */
1059
	public function remove_fee_by( $key, $value, $global = false ) {
1060
1061
		$allowed_fee_keys = apply_filters( 'give_payment_fee_keys', array(
1062
			'index',
1063
			'label',
1064
			'amount',
1065
			'type',
1066
		) );
1067
1068
		if ( ! in_array( $key, $allowed_fee_keys ) ) {
1069
			return false;
1070
		}
1071
1072
		$removed = false;
1073
		if ( 'index' === $key && array_key_exists( $value, $this->fees ) ) {
1074
1075
			$removed_fee             = $this->fees[ $value ];
1076
			$removed_fee['action']   = 'remove';
1077
			$this->pending['fees'][] = $removed_fee;
1078
1079
			$this->decrease_fees( $removed_fee['amount'] );
1080
1081
			unset( $this->fees[ $value ] );
1082
			$removed = true;
1083
1084
		} else if ( 'index' !== $key ) {
1085
1086
			foreach ( $this->fees as $index => $fee ) {
1087
1088
				if ( isset( $fee[ $key ] ) && $fee[ $key ] == $value ) {
1089
1090
					$removed_fee             = $fee;
1091
					$removed_fee['action']   = 'remove';
1092
					$this->pending['fees'][] = $removed_fee;
1093
1094
					$this->decrease_fees( $removed_fee['amount'] );
1095
1096
					unset( $this->fees[ $index ] );
1097
					$removed = true;
1098
1099
					if ( false === $global ) {
1100
						break;
1101
					}
1102
1103
				}
1104
1105
			}
1106
1107
		}
1108
1109
		if ( true === $removed ) {
1110
			$this->fees = array_values( $this->fees );
1111
		}
1112
1113
		return $removed;
1114
	}
1115
1116
	/**
1117
	 * Get the fees, filterable by type
1118
	 *
1119
	 * @since  1.5
1120
	 *
1121
	 * @param  string $type All, item, fee
1122
	 *
1123
	 * @return array        The Fees for the type specified
1124
	 */
1125
	public function get_fees( $type = 'all' ) {
1126
		$fees = array();
1127
1128
		if ( ! empty( $this->fees ) && is_array( $this->fees ) ) {
1129
1130
			foreach ( $this->fees as $fee_id => $fee ) {
1131
1132
				if ( 'all' != $type && ! empty( $fee['type'] ) && $type != $fee['type'] ) {
1133
					continue;
1134
				}
1135
1136
				$fee['id'] = $fee_id;
1137
				$fees[]    = $fee;
1138
1139
			}
1140
		}
1141
1142
		return apply_filters( 'give_get_payment_fees', $fees, $this->ID, $this );
1143
	}
1144
1145
	/**
1146
	 * Add a note to a payment
1147
	 *
1148
	 * @since 1.0
1149
	 *
1150
	 * @param string $note The note to add
1151
	 *
1152
	 * @return void
1153
	 */
1154
	public function add_note( $note = false ) {
1155
		// Bail if no note specified
1156
		if ( ! $note ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $note of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1157
			return false;
1158
		}
1159
1160
		give_insert_payment_note( $this->ID, $note );
1161
	}
1162
1163
	/**
1164
	 * Increase the payment's subtotal
1165
	 *
1166
	 * @since  1.5
1167
	 *
1168
	 * @param  float $amount The amount to increase the payment subtotal by
1169
	 *
1170
	 * @return void
1171
	 */
1172
	private function increase_subtotal( $amount = 0.00 ) {
1173
		$amount = (float) $amount;
1174
		$this->subtotal += $amount;
1175
1176
		$this->recalculate_total();
1177
	}
1178
1179
	/**
1180
	 * Decrease the payment's subtotal
1181
	 *
1182
	 * @since  1.5
1183
	 *
1184
	 * @param  float $amount The amount to decrease the payment subtotal by
1185
	 *
1186
	 * @return void
1187
	 */
1188
	private function decrease_subtotal( $amount = 0.00 ) {
1189
		$amount = (float) $amount;
1190
		$this->subtotal -= $amount;
1191
1192
		if ( $this->subtotal < 0 ) {
1193
			$this->subtotal = 0;
1194
		}
1195
1196
		$this->recalculate_total();
1197
	}
1198
1199
	/**
1200
	 * Increase the payment's subtotal
1201
	 *
1202
	 * @since  1.5
1203
	 *
1204
	 * @param  float $amount The amount to increase the payment subtotal by
1205
	 *
1206
	 * @return void
1207
	 */
1208
	private function increase_fees( $amount = 0.00 ) {
1209
		$amount = (float) $amount;
1210
		$this->fees_total += $amount;
1211
1212
		$this->recalculate_total();
1213
	}
1214
1215
	/**
1216
	 * Decrease the payment's subtotal
1217
	 *
1218
	 * @since  1.5
1219
	 *
1220
	 * @param  float $amount The amount to decrease the payment subtotal by
1221
	 *
1222
	 * @return void
1223
	 */
1224
	private function decrease_fees( $amount = 0.00 ) {
1225
		$amount = (float) $amount;
1226
		$this->fees_total -= $amount;
1227
1228
		if ( $this->fees_total < 0 ) {
1229
			$this->fees_total = 0;
1230
		}
1231
1232
		$this->recalculate_total();
1233
	}
1234
1235
	/**
1236
	 * Set or update the total for a payment
1237
	 *
1238
	 * @since 1.0
1239
	 * @return void
1240
	 */
1241
	private function recalculate_total() {
1242
		$this->total = $this->subtotal + $this->fees_total;
1243
	}
1244
1245
	/**
1246
	 * Set the payment status and run any status specific changes necessary
1247
	 *
1248
	 * @since 1.0
1249
	 *
1250
	 * @param  string $status The status to set the payment to
1251
	 *
1252
	 * @return bool Returns if the status was successfully updated
1253
	 */
1254
	public function update_status( $status = false ) {
1255
1256
		//standardize the 'complete(d)' status
1257
		if ( $status == 'completed' || $status == 'complete' ) {
1258
			$status = 'publish';
1259
		}
1260
1261
		$old_status = ! empty( $this->old_status ) ? $this->old_status : false;
1262
1263
		if ( $old_status === $status ) {
1264
			return false; // Don't permit status changes that aren't changes
1265
		}
1266
1267
		$do_change = apply_filters( 'give_should_update_payment_status', true, $this->ID, $status, $old_status );
1268
1269
		$updated = false;
1270
1271
1272
		if ( $do_change ) {
1273
1274
			do_action( 'give_before_payment_status_change', $this->ID, $status, $old_status );
1275
1276
			$update_fields = array(
1277
				'ID'          => $this->ID,
1278
				'post_status' => $status,
1279
				'edit_date'   => current_time( 'mysql' )
1280
			);
1281
1282
			$updated = wp_update_post( apply_filters( 'give_update_payment_status_fields', $update_fields ) );
1283
1284
			$all_payment_statuses  = give_get_payment_statuses();
1285
			$this->status_nicename = array_key_exists( $status, $all_payment_statuses ) ? $all_payment_statuses[ $status ] : ucfirst( $status );
1286
1287
			// Process any specific status functions
1288
			switch ( $status ) {
1289
				case 'refunded':
1290
					$this->process_refund();
1291
					break;
1292
				case 'failed':
1293
					$this->process_failure();
0 ignored issues
show
Unused Code introduced by
The call to the method Give_Payment::process_failure() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
1294
					break;
1295
				case 'pending':
1296
					$this->process_pending();
1297
					break;
1298
			}
1299
1300
			do_action( 'give_update_payment_status', $this->ID, $status, $old_status );
1301
1302
		}
1303
1304
		return $updated;
1305
1306
	}
1307
1308
	/**
1309
	 * Change the status of the payment to refunded, and run the necessary changes
1310
	 *
1311
	 * @since  1.5
1312
	 * @return void
1313
	 */
1314
	public function refund() {
1315
		$this->old_status        = $this->status;
1316
		$this->status            = 'refunded';
1317
		$this->pending['status'] = $this->status;
1318
1319
		$this->save();
1320
	}
1321
1322
	/**
1323
	 * Get a post meta item for the payment
1324
	 *
1325
	 * @since  1.5
1326
	 *
1327
	 * @param  string $meta_key The Meta Key
1328
	 * @param  boolean $single Return single item or array
1329
	 *
1330
	 * @return mixed             The value from the post meta
1331
	 */
1332
	public function get_meta( $meta_key = '_give_payment_meta', $single = true ) {
1333
1334
		$meta = get_post_meta( $this->ID, $meta_key, $single );
1335
1336
		if ( $meta_key === '_give_payment_meta' ) {
1337
1338
			if ( empty( $meta['key'] ) ) {
1339
				$meta['key'] = $this->setup_payment_key();
1340
			}
1341
1342
			if ( empty( $meta['form_title'] ) ) {
1343
				$meta['form_title'] = $this->setup_form_title();
1344
			}
1345
1346
			if ( empty( $meta['email'] ) ) {
1347
				$meta['email'] = $this->setup_email();
1348
			}
1349
1350
			if ( empty( $meta['date'] ) ) {
1351
				$meta['date'] = get_post_field( 'post_date', $this->ID );
1352
			}
1353
		}
1354
1355
		$meta = apply_filters( 'give_get_payment_meta_' . $meta_key, $meta, $this->ID );
1356
1357
		return apply_filters( 'give_get_payment_meta', $meta, $this->ID, $meta_key );
1358
	}
1359
1360
	/**
1361
	 * Update the post meta
1362
	 *
1363
	 * @since  1.5
1364
	 *
1365
	 * @param  string $meta_key The meta key to update
1366
	 * @param  string $meta_value The meta value
1367
	 * @param  string $prev_value Previous meta value
1368
	 *
1369
	 * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure
1370
	 */
1371
	public function update_meta( $meta_key = '', $meta_value = '', $prev_value = '' ) {
1372
		if ( empty( $meta_key ) ) {
1373
			return false;
1374
		}
1375
1376
		if ( $meta_key == 'key' || $meta_key == 'date' ) {
1377
1378
			$current_meta              = $this->get_meta();
1379
			$current_meta[ $meta_key ] = $meta_value;
1380
1381
			$meta_key   = '_give_payment_meta';
1382
			$meta_value = $current_meta;
1383
1384
		} else if ( $meta_key == 'email' || $meta_key == '_give_payment_user_email' ) {
1385
1386
			$meta_value = apply_filters( 'give_give_update_payment_meta_' . $meta_key, $meta_value, $this->ID );
1387
			update_post_meta( $this->ID, '_give_payment_user_email', $meta_value );
1388
1389
			$current_meta                       = $this->get_meta();
1390
			$current_meta['user_info']['email'] = $meta_value;
1391
1392
			$meta_key   = '_give_payment_meta';
1393
			$meta_value = $current_meta;
1394
1395
		}
1396
1397
		$meta_value = apply_filters( 'give_update_payment_meta_' . $meta_key, $meta_value, $this->ID );
1398
1399
		return update_post_meta( $this->ID, $meta_key, $meta_value, $prev_value );
1400
	}
1401
1402
	/**
1403
	 * When a payment is set to a status of 'refunded' process the necessary actions to reduce stats
1404
	 *
1405
	 * @since  1.5
1406
	 * @access private
1407
	 * @return void
1408
	 */
1409
	private function process_refund() {
1410
		$process_refund = true;
1411
1412
		// If the payment was not in publish or revoked status, don't decrement stats as they were never incremented
1413
		if ( ( 'publish' != $this->old_status && 'revoked' != $this->old_status ) || 'refunded' != $this->status ) {
1414
			$process_refund = false;
1415
		}
1416
1417
		// Allow extensions to filter for their own payment types, Example: Recurring Payments
1418
		$process_refund = apply_filters( 'give_should_process_refund', $process_refund, $this );
1419
1420
		if ( false === $process_refund ) {
1421
			return;
1422
		}
1423
1424
		do_action( 'give_pre_refund_payment', $this );
1425
1426
		$decrease_store_earnings = apply_filters( 'give_decrease_store_earnings_on_refund', true, $this );
1427
		$decrease_customer_value = apply_filters( 'give_decrease_customer_value_on_refund', true, $this );
1428
		$decrease_purchase_count = apply_filters( 'give_decrease_customer_purchase_count_on_refund', true, $this );
1429
1430
		$this->maybe_alter_stats( $decrease_store_earnings, $decrease_customer_value, $decrease_purchase_count );
1431
		$this->delete_sales_logs();
1432
1433
		// Clear the This Month earnings (this_monththis_month is NOT a typo)
1434
		delete_transient( md5( 'give_earnings_this_monththis_month' ) );
1435
1436
		do_action( 'give_post_refund_payment', $this );
1437
	}
1438
1439
	/**
1440
	 * Process when a payment is set to failed
1441
	 *
1442
	 * @since  1.5
1443
	 * @return void
1444
	 */
1445
	private function process_failure() {
1446
1447
1448
	}
1449
1450
	/**
1451
	 * Process when a payment moves to pending
1452
	 *
1453
	 * @since  1.5
1454
	 * @return void
1455
	 */
1456
	private function process_pending() {
1457
		$process_pending = true;
1458
1459
		// If the payment was not in publish or revoked status, don't decrement stats as they were never incremented
1460
		if ( ( 'publish' != $this->old_status && 'revoked' != $this->old_status ) || 'pending' != $this->status ) {
1461
			$process_pending = false;
1462
		}
1463
1464
		// Allow extensions to filter for their own payment types, Example: Recurring Payments
1465
		$process_pending = apply_filters( 'give_should_process_pending', $process_pending, $this );
1466
1467
		if ( false === $process_pending ) {
1468
			return;
1469
		}
1470
1471
		$decrease_store_earnings = apply_filters( 'give_decrease_store_earnings_on_pending', true, $this );
1472
		$decrease_customer_value = apply_filters( 'give_decrease_customer_value_on_pending', true, $this );
1473
		$decrease_purchase_count = apply_filters( 'give_decrease_customer_purchase_count_on_pending', true, $this );
1474
1475
		$this->maybe_alter_stats( $decrease_store_earnings, $decrease_customer_value, $decrease_purchase_count );
1476
		$this->delete_sales_logs();
1477
1478
		$this->completed_date = false;
1479
		$this->update_meta( '_give_completed_date', '' );
1480
1481
		// Clear the This Month earnings (this_monththis_month is NOT a typo)
1482
		delete_transient( md5( 'give_earnings_this_monththis_month' ) );
1483
	}
1484
1485
	/**
1486
	 * Used during the process of moving to refunded or pending, to decrement stats
1487
	 *
1488
	 * @since  1.5
1489
	 *
1490
	 * @param  bool $alter_store_earnings If the method should alter the store earnings
1491
	 * @param  bool $alter_customer_value If the method should reduce the customer value
1492
	 * @param  bool $alter_customer_purchase_count If the method should reduce the customer's purchase count
1493
	 *
1494
	 * @return void
1495
	 */
1496
	private function maybe_alter_stats( $alter_store_earnings, $alter_customer_value, $alter_customer_purchase_count ) {
1497
1498
		give_undo_purchase( false, $this->ID );
1499
1500
		// Decrease store earnings
1501
		if ( true === $alter_store_earnings ) {
1502
			give_decrease_total_earnings( $this->total );
1503
		}
1504
1505
		// Decrement the stats for the customer
1506
		if ( ! empty( $this->customer_id ) ) {
1507
1508
			$customer = new Give_Customer( $this->customer_id );
1509
1510
			if ( true === $alter_customer_value ) {
1511
				$customer->decrease_value( $this->total );
1512
			}
1513
1514
			if ( true === $alter_customer_purchase_count ) {
1515
				$customer->decrease_purchase_count();
1516
			}
1517
1518
		}
1519
1520
	}
1521
1522
	/**
1523
	 * Delete sales logs for this purchase
1524
	 *
1525
	 * @since  1.5
1526
	 * @return void
1527
	 */
1528
	private function delete_sales_logs() {
1529
		global $give_logs;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1530
1531
		// Remove related sale log entries
1532
		$give_logs->delete_logs(
1533
			null,
1534
			'sale',
1535
			array(
1536
				array(
1537
					'key'   => '_give_log_payment_id',
1538
					'value' => $this->ID,
1539
				),
1540
			)
1541
		);
1542
	}
1543
1544
	/**
1545
	 * Setup functions only, these are not to be used by developers.
1546
	 * These functions exist only to allow the setup routine to be backwards compatible with our old
1547
	 * helper functions.
1548
	 *
1549
	 * These will run whenever setup_payment is called, which should only be called once.
1550
	 * To update an attribute, update it directly instead of re-running the setup routine
1551
	 */
1552
1553
	/**
1554
	 * Setup the payment completed date
1555
	 *
1556
	 * @since  1.5
1557
	 * @return string The date the payment was completed
1558
	 */
1559
	private function setup_completed_date() {
1560
		$payment = get_post( $this->ID );
1561
1562
		if ( 'pending' == $payment->post_status || 'preapproved' == $payment->post_status ) {
1563
			return false; // This payment was never completed
1564
		}
1565
1566
		$date = ( $date = $this->get_meta( '_give_completed_date', true ) ) ? $date : $payment->modified_date;
1567
1568
		return $date;
1569
	}
1570
1571
	/**
1572
	 * Setup the payment mode
1573
	 *
1574
	 * @since  1.5
1575
	 * @return string The payment mode
1576
	 */
1577
	private function setup_mode() {
1578
		return $this->get_meta( '_give_payment_mode' );
1579
	}
1580
1581
	/**
1582
	 * Setup the payment total
1583
	 *
1584
	 * @since  1.5
1585
	 * @return float The payment total
1586
	 */
1587
	private function setup_total() {
1588
		$amount = $this->get_meta( '_give_payment_total', true );
1589
1590
		if ( empty( $amount ) && '0.00' != $amount ) {
1591
			$meta = $this->get_meta( '_give_payment_meta', true );
1592
			$meta = maybe_unserialize( $meta );
1593
1594
			if ( isset( $meta['amount'] ) ) {
1595
				$amount = $meta['amount'];
1596
			}
1597
		}
1598
1599
		return $amount;
1600
	}
1601
1602
	/**
1603
	 * Setup the payment subtotal
1604
	 *
1605
	 * @since  1.5
1606
	 * @return float The subtotal of the payment
1607
	 */
1608
	private function setup_subtotal() {
1609
		$subtotal = $this->total;
1610
1611
		return $subtotal;
1612
	}
1613
1614
	/**
1615
	 * Setup the payment fees
1616
	 *
1617
	 * @since  1.5
1618
	 * @return float The fees total for the payment
1619
	 */
1620
	private function setup_fees_total() {
1621
		$fees_total = (float) 0.00;
1622
1623
		$payment_fees = isset( $this->payment_meta['fees'] ) ? $this->payment_meta['fees'] : array();
1624
		if ( ! empty( $payment_fees ) ) {
1625
			foreach ( $payment_fees as $fee ) {
1626
				$fees_total += (float) $fee['amount'];
1627
			}
1628
		}
1629
1630
		return $fees_total;
1631
1632
	}
1633
1634
	/**
1635
	 * Setup the currency code
1636
	 *
1637
	 * @since  1.5
1638
	 * @return string              The currency for the payment
1639
	 */
1640
	private function setup_currency() {
1641
		$currency = isset( $this->payment_meta['currency'] ) ? $this->payment_meta['currency'] : apply_filters( 'give_payment_currency_default', give_get_currency(), $this );
1642
1643
		return $currency;
1644
	}
1645
1646
	/**
1647
	 * Setup any fees associated with the payment
1648
	 *
1649
	 * @since  1.5
1650
	 * @return array The Fees
1651
	 */
1652
	private function setup_fees() {
1653
		$payment_fees = isset( $this->payment_meta['fees'] ) ? $this->payment_meta['fees'] : array();
1654
1655
		return $payment_fees;
1656
	}
1657
1658
	/**
1659
	 * Setup the gateway used for the payment
1660
	 *
1661
	 * @since  1.5
1662
	 * @return string The gateway
1663
	 */
1664
	private function setup_gateway() {
1665
		$gateway = $this->get_meta( '_give_payment_gateway', true );
1666
1667
		return $gateway;
1668
	}
1669
1670
	/**
1671
	 * Setup the transaction ID
1672
	 *
1673
	 * @since  1.5
1674
	 * @return string The transaction ID for the payment
1675
	 */
1676
	private function setup_transaction_id() {
1677
		$transaction_id = $this->get_meta( '_give_payment_transaction_id', true );
1678
1679
		if ( empty( $transaction_id ) || (int) $transaction_id === (int) $this->ID ) {
1680
1681
			$gateway        = $this->gateway;
1682
			$transaction_id = apply_filters( 'give_get_payment_transaction_id-' . $gateway, $this->ID );
1683
1684
		}
1685
1686
		return $transaction_id;
1687
	}
1688
1689
	/**
1690
	 * Setup the IP Address for the payment
1691
	 *
1692
	 * @since  1.5
1693
	 * @return string The IP address for the payment
1694
	 */
1695
	private function setup_ip() {
1696
		$ip = $this->get_meta( '_give_payment_user_ip', true );
1697
1698
		return $ip;
1699
	}
1700
1701
	/**
1702
	 * Setup the customer ID
1703
	 *
1704
	 * @since  1.5
1705
	 * @return int The Customer ID
1706
	 */
1707
	private function setup_customer_id() {
1708
		$customer_id = $this->get_meta( '_give_payment_customer_id', true );
1709
1710
		return $customer_id;
1711
	}
1712
1713
	/**
1714
	 * Setup the User ID associated with the purchase
1715
	 *
1716
	 * @since  1.5
1717
	 * @return int The User ID
1718
	 */
1719
	private function setup_user_id() {
1720
		$user_id = $this->get_meta( '_give_payment_user_id', true );
1721
1722
		return $user_id;
1723
	}
1724
1725
	/**
1726
	 * Setup the email address for the purchase
1727
	 *
1728
	 * @since  1.5
1729
	 * @return string The email address for the payment
1730
	 */
1731
	private function setup_email() {
1732
		$email = $this->get_meta( '_give_payment_user_email', true );
1733
1734
		if ( empty( $email ) ) {
1735
			$email = Give()->customers->get_column( 'email', $this->customer_id );
1736
		}
1737
1738
		return $email;
1739
	}
1740
1741
	/**
1742
	 * Setup the user info
1743
	 *
1744
	 * @since  1.5
1745
	 * @return array               The user info associated with the payment
1746
	 */
1747
	private function setup_user_info() {
1748
		$defaults = array(
1749
			'first_name' => $this->first_name,
1750
			'last_name'  => $this->last_name,
1751
		);
1752
1753
		$user_info = isset( $this->payment_meta['user_info'] ) ? maybe_unserialize( $this->payment_meta['user_info'] ) : array();
1754
		$user_info = wp_parse_args( $user_info, $defaults );
1755
1756
		if ( empty( $user_info ) ) {
1757
			// Get the customer, but only if it's been created
1758
			$customer = new Give_Customer( $this->customer_id );
1759
1760
			if ( $customer->id > 0 ) {
1761
				$name      = explode( ' ', $customer->name, 2 );
1762
				$user_info = array(
1763
					'first_name' => $name[0],
1764
					'last_name'  => $name[1],
1765
					'email'      => $customer->email,
1766
					'discount'   => 'none',
1767
				);
1768
			}
1769
		} else {
1770
			// Get the customer, but only if it's been created
1771
			$customer = new Give_Customer( $this->customer_id );
1772
			if ( $customer->id > 0 ) {
1773
				foreach ( $user_info as $key => $value ) {
1774
					if ( ! empty( $value ) ) {
1775
						continue;
1776
					}
1777
1778
					switch ( $key ) {
1779
						case 'first_name':
1780
							$name = explode( ' ', $customer->name, 2 );
1781
1782
							$user_info[ $key ] = $name[0];
1783
							break;
1784
1785
						case 'last_name':
1786
							$name      = explode( ' ', $customer->name, 2 );
1787
							$last_name = ! empty( $name[1] ) ? $name[1] : '';
1788
1789
							$user_info[ $key ] = $last_name;
1790
							break;
1791
1792
						case 'email':
1793
							$user_info[ $key ] = $customer->email;
1794
							break;
1795
					}
1796
				}
1797
1798
			}
1799
		}
1800
1801
		return $user_info;
1802
1803
	}
1804
1805
	/**
1806
	 * Setup the Address for the payment
1807
	 *
1808
	 * @since  1.5
1809
	 * @return array               The Address information for the payment
1810
	 */
1811
	private function setup_address() {
1812
1813
		$address = ! empty( $this->payment_meta['user_info']['address'] ) ? $this->payment_meta['user_info']['address'] : array(
1814
			'line1'   => '',
1815
			'line2'   => '',
1816
			'city'    => '',
1817
			'country' => '',
1818
			'state'   => '',
1819
			'zip'     => ''
1820
		);
1821
1822
		return $address;
1823
	}
1824
1825
	/**
1826
	 * Setup the form title
1827
	 *
1828
	 * @since  1.5
1829
	 * @return string The Form Title
1830
	 */
1831
	private function setup_form_title() {
1832
1833
		$form_id = $this->get_meta( '_give_payment_form_title', true );
1834
1835
		return $form_id;
1836
	}
1837
1838
	/**
1839
	 * Setup the form ID
1840
	 *
1841
	 * @since  1.5
1842
	 * @return int The Form ID
1843
	 */
1844
	private function setup_form_id() {
1845
1846
		$form_id = $this->get_meta( '_give_payment_form_id', true );
1847
1848
		return $form_id;
1849
	}
1850
1851
	/**
1852
	 * Setup the price ID
1853
	 *
1854
	 * @since  1.5
1855
	 * @return int The Form Price ID
1856
	 */
1857
	private function setup_price_id() {
1858
		$price_id = $this->get_meta( '_give_payment_price_id', true );
1859
1860
		return $price_id;
1861
	}
1862
1863
	/**
1864
	 * Setup the payment key
1865
	 *
1866
	 * @since  1.5
1867
	 * @return string The Payment Key
1868
	 */
1869
	private function setup_payment_key() {
1870
		$key = $this->get_meta( '_give_payment_purchase_key', true );
1871
1872
		return $key;
1873
	}
1874
1875
	/**
1876
	 * Setup the payment number
1877
	 *
1878
	 * @since  1.5
1879
	 * @return int|string Integer by default, or string if sequential order numbers is enabled
1880
	 */
1881
	private function setup_payment_number() {
1882
		$number = $this->ID;
1883
1884
		if ( give_get_option( 'enable_sequential' ) ) {
1885
1886
			$number = $this->get_meta( '_give_payment_number', true );
1887
1888
			if ( ! $number ) {
1889
1890
				$number = $this->ID;
1891
1892
			}
1893
1894
		}
1895
1896
		return $number;
1897
	}
1898
1899
	/**
1900
	 * Converts this object into an array for special cases
1901
	 *
1902
	 * @return array The payment object as an array
1903
	 */
1904
	public function array_convert() {
1905
		return get_object_vars( $this );
1906
	}
1907
1908
	/**
1909
	 * Retrieve payment completion date
1910
	 *
1911
	 * @since  1.5
1912
	 * @return string Date payment was completed
1913
	 */
1914
	private function get_completed_date() {
1915
		return apply_filters( 'give_payment_completed_date', $this->completed_date, $this->ID, $this );
1916
	}
1917
1918
	/**
1919
	 * Retrieve payment subtotal
1920
	 *
1921
	 * @since  1.5
1922
	 * @return float Payment subtotal
1923
	 */
1924
	private function get_subtotal() {
1925
		return apply_filters( 'give_get_payment_subtotal', $this->subtotal, $this->ID, $this );
1926
	}
1927
1928
	/**
1929
	 * Retrieve payment currency
1930
	 *
1931
	 * @since  1.5
1932
	 * @return string Payment currency code
1933
	 */
1934
	private function get_currency() {
1935
		return apply_filters( 'give_payment_currency_code', $this->currency, $this->ID, $this );
1936
	}
1937
1938
	/**
1939
	 * Retrieve payment gateway
1940
	 *
1941
	 * @since  1.5
1942
	 * @return string Gateway used
1943
	 */
1944
	private function get_gateway() {
1945
		return apply_filters( 'give_payment_gateway', $this->gateway, $this->ID, $this );
1946
	}
1947
1948
	/**
1949
	 * Retrieve payment transaction ID
1950
	 *
1951
	 * @since  1.5
1952
	 * @return string Transaction ID from merchant processor
1953
	 */
1954
	private function get_transaction_id() {
1955
		return apply_filters( 'give_get_payment_transaction_id', $this->transaction_id, $this->ID, $this );
1956
	}
1957
1958
	/**
1959
	 * Retrieve payment IP
1960
	 *
1961
	 * @since  1.5
1962
	 * @return string Payment IP address
1963
	 */
1964
	private function get_ip() {
1965
		return apply_filters( 'give_payment_user_ip', $this->ip, $this->ID, $this );
1966
	}
1967
1968
	/**
1969
	 * Retrieve payment customer ID
1970
	 *
1971
	 * @since  1.5
1972
	 * @return int Payment customer ID
1973
	 */
1974
	private function get_customer_id() {
1975
		return apply_filters( 'give_payment_customer_id', $this->customer_id, $this->ID, $this );
1976
	}
1977
1978
	/**
1979
	 * Retrieve payment user ID
1980
	 *
1981
	 * @since  1.5
1982
	 * @return int Payment user ID
1983
	 */
1984
	private function get_user_id() {
1985
		return apply_filters( 'give_payment_user_id', $this->user_id, $this->ID, $this );
1986
	}
1987
1988
	/**
1989
	 * Retrieve payment email
1990
	 *
1991
	 * @since  1.5
1992
	 * @return string Payment customer email
1993
	 */
1994
	private function get_email() {
1995
		return apply_filters( 'give_payment_user_email', $this->email, $this->ID, $this );
1996
	}
1997
1998
	/**
1999
	 * Retrieve payment user info
2000
	 *
2001
	 * @since  1.5
2002
	 * @return array Payment user info
2003
	 */
2004
	private function get_user_info() {
2005
		return apply_filters( 'give_payment_meta_user_info', $this->user_info, $this->ID, $this );
2006
	}
2007
2008
	/**
2009
	 * Retrieve payment billing address
2010
	 *
2011
	 * @since  1.5
2012
	 * @return array Payment billing address
2013
	 */
2014
	private function get_address() {
2015
		return apply_filters( 'give_payment_address', $this->address, $this->ID, $this );
2016
	}
2017
2018
	/**
2019
	 * Retrieve payment key
2020
	 *
2021
	 * @since  1.5
2022
	 * @return string Payment key
2023
	 */
2024
	private function get_key() {
2025
		return apply_filters( 'give_payment_key', $this->key, $this->ID, $this );
2026
	}
2027
2028
	/**
2029
	 * Retrieve payment key
2030
	 *
2031
	 * @since  1.5
2032
	 * @return string Payment key
2033
	 */
2034
	private function get_form_id() {
2035
		return apply_filters( 'give_payment_form_id', $this->form_id, $this->ID, $this );
2036
	}
2037
2038
	/**
2039
	 * Retrieve payment number
2040
	 *
2041
	 * @since  1.5
2042
	 * @return int|string Payment number
2043
	 */
2044
	private function get_number() {
2045
		return apply_filters( 'give_payment_number', $this->number, $this->ID, $this );
2046
	}
2047
2048
}
2049