Completed
Pull Request — master (#834)
by Devin
18:40
created

Give_Payment::get_completed_date()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
ccs 0
cts 0
cp 0
crap 2
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 52
	public function __construct( $payment_id = false ) {
299
300 52
		if ( empty( $payment_id ) ) {
301 52
			return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
302
		}
303
304 52
		$this->setup_payment( $payment_id );
305 52
	}
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 52
	public function __get( $key ) {
317
318 52
		if ( method_exists( $this, 'get_' . $key ) ) {
319
320 52
			$value = call_user_func( array( $this, 'get_' . $key ) );
321
322 52
		} else {
323
324 52
			$value = $this->$key;
325
326
		}
327
328 52
		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 52
	public function __set( $key, $value ) {
342 52
		$ignore = array( '_ID' );
343
344 52
		if ( $key === 'status' ) {
345 52
			$this->old_status = $this->status;
346 52
		}
347
348 52
		if ( ! in_array( $key, $ignore ) ) {
349 52
			$this->pending[ $key ] = $value;
350 52
		}
351
352 52
		if ( '_ID' !== $key ) {
353 52
			$this->$key = $value;
354 52
		}
355 52
	}
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 42
	public function __isset( $name ) {
367 42
		if ( property_exists( $this, $name ) ) {
368 42
			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 52
	private function setup_payment( $payment_id ) {
384 52
		$this->pending = array();
385
386 52
		if ( empty( $payment_id ) ) {
387
			return false;
388
		}
389
390 52
		$payment = get_post( $payment_id );
391
392 52
		if ( ! $payment || is_wp_error( $payment ) ) {
393 7
			return false;
394
		}
395
396 52
		if ( 'give_payment' !== $payment->post_type ) {
397
			return false;
398
		}
399
400
		// Allow extensions to perform actions before the payment is loaded
401 52
		do_action( 'give_pre_setup_payment', $this, $payment_id );
402
403
		// Primary Identifier
404 52
		$this->ID = absint( $payment_id );
405
406
		// Protected ID that can never be changed
407 52
		$this->_ID = absint( $payment_id );
408
409
		// We have a payment, get the generic payment_meta item to reduce calls to it
410 52
		$this->payment_meta = $this->get_meta();
411
412
		// Status and Dates
413 52
		$this->date           = $payment->post_date;
414 52
		$this->post_date      = $payment->post_date;
415 52
		$this->completed_date = $this->setup_completed_date();
416 52
		$this->status         = $payment->post_status;
417 52
		$this->post_status    = $this->status;
418 52
		$this->mode           = $this->setup_mode();
419 52
		$this->parent_payment = $payment->post_parent;
420
421 52
		$all_payment_statuses  = give_get_payment_statuses();
422 52
		$this->status_nicename = array_key_exists( $this->status, $all_payment_statuses ) ? $all_payment_statuses[ $this->status ] : ucfirst( $this->status );
423
424
		// Items
425 52
		$this->fees = $this->setup_fees();
426
427
		// Currency Based
428 52
		$this->total      = $this->setup_total();
429 52
		$this->fees_total = $this->setup_fees_total();
430 52
		$this->subtotal   = $this->setup_subtotal();
431 52
		$this->currency   = $this->setup_currency();
432
433
		// Gateway based
434 52
		$this->gateway        = $this->setup_gateway();
435 52
		$this->transaction_id = $this->setup_transaction_id();
436
437
		// User based
438 52
		$this->ip          = $this->setup_ip();
439 52
		$this->customer_id = $this->setup_customer_id();
440 52
		$this->user_id     = $this->setup_user_id();
441 52
		$this->email       = $this->setup_email();
442 52
		$this->user_info   = $this->setup_user_info();
443 52
		$this->address     = $this->setup_address();
444 52
		$this->first_name  = $this->user_info['first_name'];
445 52
		$this->last_name   = $this->user_info['last_name'];
446
447
		// Other Identifiers
448 52
		$this->form_title = $this->setup_form_title();
449 52
		$this->form_id    = $this->setup_form_id();
450 52
		$this->price_id   = $this->setup_price_id();
451 52
		$this->key        = $this->setup_payment_key();
452 52
		$this->number     = $this->setup_payment_number();
453
454
		// Allow extensions to add items to this object via hook
455 52
		do_action( 'give_setup_payment', $this, $payment_id );
456
457 52
		return true;
458
	}
459
460
    /**
461
     * Payment class object is storing various meta value in object parameter.
462
     * So if user is updating payment meta but not updating payment object, then payment meta values will not reflect/changes on payment meta automatically
463
     * and you can still access payment meta old value in any old payment object ( previously created ) which can cause to show or save wrong payment data.
464
     * To prevent that user can use this function after updating any payment meta value ( in bulk or single update ).
465
     *  
466 52
     * @since 1.6
467
     * @access public
468
     * 
469 52
     * @param int $payment_id Payment ID.
470 52
     * 
471 31
     * @return void
472 52
     */
473
    public function update_payment_setup( $payment_id ){
474 22
        $this->setup_payment( $payment_id );
475 21
    }
476 21
477
	/**
478
	 * Create the base of a payment.
479 52
	 *
480
	 * @since  1.5
481 1
	 * @return int|bool False on failure, the payment ID on success.
482 1
	 */
483 1
	private function insert_payment() {
484 1
485
		// Construct the payment title
486
		$payment_title = '';
487 52
		if ( ! empty( $this->first_name ) && ! empty( $this->last_name ) ) {
488
			$payment_title = $this->first_name . ' ' . $this->last_name;
489 1
		} else if ( ! empty( $this->first_name ) && empty( $this->last_name ) ) {
490 1
			$payment_title = $this->first_name;
491
		} else if ( ! empty( $this->email ) && is_email( $this->email ) ) {
492 1
			$payment_title = $this->email;
493
		}
494
495 52
		//Set Key
496 52
		if ( empty( $this->key ) ) {
497 52
498 52
			$auth_key             = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
499 52
			$this->key            = strtolower( md5( $this->email . date( 'Y-m-d H:i:s' ) . $auth_key . uniqid( 'give', true ) ) );  // Unique key
500 52
			$this->pending['key'] = $this->key;
501 52
		}
502 52
503
		//Set IP
504 52
		if ( empty( $this->ip ) ) {
505 52
506 52
			$this->ip            = give_get_ip();
507 52
			$this->pending['ip'] = $this->ip;
508 52
509 52
		}
510 52
511 52
		$payment_data = array(
512 52
			'price'        => $this->total,
513
			'date'         => $this->date,
514 52
			'user_email'   => $this->email,
515 52
			'purchase_key' => $this->key,
516 52
			'form_title'   => $this->form_title,
517 52
			'form_id'      => $this->form_id,
518 52
			'price_id'     => $this->price_id,
519 52
			'currency'     => $this->currency,
520 52
			'user_info'    => array(
521 52
				'id'         => $this->user_id,
522
				'email'      => $this->email,
523
				'first_name' => $this->first_name,
524 52
				'last_name'  => $this->last_name,
525
				'address'    => $this->address,
526 52
			),
527
			'status'       => $this->status,
528 52
			'fees'         => $this->fees,
529 52
		);
530
531 52
		$args = apply_filters( 'give_insert_payment_args', array(
532
			'post_title'    => $payment_title,
533 52
			'post_status'   => $this->status,
534
			'post_type'     => 'give_payment',
535
			'post_date'     => ! empty( $this->date ) ? $this->date : null,
536
			'post_date_gmt' => ! empty( $this->date ) ? get_gmt_from_date( $this->date ) : null,
537 52
			'post_parent'   => $this->parent_payment,
538 52
		), $payment_data );
539 52
540
		// Create a blank payment
541 52
		$payment_id = wp_insert_post( $args );
542
543
		if ( ! empty( $payment_id ) ) {
544 52
545 52
			$this->ID  = $payment_id;
546 52
			$this->_ID = $payment_id;
547 52
548
			$customer = new stdClass;
549 52
550
			if ( did_action( 'give_pre_process_purchase' ) && is_user_logged_in() ) {
551 52
				$customer = new Give_Customer( get_current_user_id(), true );
552
			}
553 52
554 52
			if ( empty( $customer->id ) ) {
555 52
				$customer = new Give_Customer( $this->email );
556
			}
557 52
558 52
			if ( empty( $customer->id ) ) {
559
560
				$customer_data = array(
561
					'name'    => ! is_email( $payment_title ) ? $this->first_name . ' ' . $this->last_name : '',
562
					'email'   => $this->email,
563
					'user_id' => $this->user_id,
564
				);
565 52
566 52
				$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...
567 52
568
			}
569 52
570
			$this->customer_id            = $customer->id;
571
			$this->pending['customer_id'] = $this->customer_id;
572
			$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...
573
574
			$this->payment_meta = apply_filters( 'give_payment_meta', $this->payment_meta, $payment_data );
575
			if ( ! empty( $this->payment_meta['fees'] ) ) {
576
				$this->fees = array_merge( $this->fees, $this->payment_meta['fees'] );
577
				foreach ( $this->fees as $fee ) {
578
					$this->increase_fees( $fee['amount'] );
579
				}
580 52
			}
581
582 52
			$this->update_meta( '_give_payment_meta', $this->payment_meta );
583
			$this->new = true;
584
		}
585 52
586
		return $this->ID;
587 52
588
	}
589 52
590
	/**
591
	 * Save
592 52
	 *
593
	 * Once items have been set, an update is needed to save them to the database.
594
	 *
595 52
	 * @return bool  True of the save occurred, false if it failed or wasn't needed
596
	 */
597
	public function save() {
598 52
599 1
		$saved = false;
600 1
601
		//Must have an ID 
602
		if ( empty( $this->ID ) ) {
603 52
604
			$payment_id = $this->insert_payment();
605 52
606 52
			if ( false === $payment_id ) {
607
				$saved = false;
608 52
			} else {
609
				$this->ID = $payment_id;
610
			}
611
612 52
		}
613
614 52
		//Set ID if not matching
615
		if ( $this->ID !== $this->_ID ) {
616 52
			$this->ID = $this->_ID;
617 52
		}
618
619 52
		// If we have something pending, let's save it
620
		if ( ! empty( $this->pending ) ) {
621 52
622
			$total_increase = 0;
623 52
			$total_decrease = 0;
624
625 52
			foreach ( $this->pending as $key => $value ) {
626
627
				switch ( $key ) {
628 1
629
					case 'donations':
630 1
						// Update totals for pending donations
631 1
						foreach ( $this->pending[ $key ] as $item ) {
632
633 1
							$quantity = isset( $item['quantity'] ) ? $item['quantity'] : 1;
634 1
							$price_id = isset( $item['price_id'] ) ? $item['price_id'] : 0;
635 1
636
							switch ( $item['action'] ) {
637 1
638 1
								case 'add':
639 1
640
									$price = $item['price'];
641 1
642 1
									if ( 'publish' === $this->status || 'complete' === $this->status || 'revoked' === $this->status ) {
643 52
644
										// Add sales logs
645 2
										$log_date = date_i18n( 'Y-m-d G:i:s', current_time( 'timestamp' ) );
646
647 2
										$y = 0;
648 2
										while ( $y < $quantity ) {
649 2
650
											give_record_sale_in_log( $item['id'], $this->ID, $price_id, $log_date );
651
											$y ++;
652 2
										}
653 2
654 2
										$form = new Give_Donate_Form( $item['id'] );
655 2
										$form->increase_sales( $quantity );
656
										$form->increase_earnings( $price );
657 2
658 2
										$total_increase += $price;
659
									}
660 2
									break;
661 2
662 2
								case 'remove':
663
									$log_args = array(
664 2
										'post_type'   => 'give_log',
665 2
										'post_parent' => $item['id'],
666 1
										'numberposts' => $quantity,
667 2
										'meta_query'  => array(
668
											array(
669 2
												'key'     => '_give_log_payment_id',
670 1
												'value'   => $this->ID,
671 1
												'compare' => '=',
672 1
											),
673
											array(
674 1
												'key'     => '_give_log_price_id',
675 1
												'value'   => $price_id,
676 2
												'compare' => '='
677
											)
678 52
										)
679
									);
680 52
681 52
									$found_logs = get_posts( $log_args );
682
									foreach ( $found_logs as $log ) {
683 52
										wp_delete_post( $log->ID, true );
684
									}
685
686
									if ( 'publish' === $this->status || 'complete' === $this->status || 'revoked' === $this->status ) {
687
										$form = new Give_Donate_Form( $item['id'] );
688
										$form->decrease_sales( $quantity );
689
										$form->decrease_earnings( $item['amount'] );
690
691
										$total_decrease += $item['amount'];
692
									}
693
									break;
694
695
							}
696
697
						}
698
						break;
699
700
					case 'fees':
701
702
						if ( 'publish' !== $this->status && 'complete' !== $this->status && 'revoked' !== $this->status ) {
703
							break;
704
						}
705
706
						if ( empty( $this->pending[ $key ] ) ) {
707
							break;
708
						}
709
710
						foreach ( $this->pending[ $key ] as $fee ) {
711 52
712 52
							switch ( $fee['action'] ) {
713 52
714
								case 'add':
715 52
									$total_increase += $fee['amount'];
716 52
									break;
717 52
718
								case 'remove':
719 52
									$total_decrease += $fee['amount'];
720 52
									break;
721 52
722
							}
723 52
724 20
						}
725 20
726
						break;
727 52
728 52
					case 'status':
729 52
						$this->update_status( $this->status );
730
						break;
731 52
732 52
					case 'gateway':
733 52
						$this->update_meta( '_give_payment_gateway', $this->gateway );
734
						break;
735 52
736 52
					case 'mode':
737 52
						$this->update_meta( '_give_payment_mode', $this->mode );
738
						break;
739 52
740 52
					case 'transaction_id':
741 52
						$this->update_meta( '_give_payment_transaction_id', $this->transaction_id );
742
						break;
743 52
744 52
					case 'ip':
745 52
						$this->update_meta( '_give_payment_user_ip', $this->ip );
746
						break;
747 52
748 52
					case 'customer_id':
749 52
						$this->update_meta( '_give_payment_customer_id', $this->customer_id );
750
						break;
751 52
752 52
					case 'user_id':
753 52
						$this->update_meta( '_give_payment_user_id', $this->user_id );
754
						break;
755 52
756 52
					case 'form_title':
757 52
						$this->update_meta( '_give_payment_form_title', $this->form_title );
758
						break;
759 52
760
					case 'form_id':
761
						$this->update_meta( '_give_payment_form_id', $this->form_id );
762
						break;
763 52
764 52
					case 'price_id':
765 52
						$this->update_meta( '_give_payment_price_id', $this->price_id );
766
						break;
767 52
768 52
					case 'first_name':
769 52
						$this->user_info['first_name'] = $this->first_name;
770
						break;
771 52
772 20
					case 'last_name':
773 20
						$this->user_info['last_name'] = $this->last_name;
774
						break;
775 52
776
					case 'address':
777 2
						$this->user_info['address'] = $this->address;
778 2
						break;
779 2
780 2
					case 'email':
781
						$this->update_meta( '_give_payment_user_email', $this->email );
782 2
						break;
783 2
784
					case 'key':
785 52
						$this->update_meta( '_give_payment_purchase_key', $this->key );
786 42
						break;
787 42
788
					case 'number':
789 52
						$this->update_meta( '_give_payment_number', $this->number );
790
						break;
791 52
792 52
					case 'date':
793 52
						$args = array(
794
							'ID'        => $this->ID,
795 52
							'post_date' => $this->date,
796 52
							'edit_date' => true,
797
						);
798 52
799 52
						wp_update_post( $args );
800 52
						break;
801 52
802 52
					case 'completed_date':
803
						$this->update_meta( '_give_completed_date', $this->completed_date );
804 52
						break;
805
806 42
					case 'parent_payment':
807
						$args = array(
808 42
							'ID'          => $this->ID,
809 42
							'post_parent' => $this->parent_payment,
810
						);
811 1
812
						wp_update_post( $args );
813 1
						break;
814 1
815
					default:
816 42
						do_action( 'give_payment_save', $this, $key );
817
						break;
818
				}
819 1
			}
820 1
821
			if ( 'pending' !== $this->status ) {
822 1
823
				$customer = new Give_Customer( $this->customer_id );
824 42
825
				$total_change = $total_increase - $total_decrease;
826 52
				if ( $total_change < 0 ) {
827
828
					$total_change = - ( $total_change );
829 52
					// Decrease the customer's purchase stats
830 52
					$customer->decrease_value( $total_change );
831 52
					give_decrease_total_earnings( $total_change );
832 52
833 52
				} else if ( $total_change > 0 ) {
834 52
835 52
					// Increase the customer's purchase stats
836
					$customer->increase_value( $total_change );
837 52
					give_increase_total_earnings( $total_change );
838 52
839
				}
840
841 52
			}
842 52
843 52
			$this->update_meta( '_give_payment_total', $this->total );
844 52
845 52
			$new_meta = array(
846 52
				'form_title' => $this->form_title,
847
				'form_id'    => $this->form_id,
848 52
				'price_id'   => $this->price_id,
849 52
				'fees'       => $this->fees,
850 52
				'currency'   => $this->currency,
851
				'user_info'  => $this->user_info,
852 52
			);
853 52
854 52
			$meta        = $this->get_meta();
855
			$merged_meta = array_merge( $meta, $new_meta );
856 52
857
			// Only save the payment meta if it's changed
858
			if ( md5( serialize( $meta ) ) !== md5( serialize( $merged_meta ) ) ) {
859
				$updated = $this->update_meta( '_give_payment_meta', $merged_meta );
860
				if ( false !== $updated ) {
861
					$saved = true;
862
				}
863
			}
864
865
			$this->pending = array();
866
			$saved         = true;
867
		}
868
869
		if ( true === $saved ) {
870 52
			$this->setup_payment( $this->ID );
871
		}
872 52
873
		return $saved;
874
	}
875 52
876
	/**
877
	 * Add a donation to a given payment
878
	 *
879
	 * @since 1.5
880
	 *
881 52
	 * @param int $form_id The donation form to add
882 52
	 * @param array $args Other arguments to pass to the function
883 52
	 * @param array $options List of donation options
884 52
	 *
885
	 * @return bool True when successful, false otherwise
886 52
	 */
887
	public function add_donation( $form_id = 0, $args = array(), $options = array() ) {
888
889 52
		$donation = new Give_Donate_Form( $form_id );
890 52
891 52
		// Bail if this post isn't a give donation form
892
		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...
893
			return false;
894 2
		}
895 1
896 1
		// Set some defaults
897
		$defaults = array(
898 1
			'price'    => false,
899
			'price_id' => false,
900
			'fees'     => array(),
901 1
		);
902 1
903 1
		$args = wp_parse_args( apply_filters( 'give_payment_add_donation_args', $args, $donation->ID ), $defaults );
904 1
905 1
		// Allow overriding the price
906 1
		if ( false !== $args['price'] ) {
907
			$item_price = $args['price'];
908 1
		} else {
909
910
			// Deal with variable pricing
911
			if ( give_has_variable_prices( $donation->ID ) ) {
912 1
				$prices     = maybe_unserialize( get_post_meta( $form_id, '_give_donation_levels', true ) );
913
				$item_price = '';
914 1
				//Loop through prices
915
				foreach ( $prices as $price ) {
916
					//Find a match between price_id and level_id
917
					//First verify array keys exists THEN make the match
918
					if ( ( isset( $args['price_id'] ) && isset( $price['_give_id']['level_id'] ) )
919
					     && $args['price_id'] == $price['_give_id']['level_id']
920 52
					) {
921 52
						$item_price = $price['_give_amount'];
922
					}
923
				}
924 52
				//Fallback to the lowest price point
925 52
				if ( $item_price == '' ) {
926 52
					$item_price       = give_get_lowest_price_option( $donation->ID );
927 52
					$args['price_id'] = give_get_lowest_price_id( $donation->ID );
928 52
				}
929
			} else {
930
				//Simple form price
931 52
				$item_price = give_get_form_price( $donation->ID );
932
			}
933
934
		}
935
936 52
		// Sanitizing the price here so we don't have a dozen calls later
937 52
		$item_price = give_sanitize_amount( $item_price );
938 52
		$total      = round( $item_price, give_currency_decimal_filter() );
939 52
940 52
		//Add Options
941 52
		$default_options = array();
942 52
		if ( false !== $args['price_id'] ) {
943
			$default_options['price_id'] = (int) $args['price_id'];
944 52
		}
945
		$options = wp_parse_args( $options, $default_options );
946 52
947
		// Do not allow totals to go negative
948 52
		if ( $total < 0 ) {
949
			$total = 0;
950 52
		}
951
952
		$donation = array(
953
			'name'     => $donation->post_title,
954
			'id'       => $donation->ID,
955
			'price'    => round( $total, give_currency_decimal_filter() ),
956
			'subtotal' => round( $total, give_currency_decimal_filter() ),
957
			'fees'     => $args['fees'],
958
			'price_id' => $args['price_id'],
959
			'action'   => 'add',
960
			'options'  => $options
961
		);
962
963
		$this->pending['donations'][] = $donation;
964 2
965
		$this->increase_subtotal( $total );
966
967
		return true;
968 2
969 2
	}
970 2
971 2
	/**
972 2
	 * Remove a donation from the payment
973
	 *
974 2
	 * @since  1.5
975
	 *
976
	 * @param  int $form_id The form ID to remove
977 2
	 * @param  array $args Arguments to pass to identify (quantity, amount, price_id)
978
	 *
979
	 * @return bool If the item was removed or not
980
	 */
981 2
	public function remove_donation( $form_id, $args = array() ) {
982 2
983 2
		// Set some defaults
984 2
		$defaults = array(
985 2
			'quantity' => 1,
986 2
			'price'    => false,
987
			'price_id' => false,
988 2
		);
989
		$args     = wp_parse_args( $args, $defaults );
990 2
991
		$form = new Give_Donate_Form( $form_id );
992 2
993
		// Bail if this post isn't a valid give donation form
994
		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...
995
			return false;
996
		}
997
998
		$pending_args             = $args;
999
		$pending_args['id']       = $form_id;
1000
		$pending_args['amount']   = $this->total;
1001
		$pending_args['price_id'] = false !== $args['price_id'] ? (int) $args['price_id'] : false;
1002
		$pending_args['quantity'] = $args['quantity'];
1003
		$pending_args['action']   = 'remove';
1004
1005
		$this->pending['donations'][] = $pending_args;
1006
1007
		$this->decrease_subtotal( $this->total );
1008
1009
		return true;
1010
	}
1011
1012
	/**
1013
	 * Add a fee to a given payment
1014
	 *
1015
	 * @since  1.5
1016
	 *
1017
	 * @param  array $args Array of arguments for the fee to add
1018
	 * @param bool $global
1019
	 *
1020
	 * @return bool If the fee was added
1021
	 */
1022
	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...
1023
1024
		$default_args = array(
1025
			'label'    => '',
1026
			'amount'   => 0,
1027
			'type'     => 'fee',
1028
			'id'       => '',
1029
			'price_id' => 0,
1030
		);
1031
1032
		$fee          = wp_parse_args( $args, $default_args );
1033
		$this->fees[] = $fee;
1034
1035
1036
		$added_fee               = $fee;
1037
		$added_fee['action']     = 'add';
1038
		$this->pending['fees'][] = $added_fee;
1039
		reset( $this->fees );
1040
1041
		$this->increase_fees( $fee['amount'] );
1042
1043
		return true;
1044
	}
1045
1046
	/**
1047
	 * Remove a fee from the payment
1048
	 *
1049
	 * @since  1.5
1050
	 *
1051
	 * @param  int $key The array key index to remove
1052
	 *
1053
	 * @return bool     If the fee was removed successfully
1054
	 */
1055
	public function remove_fee( $key ) {
1056
		$removed = false;
1057
1058
		if ( is_numeric( $key ) ) {
1059
			$removed = $this->remove_fee_by( 'index', $key );
1060
		}
1061
1062
		return $removed;
1063
	}
1064
1065
	/**
1066
	 * Remove a fee by the defined attributed
1067
	 *
1068
	 * @since  1.5
1069
	 *
1070
	 * @param  string $key The key to remove by
1071
	 * @param  int|string $value The value to search for
1072
	 * @param  boolean $global False - removes the first value it fines, True - removes all matches
1073
	 *
1074
	 * @return boolean             If the item is removed
1075
	 */
1076
	public function remove_fee_by( $key, $value, $global = false ) {
1077
1078
		$allowed_fee_keys = apply_filters( 'give_payment_fee_keys', array(
1079
			'index',
1080
			'label',
1081
			'amount',
1082
			'type',
1083
		) );
1084
1085
		if ( ! in_array( $key, $allowed_fee_keys ) ) {
1086
			return false;
1087
		}
1088
1089
		$removed = false;
1090
		if ( 'index' === $key && array_key_exists( $value, $this->fees ) ) {
1091
1092
			$removed_fee             = $this->fees[ $value ];
1093
			$removed_fee['action']   = 'remove';
1094
			$this->pending['fees'][] = $removed_fee;
1095
1096
			$this->decrease_fees( $removed_fee['amount'] );
1097
1098
			unset( $this->fees[ $value ] );
1099
			$removed = true;
1100
1101
		} else if ( 'index' !== $key ) {
1102
1103
			foreach ( $this->fees as $index => $fee ) {
1104
1105
				if ( isset( $fee[ $key ] ) && $fee[ $key ] == $value ) {
1106
1107
					$removed_fee             = $fee;
1108
					$removed_fee['action']   = 'remove';
1109
					$this->pending['fees'][] = $removed_fee;
1110
1111
					$this->decrease_fees( $removed_fee['amount'] );
1112
1113
					unset( $this->fees[ $index ] );
1114
					$removed = true;
1115
1116
					if ( false === $global ) {
1117
						break;
1118
					}
1119
1120
				}
1121
1122
			}
1123
1124
		}
1125
1126
		if ( true === $removed ) {
1127
			$this->fees = array_values( $this->fees );
1128
		}
1129
1130
		return $removed;
1131
	}
1132
1133
	/**
1134
	 * Get the fees, filterable by type
1135
	 *
1136
	 * @since  1.5
1137
	 *
1138
	 * @param  string $type All, item, fee
1139
	 *
1140
	 * @return array        The Fees for the type specified
1141
	 */
1142
	public function get_fees( $type = 'all' ) {
1143
		$fees = array();
1144
1145
		if ( ! empty( $this->fees ) && is_array( $this->fees ) ) {
1146
1147
			foreach ( $this->fees as $fee_id => $fee ) {
1148
1149
				if ( 'all' != $type && ! empty( $fee['type'] ) && $type != $fee['type'] ) {
1150
					continue;
1151
				}
1152
1153
				$fee['id'] = $fee_id;
1154
				$fees[]    = $fee;
1155
1156
			}
1157
		}
1158
1159
		return apply_filters( 'give_get_payment_fees', $fees, $this->ID, $this );
1160
	}
1161
1162
	/**
1163
	 * Add a note to a payment
1164
	 *
1165
	 * @since 1.0
1166
	 *
1167
	 * @param string $note The note to add
1168
	 *
1169
	 * @return void
1170
	 */
1171
	public function add_note( $note = false ) {
1172 52
		// Bail if no note specified
1173 52
		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...
1174 52
			return false;
1175
		}
1176 52
1177 52
		give_insert_payment_note( $this->ID, $note );
1178
	}
