Issues (850)

Security Analysis    4 potential vulnerabilities

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

  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.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  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.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  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.
  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.
  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.
  Code Injection (1)
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection (2)
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  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.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  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.
  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.
  Cross-Site Scripting (1)
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.
  Header Injection
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.

payments/class-getpaid-payment-form-submission.php (5 issues)

Labels
Severity
1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * Payment form submission class
8
 *
9
 */
10
class GetPaid_Payment_Form_Submission {
11
12
    /**
13
	 * Submission ID
14
	 *
15
	 * @var string
16
	 */
17
	public $id = null;
18
19
	/**
20
	 * The raw submission data.
21
	 *
22
	 * @var array
23
	 */
24
	protected $data = null;
25
26
	/**
27
	 * Submission totals
28
	 *
29
	 * @var array
30
	 */
31
	protected $totals = array(
32
33
		'subtotal' => array(
34
			'initial'   => 0,
35
			'recurring' => 0,
36
		),
37
38
		'discount' => array(
39
			'initial'   => 0,
40
			'recurring' => 0,
41
		),
42
43
		'fees'     => array(
44
			'initial'   => 0,
45
			'recurring' => 0,
46
		),
47
48
		'taxes'    => array(
49
			'initial'   => 0,
50
			'recurring' => 0,
51
		),
52
53
		'shipping' => array(
54
			'initial'   => 0,
55
			'recurring' => 0,
56
		),
57
58
	);
59
60
	/**
61
	 * Sets the associated payment form.
62
	 *
63
	 * @var GetPaid_Payment_Form
64
	 */
65
    protected $payment_form = null;
66
67
    /**
68
	 * The country for the submission.
69
	 *
70
	 * @var string
71
	 */
72
	public $country = null;
73
74
    /**
75
	 * The state for the submission.
76
	 *
77
	 * @since 1.0.19
78
	 * @var string
79
	 */
80
	public $state = null;
81
82
	/**
83
	 * The invoice associated with the submission.
84
	 *
85
	 * @var WPInv_Invoice
86
	 */
87
	protected $invoice = null;
88
89
	/**
90
	 * The recurring item for the submission.
91
	 *
92
	 * @var int
93
	 */
94
	public $has_recurring = 0;
95
96
	/**
97
	 * An array of fees for the submission.
98
	 *
99
	 * @var array
100
	 */
101
	protected $fees = array();
102
103
	/**
104
	 * An array of discounts for the submission.
105
	 *
106
	 * @var array
107
	 */
108
	protected $discounts = array();
109
110
	/**
111
	 * An array of taxes for the submission.
112
	 *
113
	 * @var array
114
	 */
115
	protected $taxes = array();
116
117
	/**
118
	 * An array of items for the submission.
119
	 *
120
	 * @var GetPaid_Form_Item[]
121
	 */
122
	protected $items = array();
123
124
	/**
125
	 * The last error.
126
	 *
127
	 * @var string
128
	 */
129
	public $last_error = null;
130
131
	/**
132
	 * The last error code.
133
	 *
134
	 * @var string
135
	 */
136
	public $last_error_code = null;
137
138
    /**
139
	 * Class constructor.
140
	 *
141
	 */
142
	public function __construct() {
143
144
		// Set the state and country to the default state and country.
145
		$this->country = wpinv_default_billing_country();
146
		$this->state   = wpinv_get_default_state();
147
148
		// Do we have an actual submission?
149
		if ( isset( $_POST['getpaid_payment_form_submission'] ) ) {
150
			$this->load_data( wp_kses_post_deep( wp_unslash( $_POST ) ) );
151
		}
152
153
	}
154
155
	/**
156
	 * Loads submission data.
157
	 *
158
	 * @param array $data
159
	 */
160
	public function load_data( $data ) {
161
162
		// Allow plugins to filter the data.
163
		$data       = apply_filters( 'getpaid_submission_data', $data, $this );
164
165
		// Cache it...
166
		$this->data = $data;
167
168
		// Then generate a unique id from the data.
169
		$this->id   = md5( wp_json_encode( $data ) );
0 ignored issues
show
It seems like wp_json_encode($data) can also be of type false; however, parameter $string of md5() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

169
		$this->id   = md5( /** @scrutinizer ignore-type */ wp_json_encode( $data ) );
Loading history...
170
171
		// Finally, process the submission.
172
		try {
173
174
			// Each process is passed an instance of the class (with reference)
175
			// and should throw an Exception whenever it encounters one.
176
			$processors = apply_filters(
177
				'getpaid_payment_form_submission_processors',
178
				array(
179
					array( $this, 'process_payment_form' ),
180
					array( $this, 'process_invoice' ),
181
					array( $this, 'process_fees' ),
182
					array( $this, 'process_items' ),
183
					array( $this, 'process_discount' ),
184
					array( $this, 'process_taxes' ),
185
				),
186
				$this
187
			);
188
189
			foreach ( $processors as $processor ) {
190
				call_user_func_array( $processor, array( &$this ) );
191
			}
192
		} catch ( GetPaid_Payment_Exception $e ) {
193
			$this->last_error      = $e->getMessage();
194
			$this->last_error_code = $e->getErrorCode();
195
		} catch ( Exception $e ) {
196
			$this->last_error      = $e->getMessage();
197
			$this->last_error_code = $e->getCode();
198
		}
199
200
		// Fired when we are done processing a submission.
201
		do_action_ref_array( 'getpaid_process_submission', array( &$this ) );
202
203
	}
204
205
	/*
206
	|--------------------------------------------------------------------------
207
	| Payment Forms.
208
	|--------------------------------------------------------------------------
209
	|
210
	| Functions for dealing with the submission's payment form. Ensure that the
211
	| submission has an active payment form etc.
212
    */
213
214
	/**
215
	 * Prepares the submission's payment form.
216
	 *
217
	 * @since 1.0.19
218
	 */
219
	public function process_payment_form() {
220
221
		// Every submission needs an active payment form.
222
		if ( empty( $this->data['form_id'] ) ) {
223
			throw new Exception( __( 'Missing payment form', 'invoicing' ) );
224
		}
225
226
		// Fetch the payment form.
227
		$this->payment_form = new GetPaid_Payment_Form( $this->data['form_id'] );
228
229
		if ( ! $this->payment_form->is_active() ) {
230
			throw new Exception( __( 'Payment form not active', 'invoicing' ) );
231
		}
232
233
		do_action_ref_array( 'getpaid_submissions_process_payment_form', array( &$this ) );
234
	}
235
236
    /**
237
	 * Returns the payment form.
238
	 *
239
	 * @since 1.0.19
240
	 * @return GetPaid_Payment_Form
241
	 */
242
	public function get_payment_form() {
243
		return $this->payment_form;
244
	}
245
246
	/*
247
	|--------------------------------------------------------------------------
248
	| Invoices.
249
	|--------------------------------------------------------------------------
250
	|
251
	| Functions for dealing with the submission's invoice. Some submissions
252
	| might be for an existing invoice.
253
	*/
254
255
	/**
256
	 * Prepares the submission's invoice.
257
	 *
258
	 * @since 1.0.19
259
	 */
260
	public function process_invoice() {
261
262
		// Abort if there is no invoice.
263
		if ( empty( $this->data['invoice_id'] ) ) {
264
265
			// Check if we are resuming a payment.
266
			if ( empty( $this->data['maybe_use_invoice'] ) ) {
267
				return;
268
			}
269
270
			$invoice = wpinv_get_invoice( $this->data['maybe_use_invoice'] );
271
			if ( empty( $invoice ) || ! $invoice->has_status( 'draft, auto-draft, wpi-pending' ) ) {
272
				return;
273
			}
274
		}
275
276
		// If the submission is for an existing invoice, ensure that it exists
277
		// and that it is not paid for.
278
		if ( empty( $invoice ) ) {
279
			$invoice = wpinv_get_invoice( $this->data['invoice_id'] );
280
		}
281
282
        if ( empty( $invoice ) ) {
283
			throw new Exception( __( 'Invalid invoice', 'invoicing' ) );
284
		}
285
286
		if ( $invoice->is_paid() ) {
287
			throw new Exception( __( 'This invoice is already paid for.', 'invoicing' ) );
288
		}
289
290
		$this->payment_form->invoice = $invoice;
291
		if ( ! $this->payment_form->is_default() ) {
292
293
			$items    = array();
294
			$item_ids = array();
295
296
			foreach ( $invoice->get_items() as $item ) {
297
				if ( ! in_array( $item->get_id(), $item_ids ) ) {
298
					$item_ids[] = $item->get_id();
299
					$items[]    = $item;
300
				}
301
			}
302
303
			foreach ( $this->payment_form->get_items() as $item ) {
304
				if ( ! in_array( $item->get_id(), $item_ids ) ) {
305
					$item_ids[] = $item->get_id();
306
					$items[]    = $item;
307
				}
308
			}
309
310
			$this->payment_form->set_items( $items );
311
312
		} else {
313
			$this->payment_form->set_items( $invoice->get_items() );
314
		}
315
316
		$this->country = $invoice->get_country();
317
		$this->state   = $invoice->get_state();
318
		$this->invoice = $invoice;
319
320
		do_action_ref_array( 'getpaid_submissions_process_invoice', array( &$this ) );
321
	}
322
323
	/**
324
	 * Returns the associated invoice.
325
	 *
326
	 * @since 1.0.19
327
	 * @return WPInv_Invoice
328
	 */
329
	public function get_invoice() {
330
		return $this->invoice;
331
	}
332
333
	/**
334
	 * Checks whether there is an invoice associated with this submission.
335
	 *
336
	 * @since 1.0.19
337
	 * @return bool
338
	 */
339
	public function has_invoice() {
340
		return ! empty( $this->invoice );
341
	}
342
343
	/*
344
	|--------------------------------------------------------------------------
345
	| Items.
346
	|--------------------------------------------------------------------------
347
	|
348
	| Functions for dealing with the submission's items. Submissions can only have one
349
	| recurring item. But can have an unlimited number of non-recurring items.
350
	*/
351
352
	/**
353
	 * Prepares the submission's items.
354
	 *
355
	 * @since 1.0.19
356
	 */
357
	public function process_items() {
358
359
		$processor = new GetPaid_Payment_Form_Submission_Items( $this );
360
361
		foreach ( $processor->items as $item ) {
362
			$this->add_item( $item );
363
		}
364
365
		do_action_ref_array( 'getpaid_submissions_process_items', array( &$this ) );
366
	}
367
368
	/**
369
	 * Adds an item to the submission.
370
	 *
371
	 * @since 1.0.19
372
	 * @param GetPaid_Form_Item $item
373
	 */
374
	public function add_item( $item ) {
375
376
		// Make sure that it is available for purchase.
377
		if ( ! $item->can_purchase() || isset( $this->items[ $item->get_id() ] ) ) {
378
			return;
379
		}
380
381
		// Each submission can only contain one recurring item.
382
		if ( $item->is_recurring() ) {
383
			$this->has_recurring = $item->get_id();
384
		}
385
386
		// Update the items and totals.
387
		$this->items[ $item->get_id() ]         = $item;
388
		$this->totals['subtotal']['initial']   += $item->get_sub_total();
389
		$this->totals['subtotal']['recurring'] += $item->get_recurring_sub_total();
390
391
	}
392
393
	/**
394
	 * Removes a specific item.
395
	 *
396
	 * You should not call this method after the discounts and taxes
397
	 * have been calculated.
398
	 *
399
	 * @since 1.0.19
400
	 */
401
	public function remove_item( $item_id ) {
402
403
		if ( isset( $this->items[ $item_id ] ) ) {
404
			$this->totals['subtotal']['initial']   -= $this->items[ $item_id ]->get_sub_total();
405
			$this->totals['subtotal']['recurring'] -= $this->items[ $item_id ]->get_recurring_sub_total();
406
407
			if ( $this->items[ $item_id ]->is_recurring() ) {
408
				$this->has_recurring = 0;
409
			}
410
411
			unset( $this->items[ $item_id ] );
412
		}
413
414
	}
415
416
	/**
417
	 * Returns the subtotal.
418
	 *
419
	 * @since 1.0.19
420
	 */
421
	public function get_subtotal() {
422
423
		if ( wpinv_prices_include_tax() ) {
424
			return $this->totals['subtotal']['initial'] - $this->totals['taxes']['initial'];
425
		}
426
427
		return $this->totals['subtotal']['initial'];
428
	}
429
430
	/**
431
	 * Returns the recurring subtotal.
432
	 *
433
	 * @since 1.0.19
434
	 */
435
	public function get_recurring_subtotal() {
436
437
		if ( wpinv_prices_include_tax() ) {
438
			return $this->totals['subtotal']['recurring'] - $this->totals['taxes']['recurring'];
439
		}
440
441
		return $this->totals['subtotal']['recurring'];
442
	}
443
444
	/**
445
	 * Returns all items.
446
	 *
447
	 * @since 1.0.19
448
	 * @return GetPaid_Form_Item[]
449
	 */
450
	public function get_items() {
451
		return $this->items;
452
	}
453
454
	/**
455
	 * Checks if there's a single subscription group in the submission.
456
	 *
457
	 * @since 2.3.0
458
	 * @return bool
459
	 */
460
	public function has_subscription_group() {
461
		return $this->has_recurring && getpaid_should_group_subscriptions( $this ) && 1 == count( getpaid_get_subscription_groups( $this ) );
462
	}
463
464
	/**
465
	 * Checks if there are multipe subscription groups in the submission.
466
	 *
467
	 * @since 2.3.0
468
	 * @return bool
469
	 */
470
	public function has_multiple_subscription_groups() {
471
		return $this->has_recurring && 1 < count( getpaid_get_subscription_groups( $this ) );
472
	}
473
474
	/*
475
	|--------------------------------------------------------------------------
476
	| Taxes
477
	|--------------------------------------------------------------------------
478
	|
479
	| Functions for dealing with submission taxes. Taxes can be recurring
480
	| or only one-time.
481
    */
482
483
	/**
484
	 * Prepares the submission's taxes.
485
	 *
486
	 * @since 1.0.19
487
	 */
488
	public function process_taxes() {
489
490
		// Abort if we're not using taxes.
491
		if ( ! $this->use_taxes() ) {
492
			return;
493
		}
494
495
		// If a custom country && state has been passed in, use it to calculate taxes.
496
		$country = $this->get_field( 'wpinv_country', 'billing' );
0 ignored issues
show
Are you sure the assignment to $country is correct as $this->get_field('wpinv_country', 'billing') targeting GetPaid_Payment_Form_Submission::get_field() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
497
		if ( ! empty( $country ) ) {
498
			$this->country = $country;
499
		}
500
501
		$state = $this->get_field( 'wpinv_state', 'billing' );
0 ignored issues
show
Are you sure the assignment to $state is correct as $this->get_field('wpinv_state', 'billing') targeting GetPaid_Payment_Form_Submission::get_field() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
502
		if ( ! empty( $state ) ) {
503
			$this->state = $state;
504
		}
505
506
		// Confirm if the provided country and the ip country are similar.
507
		$address_confirmed = $this->get_field( 'confirm-address' );
0 ignored issues
show
Are you sure the assignment to $address_confirmed is correct as $this->get_field('confirm-address') targeting GetPaid_Payment_Form_Submission::get_field() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
508
		if ( isset( $_POST['billing']['country'] ) && wpinv_should_validate_vat_number() && getpaid_get_ip_country() != $this->country && empty( $address_confirmed ) ) {
509
			throw new Exception( __( 'The country of your current location must be the same as the country of your billing location or you must confirm the billing address is your home country.', 'invoicing' ) );
510
		}
511
512
		// Abort if the country is not taxable.
513
		if ( ! wpinv_is_country_taxable( $this->country ) ) {
514
			return;
515
		}
516
517
		$processor = new GetPaid_Payment_Form_Submission_Taxes( $this );
518
519
		foreach ( $processor->taxes as $tax ) {
520
			$this->add_tax( $tax );
521
		}
522
523
		do_action_ref_array( 'getpaid_submissions_process_taxes', array( &$this ) );
524
	}
525
526
	/**
527
	 * Adds a tax to the submission.
528
	 *
529
	 * @param array $tax An array of tax details. name, initial_tax, and recurring_tax are required.
530
	 * @since 1.0.19
531
	 */
532
	public function add_tax( $tax ) {
533
534
		if ( wpinv_round_tax_per_tax_rate() ) {
535
			$tax['initial_tax']   = wpinv_round_amount( $tax['initial_tax'] );
536
			$tax['recurring_tax'] = wpinv_round_amount( $tax['recurring_tax'] );
537
		}
538
539
		$this->taxes[ $tax['name'] ]         = $tax;
540
		$this->totals['taxes']['initial']   += wpinv_sanitize_amount( $tax['initial_tax'] );
541
		$this->totals['taxes']['recurring'] += wpinv_sanitize_amount( $tax['recurring_tax'] );
542
543
	}
544
545
	/**
546
	 * Removes a specific tax.
547
	 *
548
	 * @since 1.0.19
549
	 */
550
	public function remove_tax( $tax_name ) {
551
552
		if ( isset( $this->taxes[ $tax_name ] ) ) {
553
			$this->totals['taxes']['initial']   -= $this->taxes[ $tax_name ]['initial_tax'];
554
			$this->totals['taxes']['recurring'] -= $this->taxes[ $tax_name ]['recurring_tax'];
555
			unset( $this->taxes[ $tax_name ] );
556
		}
557
558
	}
559
560
	/**
561
	 * Whether or not we'll use taxes for the submission.
562
	 *
563
	 * @since 1.0.19
564
	 */
565
	public function use_taxes() {
566
567
		$use_taxes = wpinv_use_taxes();
568
569
		if ( $this->has_invoice() && ! $this->invoice->is_taxable() ) {
570
			$use_taxes = false;
571
		}
572
573
		return apply_filters( 'getpaid_submission_use_taxes', $use_taxes, $this );
574
575
	}
576
577
	/**
578
	 * Returns the tax.
579
	 *
580
	 * @since 1.0.19
581
	 */
582
	public function get_tax() {
583
		return $this->totals['taxes']['initial'];
584
	}
585
586
	/**
587
	 * Returns the recurring tax.
588
	 *
589
	 * @since 1.0.19
590
	 */
591
	public function get_recurring_tax() {
592
		return $this->totals['taxes']['recurring'];
593
	}
594
595
	/**
596
	 * Returns all taxes.
597
	 *
598
	 * @since 1.0.19
599
	 */
600
	public function get_taxes() {
601
		return $this->taxes;
602
	}
603
604
	/*
605
	|--------------------------------------------------------------------------
606
	| Discounts
607
	|--------------------------------------------------------------------------
608
	|
609
	| Functions for dealing with submission discounts. Discounts can be recurring
610
	| or only one-time. They also do not have to come from a discount code.
611
    */
612
613
	/**
614
	 * Prepares the submission's discount.
615
	 *
616
	 * @since 1.0.19
617
	 */
618
	public function process_discount() {
619
620
		$initial_total    = $this->get_subtotal() + $this->get_fee() + $this->get_tax();
621
		$recurring_total  = $this->get_recurring_subtotal() + $this->get_recurring_fee() + $this->get_recurring_tax();
622
		$processor        = new GetPaid_Payment_Form_Submission_Discount( $this, $initial_total, $recurring_total );
623
624
		foreach ( $processor->discounts as $discount ) {
625
			$this->add_discount( $discount );
626
		}
627
628
		do_action_ref_array( 'getpaid_submissions_process_discounts', array( &$this ) );
629
	}
630
631
	/**
632
	 * Adds a discount to the submission.
633
	 *
634
	 * @param array $discount An array of discount details. name, initial_discount, and recurring_discount are required. Include discount_code if the discount is from a discount code.
635
	 * @since 1.0.19
636
	 */
637
	public function add_discount( $discount ) {
638
		$this->discounts[ $discount['name'] ]   = $discount;
639
		$this->totals['discount']['initial']   += wpinv_sanitize_amount( $discount['initial_discount'] );
640
		$this->totals['discount']['recurring'] += wpinv_sanitize_amount( $discount['recurring_discount'] );
641
	}
642
643
	/**
644
	 * Removes a discount from the submission.
645
	 *
646
	 * @since 1.0.19
647
	 */
648
	public function remove_discount( $name ) {
649
650
		if ( isset( $this->discounts[ $name ] ) ) {
651
			$this->totals['discount']['initial']   -= $this->discounts[ $name ]['initial_discount'];
652
			$this->totals['discount']['recurring'] -= $this->discounts[ $name ]['recurring_discount'];
653
			unset( $this->discounts[ $name ] );
654
		}
655
656
	}
657
658
	/**
659
	 * Checks whether there is a discount code associated with this submission.
660
	 *
661
	 * @since 1.0.19
662
	 * @return bool
663
	 */
664
	public function has_discount_code() {
665
		return ! empty( $this->discounts['discount_code'] );
666
	}
667
668
	/**
669
	 * Returns the discount code.
670
	 *
671
	 * @since 1.0.19
672
	 * @return string
673
	 */
674
	public function get_discount_code() {
675
		return $this->has_discount_code() ? $this->discounts['discount_code']['discount_code'] : '';
676
	}
677
678
	/**
679
	 * Returns the discount.
680
	 *
681
	 * @since 1.0.19
682
	 */
683
	public function get_discount() {
684
		return $this->totals['discount']['initial'];
685
	}
686
687
	/**
688
	 * Returns the recurring discount.
689
	 *
690
	 * @since 1.0.19
691
	 */
692
	public function get_recurring_discount() {
693
		return $this->totals['discount']['recurring'];
694
	}
695
696
	/**
697
	 * Returns all discounts.
698
	 *
699
	 * @since 1.0.19
700
	 */
701
	public function get_discounts() {
702
		return $this->discounts;
703
	}
704
705
	/*
706
	|--------------------------------------------------------------------------
707
	| Fees
708
	|--------------------------------------------------------------------------
709
	|
710
	| Functions for dealing with submission fees. Fees can be recurring
711
    | or only one-time. Price input and Price select elements are treated as
712
	| fees.
713
    */
714
715
	/**
716
	 * Prepares the submission's fees.
717
	 *
718
	 * @since 1.0.19
719
	 */
720
	public function process_fees() {
721
722
		$fees_processor = new GetPaid_Payment_Form_Submission_Fees( $this );
723
724
		foreach ( $fees_processor->fees as $fee ) {
725
			$this->add_fee( $fee );
726
		}
727
728
		do_action_ref_array( 'getpaid_submissions_process_fees', array( &$this ) );
729
	}
730
731
	/**
732
	 * Adds a fee to the submission.
733
	 *
734
	 * @param array $fee An array of fee details. name, initial_fee, and recurring_fee are required.
735
	 * @since 1.0.19
736
	 */
737
	public function add_fee( $fee ) {
738
739
		if ( $fee['name'] == 'shipping' ) {
740
			$this->totals['shipping']['initial']   += wpinv_sanitize_amount( $fee['initial_fee'] );
741
			$this->totals['shipping']['recurring'] += wpinv_sanitize_amount( $fee['recurring_fee'] );
742
			return;
743
		}
744
745
		$this->fees[ $fee['name'] ]         = $fee;
746
		$this->totals['fees']['initial']   += wpinv_sanitize_amount( $fee['initial_fee'] );
747
		$this->totals['fees']['recurring'] += wpinv_sanitize_amount( $fee['recurring_fee'] );
748
749
	}
750
751
	/**
752
	 * Removes a fee from the submission.
753
	 *
754
	 * @since 1.0.19
755
	 */
756
	public function remove_fee( $name ) {
757
758
		if ( isset( $this->fees[ $name ] ) ) {
759
			$this->totals['fees']['initial']   -= $this->fees[ $name ]['initial_fee'];
760
			$this->totals['fees']['recurring'] -= $this->fees[ $name ]['recurring_fee'];
761
			unset( $this->fees[ $name ] );
762
		}
763
764
		if ( 'shipping' == $name ) {
765
			$this->totals['shipping']['initial']   = 0;
766
			$this->totals['shipping']['recurring'] = 0;
767
		}
768
769
	}
770
771
	/**
772
	 * Returns the fees.
773
	 *
774
	 * @since 1.0.19
775
	 */
776
	public function get_fee() {
777
		return $this->totals['fees']['initial'];
778
	}
779
780
	/**
781
	 * Returns the recurring fees.
782
	 *
783
	 * @since 1.0.19
784
	 */
785
	public function get_recurring_fee() {
786
		return $this->totals['fees']['recurring'];
787
	}
788
789
	/**
790
	 * Returns all fees.
791
	 *
792
	 * @since 1.0.19
793
	 */
794
	public function get_fees() {
795
		return $this->fees;
796
	}
797
798
	/**
799
	 * Checks if there are any fees for the form.
800
	 *
801
	 * @return bool
802
	 * @since 1.0.19
803
	 */
804
	public function has_fees() {
805
		return count( $this->fees ) !== 0;
806
	}
807
808
	/*
809
	|--------------------------------------------------------------------------
810
	| MISC
811
	|--------------------------------------------------------------------------
812
	|
813
	| Extra submission functions.
814
    */
815
816
	/**
817
	 * Returns the shipping amount.
818
	 *
819
	 * @since 1.0.19
820
	 */
821
	public function get_shipping() {
822
		return $this->totals['shipping']['initial'];
823
	}
824
825
	/**
826
	 * Returns the recurring shipping.
827
	 *
828
	 * @since 1.0.19
829
	 */
830
	public function get_recurring_shipping() {
831
		return $this->totals['shipping']['recurring'];
832
	}
833
834
	/**
835
	 * Checks if there are any shipping fees for the form.
836
	 *
837
	 * @return bool
838
	 * @since 1.0.19
839
	 */
840
	public function has_shipping() {
841
		return apply_filters( 'getpaid_payment_form_has_shipping', false, $this );
842
	}
843
844
	/**
845
	 * Checks if this is the initial fetch.
846
	 *
847
	 * @return bool
848
	 * @since 1.0.19
849
	 */
850
	public function is_initial_fetch() {
851
		return isset( $this->data['initial_state'] ) && empty( $this->data['initial_state'] );
852
	}
853
854
	/**
855
	 * Returns the total amount to collect for this submission.
856
	 *
857
	 * @since 1.0.19
858
	 */
859
	public function get_total() {
860
		$total = $this->get_subtotal() + $this->get_fee() + $this->get_tax() + $this->get_shipping() - $this->get_discount();
861
		return max( $total, 0 );
862
	}
863
864
	/**
865
	 * Returns the recurring total amount to collect for this submission.
866
	 *
867
	 * @since 1.0.19
868
	 */
869
	public function get_recurring_total() {
870
		$total = $this->get_recurring_subtotal() + $this->get_recurring_fee() + $this->get_recurring_tax() + $this->get_recurring_shipping() - $this->get_recurring_discount();
871
		return max( $total, 0 );
872
	}
873
874
	/**
875
	 * Whether payment details should be collected for this submission.
876
	 *
877
	 * @since 1.0.19
878
	 */
879
	public function should_collect_payment_details() {
880
		$initial   = $this->get_total();
881
		$recurring = $this->get_recurring_total();
882
883
		if ( $this->has_recurring == 0 ) {
884
			$recurring = 0;
885
		}
886
887
		$collect = $initial > 0 || $recurring > 0;
888
		return apply_filters( 'getpaid_submission_should_collect_payment_details', $collect, $this );
889
	}
890
891
	/**
892
	 * Returns the billing email of the user.
893
	 *
894
	 * @since 1.0.19
895
	 */
896
	public function get_billing_email() {
897
		return apply_filters( 'getpaid_get_submission_billing_email', $this->get_field( 'billing_email' ), $this );
0 ignored issues
show
Are you sure the usage of $this->get_field('billing_email') targeting GetPaid_Payment_Form_Submission::get_field() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
898
	}
899
900
	/**
901
	 * Checks if the submitter has a billing email.
902
	 *
903
	 * @since 1.0.19
904
	 */
905
	public function has_billing_email() {
906
		$billing_email = $this->get_billing_email();
907
		return ! empty( $billing_email ) && is_email( $billing_email );
908
	}
909
910
	/**
911
	 * Returns the appropriate currency for the submission.
912
	 *
913
	 * @since 1.0.19
914
	 * @return string
915
	 */
916
	public function get_currency() {
917
		return $this->has_invoice() ? $this->invoice->get_currency() : wpinv_get_currency();
918
    }
919
920
    /**
921
	 * Returns the raw submission data.
922
	 *
923
	 * @since 1.0.19
924
	 * @return array
925
	 */
926
	public function get_data() {
927
		return $this->data;
928
	}
929
930
	/**
931
	 * Returns a field from the submission data
932
	 *
933
	 * @param string $field
934
	 * @since 1.0.19
935
	 * @return mixed|null
936
	 */
937
	public function get_field( $field, $sub_array_key = null ) {
938
		return getpaid_get_array_field( $this->data, $field, $sub_array_key );
939
	}
940
941
	/**
942
	 * Checks if a required field is set.
943
	 *
944
	 * @since 1.0.19
945
	 */
946
	public function is_required_field_set( $field ) {
947
		return empty( $field['required'] ) || ! empty( $this->data[ $field['id'] ] );
948
	}
949
950
	/**
951
	 * Formats an amount
952
	 *
953
	 * @since 1.0.19
954
	 */
955
	public function format_amount( $amount ) {
956
		return wpinv_price( $amount, $this->get_currency() );
957
	}
958
959
}
960