Issues (1282)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/payments/functions.php (21 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

Code
1
<?php
2
/**
3
 * Payment Functions
4
 *
5
 * @package     Give
6
 * @subpackage  Payments
7
 * @copyright   Copyright (c) 2016, GiveWP
8
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
9
 * @since       1.0
10
 */
11
12
// Exit if accessed directly.
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * Get Payments
19
 *
20
 * Retrieve payments from the database.
21
 *
22
 * Since 1.0, this function takes an array of arguments, instead of individual
23
 * parameters. All of the original parameters remain, but can be passed in any
24
 * order via the array.
25
 *
26
 * @since 1.0
27
 *
28
 * @param array $args     {
29
 *                        Optional. Array of arguments passed to payments query.
30
 *
31
 * @type int    $offset   The number of payments to offset before retrieval.
32
 *                            Default is 0.
33
 * @type int    $number   The number of payments to query for. Use -1 to request all
34
 *                            payments. Default is 20.
35
 * @type string $mode     Default is 'live'.
36
 * @type string $order    Designates ascending or descending order of payments.
37
 *                            Accepts 'ASC', 'DESC'. Default is 'DESC'.
38
 * @type string $orderby  Sort retrieved payments by parameter. Default is 'ID'.
39
 * @type string $status   The status of the payments. Default is 'any'.
40
 * @type string $user     User. Default is null.
41
 * @type string $meta_key Custom field key. Default is null.
42
 *
43
 * }
44
 *
45
 * @return array $payments Payments retrieved from the database
46
 */
47
function give_get_payments( $args = array() ) {
48
49
	// Fallback to post objects to ensure backwards compatibility.
50
	if ( ! isset( $args['output'] ) ) {
51
		$args['output'] = 'posts';
52
	}
53
54
	$args     = apply_filters( 'give_get_payments_args', $args );
55
	$payments = new Give_Payments_Query( $args );
56
57
	return $payments->get_payments();
58
}
59
60
/**
61
 * Retrieve payment by a given field
62
 *
63
 * @since  1.0
64
 *
65
 * @param  string $field The field to retrieve the payment with.
66
 * @param  mixed  $value The value for $field.
67
 *
68
 * @return mixed
69
 */
70
function give_get_payment_by( $field = '', $value = '' ) {
71
72
	if ( empty( $field ) || empty( $value ) ) {
73
		return false;
74
	}
75
76
	switch ( strtolower( $field ) ) {
77
78
		case 'id':
79
			$payment = new Give_Payment( $value );
80
			$id      = $payment->ID;
81
82
			if ( empty( $id ) ) {
83
				return false;
84
			}
85
86
			break;
87
88 View Code Duplication
		case 'key':
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
89
			$payment = give_get_payments(
90
				array(
91
					'meta_key'       => '_give_payment_purchase_key',
92
					'meta_value'     => $value,
93
					'posts_per_page' => 1,
94
					'fields'         => 'ids',
95
				)
96
			);
97
98
			if ( $payment ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $payment of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
99
				$payment = new Give_Payment( $payment[0] );
100
			}
101
102
			break;
103
104 View Code Duplication
		case 'payment_number':
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
105
			$payment = give_get_payments(
106
				array(
107
					'meta_key'       => '_give_payment_number',
108
					'meta_value'     => $value,
109
					'posts_per_page' => 1,
110
					'fields'         => 'ids',
111
				)
112
			);
113
114
			if ( $payment ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $payment of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
115
				$payment = new Give_Payment( $payment[0] );
116
			}
117
118
			break;
119
120
		default:
121
			return false;
122
	}// End switch().
123
124
	if ( $payment ) {
125
		return $payment;
126
	}
127
128
	return false;
129
}
130
131
/**
132
 * Insert Payment
133
 *
134
 * @since  1.0
135
 *
136
 * @param  array $payment_data Arguments passed.
137
 *
138
 * @return int|bool Payment ID if payment is inserted, false otherwise.
139
 */
140
function give_insert_payment( $payment_data = array() ) {
141
142
	if ( empty( $payment_data ) ) {
143
		return false;
144
	}
145
146
	/**
147
	 * Fire the filter on donation data before insert.
148
	 *
149
	 * @since 1.8.15
150
	 *
151
	 * @param array $payment_data Arguments passed.
152
	 */
153
	$payment_data = apply_filters( 'give_pre_insert_payment', $payment_data );
154
155
	$payment    = new Give_Payment();
156
	$gateway    = ! empty( $payment_data['gateway'] ) ? $payment_data['gateway'] : '';
157
	$gateway    = empty( $gateway ) && isset( $_POST['give-gateway'] ) ? give_clean( $_POST['give-gateway'] ) : $gateway; // WPCS: input var ok, sanitization ok, CSRF ok.
158
	$form_id    = isset( $payment_data['give_form_id'] ) ? $payment_data['give_form_id'] : 0;
159
	$price_id   = give_get_payment_meta_price_id( $payment_data );
160
	$form_title = isset( $payment_data['give_form_title'] ) ? $payment_data['give_form_title'] : get_the_title( $form_id );
161
162
	// Set properties.
163
	$payment->total          = $payment_data['price'];
164
	$payment->status         = ! empty( $payment_data['status'] ) ? $payment_data['status'] : 'pending';
165
	$payment->currency       = ! empty( $payment_data['currency'] ) ? $payment_data['currency'] : give_get_currency( $payment_data['give_form_id'], $payment_data );
166
	$payment->user_info      = $payment_data['user_info'];
167
	$payment->gateway        = $gateway;
168
	$payment->form_title     = $form_title;
169
	$payment->form_id        = $form_id;
170
	$payment->price_id       = $price_id;
171
	$payment->donor_id       = ( ! empty( $payment_data['donor_id'] ) ? $payment_data['donor_id'] : '' );
172
	$payment->user_id        = $payment_data['user_info']['id'];
173
	$payment->email          = $payment_data['user_email'];
174
	$payment->first_name     = $payment_data['user_info']['first_name'];
175
	$payment->last_name      = $payment_data['user_info']['last_name'];
176
	$payment->title_prefix   = ! empty( $payment_data['user_info']['title'] ) ? $payment_data['user_info']['title'] : '';
177
	$payment->email          = $payment_data['user_info']['email'];
178
	$payment->ip             = give_get_ip();
0 ignored issues
show
Documentation Bug introduced by
It seems like give_get_ip() can also be of type array. However, the property $ip is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
179
	$payment->key            = $payment_data['purchase_key'];
180
	$payment->mode           = ( ! empty( $payment_data['mode'] ) ? (string) $payment_data['mode'] : ( give_is_test_mode() ? 'test' : 'live' ) );
181
	$payment->parent_payment = ! empty( $payment_data['parent'] ) ? absint( $payment_data['parent'] ) : '';
182
183
	// Add the donation.
184
	$args = array(
185
		'price'    => $payment->total,
186
		'price_id' => $payment->price_id,
187
	);
188
189
	$payment->add_donation( $payment->form_id, $args );
190
191
	// Set date if present.
192
	if ( isset( $payment_data['post_date'] ) ) {
193
		$payment->date = $payment_data['post_date'];
194
	}
195
196
	// Save payment.
197
	$payment->save();
198
199
	// Setup donor id.
200
	$payment_data['user_info']['donor_id'] = $payment->donor_id;
201
202
	// Set donation id to purchase session.
203
	$purchase_session = Give()->session->get( 'give_purchase' );
204
	$purchase_session['donation_id'] = $payment->ID;
205
	Give()->session->set( 'give_purchase', $purchase_session );
206
207
	/**
208
	 * Fires while inserting payments.
209
	 *
210
	 * @since 1.0
211
	 *
212
	 * @param int   $payment_id   The payment ID.
213
	 * @param array $payment_data Arguments passed.
214
	 */
215
	do_action( 'give_insert_payment', $payment->ID, $payment_data );
216
217
	// Return payment ID upon success.
218
	if ( ! empty( $payment->ID ) ) {
219
		return $payment->ID;
220
	}
221
222
	// Return false if no payment was inserted.
223
	return false;
224
225
}
226
227
/**
228
 * Create payment.
229
 *
230
 * @param $payment_data
231
 *
232
 * @return bool|int
233
 */
234
function give_create_payment( $payment_data ) {
235
236
	$form_id  = intval( $payment_data['post_data']['give-form-id'] );
237
	$price_id = isset( $payment_data['post_data']['give-price-id'] ) ? $payment_data['post_data']['give-price-id'] : '';
238
239
	// Collect payment data.
240
	$insert_payment_data = array(
241
		'price'           => $payment_data['price'],
242
		'give_form_title' => $payment_data['post_data']['give-form-title'],
243
		'give_form_id'    => $form_id,
244
		'give_price_id'   => $price_id,
245
		'date'            => $payment_data['date'],
246
		'user_email'      => $payment_data['user_email'],
247
		'purchase_key'    => $payment_data['purchase_key'],
248
		'currency'        => give_get_currency( $form_id, $payment_data ),
249
		'user_info'       => $payment_data['user_info'],
250
		'status'          => 'pending',
251
		'gateway'         => 'paypal',
252
	);
253
254
	/**
255
	 * Filter the payment params.
256
	 *
257
	 * @since 1.8
258
	 *
259
	 * @param array $insert_payment_data
260
	 */
261
	$insert_payment_data = apply_filters( 'give_create_payment', $insert_payment_data );
262
263
	// Record the pending payment.
264
	return give_insert_payment( $insert_payment_data );
265
}
266
267
/**
268
 * Updates a payment status.
269
 *
270
 * @param  int    $payment_id Payment ID.
271
 * @param  string $new_status New Payment Status. Default is 'publish'.
272
 *
273
 * @since  1.0
274
 *
275
 * @return bool
276
 */
277
function give_update_payment_status( $payment_id, $new_status = 'publish' ) {
278
279
	$updated = false;
280
	$payment = new Give_Payment( $payment_id );
281
282
	if ( $payment && $payment->ID > 0 ) {
283
284
		$payment->status = $new_status;
285
		$updated         = $payment->save();
286
287
	}
288
289
	return $updated;
290
}
291
292
293
/**
294
 * Deletes a Donation
295
 *
296
 * @since  1.0
297
 *
298
 * @param  int  $payment_id   Payment ID (default: 0).
299
 * @param  bool $update_donor If we should update the donor stats (default:true).
300
 *
301
 * @return void
302
 */
303
function give_delete_donation( $payment_id = 0, $update_donor = true ) {
304
	$payment = new Give_Payment( $payment_id );
305
306
	// Bailout.
307
	if ( ! $payment->ID ) {
308
		return;
309
	}
310
311
	$amount = give_donation_amount( $payment_id );
312
	$status = $payment->post_status;
313
	$donor  = new Give_Donor( $payment->donor_id );
314
315
	// Only undo donations that aren't these statuses.
316
	$dont_undo_statuses = apply_filters(
317
		'give_undo_donation_statuses', array(
318
			'pending',
319
			'cancelled',
320
		)
321
	);
322
323
	if ( ! in_array( $status, $dont_undo_statuses ) ) {
324
		give_undo_donation( $payment_id );
325
	}
326
327
	// Only undo donations that aren't these statuses.
328
	$status_to_decrease_stats = apply_filters( 'give_decrease_donor_statuses', array( 'publish' ) );
329
330
	if ( in_array( $status, $status_to_decrease_stats ) ) {
331
332
		// Only decrease earnings if they haven't already been decreased (or were never increased for this payment).
333
		give_decrease_total_earnings( $amount );
334
335
		// @todo: Refresh only range related stat cache
336
		give_delete_donation_stats();
337
338
		if ( $donor->id && $update_donor ) {
339
340
			// Decrement the stats for the donor.
341
			$donor->decrease_donation_count();
342
			$donor->decrease_value( $amount );
343
344
		}
345
	}
346
347
	/**
348
	 * Fires before deleting payment.
349
	 *
350
	 * @param int $payment_id Payment ID.
351
	 *
352
	 * @since 1.0
353
	 */
354
	do_action( 'give_payment_delete', $payment_id );
355
356
	if ( $donor->id && $update_donor ) {
357
		// Remove the payment ID from the donor.
358
		$donor->remove_payment( $payment_id );
359
	}
360
361
	// Remove the payment.
362
	wp_delete_post( $payment_id, true );
363
364
	Give()->payment_meta->delete_all_meta( $payment_id );
365
366
	// Remove related sale log entries.
367
	Give()->logs->delete_logs( $payment_id );
368
369
	/**
370
	 * Fires after payment deleted.
371
	 *
372
	 * @param int $payment_id Payment ID.
373
	 *
374
	 * @since 1.0
375
	 */
376
	do_action( 'give_payment_deleted', $payment_id );
377
}
378
379
/**
380
 * Undo Donation
381
 *
382
 * Undoes a donation, including the decrease of donations and earning stats.
383
 * Used for when refunding or deleting a donation.
384
 *
385
 * @param  int $payment_id Payment ID.
386
 *
387
 * @since  1.0
388
 *
389
 * @return void
390
 */
391
function give_undo_donation( $payment_id ) {
392
393
	$payment = new Give_Payment( $payment_id );
394
395
	$maybe_decrease_earnings = apply_filters( 'give_decrease_earnings_on_undo', true, $payment, $payment->form_id );
396
	if ( true === $maybe_decrease_earnings ) {
397
		// Decrease earnings.
398
		give_decrease_form_earnings( $payment->form_id, $payment->total, $payment_id );
399
	}
400
401
	$maybe_decrease_donations = apply_filters( 'give_decrease_donations_on_undo', true, $payment, $payment->form_id );
402
	if ( true === $maybe_decrease_donations ) {
403
		// Decrease donation count.
404
		give_decrease_donation_count( $payment->form_id );
405
	}
406
407
}
408
409
410
/**
411
 * Count Payments
412
 *
413
 * Returns the total number of payments recorded.
414
 *
415
 * @param  array $args Arguments passed.
416
 *
417
 * @since  1.0
418
 *
419
 * @return object $stats Contains the number of payments per payment status.
420
 */
421
function give_count_payments( $args = array() ) {
422
	// Backward compatibility.
423
	if ( ! empty( $args['start-date'] ) ) {
424
		$args['start_date'] = $args['start-date'];
425
		unset( $args['start-date'] );
426
	}
427
428
	if ( ! empty( $args['end-date'] ) ) {
429
		$args['end_date'] = $args['end-date'];
430
		unset( $args['end-date'] );
431
	}
432
433
	if ( ! empty( $args['form_id'] ) ) {
434
		$args['give_forms'] = $args['form_id'];
435
		unset( $args['form_id'] );
436
	}
437
438
	// Extract all donations
439
	$args['number']   = - 1;
440
	$args['group_by'] = 'post_status';
441
	$args['count']    = 'true';
442
443
	$donations_obj   = new Give_Payments_Query( $args );
444
	$donations_count = $donations_obj->get_payment_by_group();
445
446
	/**
447
	 * Filter the payment counts group by status
448
	 *
449
	 * @since 1.0
450
	 */
451
	return (object) apply_filters( 'give_count_payments', $donations_count, $args, $donations_obj );
452
}
453
454
455
/**
456
 * Check For Existing Payment
457
 *
458
 * @param  int $payment_id Payment ID.
459
 *
460
 * @since  1.0
461
 *
462
 * @return bool $exists True if payment exists, false otherwise.
463
 */
464
function give_check_for_existing_payment( $payment_id ) {
465
	global $wpdb;
466
467
	return (bool) $wpdb->get_var(
468
		$wpdb->prepare(
469
			"
470
			SELECT ID
471
			FROM {$wpdb->posts}
472
			WHERE ID=%s
473
			AND post_status=%s
474
			",
475
			$payment_id,
476
			'publish'
477
		)
478
	);
479
}
480
481
/**
482
 * Get Payment Status
483
 *
484
 * @param WP_Post|Give_Payment|int $payment_id      Payment object or payment ID.
485
 * @param bool                     $return_label Whether to return the translated status label instead of status value.
486
 *                                               Default false.
487
 *
488
 * @since 1.0
489
 *
490
 * @return bool|mixed True if payment status exists, false otherwise.
491
 */
492
function give_get_payment_status( $payment_id, $return_label = false ) {
493
494
	if ( ! is_numeric( $payment_id ) ) {
495
		if (
496
			$payment_id instanceof  Give_Payment
497
			|| $payment_id instanceof WP_Post
0 ignored issues
show
The class WP_Post does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
498
		) {
499
			$payment_id = $payment_id->ID;
500
		}
501
	}
502
503
	if ( ! $payment_id > 0 ) {
504
		return false;
505
	}
506
507
	$payment_status = get_post_status( $payment_id );
508
509
	$statuses = give_get_payment_statuses();
510
511
	if ( empty( $payment_status ) || ! is_array( $statuses ) || empty( $statuses ) ) {
512
		return false;
513
	}
514
515
	if ( array_key_exists( $payment_status, $statuses ) ) {
516
		if ( true === $return_label ) {
517
			// Return translated status label.
518
			return $statuses[ $payment_status ];
519
		} else {
520
			// Account that our 'publish' status is labeled 'Complete'
521
			$post_status = 'publish' === $payment_status ? 'Complete' : $payment_status;
522
523
			// Make sure we're matching cases, since they matter
524
			return array_search( strtolower( $post_status ), array_map( 'strtolower', $statuses ) );
525
		}
526
	}
527
528
	return false;
529
}
530
531
/**
532
 * Retrieves all available statuses for payments.
533
 *
534
 * @since  1.0
535
 *
536
 * @return array $payment_status All the available payment statuses.
537
 */
538
function give_get_payment_statuses() {
539
	$payment_statuses = array(
540
		'pending'     => __( 'Pending', 'give' ),
541
		'publish'     => __( 'Complete', 'give' ),
542
		'refunded'    => __( 'Refunded', 'give' ),
543
		'failed'      => __( 'Failed', 'give' ),
544
		'cancelled'   => __( 'Cancelled', 'give' ),
545
		'abandoned'   => __( 'Abandoned', 'give' ),
546
		'preapproval' => __( 'Pre-Approved', 'give' ),
547
		'processing'  => __( 'Processing', 'give' ),
548
		'revoked'     => __( 'Revoked', 'give' ),
549
	);
550
551
	return apply_filters( 'give_payment_statuses', $payment_statuses );
552
}
553
554
/**
555
 * Get Payment Status Keys
556
 *
557
 * Retrieves keys for all available statuses for payments
558
 *
559
 * @since 1.0
560
 *
561
 * @return array $payment_status All the available payment statuses.
562
 */
563
function give_get_payment_status_keys() {
564
	$statuses = array_keys( give_get_payment_statuses() );
565
	asort( $statuses );
566
567
	return array_values( $statuses );
568
}
569
570
/**
571
 * Get Earnings By Date
572
 *
573
 * @param int $day       Day number. Default is null.
574
 * @param int $month_num Month number. Default is null.
575
 * @param int $year      Year number. Default is null.
576
 * @param int $hour      Hour number. Default is null.
577
 *
578
 * @since 1.0
579
 *
580
 * @return int $earnings Earnings
581
 */
582
function give_get_earnings_by_date( $day = null, $month_num, $year = null, $hour = null ) {
583
	// This is getting deprecated soon. Use Give_Payment_Stats with the get_earnings() method instead.
584
	global $wpdb;
585
586
	$args = array(
587
		'post_type'              => 'give_payment',
588
		'nopaging'               => true,
589
		'year'                   => $year,
590
		'monthnum'               => $month_num,
591
		'post_status'            => array( 'publish' ),
592
		'fields'                 => 'ids',
593
		'update_post_term_cache' => false,
594
	);
595
	if ( ! empty( $day ) ) {
596
		$args['day'] = $day;
597
	}
598
599
	if ( isset( $hour ) ) {
600
		$args['hour'] = $hour;
601
	}
602
603
	$args = apply_filters( 'give_get_earnings_by_date_args', $args );
604
	$key  = Give_Cache::get_key( 'give_stats', $args );
605
606 View Code Duplication
	if ( ! empty( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], 'give-refresh-reports' ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
607
		$earnings = false;
608
	} else {
609
		$earnings = Give_Cache::get( $key );
610
	}
611
612
	if ( false === $earnings ) {
613
		$donations = get_posts( $args );
614
		$earnings  = 0;
615
616
		$donation_table     = Give()->payment_meta->table_name;
617
		$donation_table_col = Give()->payment_meta->get_meta_type() . '_id';
618
619
		if ( $donations ) {
620
			$donations      = implode( ',', $donations );
621
			$earning_totals = $wpdb->get_var( "SELECT SUM(meta_value) FROM {$donation_table} WHERE meta_key = '_give_payment_total' AND {$donation_table_col} IN ({$donations})" );
622
623
			/**
624
			 * Filter The earnings by dates.
625
			 *
626
			 * @since 1.8.17
627
			 *
628
			 * @param float $earning_totals Total earnings between the dates.
629
			 * @param array $donations      Donations lists.
630
			 * @param array $args           Donation query args.
631
			 */
632
			$earnings = apply_filters( 'give_get_earnings_by_date', $earning_totals, $donations, $args );
633
		}
634
		// Cache the results for one hour.
635
		Give_Cache::set( $key, $earnings, HOUR_IN_SECONDS );
636
	}
637
638
	return round( $earnings, 2 );
639
}
640
641
/**
642
 * Get Donations (sales) By Date
643
 *
644
 * @param int $day       Day number. Default is null.
645
 * @param int $month_num Month number. Default is null.
646
 * @param int $year      Year number. Default is null.
647
 * @param int $hour      Hour number. Default is null.
648
 *
649
 * @since 1.0
650
 *
651
 * @return int $count Sales
652
 */
653
function give_get_sales_by_date( $day = null, $month_num = null, $year = null, $hour = null ) {
654
655
	// This is getting deprecated soon. Use Give_Payment_Stats with the get_sales() method instead.
656
	$args = array(
657
		'post_type'              => 'give_payment',
658
		'nopaging'               => true,
659
		'year'                   => $year,
660
		'fields'                 => 'ids',
661
		'post_status'            => array( 'publish' ),
662
		'update_post_meta_cache' => false,
663
		'update_post_term_cache' => false,
664
	);
665
666
	$show_free = apply_filters( 'give_sales_by_date_show_free', true, $args );
667
668
	if ( false === $show_free ) {
669
		$args['meta_query'] = array(
670
			array(
671
				'key'     => '_give_payment_total',
672
				'value'   => 0,
673
				'compare' => '>',
674
				'type'    => 'NUMERIC',
675
			),
676
		);
677
	}
678
679
	if ( ! empty( $month_num ) ) {
680
		$args['monthnum'] = $month_num;
681
	}
682
683
	if ( ! empty( $day ) ) {
684
		$args['day'] = $day;
685
	}
686
687
	if ( isset( $hour ) ) {
688
		$args['hour'] = $hour;
689
	}
690
691
	$args = apply_filters( 'give_get_sales_by_date_args', $args );
692
693
	$key = Give_Cache::get_key( 'give_stats', $args );
694
695 View Code Duplication
	if ( ! empty( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], 'give-refresh-reports' ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
696
		$count = false;
697
	} else {
698
		$count = Give_Cache::get( $key );
699
	}
700
701
	if ( false === $count ) {
702
		$donations = new WP_Query( $args );
703
		$count     = (int) $donations->post_count;
704
		// Cache the results for one hour.
705
		Give_Cache::set( $key, $count, HOUR_IN_SECONDS );
706
	}
707
708
	return $count;
709
}
710
711
/**
712
 * Checks whether a payment has been marked as complete.
713
 *
714
 * @param int $payment_id Payment ID to check against.
715
 *
716
 * @since 1.0
717
 *
718
 * @return bool $ret True if complete, false otherwise.
719
 */
720
function give_is_payment_complete( $payment_id ) {
721
	$ret            = false;
722
	$payment_status = '';
723
724
	if ( $payment_id > 0 && 'give_payment' === get_post_type( $payment_id ) ) {
725
		$payment_status = get_post_status( $payment_id );
726
727
		if ( 'publish' === $payment_status ) {
728
			$ret = true;
729
		}
730
	}
731
732
	/**
733
	 * Filter the flag
734
	 *
735
	 * @since 1.0
736
	 */
737
	return apply_filters( 'give_is_payment_complete', $ret, $payment_id, $payment_status );
738
}
739
740
/**
741
 * Get Total Donations.
742
 *
743
 * @since 1.0
744
 *
745
 * @return int $count Total number of donations.
746
 */
747
function give_get_total_donations() {
748
749
	$payments = give_count_payments();
750
751
	return $payments->publish;
752
}
753
754
/**
755
 * Get Total Earnings
756
 *
757
 * @param bool $recalculate Recalculate earnings forcefully.
758
 *
759
 * @since 1.0
760
 *
761
 * @return float $total Total earnings.
762
 */
763
function give_get_total_earnings( $recalculate = false ) {
764
765
	$total      = get_option( 'give_earnings_total', 0 );
766
	$meta_table = __give_v20_bc_table_details( 'payment' );
767
768
	// Calculate total earnings.
769
	if ( ! $total || $recalculate ) {
770
		global $wpdb;
771
772
		$total = (float) 0;
773
774
		$args = apply_filters(
775
			'give_get_total_earnings_args', array(
776
				'offset' => 0,
777
				'number' => - 1,
778
				'status' => array( 'publish' ),
779
				'fields' => 'ids',
780
			)
781
		);
782
783
		$payments = give_get_payments( $args );
784
		if ( $payments ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $payments of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
785
786
			/**
787
			 * If performing a donation, we need to skip the very last payment in the database,
788
			 * since it calls give_increase_total_earnings() on completion,
789
			 * which results in duplicated earnings for the very first donation.
790
			 */
791
			if ( did_action( 'give_update_payment_status' ) ) {
792
				array_pop( $payments );
793
			}
794
795
			if ( ! empty( $payments ) ) {
796
				$payments = implode( ',', $payments );
797
				$total   += $wpdb->get_var( "SELECT SUM(meta_value) FROM {$meta_table['name']} WHERE meta_key = '_give_payment_total' AND {$meta_table['column']['id']} IN({$payments})" );
798
			}
799
		}
800
801
		update_option( 'give_earnings_total', $total, false );
802
	}
803
804
	if ( $total < 0 ) {
805
		$total = 0; // Don't ever show negative earnings.
806
	}
807
808
	return apply_filters( 'give_total_earnings', round( $total, give_get_price_decimals() ), $total );
809
}
810
811
/**
812
 * Increase the Total Earnings
813
 *
814
 * @param int $amount The amount you would like to increase the total earnings by. Default is 0.
815
 *
816
 * @since 1.0
817
 *
818
 * @return float $total Total earnings.
819
 */
820
function give_increase_total_earnings( $amount = 0 ) {
821
	$total  = give_get_total_earnings();
822
	$total += $amount;
823
	update_option( 'give_earnings_total', $total, false );
824
825
	return $total;
826
}
827
828
/**
829
 * Decrease the Total Earnings
830
 *
831
 * @param int $amount The amount you would like to decrease the total earnings by.
832
 *
833
 * @since 1.0
834
 *
835
 * @return float $total Total earnings.
836
 */
837
function give_decrease_total_earnings( $amount = 0 ) {
838
	$total  = give_get_total_earnings();
839
	$total -= $amount;
840
	if ( $total < 0 ) {
841
		$total = 0;
842
	}
843
	update_option( 'give_earnings_total', $total, false );
844
845
	return $total;
846
}
847
848
/**
849
 * Get Payment Meta for a specific Payment
850
 *
851
 * @param int    $payment_id Payment ID.
852
 * @param string $meta_key   The meta key to pull.
853
 * @param bool   $single     Pull single meta entry or as an object.
854
 *
855
 * @since 1.0
856
 *
857
 * @return mixed $meta Payment Meta.
858
 */
859
function give_get_payment_meta( $payment_id = 0, $meta_key = '_give_payment_meta', $single = true ) {
860
	return give_get_meta( $payment_id, $meta_key, $single );
861
}
862
863
/**
864
 * Update the meta for a payment
865
 *
866
 * @param  int    $payment_id Payment ID.
867
 * @param  string $meta_key   Meta key to update.
868
 * @param  string $meta_value Value to update to.
869
 * @param  string $prev_value Previous value.
870
 *
871
 * @return mixed Meta ID if successful, false if unsuccessful.
872
 */
873
function give_update_payment_meta( $payment_id = 0, $meta_key = '', $meta_value = '', $prev_value = '' ) {
0 ignored issues
show
The parameter $prev_value 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...
874
	return give_update_meta( $payment_id, $meta_key, $meta_value );
875
}
876
877
/**
878
 * Get the user_info Key from Payment Meta
879
 *
880
 * @param int $payment_id Payment ID.
881
 *
882
 * @since 1.0
883
 *
884
 * @return array $user_info User Info Meta Values.
885
 */
886
function give_get_payment_meta_user_info( $payment_id ) {
887
	$donor_id   = 0;
888
	$donor_info = array(
889
		'first_name' => give_get_meta( $payment_id, '_give_donor_billing_first_name', true ),
890
		'last_name'  => give_get_meta( $payment_id, '_give_donor_billing_last_name', true ),
891
		'email'      => give_get_meta( $payment_id, '_give_donor_billing_donor_email', true ),
892
	);
893
894 View Code Duplication
	if ( empty( $donor_info['first_name'] ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
895
		$donor_id                 = give_get_payment_donor_id( $payment_id );
896
		$donor_info['first_name'] = Give()->donor_meta->get_meta( $donor_id, '_give_donor_first_name', true );
897
	}
898
899 View Code Duplication
	if ( empty( $donor_info['last_name'] ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
900
		$donor_id                = $donor_id ? $donor_id : give_get_payment_donor_id( $payment_id );
901
		$donor_info['last_name'] = Give()->donor_meta->get_meta( $donor_id, '_give_donor_last_name', true );
902
	}
903
904
	if ( empty( $donor_info['email'] ) ) {
905
		$donor_id            = $donor_id ? $donor_id : give_get_payment_donor_id( $payment_id );
906
		$donor_info['email'] = Give()->donors->get_column_by( 'email', 'id', $donor_id );
907
	}
908
909
	$donor_info['title'] = Give()->donor_meta->get_meta( $donor_id, '_give_donor_title_prefix', true );
910
911
	$donor_info['address']  = give_get_donation_address( $payment_id );
912
	$donor_info['id']       = give_get_payment_user_id( $payment_id );
913
	$donor_info['donor_id'] = give_get_payment_donor_id( $payment_id );
914
915
	return $donor_info;
916
}
917
918
/**
919
 * Get the donations Key from Payment Meta
920
 *
921
 * Retrieves the form_id from a (Previously titled give_get_payment_meta_donations)
922
 *
923
 * @param int $payment_id Payment ID.
924
 *
925
 * @since 1.0
926
 *
927
 * @return int $form_id Form ID.
928
 */
929
function give_get_payment_form_id( $payment_id ) {
930
	return (int) give_get_meta( $payment_id, '_give_payment_form_id', true );
931
}
932
933
/**
934
 * Get the user email associated with a payment
935
 *
936
 * @param int $payment_id Payment ID.
937
 *
938
 * @since 1.0
939
 *
940
 * @return string $email User email.
941
 */
942
function give_get_payment_user_email( $payment_id ) {
943
	$email = give_get_meta( $payment_id, '_give_payment_donor_email', true );
944
945
	if ( empty( $email ) && ( $donor_id = give_get_payment_donor_id( $payment_id ) ) ) {
946
		$email = Give()->donors->get_column( 'email', $donor_id );
947
	}
948
949
	return $email;
950
}
951
952
/**
953
 * Is the payment provided associated with a user account
954
 *
955
 * @param int $payment_id The payment ID.
956
 *
957
 * @since 1.3
958
 *
959
 * @return bool $is_guest_payment If the payment is associated with a user (false) or not (true)
960
 */
961
function give_is_guest_payment( $payment_id ) {
962
	$payment_user_id  = give_get_payment_user_id( $payment_id );
963
	$is_guest_payment = ! empty( $payment_user_id ) && $payment_user_id > 0 ? false : true;
964
965
	return (bool) apply_filters( 'give_is_guest_payment', $is_guest_payment, $payment_id );
966
}
967
968
/**
969
 * Get the user ID associated with a payment
970
 *
971
 * @param int $payment_id Payment ID.
972
 *
973
 * @since 1.3
974
 *
975
 * @return int $user_id User ID.
976
 */
977
function give_get_payment_user_id( $payment_id ) {
978
	global $wpdb;
979
	$paymentmeta_table = Give()->payment_meta->table_name;
980
	$donationmeta_primary_key = Give()->payment_meta->get_meta_type() . '_id';
981
982
	return (int) $wpdb->get_var(
983
		$wpdb->prepare(
984
			"
985
			SELECT user_id
986
			FROM $wpdb->donors
987
			WHERE id=(
988
				SELECT meta_value
989
				FROM $paymentmeta_table
990
				WHERE {$donationmeta_primary_key}=%s
991
				AND meta_key=%s
992
			)
993
			",
994
			$payment_id,
995
			'_give_payment_donor_id'
996
		)
997
	);
998
}
999
1000
/**
1001
 * Get the donor ID associated with a payment.
1002
 *
1003
 * @param int $payment_id Payment ID.
1004
 *
1005
 * @since 1.0
1006
 *
1007
 * @return int $payment->customer_id Donor ID.
1008
 */
1009
function give_get_payment_donor_id( $payment_id ) {
1010
	return give_get_meta( $payment_id, '_give_payment_donor_id', true );
1011
}
1012
1013
/**
1014
 * Get the donor email associated with a donation.
1015
 *
1016
 * @param int $payment_id Payment ID.
1017
 *
1018
 * @since 2.1.0
1019
 *
1020
 * @return string
1021
 */
1022
function give_get_donation_donor_email( $payment_id ) {
1023
	return give_get_meta( $payment_id, '_give_payment_donor_email', true );
1024
}
1025
1026
/**
1027
 * Get the IP address used to make a donation
1028
 *
1029
 * @param int $payment_id Payment ID.
1030
 *
1031
 * @since 1.0
1032
 *
1033
 * @return string $ip User IP.
1034
 */
1035
function give_get_payment_user_ip( $payment_id ) {
1036
	return give_get_meta( $payment_id, '_give_payment_donor_ip', true );
1037
}
1038
1039
/**
1040
 * Get the date a payment was completed
1041
 *
1042
 * @param int $payment_id Payment ID.
1043
 *
1044
 * @since 1.0
1045
 *
1046
 * @return string $date The date the payment was completed.
1047
 */
1048
function give_get_payment_completed_date( $payment_id = 0 ) {
1049
	return give_get_meta( $payment_id, '_give_completed_date', true );
1050
}
1051
1052
/**
1053
 * Get the gateway associated with a payment
1054
 *
1055
 * @param int $payment_id Payment ID.
1056
 *
1057
 * @since 1.0
1058
 *
1059
 * @return string $gateway Gateway.
1060
 */
1061
function give_get_payment_gateway( $payment_id ) {
1062
	return give_get_meta( $payment_id, '_give_payment_gateway', true );
1063
}
1064
1065
/**
1066
 * Check if donation have specific gateway or not
1067
 *
1068
 * @since 2.1.0
1069
 *
1070
 * @param int|Give_Payment $donation_id Donation ID
1071
 * @param string           $gateway_id  Gateway ID
1072
 *
1073
 * @return bool
1074
 */
1075
function give_has_payment_gateway( $donation_id, $gateway_id ) {
1076
	$donation_gateway = $donation_id instanceof Give_Payment ?
1077
		$donation_id->gateway :
1078
		give_get_payment_gateway( $donation_id );
1079
1080
	return $gateway_id === $donation_gateway;
1081
}
1082
1083
/**
1084
 * Get the currency code a payment was made in
1085
 *
1086
 * @param int $payment_id Payment ID.
1087
 *
1088
 * @since 1.0
1089
 *
1090
 * @return string $currency The currency code.
1091
 */
1092
function give_get_payment_currency_code( $payment_id = 0 ) {
1093
	return give_get_meta( $payment_id, '_give_payment_currency', true );
1094
}
1095
1096
/**
1097
 * Get the currency name a payment was made in
1098
 *
1099
 * @param int $payment_id Payment ID.
1100
 *
1101
 * @since 1.0
1102
 *
1103
 * @return string $currency The currency name.
1104
 */
1105
function give_get_payment_currency( $payment_id = 0 ) {
1106
	$currency = give_get_payment_currency_code( $payment_id );
1107
1108
	return apply_filters( 'give_payment_currency', give_get_currency_name( $currency ), $payment_id );
1109
}
1110
1111
/**
1112
 * Get the key for a donation
1113
 *
1114
 * @param int $payment_id Payment ID.
1115
 *
1116
 * @since 1.0
1117
 *
1118
 * @return string $key Donation key.
1119
 */
1120
function give_get_payment_key( $payment_id = 0 ) {
1121
	return give_get_meta( $payment_id, '_give_payment_purchase_key', true );
1122
}
1123
1124
/**
1125
 * Get the payment order number
1126
 *
1127
 * This will return the payment ID if sequential order numbers are not enabled or the order number does not exist
1128
 *
1129
 * @param int $payment_id Payment ID.
1130
 *
1131
 * @since 1.0
1132
 *
1133
 * @return string $number Payment order number.
1134
 */
1135
function give_get_payment_number( $payment_id = 0 ) {
1136
	return Give()->seq_donation_number->get_serial_code( $payment_id );
1137
}
1138
1139
1140
/**
1141
 * Get Donation Amount
1142
 *
1143
 * Get the fully formatted or unformatted donation amount which is sent through give_currency_filter()
1144
 * and give_format_amount() to format the amount correctly in case of formatted amount.
1145
 *
1146
 * @param int|Give_Payment $donation_id Donation ID or Donation Object.
1147
 * @param bool|array       $format_args Currency Formatting Arguments.
1148
 *
1149
 * @since 1.0
1150
 * @since 1.8.17 Added filter and internally use functions.
1151
 *
1152
 * @return string $amount Fully formatted donation amount.
1153
 */
1154
function give_donation_amount( $donation_id, $format_args = array() ) {
1155
	if ( ! $donation_id ) {
1156
		return '';
1157
	} elseif ( ! is_numeric( $donation_id ) && ( $donation_id instanceof Give_Payment ) ) {
1158
		$donation_id = $donation_id->ID;
1159
	}
1160
1161
	$amount        = $formatted_amount = give_get_payment_total( $donation_id );
1162
	$currency_code = give_get_payment_currency_code( $donation_id );
1163
1164
	if ( is_bool( $format_args ) ) {
1165
		$format_args = array(
1166
			'currency' => (bool) $format_args,
1167
			'amount'   => (bool) $format_args,
1168
		);
1169
	}
1170
1171
	$format_args = wp_parse_args(
1172
		$format_args,
1173
		array(
1174
			'currency' => false,
1175
			'amount'   => false,
1176
1177
			// Define context of donation amount, by default keep $type as blank.
1178
			// Pass as 'stats' to calculate donation report on basis of base amount for the Currency-Switcher Add-on.
1179
			// For Eg. In Currency-Switcher add on when donation has been made through
1180
			// different currency other than base currency, in that case for correct
1181
			// report calculation based on base currency we will need to return donation
1182
			// base amount and not the converted amount .
1183
			'type'     => '',
1184
		)
1185
	);
1186
1187
	if ( $format_args['amount'] || $format_args['currency'] ) {
1188
1189
		if ( $format_args['amount'] ) {
1190
1191
			$formatted_amount = give_format_amount(
1192
				$amount,
1193
				! is_array( $format_args['amount'] ) ?
1194
					array(
1195
						'sanitize' => false,
1196
						'currency' => $currency_code,
1197
					) :
1198
					$format_args['amount']
1199
			);
1200
		}
1201
1202
		if ( $format_args['currency'] ) {
1203
			$formatted_amount = give_currency_filter(
1204
				$formatted_amount,
1205
				! is_array( $format_args['currency'] ) ?
1206
					array( 'currency_code' => $currency_code ) :
1207
					$format_args['currency']
1208
			);
1209
		}
1210
	}
1211
1212
	/**
1213
	 * Filter Donation amount.
1214
	 *
1215
	 * @since 1.8.17
1216
	 *
1217
	 * @param string $formatted_amount Formatted/Un-formatted amount.
1218
	 * @param float  $amount           Donation amount.
1219
	 * @param int    $donation_id      Donation ID.
1220
	 * @param string $type             Donation amount type.
1221
	 */
1222
	return apply_filters( 'give_donation_amount', (string) $formatted_amount, $amount, $donation_id, $format_args );
1223
}
1224
1225
/**
1226
 * Payment Subtotal
1227
 *
1228
 * Retrieves subtotal for payment and then returns a full formatted amount. This
1229
 * function essentially calls give_get_payment_subtotal()
1230
 *
1231
 * @param int $payment_id Payment ID.
1232
 *
1233
 * @since 1.5
1234
 *
1235
 * @see   give_get_payment_subtotal()
1236
 *
1237
 * @return array Fully formatted payment subtotal.
1238
 */
1239
function give_payment_subtotal( $payment_id = 0 ) {
1240
	$subtotal = give_get_payment_subtotal( $payment_id );
1241
1242
	return give_currency_filter( give_format_amount( $subtotal, array( 'sanitize' => false ) ), array( 'currency_code' => give_get_payment_currency_code( $payment_id ) ) );
1243
}
1244
1245
/**
1246
 * Get Payment Subtotal
1247
 *
1248
 * Retrieves subtotal for payment and then returns a non formatted amount.
1249
 *
1250
 * @param int $payment_id Payment ID.
1251
 *
1252
 * @since 1.5
1253
 *
1254
 * @return float $subtotal Subtotal for payment (non formatted).
1255
 */
1256
function give_get_payment_subtotal( $payment_id = 0 ) {
1257
	$payment = new Give_Payment( $payment_id );
1258
1259
	return $payment->subtotal;
1260
}
1261
1262
/**
1263
 * Retrieves the donation ID
1264
 *
1265
 * @param int $payment_id Payment ID.
1266
 *
1267
 * @since  1.0
1268
 *
1269
 * @return string The donation ID.
1270
 */
1271 View Code Duplication
function give_get_payment_transaction_id( $payment_id = 0 ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1272
	$transaction_id = give_get_meta( $payment_id, '_give_payment_transaction_id', true );
1273
1274
	if ( empty( $transaction_id ) ) {
1275
		$gateway        = give_get_payment_gateway( $payment_id );
1276
		$transaction_id = apply_filters( "give_get_payment_transaction_id-{$gateway}", $payment_id );
1277
	}
1278
1279
	return $transaction_id;
1280
}
1281
1282
/**
1283
 * Sets a Transaction ID in post meta for the given Payment ID.
1284
 *
1285
 * @param int    $payment_id     Payment ID.
1286
 * @param string $transaction_id The transaction ID from the gateway.
1287
 *
1288
 * @since  1.0
1289
 *
1290
 * @return bool|mixed
1291
 */
1292
function give_set_payment_transaction_id( $payment_id = 0, $transaction_id = '' ) {
1293
1294
	if ( empty( $payment_id ) || empty( $transaction_id ) ) {
1295
		return false;
1296
	}
1297
1298
	$transaction_id = apply_filters( 'give_set_payment_transaction_id', $transaction_id, $payment_id );
1299
1300
	return give_update_payment_meta( $payment_id, '_give_payment_transaction_id', $transaction_id );
1301
}
1302
1303
/**
1304
 * Retrieve the donation ID based on the key
1305
 *
1306
 * @param string $key  the key to search for.
1307
 *
1308
 * @since 1.0
1309
 * @global object $wpdb Used to query the database using the WordPress Database API.
1310
 *
1311
 * @return int $purchase Donation ID.
1312
 */
1313 View Code Duplication
function give_get_donation_id_by_key( $key ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1314
	global $wpdb;
1315
1316
	$meta_table = __give_v20_bc_table_details( 'payment' );
1317
1318
	$purchase = $wpdb->get_var(
1319
		$wpdb->prepare(
1320
			"
1321
				SELECT {$meta_table['column']['id']}
1322
				FROM {$meta_table['name']}
1323
				WHERE meta_key = '_give_payment_purchase_key'
1324
				AND meta_value = %s
1325
				ORDER BY {$meta_table['column']['id']} DESC
1326
				LIMIT 1
1327
				",
1328
			$key
1329
		)
1330
	);
1331
1332
	if ( $purchase != null ) {
1333
		return $purchase;
1334
	}
1335
1336
	return 0;
1337
}
1338
1339
1340
/**
1341
 * Retrieve the donation ID based on the transaction ID
1342
 *
1343
 * @param string $key  The transaction ID to search for.
1344
 *
1345
 * @since 1.3
1346
 * @global object $wpdb Used to query the database using the WordPress Database API.
1347
 *
1348
 * @return int $purchase Donation ID.
1349
 */
1350 View Code Duplication
function give_get_purchase_id_by_transaction_id( $key ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1351
	global $wpdb;
1352
	$meta_table = __give_v20_bc_table_details( 'payment' );
1353
1354
	$purchase = $wpdb->get_var( $wpdb->prepare( "SELECT {$meta_table['column']['id']} FROM {$meta_table['name']} WHERE meta_key = '_give_payment_transaction_id' AND meta_value = %s LIMIT 1", $key ) );
1355
1356
	if ( $purchase != null ) {
1357
		return $purchase;
1358
	}
1359
1360
	return 0;
1361
}
1362
1363
/**
1364
 * Retrieve all notes attached to a donation
1365
 *
1366
 * @param int    $payment_id The donation ID to retrieve notes for.
1367
 * @param string $search     Search for notes that contain a search term.
1368
 *
1369
 * @since 1.0
1370
 *
1371
 * @return array $notes Donation Notes
1372
 */
1373
function give_get_payment_notes( $payment_id = 0, $search = '' ) {
1374
	return Give_Comment::get( $payment_id,'payment', array(), $search );
1375
}
1376
1377
1378
/**
1379
 * Add a note to a payment
1380
 *
1381
 * @param int    $payment_id The payment ID to store a note for.
1382
 * @param string $note       The note to store.
1383
 *
1384
 * @since 1.0
1385
 *
1386
 * @return int The new note ID
1387
 */
1388
function give_insert_payment_note( $payment_id = 0, $note = '' ) {
1389
	return Give_Comment::add( $payment_id, $note, 'payment' );
1390
}
1391
1392
/**
1393
 * Deletes a payment note
1394
 *
1395
 * @param int $comment_id The comment ID to delete.
1396
 * @param int $payment_id The payment ID the note is connected to.
1397
 *
1398
 * @since 1.0
1399
 *
1400
 * @return bool True on success, false otherwise.
1401
 */
1402
function give_delete_payment_note( $comment_id = 0, $payment_id = 0 ) {
1403
	return Give_Comment::delete( $comment_id, $payment_id, 'payment' );
1404
}
1405
1406
/**
1407
 * Gets the payment note HTML
1408
 *
1409
 * @param object|int $note       The comment object or ID.
1410
 * @param int        $payment_id The payment ID the note is connected to.
1411
 *
1412
 * @since 1.0
1413
 *
1414
 * @return string
1415
 */
1416
function give_get_payment_note_html( $note, $payment_id = 0 ) {
1417
1418
	if ( is_numeric( $note ) ) {
1419
		if ( ! give_has_upgrade_completed( 'v230_move_donor_note' ) ) {
1420
			$note = get_comment( $note );
1421
		} else{
1422
			$note = Give()->comment->db->get( $note );
1423
		}
1424
	}
1425
1426
	if ( ! empty( $note->user_id ) ) {
1427
		$user = get_userdata( $note->user_id );
1428
		$user = $user->display_name;
1429
	} else {
1430
		$user = __( 'System', 'give' );
1431
	}
1432
1433
	$date_format = give_date_format() . ', ' . get_option( 'time_format' );
1434
1435
	$delete_note_url = wp_nonce_url(
1436
		add_query_arg(
1437
			array(
1438
				'give-action' => 'delete_payment_note',
1439
				'note_id'     => $note->comment_ID,
1440
				'payment_id'  => $payment_id,
1441
			)
1442
		), 'give_delete_payment_note_' . $note->comment_ID
1443
	);
1444
1445
	$note_html  = '<div class="give-payment-note" id="give-payment-note-' . $note->comment_ID . '">';
1446
	$note_html .= '<p>';
1447
	$note_html .= '<strong>' . $user . '</strong>&nbsp;&ndash;&nbsp;<span style="color:#aaa;font-style:italic;">' . date_i18n( $date_format, strtotime( $note->comment_date ) ) . '</span><br/>';
1448
	$note_html .= nl2br( $note->comment_content );
1449
	$note_html .= '&nbsp;&ndash;&nbsp;<a href="' . esc_url( $delete_note_url ) . '" class="give-delete-payment-note" data-note-id="' . absint( $note->comment_ID ) . '" data-payment-id="' . absint( $payment_id ) . '" aria-label="' . __( 'Delete this donation note.', 'give' ) . '">' . __( 'Delete', 'give' ) . '</a>';
1450
	$note_html .= '</p>';
1451
	$note_html .= '</div>';
1452
1453
	return $note_html;
1454
1455
}
1456
1457
1458
/**
1459
 * Filter where older than one week
1460
 *
1461
 * @param string $where Where clause.
1462
 *
1463
 * @access public
1464
 * @since  1.0
1465
 *
1466
 * @return string $where Modified where clause.
1467
 */
1468
function give_filter_where_older_than_week( $where = '' ) {
1469
	// Payments older than one week.
1470
	$start  = date( 'Y-m-d', strtotime( '-7 days' ) );
1471
	$where .= " AND post_date <= '{$start}'";
1472
1473
	return $where;
1474
}
1475
1476
1477
/**
1478
 * Get Payment Form ID.
1479
 *
1480
 * Retrieves the form title and appends the level name if present.
1481
 *
1482
 * @param int|Give_Payment $donation_id Donation Data Object.
1483
 * @param array            $args     a. only_level = If set to true will only return the level name if multi-level
1484
 *                                   enabled. b. separator  = The separator between the Form Title and the Donation
1485
 *                                   Level.
1486
 *
1487
 * @since 1.5
1488
 *
1489
 * @return string $form_title Returns the full title if $only_level is false, otherwise returns the levels title.
1490
 */
1491
function give_get_donation_form_title( $donation_id, $args = array() ) {
1492
	// Backward compatibility.
1493
	if ( ! is_numeric( $donation_id ) && $donation_id instanceof Give_Payment ) {
1494
		$donation_id = $donation_id->ID;
1495
	}
1496
1497
	if ( ! $donation_id ) {
1498
		return '';
1499
	}
1500
1501
	$defaults = array(
1502
		'only_level' => false,
1503
		'separator'  => '',
1504
	);
1505
1506
	$args = wp_parse_args( $args, $defaults );
1507
1508
	$form_id     = give_get_payment_form_id( $donation_id );
1509
	$price_id    = give_get_meta( $donation_id, '_give_payment_price_id', true );
1510
	$form_title  = give_get_meta( $donation_id, '_give_payment_form_title', true );
1511
	$only_level  = $args['only_level'];
1512
	$separator   = $args['separator'];
1513
	$level_label = '';
1514
1515
	$cache_key = Give_Cache::get_key(
1516
		'give_forms',
1517
		array(
1518
			$form_id,
1519
			$price_id,
1520
			$form_title,
1521
			$only_level,
1522
			$separator,
1523
		), false
1524
	);
1525
1526
	$form_title_html = Give_Cache::get_db_query( $cache_key );
1527
1528
	if ( is_null( $form_title_html ) ) {
1529
		if ( true === $only_level ) {
1530
			$form_title = '';
1531
		}
1532
1533
		$form_title_html = $form_title;
1534
1535
		if ( 'custom' === $price_id ) {
1536
1537
			$custom_amount_text = give_get_meta( $form_id, '_give_custom_amount_text', true );
1538
			$level_label        = ! empty( $custom_amount_text ) ? $custom_amount_text : __( 'Custom Amount', 'give' );
1539
1540
			// Show custom amount level only in backend otherwise hide it.
1541
			if ( 'set' === give_get_meta( $form_id, '_give_price_option', true ) && ! is_admin() ) {
1542
				$level_label = '';
1543
			}
1544
		} elseif ( give_has_variable_prices( $form_id ) ) {
1545
			$level_label = give_get_price_option_name( $form_id, $price_id, $donation_id, false );
1546
		}
1547
1548
		// Only add separator if there is a form title.
1549
		if (
1550
			! empty( $form_title_html ) &&
1551
			! empty( $level_label )
1552
		) {
1553
			$form_title_html .= " {$separator} ";
1554
		}
1555
1556
		$form_title_html .= "<span class=\"donation-level-text-wrap\">{$level_label}</span>";
1557
		Give_Cache::set_db_query( $cache_key, $form_title_html );
1558
	}
1559
1560
	/**
1561
	 * Filter form title with level html
1562
	 *
1563
	 * @since 1.0
1564
	 * @todo: remove third param after 2.1.0
1565
	 */
1566
	return apply_filters( 'give_get_donation_form_title', $form_title_html, $donation_id, '' );
1567
}
1568
1569
/**
1570
 * Get Price ID
1571
 *
1572
 * Retrieves the Price ID when provided a proper form ID and price (donation) total
1573
 *
1574
 * @param int    $form_id Form ID.
1575
 * @param string $price   Donation Amount.
1576
 *
1577
 * @return string $price_id
1578
 */
1579
function give_get_price_id( $form_id, $price ) {
1580
	$price_id = null;
1581
1582
	if ( give_has_variable_prices( $form_id ) ) {
1583
1584
		$levels = give_get_meta( $form_id, '_give_donation_levels', true );
1585
1586
		foreach ( $levels as $level ) {
1587
1588
			$level_amount = give_maybe_sanitize_amount( $level['_give_amount'] );
1589
1590
			// Check that this indeed the recurring price.
1591
			if ( $level_amount == $price ) {
1592
1593
				$price_id = $level['_give_id']['level_id'];
1594
				break;
1595
1596
			}
1597
		}
1598
1599
		if ( is_null( $price_id ) && give_is_custom_price_mode( $form_id ) ) {
1600
			$price_id = 'custom';
1601
		}
1602
	}
1603
1604
	// Price ID must be numeric or string.
1605
	$price_id = ! is_numeric( $price_id ) && ! is_string( $price_id ) ? 0 : $price_id;
1606
1607
	/**
1608
	 * Filter the price id
1609
	 *
1610
	 * @since 2.0
1611
	 *
1612
	 * @param string $price_id
1613
	 * @param int    $form_id
1614
	 */
1615
	return apply_filters( 'give_get_price_id', $price_id, $form_id );
1616
}
1617
1618
/**
1619
 * Get/Print give form dropdown html
1620
 *
1621
 * This function is wrapper to public method forms_dropdown of Give_HTML_Elements class to get/print form dropdown html.
1622
 * Give_HTML_Elements is defined in includes/class-give-html-elements.php.
1623
 *
1624
 * @param array $args Arguments for form dropdown.
1625
 * @param bool  $echo This parameter decides if print form dropdown html output or not.
1626
 *
1627
 * @since 1.6
1628
 *
1629
 * @return string
1630
 */
1631
function give_get_form_dropdown( $args = array(), $echo = false ) {
1632
	$form_dropdown_html = Give()->html->forms_dropdown( $args );
1633
1634
	if ( ! $echo ) {
1635
		return $form_dropdown_html;
1636
	}
1637
1638
	echo $form_dropdown_html;
1639
}
1640
1641
/**
1642
 * Get/Print give form variable price dropdown html
1643
 *
1644
 * @param array $args Arguments for form dropdown.
1645
 * @param bool  $echo This parameter decide if print form dropdown html output or not.
1646
 *
1647
 * @since 1.6
1648
 *
1649
 * @return string|bool
1650
 */
1651
function give_get_form_variable_price_dropdown( $args = array(), $echo = false ) {
1652
1653
	// Check for give form id.
1654
	if ( empty( $args['id'] ) ) {
1655
		return false;
1656
	}
1657
1658
	$form = new Give_Donate_Form( $args['id'] );
1659
1660
	// Check if form has variable prices or not.
1661
	if ( ! $form->ID || ! $form->has_variable_prices() ) {
1662
		return false;
1663
	}
1664
1665
	$variable_prices        = $form->get_prices();
1666
	$variable_price_options = array();
1667
1668
	// Check if multi donation form support custom donation or not.
1669
	if ( $form->is_custom_price_mode() ) {
1670
		$variable_price_options['custom'] = _x( 'Custom', 'custom donation dropdown item', 'give' );
1671
	}
1672
1673
	// Get variable price and ID from variable price array.
1674
	foreach ( $variable_prices as $variable_price ) {
1675
		$variable_price_options[ $variable_price['_give_id']['level_id'] ] = ! empty( $variable_price['_give_text'] ) ? $variable_price['_give_text'] : give_currency_filter( give_format_amount( $variable_price['_give_amount'], array( 'sanitize' => false ) ) );
1676
	}
1677
1678
	// Update options.
1679
	$args = array_merge(
1680
		$args, array(
1681
			'options' => $variable_price_options,
1682
		)
1683
	);
1684
1685
	// Generate select html.
1686
	$form_dropdown_html = Give()->html->select( $args );
1687
1688
	if ( ! $echo ) {
1689
		return $form_dropdown_html;
1690
	}
1691
1692
	echo $form_dropdown_html;
1693
}
1694
1695
/**
1696
 * Get the price_id from the payment meta.
1697
 *
1698
 * Some gateways use `give_price_id` and others were using just `price_id`;
1699
 * This checks for the difference and falls back to retrieving it from the form as a last resort.
1700
 *
1701
 * @param array $payment_meta Payment Meta.
1702
 *
1703
 * @since 1.8.6
1704
 *
1705
 * @return string
1706
 */
1707
function give_get_payment_meta_price_id( $payment_meta ) {
1708
1709
	if ( isset( $payment_meta['give_price_id'] ) ) {
1710
		$price_id = $payment_meta['give_price_id'];
1711
	} elseif ( isset( $payment_meta['price_id'] ) ) {
1712
		$price_id = $payment_meta['price_id'];
1713
	} else {
1714
		$price_id = give_get_price_id( $payment_meta['give_form_id'], $payment_meta['price'] );
1715
	}
1716
1717
	/**
1718
	 * Filter the price id
1719
	 *
1720
	 * @since 1.8.6
1721
	 *
1722
	 * @param string $price_id
1723
	 * @param array  $payment_meta
1724
	 */
1725
	return apply_filters( 'give_get_payment_meta_price_id', $price_id, $payment_meta );
1726
1727
}
1728
1729
1730
/**
1731
 * Get payment total amount
1732
 *
1733
 * @since 2.1.0
1734
 *
1735
 * @param int $payment_id
1736
 *
1737
 * @return float
1738
 */
1739
function give_get_payment_total( $payment_id = 0 ) {
1740
	return round(
1741
		floatval( give_get_meta( $payment_id, '_give_payment_total', true ) ),
1742
		give_get_price_decimals( $payment_id )
1743
	);
1744
}
1745
1746
/**
1747
 * Get donation address
1748
 *
1749
 * since 2.1.0
1750
 *
1751
 * @param int $donation_id
1752
 *
1753
 * @return array
1754
 */
1755
function give_get_donation_address( $donation_id ) {
1756
	$address['line1']   = give_get_meta( $donation_id, '_give_donor_billing_address1', true, '' );
0 ignored issues
show
'' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1757
	$address['line2']   = give_get_meta( $donation_id, '_give_donor_billing_address2', true, '' );
0 ignored issues
show
'' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1758
	$address['city']    = give_get_meta( $donation_id, '_give_donor_billing_city', true, '' );
0 ignored issues
show
'' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1759
	$address['state']   = give_get_meta( $donation_id, '_give_donor_billing_state', true, '' );
0 ignored issues
show
'' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1760
	$address['zip']     = give_get_meta( $donation_id, '_give_donor_billing_zip', true, '' );
0 ignored issues
show
'' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1761
	$address['country'] = give_get_meta( $donation_id, '_give_donor_billing_country', true, '' );
0 ignored issues
show
'' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1762
1763
	return $address;
1764
}
1765
1766
1767
/**
1768
 *  Check if donation completed or not
1769
 *
1770
 * @since 2.1.0
1771
 *
1772
 * @param int $donation_id
1773
 *
1774
 * @return bool
1775
 */
1776
function give_is_donation_completed( $donation_id ) {
1777
	global $wpdb;
1778
1779
	/**
1780
	 * Filter the flag
1781
	 *
1782
	 * @since 2.1.0
1783
	 *
1784
	 * @param bool
1785
	 * @param int $donation_id
1786
	 */
1787
	return apply_filters(
1788
		'give_is_donation_completed', (bool) $wpdb->get_var(
1789
			$wpdb->prepare(
1790
				"
1791
				SELECT meta_value
1792
				FROM {$wpdb->donationmeta}
1793
				WHERE EXISTS (
1794
					SELECT ID
1795
					FROM {$wpdb->posts}
1796
					WHERE post_status=%s
1797
					AND ID=%d
1798
				)
1799
				AND {$wpdb->donationmeta}.meta_key=%s
1800
				",
1801
				'publish',
1802
				$donation_id,
1803
				'_give_completed_date'
1804
			)
1805
		), $donation_id
1806
	);
1807
}
1808
1809
/**
1810
 * Verify if donation anonymous or not
1811
 *
1812
 * @since 2.2.1
1813
 * @param $donation_id
1814
 *
1815
 * @return bool
1816
 */
1817
function give_is_anonymous_donation( $donation_id ) {
1818
	$value = false;
1819
1820
	if( (int) give_get_meta( $donation_id, '_give_anonymous_donation', true ) ){
1821
		$value = true;
1822
	}
1823
1824
	return $value;
1825
}
1826