1179
1180
	/**
1181
	 * Increase the payment's subtotal
1182
	 *
1183
	 * @since  1.5
1184
	 *
1185
	 * @param  float $amount The amount to increase the payment subtotal by
1186
	 *
1187
	 * @return void
1188 2
	 */
1189 2
	private function increase_subtotal( $amount = 0.00 ) {
1190 2
		$amount = (float) $amount;
1191
		$this->subtotal += $amount;
1192 2
1193
		$this->recalculate_total();
1194
	}
1195
1196 2
	/**
1197 2
	 * Decrease the payment's subtotal
1198
	 *
1199
	 * @since  1.5
1200
	 *
1201
	 * @param  float $amount The amount to decrease the payment subtotal by
1202
	 *
1203
	 * @return void
1204
	 */
1205
	private function decrease_subtotal( $amount = 0.00 ) {
1206
		$amount = (float) $amount;
1207
		$this->subtotal -= $amount;
1208
1209
		if ( $this->subtotal < 0 ) {
1210
			$this->subtotal = 0;
1211
		}
1212
1213
		$this->recalculate_total();
1214
	}
1215
1216
	/**
1217
	 * Increase the payment's subtotal
1218
	 *
1219
	 * @since  1.5
1220
	 *
1221
	 * @param  float $amount The amount to increase the payment subtotal by
1222
	 *
1223
	 * @return void
1224
	 */
1225
	private function increase_fees( $amount = 0.00 ) {
1226
		$amount = (float) $amount;
1227
		$this->fees_total += $amount;
1228
1229
		$this->recalculate_total();
1230
	}
1231
1232
	/**
1233
	 * Decrease the payment's subtotal
1234
	 *
1235
	 * @since  1.5
1236
	 *
1237
	 * @param  float $amount The amount to decrease the payment subtotal by
1238
	 *
1239
	 * @return void
1240
	 */
1241 52
	private function decrease_fees( $amount = 0.00 ) {
1242 52
		$amount = (float) $amount;
1243 52
		$this->fees_total -= $amount;
1244
1245
		if ( $this->fees_total < 0 ) {
1246
			$this->fees_total = 0;
1247
		}
1248
1249
		$this->recalculate_total();
1250
	}
1251
1252
	/**
1253
	 * Set or update the total for a payment
1254 52
	 *
1255
	 * @since 1.0
1256
	 * @return void
1257 52
	 */
1258 39
	private function recalculate_total() {
1259 39
		$this->total = $this->subtotal + $this->fees_total;
1260
	}
1261 52
1262
	/**
1263 52
	 * Set the payment status and run any status specific changes necessary
1264 52
	 *
1265
	 * @since 1.0
1266
	 *
1267 42
	 * @param  string $status The status to set the payment to
1268
	 *
1269 42
	 * @return bool Returns if the status was successfully updated
1270
	 */
1271
	public function update_status( $status = false ) {
1272 42
1273
		//standardize the 'complete(d)' status
1274 42
		if ( $status == 'completed' || $status == 'complete' ) {
1275
			$status = 'publish';
1276
		}
1277 42
1278 42
		$old_status = ! empty( $this->old_status ) ? $this->old_status : false;
1279 42
1280 42
		if ( $old_status === $status ) {
1281
			return false; // Don't permit status changes that aren't changes
1282 42
		}
1283
1284 42
		$do_change = apply_filters( 'give_should_update_payment_status', true, $this->ID, $status, $old_status );
1285 42
1286
		$updated = false;
1287
1288
1289 42
		if ( $do_change ) {
1290 4
1291 4
			do_action( 'give_before_payment_status_change', $this->ID, $status, $old_status );
1292 42
1293
			$update_fields = array(
1294
				'ID'          => $this->ID,
1295 42
				'post_status' => $status,
1296 3
				'edit_date'   => current_time( 'mysql' )
1297 3
			);
1298
1299
			$updated = wp_update_post( apply_filters( 'give_update_payment_status_fields', $update_fields ) );
1300 42
1301
			$all_payment_statuses  = give_get_payment_statuses();
1302 42
			$this->status_nicename = array_key_exists( $status, $all_payment_statuses ) ? $all_payment_statuses[ $status ] : ucfirst( $status );
1303
1304 42
			// Process any specific status functions
1305
			switch ( $status ) {
1306
				case 'refunded':
1307
					$this->process_refund();
1308
					break;
1309
				case 'failed':
1310
					$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...
1311
					break;
1312
				case 'pending':
1313
					$this->process_pending();
1314 4
					break;
1315 4
                case 'cancelled':
1316 4
                    $this->process_cancelled();
1317 4
                    break;
1318
			}
1319 4
1320 4
			do_action( 'give_update_payment_status', $this->ID, $status, $old_status );
1321
1322
		}
1323
1324
		return $updated;
1325
1326
	}
1327
1328
	/**
1329
	 * Change the status of the payment to refunded, and run the necessary changes
1330
	 *
1331
	 * @since  1.5
1332 52
	 * @return void
1333
	 */
1334 52
	public function refund() {
1335
		$this->old_status        = $this->status;
1336 52
		$this->status            = 'refunded';
1337
		$this->pending['status'] = $this->status;
1338 52
1339 52
		$this->save();
1340 52
	}
1341
1342 52
	/**
1343 52
	 * Get a post meta item for the payment
1344 52
	 *
1345
	 * @since  1.5
1346 52
	 *
1347 52
	 * @param  string $meta_key The Meta Key
1348 52
	 * @param  boolean $single Return single item or array
1349
	 *
1350 52
	 * @return mixed             The value from the post meta
1351 52
	 */
1352 52
	public function get_meta( $meta_key = '_give_payment_meta', $single = true ) {
1353 52
1354
		$meta = get_post_meta( $this->ID, $meta_key, $single );
1355 52
1356
		if ( $meta_key === '_give_payment_meta' ) {
1357 52
1358
			if ( empty( $meta['key'] ) ) {
1359
				$meta['key'] = $this->setup_payment_key();
1360
			}
1361
1362
			if ( empty( $meta['form_title'] ) ) {
1363
				$meta['form_title'] = $this->setup_form_title();
1364
			}
1365
1366
			if ( empty( $meta['email'] ) ) {
1367
				$meta['email'] = $this->setup_email();
1368
			}
1369
1370
			if ( empty( $meta['date'] ) ) {
1371 52
				$meta['date'] = get_post_field( 'post_date', $this->ID );
1372 52
			}
1373
		}
1374
1375
		$meta = apply_filters( 'give_get_payment_meta_' . $meta_key, $meta, $this->ID );
1376 52
1377
		return apply_filters( 'give_get_payment_meta', $meta, $this->ID, $meta_key );
1378
	}
1379
1380
	/**
1381
	 * Update the post meta
1382
	 *
1383
	 * @since  1.5
1384 52
	 *
1385
	 * @param  string $meta_key The meta key to update
1386 52
	 * @param  string $meta_value The meta value
1387 52
	 * @param  string $prev_value Previous meta value
1388
	 *
1389 52
	 * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure
1390 52
	 */
1391
	public function update_meta( $meta_key = '', $meta_value = '', $prev_value = '' ) {
1392 52
		if ( empty( $meta_key ) ) {
1393 52
			return false;
1394
		}
1395 52
1396
		if ( $meta_key == 'key' || $meta_key == 'date' ) {
1397 52
1398
			$current_meta              = $this->get_meta();
1399 52
			$current_meta[ $meta_key ] = $meta_value;
1400
1401
			$meta_key   = '_give_payment_meta';
1402
			$meta_value = $current_meta;
1403
1404
		} else if ( $meta_key == 'email' || $meta_key == '_give_payment_user_email' ) {
1405
1406
			$meta_value = apply_filters( 'give_give_update_payment_meta_' . $meta_key, $meta_value, $this->ID );
1407
			update_post_meta( $this->ID, '_give_payment_user_email', $meta_value );
1408
1409 4
			$current_meta                       = $this->get_meta();
1410 4
			$current_meta['user_info']['email'] = $meta_value;
1411
1412
			$meta_key   = '_give_payment_meta';
1413 4
			$meta_value = $current_meta;
1414
1415
		}
1416
1417
		$meta_value = apply_filters( 'give_update_payment_meta_' . $meta_key, $meta_value, $this->ID );
1418 4
1419
		return update_post_meta( $this->ID, $meta_key, $meta_value, $prev_value );
1420 4
	}
1421
1422
	/**
1423
	 * When a payment is set to a status of 'refunded' process the necessary actions to reduce stats
1424 4
	 *
1425
	 * @since  1.5
1426 4
	 * @access private
1427 4
	 * @return void
1428 4
	 */
1429
	private function process_refund() {
1430 4
		$process_refund = true;
1431 4
1432
		// If the payment was not in publish or revoked status, don't decrement stats as they were never incremented
1433
		if ( ( 'publish' != $this->old_status && 'revoked' != $this->old_status ) || 'refunded' != $this->status ) {
1434 4
			$process_refund = false;
1435
		}
1436 4
1437 4
		// Allow extensions to filter for their own payment types, Example: Recurring Payments
1438
		$process_refund = apply_filters( 'give_should_process_refund', $process_refund, $this );
1439
1440
		if ( false === $process_refund ) {
1441
			return;
1442
		}
1443
1444
		do_action( 'give_pre_refund_payment', $this );
1445
1446
		$decrease_store_earnings = apply_filters( 'give_decrease_store_earnings_on_refund', true, $this );
1447
		$decrease_customer_value = apply_filters( 'give_decrease_customer_value_on_refund', true, $this );
1448
		$decrease_purchase_count = apply_filters( 'give_decrease_customer_purchase_count_on_refund', true, $this );
1449
1450
		$this->maybe_alter_stats( $decrease_store_earnings, $decrease_customer_value, $decrease_purchase_count );
1451
		$this->delete_sales_logs();
1452
1453
		// Clear the This Month earnings (this_monththis_month is NOT a typo)
1454
		delete_transient( md5( 'give_earnings_this_monththis_month' ) );
1455
1456 3
		do_action( 'give_post_refund_payment', $this );
1457 3
	}
1458
1459
	/**
1460 3
	 * Process when a payment is set to failed
1461 1
	 *
1462 1
	 * @since  1.5
1463
	 * @return void
1464
	 */
1465 3
	private function process_failure() {
1466
1467 3
1468 1
	}
1469
1470
	/**
1471 2
	 * Process when a payment moves to pending
1472 2
	 *
1473 2
	 * @since  1.5
1474
	 * @return void
1475 2
	 */
1476 2
	private function process_pending() {
1477
		$process_pending = true;
1478 2
1479 2
		// If the payment was not in publish or revoked status, don't decrement stats as they were never incremented
1480
		if ( ( 'publish' != $this->old_status && 'revoked' != $this->old_status ) || 'pending' != $this->status ) {
1481
			$process_pending = false;
1482 2
		}
1483 2
1484
		// Allow extensions to filter for their own payment types, Example: Recurring Payments
1485
		$process_pending = apply_filters( 'give_should_process_pending', $process_pending, $this );
1486
1487
		if ( false === $process_pending ) {
1488
			return;
1489
		}
1490
1491
		$decrease_store_earnings = apply_filters( 'give_decrease_store_earnings_on_pending', true, $this );
1492
		$decrease_customer_value = apply_filters( 'give_decrease_customer_value_on_pending', true, $this );
1493
		$decrease_purchase_count = apply_filters( 'give_decrease_customer_purchase_count_on_pending', true, $this );
1494
1495
		$this->maybe_alter_stats( $decrease_store_earnings, $decrease_customer_value, $decrease_purchase_count );
1496 6
		$this->delete_sales_logs();
1497
1498 6
		$this->completed_date = false;
1499
		$this->update_meta( '_give_completed_date', '' );
1500
1501 6
		// Clear the This Month earnings (this_monththis_month is NOT a typo)
1502 4
		delete_transient( md5( 'give_earnings_this_monththis_month' ) );
1503 4
	}
1504
1505
    /**
1506 6
     * Process when a payment moves to cancelled
1507
     *
1508 6
     * @since  1.5
1509
     * @return void
1510 6
     */
1511 4
    private function process_cancelled() {
1512 4
        $process_cancelled = true;
1513
1514 6
        // If the payment was not in publish or revoked status, don't decrement stats as they were never incremented
1515 4
        if ( ( 'publish' != $this->old_status && 'revoked' != $this->old_status ) || 'cancelled' != $this->status ) {
1516 4
            $process_cancelled = false;
1517
        }
1518 6
1519
        // Allow extensions to filter for their own payment types, Example: Recurring Payments
1520 6
        $process_cancelled = apply_filters( 'give_should_process_cancelled', $process_cancelled, $this );
1521
1522
        if ( false === $process_cancelled ) {
1523
            return;
1524
        }
1525
1526
        $decrease_store_earnings = apply_filters( 'give_decrease_store_earnings_on_cancelled', true, $this );
1527
        $decrease_customer_value = apply_filters( 'give_decrease_customer_value_on_cancelled', true, $this );
1528 6
        $decrease_purchase_count = apply_filters( 'give_decrease_customer_purchase_count_on_cancelled', true, $this );
1529 6
1530
        $this->maybe_alter_stats( $decrease_store_earnings, $decrease_customer_value, $decrease_purchase_count );
1531
        $this->delete_sales_logs();
1532 6
1533 6
        $this->completed_date = false;
1534 6
        $this->update_meta( '_give_completed_date', '' );
1535
1536
        // Clear the This Month earnings (this_monththis_month is NOT a typo)
1537 6
        delete_transient( md5( 'give_earnings_this_monththis_month' ) );
1538 6
    }
1539 6
1540
	/**
1541 6
	 * Used during the process of moving to refunded or pending, to decrement stats
1542 6
	 *
1543
	 * @since  1.5
1544
	 *
1545
	 * @param  bool $alter_store_earnings If the method should alter the store earnings
1546
	 * @param  bool $alter_customer_value If the method should reduce the customer value
1547
	 * @param  bool $alter_customer_purchase_count If the method should reduce the customer's purchase count
1548
	 *
1549
	 * @return void
1550
	 */
1551
	private function maybe_alter_stats( $alter_store_earnings, $alter_customer_value, $alter_customer_purchase_count ) {
1552
1553
		give_undo_purchase( false, $this->ID );
1554
1555
		// Decrease store earnings
1556
		if ( true === $alter_store_earnings ) {
1557
			give_decrease_total_earnings( $this->total );
1558
		}
1559 52
1560 52
		// Decrement the stats for the customer
1561
		if ( ! empty( $this->customer_id ) ) {
1562 52
1563 52
			$customer = new Give_Customer( $this->customer_id );
1564
1565
			if ( true === $alter_customer_value ) {
1566 42
				$customer->decrease_value( $this->total );
1567
			}
1568 42
1569
			if ( true === $alter_customer_purchase_count ) {
1570
				$customer->decrease_purchase_count();
1571
			}
1572
1573
		}
1574
1575
	}
1576
1577 52
	/**
1578 52
	 * Delete sales logs for this purchase
1579
	 *
1580
	 * @since  1.5
1581
	 * @return void
1582
	 */
1583
	private function delete_sales_logs() {
1584
		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...
1585
1586
		// Remove related sale log entries
1587 52
		$give_logs->delete_logs(
1588 52
			null,
1589
			'sale',
1590 52
			array(
1591 2
				array(
1592 2
					'key'   => '_give_log_payment_id',
1593
					'value' => $this->ID,
1594 2
				),
1595
			)
1596
		);
1597 2
	}
1598
1599 52
	/**
1600
	 * Setup functions only, these are not to be used by developers.
1601
	 * These functions exist only to allow the setup routine to be backwards compatible with our old
1602
	 * helper functions.
1603
	 *
1604
	 * These will run whenever setup_payment is called, which should only be called once.
1605
	 * To update an attribute, update it directly instead of re-running the setup routine
1606
	 */
1607
1608 52
	/**
1609 52
	 * Setup the payment completed date
1610
	 *
1611 52
	 * @since  1.5
1612
	 * @return string The date the payment was completed
1613
	 */
1614
	private function setup_completed_date() {
1615
		$payment = get_post( $this->ID );
1616
1617
		if ( 'pending' == $payment->post_status || 'preapproved' == $payment->post_status ) {
1618
			return false; // This payment was never completed
1619
		}
1620 52
1621 52
		$date = ( $date = $this->get_meta( '_give_completed_date', true ) ) ? $date : $payment->modified_date;
1622
1623 52
		return $date;
1624 52
	}
1625
1626
	/**
1627
	 * Setup the payment mode
1628
	 *
1629
	 * @since  1.5
1630 52
	 * @return string The payment mode
1631
	 */
1632
	private function setup_mode() {
1633
		return $this->get_meta( '_give_payment_mode' );
1634
	}
1635
1636
	/**
1637
	 * Setup the payment total
1638
	 *
1639
	 * @since  1.5
1640 52
	 * @return float The payment total
1641 52
	 */
1642
	private function setup_total() {
1643 52
		$amount = $this->get_meta( '_give_payment_total', true );
1644
1645
		if ( empty( $amount ) && '0.00' != $amount ) {
1646
			$meta = $this->get_meta( '_give_payment_meta', true );
1647
			$meta = maybe_unserialize( $meta );
1648
1649
			if ( isset( $meta['amount'] ) ) {
1650
				$amount = $meta['amount'];
1651
			}
1652 52
		}
1653 52
1654
		return $amount;
1655 52
	}
1656
1657
	/**
1658
	 * Setup the payment subtotal
1659
	 *
1660
	 * @since  1.5
1661
	 * @return float The subtotal of the payment
1662
	 */
1663
	private function setup_subtotal() {
1664 52
		$subtotal = $this->total;
1665 52
1666
		return $subtotal;
1667 52
	}
1668
1669
	/**
1670
	 * Setup the payment fees
1671
	 *
1672
	 * @since  1.5
1673
	 * @return float The fees total for the payment
1674
	 */
1675
	private function setup_fees_total() {
1676 52
		$fees_total = (float) 0.00;
1677 52
1678
		$payment_fees = isset( $this->payment_meta['fees'] ) ? $this->payment_meta['fees'] : array();
1679 52
		if ( ! empty( $payment_fees ) ) {
1680
			foreach ( $payment_fees as $fee ) {
1681 52
				$fees_total += (float) $fee['amount'];
1682 52
			}
1683
		}
1684 52
1685
		return $fees_total;
1686 52
1687
	}
1688
1689
	/**
1690
	 * Setup the currency code
1691
	 *
1692
	 * @since  1.5
1693
	 * @return string              The currency for the payment
1694
	 */
1695 52
	private function setup_currency() {
1696 52
		$currency = isset( $this->payment_meta['currency'] ) ? $this->payment_meta['currency'] : apply_filters( 'give_payment_currency_default', give_get_currency(), $this );
1697
1698 52
		return $currency;
1699
	}
1700
1701
	/**
1702
	 * Setup any fees associated with the payment
1703
	 *
1704
	 * @since  1.5
1705
	 * @return array The Fees
1706
	 */
1707 52
	private function setup_fees() {
1708 52
		$payment_fees = isset( $this->payment_meta['fees'] ) ? $this->payment_meta['fees'] : array();
1709
1710 52
		return $payment_fees;
1711
	}
1712
1713
	/**
1714
	 * Setup the gateway used for the payment
1715
	 *
1716
	 * @since  1.5
1717
	 * @return string The gateway
1718
	 */
1719 52
	private function setup_gateway() {
1720 52
		$gateway = $this->get_meta( '_give_payment_gateway', true );
1721
1722 52
		return $gateway;
1723
	}
1724
1725
	/**
1726
	 * Setup the transaction ID
1727
	 *
1728
	 * @since  1.5
1729
	 * @return string The transaction ID for the payment
1730
	 */
1731 52
	private function setup_transaction_id() {
1732 52
		$transaction_id = $this->get_meta( '_give_payment_transaction_id', true );
1733
1734 52
		if ( empty( $transaction_id ) || (int) $transaction_id === (int) $this->ID ) {
1735 2
1736 2
			$gateway        = $this->gateway;
1737
			$transaction_id = apply_filters( 'give_get_payment_transaction_id-' . $gateway, $this->ID );
1738 52
1739
		}
1740
1741
		return $transaction_id;
1742
	}
1743
1744
	/**
1745
	 * Setup the IP Address for the payment
1746
	 *
1747 52
	 * @since  1.5
1748
	 * @return string The IP address for the payment
1749 52
	 */
1750 52
	private function setup_ip() {
1751 52
		$ip = $this->get_meta( '_give_payment_user_ip', true );
1752
1753 52
		return $ip;
1754 52
	}
1755
1756 52
	/**
1757
	 * Setup the customer ID
1758
	 *
1759
	 * @since  1.5
1760
	 * @return int The Customer ID
1761
	 */
1762
	private function setup_customer_id() {
1763
		$customer_id = $this->get_meta( '_give_payment_customer_id', true );
1764
1765
		return $customer_id;
1766
	}
1767
1768
	/**
1769
	 * Setup the User ID associated with the purchase
1770
	 *
1771 52
	 * @since  1.5
1772 52
	 * @return int The User ID
1773 52
	 */
1774 52
	private function setup_user_id() {
1775 52
		$user_id = $this->get_meta( '_give_payment_user_id', true );
1776
1777
		return $user_id;
1778
	}
1779 21
1780 21
	/**
1781
	 * Setup the email address for the purchase
1782 21
	 *
1783 21
	 * @since  1.5
1784
	 * @return string The email address for the payment
1785 21
	 */
1786 21
	private function setup_email() {
1787 21
		$email = $this->get_meta( '_give_payment_user_email', true );
1788
1789 21
		if ( empty( $email ) ) {
1790 21
			$email = Give()->customers->get_column( 'email', $this->customer_id );
1791
		}
1792
1793
		return $email;
1794
	}
1795
1796 52
	/**
1797
	 * Setup the user info
1798 52
	 *
1799
	 * @since  1.5
1800
	 * @return array               The user info associated with the payment
1801 52
	 */
1802
	private function setup_user_info() {
1803
		$defaults = array(
1804
			'first_name' => $this->first_name,
1805
			'last_name'  => $this->last_name,
1806
		);
1807
1808
		$user_info = isset( $this->payment_meta['user_info'] ) ? maybe_unserialize( $this->payment_meta['user_info'] ) : array();
1809
		$user_info = wp_parse_args( $user_info, $defaults );
1810
1811 52
		if ( empty( $user_info ) ) {
1812
			// Get the customer, but only if it's been created
1813 52
			$customer = new Give_Customer( $this->customer_id );
1814 52
1815 52
			if ( $customer->id > 0 ) {
1816 52
				$name      = explode( ' ', $customer->name, 2 );
1817 52
				$user_info = array(
1818 52
					'first_name' => $name[0],
1819
					'last_name'  => $name[1],
1820 52
					'email'      => $customer->email,
1821
					'discount'   => 'none',
1822 52
				);
1823
			}
1824
		} else {
1825
			// Get the customer, but only if it's been created
1826
			$customer = new Give_Customer( $this->customer_id );
1827
			if ( $customer->id > 0 ) {
1828
				foreach ( $user_info as $key => $value ) {
1829
					if ( ! empty( $value ) ) {
1830
						continue;
1831 52
					}
1832
1833 52
					switch ( $key ) {
1834
						case 'first_name':
1835 52
							$name = explode( ' ', $customer->name, 2 );
1836
1837
							$user_info[ $key ] = $name[0];
1838
							break;
1839
1840
						case 'last_name':
1841
							$name      = explode( ' ', $customer->name, 2 );
1842
							$last_name = ! empty( $name[1] ) ? $name[1] : '';
1843
1844 52
							$user_info[ $key ] = $last_name;
1845
							break;
1846 52
1847
						case 'email':
1848 52
							$user_info[ $key ] = $customer->email;
1849
							break;
1850
					}
1851
				}
1852
1853
			}
1854
		}
1855
1856
		return $user_info;
1857 52
1858 52
	}
1859
1860 52
	/**
1861
	 * Setup the Address for the payment
1862
	 *
1863
	 * @since  1.5
1864
	 * @return array               The Address information for the payment
1865
	 */
1866
	private function setup_address() {
1867
1868
		$address = ! empty( $this->payment_meta['user_info']['address'] ) ? $this->payment_meta['user_info']['address'] : array(
1869 52
			'line1'   => '',
1870 52
			'line2'   => '',
1871
			'city'    => '',
1872 52
			'country' => '',
1873
			'state'   => '',
1874
			'zip'     => ''
1875
		);
1876
1877
		return $address;
1878
	}
1879
1880
	/**
1881 52
	 * Setup the form title
1882 52
	 *
1883
	 * @since  1.5
1884 52
	 * @return string The Form Title
1885
	 */
1886 20
	private function setup_form_title() {
1887
1888 20
		$form_id = $this->get_meta( '_give_payment_form_title', true );
1889
1890 2
		return $form_id;
1891
	}
1892 2
1893
	/**
1894 20
	 * Setup the form ID
1895
	 *
1896 52
	 * @since  1.5
1897
	 * @return int The Form ID
1898
	 */
1899
	private function setup_form_id() {
1900
1901
		$form_id = $this->get_meta( '_give_payment_form_id', true );
1902
1903
		return $form_id;
1904
	}
1905
1906
	/**
1907
	 * Setup the price ID
1908
	 *
1909
	 * @since  1.5
1910
	 * @return int The Form Price ID
1911
	 */
1912
	private function setup_price_id() {
1913
		$price_id = $this->get_meta( '_give_payment_price_id', true );
1914 42
1915 42
		return $price_id;
1916
	}
1917
1918
	/**
1919
	 * Setup the payment key
1920
	 *
1921
	 * @since  1.5
1922
	 * @return string The Payment Key
1923
	 */
1924
	private function setup_payment_key() {
1925
		$key = $this->get_meta( '_give_payment_purchase_key', true );
1926
1927
		return $key;
1928
	}
1929
1930
	/**
1931
	 * Setup the payment number
1932
	 *
1933
	 * @since  1.5
1934 42
	 * @return int|string Integer by default, or string if sequential order numbers is enabled
1935 42
	 */
1936
	private function setup_payment_number() {
1937
		$number = $this->ID;
1938
1939
		if ( give_get_option( 'enable_sequential' ) ) {
1940
1941
			$number = $this->get_meta( '_give_payment_number', true );
1942
1943
			if ( ! $number ) {
1944 42
1945 42
				$number = $this->ID;
1946
1947
			}
1948
1949
		}
1950
1951
		return $number;
1952
	}
1953
1954 11
	/**
1955 11
	 * Converts this object into an array for special cases
1956
	 *
1957
	 * @return array The payment object as an array
1958
	 */
1959
	public function array_convert() {
1960
		return get_object_vars( $this );
1961
	}
1962
1963
	/**
1964
	 * Retrieve payment completion date
1965
	 *
1966
	 * @since  1.5
1967
	 * @return string Date payment was completed
1968
	 */
1969
	private function get_completed_date() {
1970
		return apply_filters( 'give_payment_completed_date', $this->completed_date, $this->ID, $this );
1971
	}
1972
1973
	/**
1974 52
	 * Retrieve payment subtotal
1975 52
	 *
1976
	 * @since  1.5
1977
	 * @return float Payment subtotal
1978
	 */
1979
	private function get_subtotal() {
1980
		return apply_filters( 'give_get_payment_subtotal', $this->subtotal, $this->ID, $this );
1981
	}
1982
1983
	/**
1984 41
	 * Retrieve payment currency
1985 41
	 *
1986
	 * @since  1.5
1987
	 * @return string Payment currency code
1988
	 */
1989
	private function get_currency() {
1990
		return apply_filters( 'give_payment_currency_code', $this->currency, $this->ID, $this );
1991
	}
1992
1993
	/**
1994 42
	 * Retrieve payment gateway
1995 42
	 *
1996
	 * @since  1.5
1997
	 * @return string Gateway used
1998
	 */
1999
	private function get_gateway() {
2000
		return apply_filters( 'give_payment_gateway', $this->gateway, $this->ID, $this );
2001
	}
2002
2003
	/**
2004 43
	 * Retrieve payment transaction ID
2005 43
	 *
2006
	 * @since  1.5
2007
	 * @return string Transaction ID from merchant processor
2008
	 */
2009
	private function get_transaction_id() {
2010
		return apply_filters( 'give_get_payment_transaction_id', $this->transaction_id, $this->ID, $this );
2011
	}
2012
2013
	/**
2014
	 * Retrieve payment IP
2015
	 *
2016
	 * @since  1.5
2017
	 * @return string Payment IP address
2018
	 */
2019
	private function get_ip() {
2020
		return apply_filters( 'give_payment_user_ip', $this->ip, $this->ID, $this );
2021
	}
2022
2023
	/**
2024 52
	 * Retrieve payment customer ID
2025 52
	 *
2026
	 * @since  1.5
2027
	 * @return int Payment customer ID
2028
	 */
2029
	private function get_customer_id() {
2030
		return apply_filters( 'give_payment_customer_id', $this->customer_id, $this->ID, $this );
2031
	}
2032
2033
	/**
2034 52
	 * Retrieve payment user ID
2035 52
	 *
2036
	 * @since  1.5
2037
	 * @return int Payment user ID
2038
	 */
2039
	private function get_user_id() {
2040
		return apply_filters( 'give_payment_user_id', $this->user_id, $this->ID, $this );
2041
	}
2042
2043
	/**
2044 42
	 * Retrieve payment email
2045 42
	 *
2046
	 * @since  1.5
2047
	 * @return string Payment customer email
2048
	 */
2049
	private function get_email() {
2050
		return apply_filters( 'give_payment_user_email', $this->email, $this->ID, $this );
2051
	}
2052
2053
	/**
2054
	 * Retrieve payment user info
2055
	 *
2056
	 * @since  1.5
2057
	 * @return array Payment user info
2058
	 */
2059
	private function get_user_info() {
2060
		return apply_filters( 'give_payment_meta_user_info', $this->user_info, $this->ID, $this );
2061
	}
2062
2063
	/**
2064
	 * Retrieve payment billing address
2065
	 *
2066
	 * @since  1.5
2067
	 * @return array Payment billing address
2068
	 */
2069
	private function get_address() {
2070
		return apply_filters( 'give_payment_address', $this->address, $this->ID, $this );
2071
	}
2072
2073
	/**
2074
	 * Retrieve payment key
2075
	 *
2076
	 * @since  1.5
2077
	 * @return string Payment key
2078
	 */
2079
	private function get_key() {
2080
		return apply_filters( 'give_payment_key', $this->key, $this->ID, $this );
2081
	}
2082
2083
	/**
2084
	 * Retrieve payment key
2085
	 *
2086
	 * @since  1.5
2087
	 * @return string Payment key
2088
	 */
2089
	private function get_form_id() {
2090
		return apply_filters( 'give_payment_form_id', $this->form_id, $this->ID, $this );
2091
	}
2092
2093
	/**
2094
	 * Retrieve payment number
2095
	 *
2096
	 * @since  1.5
2097
	 * @return int|string Payment number
2098
	 */
2099
	private function get_number() {
2100
		return apply_filters( 'give_payment_number', $this->number, $this->ID, $this );
2101
	}
2102
2103
}
2104