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.

includes/class-wpinv-legacy-invoice.php (1 issue)

1
<?php
2
/**
3
 * Contains functions related to Invoicing plugin.
4
 *
5
 * @since 1.0.0
6
 * @package Invoicing
7
 */
8
9
// MUST have WordPress.
10
if ( ! defined( 'WPINC' ) ) {
11
    exit;
12
}
13
14
/**
15
 * This class is used to migrate invoices to the new store.
16
 */
17
final class WPInv_Legacy_Invoice {
18
19
    /**
20
     * Invoice id.
21
     */
22
    public $ID  = 0;
23
24
    /**
25
     * The title of the invoice. Usually the invoice number.
26
     */
27
    public $title;
28
29
    /**
30
     * The post type - either wpi_quote or wpi_invoice
31
     */
32
    public $post_type;
33
34
    /**
35
     * An array of pending changes.
36
     */
37
    public $pending;
38
39
    /**
40
     * An array of invoice items.
41
     */
42
    public $items = array();
43
44
    /**
45
     * Information on the invoice client.
46
     */
47
    public $user_info = array();
48
49
    /**
50
     * Payment information for the invoice.
51
     */
52
    public $payment_meta = array();
53
54
    /**
55
     * Whether this invoice exists in the db or not.
56
     */
57
    public $new = false;
58
59
    /**
60
     * The invoice number.
61
     */
62
    public $number = '';
63
64
    /**
65
     * Whether an actual payment occurred (live) or the transaction
66
     * happened in a sandbox environment (test).
67
     */
68
    public $mode = 'live';
69
70
    /**
71
     * A unique key for this invoice.
72
     */
73
    public $key = '';
74
75
    /**
76
     * The invoice total.
77
     */
78
    public $total = 0.00;
79
80
    /**
81
     * The invoice subtotal.
82
     */
83
    public $subtotal = 0;
84
85
    /**
86
     * 1 to disable taxes and 0 otherwise.
87
     */
88
    public $disable_taxes = 0;
89
90
    /**
91
     * Total tax for this invoice.
92
     */
93
    public $tax = 0;
94
95
    /**
96
     * Other fees for the invoice.
97
     */
98
    public $fees = array();
99
100
    /**
101
     * Total amount of the fees.
102
     */
103
    public $fees_total = 0;
104
105
    /**
106
     * A comma separated array of discount codes.
107
     */
108
    public $discounts = '';
109
110
    /**
111
     * Total discount.
112
     */
113
    public $discount = 0;
114
115
    /**
116
     * Main discount code.
117
     */
118
    public $discount_code = 0;
119
120
    /**
121
     * The date the invoice was created.
122
     */
123
    public $date = '';
124
125
    /**
126
     * The date that the invoice will be due.
127
     */
128
    public $due_date = '';
129
130
    /**
131
     * The date the invoice was paid for.
132
     */
133
    public $completed_date = '';
134
135
    /**
136
     * The invoice status.
137
     */
138
    public $status      = 'wpi-pending';
139
140
    /**
141
     * Same as self::$status.
142
     */
143
    public $post_status = 'wpi-pending';
144
145
    /**
146
     * The old invoice status (used when transitioning statuses).
147
     */
148
    public $old_status = '';
149
150
    /**
151
     * A human readable status name.
152
     */
153
    public $status_nicename = '';
154
155
    /**
156
     * The user id of the invoice client.
157
     */
158
    public $user_id = 0;
159
160
    /**
161
     * The first name of the invoice client.
162
     */
163
    public $first_name = '';
164
165
    /**
166
     * The last name of the invoice client.
167
     */
168
    public $last_name = '';
169
170
    /**
171
     * The email address of the invoice client.
172
     */
173
    public $email = '';
174
175
    /**
176
     * The phone number of the invoice client.
177
     */
178
    public $phone = '';
179
180
    /**
181
     * The street address of the invoice client.
182
     */
183
    public $address = '';
184
185
    /**
186
     * The city of the invoice client.
187
     */
188
    public $city = '';
189
190
    /**
191
     * The country of the invoice client.
192
     */
193
    public $country = '';
194
195
    /**
196
     * The state of the invoice client.
197
     */
198
    public $state = '';
199
200
    /**
201
     * The postal code of the invoice client.
202
     */
203
    public $zip = '';
204
205
    /**
206
     * The transaction id of the invoice.
207
     */
208
    public $transaction_id = '';
209
210
    /**
211
     * The ip address of the invoice client.
212
     */
213
    public $ip = '';
214
215
    /**
216
     * The gateway used to pay the invoice.
217
     */
218
    public $gateway = '';
219
220
    /**
221
     * The title of the gateway used to pay for the invoice.
222
     */
223
    public $gateway_title = '';
224
225
    /**
226
     * The currency of the invoice.
227
     */
228
    public $currency = '';
229
230
    /**
231
     * The cart details of the invoice.
232
     */
233
    public $cart_details = array();
234
235
    /**
236
     * The company of the client.
237
     */
238
    public $company = '';
239
240
    /**
241
     * The vat number of the client.
242
     */
243
    public $vat_number = '';
244
245
    /**
246
     * The vat rate used on the invoice.
247
     */
248
    public $vat_rate = '';
249
250
    /**
251
     * Whether or not the client confirmed the address
252
     */
253
    public $adddress_confirmed = '';
254
255
    /**
256
     * The full name of the client.
257
     */
258
    public $full_name = '';
259
260
    /**
261
     * The parent invoice id of this invoice.
262
     */
263
    public $parent_invoice = 0;
264
265
    public function __construct( $invoice_id = false ) {
266
        if ( empty( $invoice_id ) ) {
267
            return false;
268
        }
269
270
        $this->setup_invoice( $invoice_id );
271
    }
272
273
    public function get( $key ) {
274
        if ( method_exists( $this, 'get_' . $key ) ) {
275
            $value = call_user_func( array( $this, 'get_' . $key ) );
276
        } else {
277
            $value = $this->$key;
278
        }
279
280
        return $value;
281
    }
282
283
    public function set( $key, $value ) {
284
        $ignore = array( 'items', 'cart_details', 'fees', '_ID' );
285
286
        if ( $key === 'status' ) {
287
            $this->old_status = $this->status;
288
        }
289
290
        if ( ! in_array( $key, $ignore ) ) {
291
            $this->pending[ $key ] = $value;
292
        }
293
294
        if ( '_ID' !== $key ) {
295
            $this->$key = $value;
296
        }
297
    }
298
299
    public function _isset( $name ) {
300
        if ( property_exists( $this, $name ) ) {
301
            return false === empty( $this->$name );
302
        } else {
303
            return null;
304
        }
305
    }
306
307
    private function setup_invoice( $invoice_id ) {
308
        $this->pending = array();
309
310
        $invoice = get_post( $invoice_id );
311
312
        if ( ! $invoice || is_wp_error( $invoice ) ) {
313
            return false;
314
        }
315
316
        do_action( 'wpinv_pre_setup_invoice', $this, $invoice_id );
317
318
        // Primary Identifier
319
        $this->ID              = absint( $invoice_id );
320
        $this->post_type       = $invoice->post_type;
321
322
        // We have a payment, get the generic payment_meta item to reduce calls to it
323
        $this->payment_meta    = $this->get_meta();
324
        $this->date            = $invoice->post_date;
325
        $this->due_date        = $this->setup_due_date();
326
        $this->completed_date  = $this->setup_completed_date();
327
        $this->status          = $invoice->post_status;
328
329
        if ( 'future' == $this->status ) {
330
            $this->status = 'publish';
331
        }
332
333
        $this->post_status     = $this->status;
334
        $this->mode            = $this->setup_mode();
335
        $this->parent_invoice  = $invoice->post_parent;
336
        $this->post_name       = $this->setup_post_name( $invoice );
337
        $this->status_nicename = $this->setup_status_nicename( $invoice->post_status );
338
339
        // Items
340
        $this->fees            = $this->setup_fees();
341
        $this->cart_details    = $this->setup_cart_details();
342
        $this->items           = $this->setup_items();
343
344
        // Currency Based
345
        $this->total           = $this->setup_total();
346
        $this->disable_taxes   = $this->setup_is_taxable();
347
        $this->tax             = $this->setup_tax();
348
        $this->fees_total      = $this->get_fees_total();
349
        $this->subtotal        = $this->setup_subtotal();
350
        $this->currency        = $this->setup_currency();
351
352
        // Gateway based
353
        $this->gateway         = $this->setup_gateway();
354
        $this->gateway_title   = $this->setup_gateway_title();
355
        $this->transaction_id  = $this->setup_transaction_id();
356
357
        // User based
358
        $this->ip              = $this->setup_ip();
359
        $this->user_id         = ! empty( $invoice->post_author ) ? $invoice->post_author : get_current_user_id();///$this->setup_user_id();
360
        $this->email           = get_the_author_meta( 'email', $this->user_id );
361
362
        $this->user_info       = $this->setup_user_info();
363
364
        $this->first_name      = $this->user_info['first_name'];
365
        $this->last_name       = $this->user_info['last_name'];
366
        $this->company         = $this->user_info['company'];
367
        $this->vat_number      = $this->user_info['vat_number'];
368
        $this->vat_rate        = $this->user_info['vat_rate'];
369
        $this->adddress_confirmed  = $this->user_info['adddress_confirmed'];
370
        $this->address         = $this->user_info['address'];
371
        $this->city            = $this->user_info['city'];
372
        $this->country         = $this->user_info['country'];
373
        $this->state           = $this->user_info['state'];
374
        $this->zip             = $this->user_info['zip'];
375
        $this->phone           = $this->user_info['phone'];
376
377
        $this->discounts       = $this->user_info['discount'];
378
            $this->discount        = $this->setup_discount();
379
            $this->discount_code   = $this->setup_discount_code();
380
381
        // Other Identifiers
382
        $this->key             = $this->setup_invoice_key();
383
        $this->number          = $this->setup_invoice_number();
384
        $this->title           = ! empty( $invoice->post_title ) ? $invoice->post_title : $this->number;
385
386
        $this->full_name       = trim( $this->first_name . ' ' . $this->last_name );
387
388
        // Allow extensions to add items to this object via hook
389
        do_action( 'wpinv_setup_invoice', $this, $invoice_id );
390
391
        return true;
392
    }
393
394
    private function setup_status_nicename( $status ) {
395
        return $status;
396
    }
397
398
    private function setup_post_name( $post ) {
399
        $this->post_name = $post->post_name;
400
    }
401
402
    private function setup_due_date() {
403
        $due_date = $this->get_meta( '_wpinv_due_date' );
404
405
        if ( empty( $due_date ) ) {
406
            $overdue_time = strtotime( $this->date ) + ( DAY_IN_SECONDS * absint( wpinv_get_option( 'overdue_days', 0 ) ) );
407
            $due_date = date_i18n( 'Y-m-d', $overdue_time );
408
        } elseif ( $due_date == 'none' ) {
409
            $due_date = '';
410
        }
411
412
        return $due_date;
413
    }
414
415
    private function setup_completed_date() {
416
        $invoice = get_post( $this->ID );
417
418
        if ( 'wpi-pending' == $invoice->post_status || 'preapproved' == $invoice->post_status ) {
419
            return false; // This invoice was never paid
420
        }
421
422
        $date = ( $date = $this->get_meta( '_wpinv_completed_date', true ) ) ? $date : $invoice->modified_date;
423
424
        return $date;
425
    }
426
427
    private function setup_cart_details() {
428
        $cart_details = isset( $this->payment_meta['cart_details'] ) ? maybe_unserialize( $this->payment_meta['cart_details'] ) : array();
429
        return $cart_details;
430
    }
431
432
    public function array_convert() {
433
        return get_object_vars( $this );
434
    }
435
436
    private function setup_items() {
437
        $items = isset( $this->payment_meta['items'] ) ? maybe_unserialize( $this->payment_meta['items'] ) : array();
438
        return $items;
439
    }
440
441
    private function setup_fees() {
442
        $payment_fees = isset( $this->payment_meta['fees'] ) ? $this->payment_meta['fees'] : array();
443
        return $payment_fees;
444
    }
445
446
    private function setup_currency() {
447
        $currency = isset( $this->payment_meta['currency'] ) ? $this->payment_meta['currency'] : apply_filters( 'wpinv_currency_default', wpinv_get_currency(), $this );
448
        return $currency;
449
    }
450
451
    private function setup_discount() {
452
        //$discount = $this->get_meta( '_wpinv_discount', true );
453
        $discount = (float)$this->subtotal - ( (float)$this->total - (float)$this->tax - (float)$this->fees_total );
454
        if ( $discount < 0 ) {
455
            $discount = 0;
456
        }
457
        $discount = wpinv_round_amount( $discount );
458
459
        return $discount;
460
    }
461
462
    private function setup_discount_code() {
463
        $discount_code = ! empty( $this->discounts ) ? $this->discounts : $this->get_meta( '_wpinv_discount_code', true );
464
        return $discount_code;
465
    }
466
467
    private function setup_tax() {
468
469
        $tax = $this->get_meta( '_wpinv_tax', true );
470
471
        // We don't have tax as it's own meta and no meta was passed
472
        if ( '' === $tax ) {
473
            $tax = isset( $this->payment_meta['tax'] ) ? $this->payment_meta['tax'] : 0;
474
        }
475
476
        if ( $tax < 0 || ! $this->is_taxable() ) {
477
            $tax = 0;
478
        }
479
480
        return $tax;
481
    }
482
483
    /**
484
     * If taxes are enabled, allow users to enable/disable taxes per invoice.
485
     */
486
    private function setup_is_taxable() {
487
        return (int) $this->get_meta( '_wpinv_disable_taxes', true );
488
    }
489
490
    private function setup_subtotal() {
491
        $subtotal     = 0;
492
        $cart_details = $this->cart_details;
493
494
        if ( is_array( $cart_details ) ) {
495
            foreach ( $cart_details as $item ) {
496
                if ( isset( $item['subtotal'] ) ) {
497
                    $subtotal += $item['subtotal'];
498
                }
499
            }
500
        } else {
501
            $subtotal  = $this->total;
502
            $tax       = wpinv_use_taxes() ? $this->tax : 0;
503
            $subtotal -= $tax;
504
        }
505
506
        return $subtotal;
507
    }
508
509
    private function setup_discounts() {
510
        $discounts = ! empty( $this->payment_meta['user_info']['discount'] ) ? $this->payment_meta['user_info']['discount'] : array();
511
        return $discounts;
512
    }
513
514
    private function setup_total() {
515
        $amount = $this->get_meta( '_wpinv_total', true );
516
517
        if ( empty( $amount ) && '0.00' != $amount ) {
518
            $meta   = $this->get_meta( '_wpinv_payment_meta', true );
519
            $meta   = maybe_unserialize( $meta );
520
521
            if ( isset( $meta['amount'] ) ) {
522
                $amount = $meta['amount'];
523
            }
524
        }
525
526
        if ( $amount < 0 ) {
527
            $amount = 0;
528
        }
529
530
        return $amount;
531
    }
532
533
    private function setup_mode() {
534
        return $this->get_meta( '_wpinv_mode' );
535
    }
536
537
    private function setup_gateway() {
538
        $gateway = $this->get_meta( '_wpinv_gateway' );
539
540
        if ( empty( $gateway ) && 'publish' === $this->status ) {
541
            $gateway = 'manual';
542
        }
543
544
        return $gateway;
545
    }
546
547
    private function setup_gateway_title() {
548
        $gateway_title = wpinv_get_gateway_checkout_label( $this->gateway );
549
        return $gateway_title;
550
    }
551
552
    private function setup_transaction_id() {
553
        $transaction_id = $this->get_meta( '_wpinv_transaction_id' );
554
555
        if ( empty( $transaction_id ) || (int) $transaction_id === (int) $this->ID ) {
556
            $gateway        = $this->gateway;
557
            $transaction_id = apply_filters( 'wpinv_get_invoice_transaction_id-' . $gateway, $this->ID );
558
        }
559
560
        return $transaction_id;
561
    }
562
563
    private function setup_ip() {
564
        $ip = $this->get_meta( '_wpinv_user_ip' );
565
        return $ip;
566
    }
567
568
    ///private function setup_user_id() {
569
        ///$user_id = $this->get_meta( '_wpinv_user_id' );
570
        ///return $user_id;
571
    ///}
572
573
    private function setup_first_name() {
574
        $first_name = $this->get_meta( '_wpinv_first_name' );
575
        return $first_name;
576
    }
577
578
    private function setup_last_name() {
579
        $last_name = $this->get_meta( '_wpinv_last_name' );
580
        return $last_name;
581
    }
582
583
    private function setup_company() {
584
        $company = $this->get_meta( '_wpinv_company' );
585
        return $company;
586
    }
587
588
    private function setup_vat_number() {
589
        $vat_number = $this->get_meta( '_wpinv_vat_number' );
590
        return $vat_number;
591
    }
592
593
    private function setup_vat_rate() {
594
        $vat_rate = $this->get_meta( '_wpinv_vat_rate' );
595
        return $vat_rate;
596
    }
597
598
    private function setup_adddress_confirmed() {
599
        $adddress_confirmed = $this->get_meta( '_wpinv_adddress_confirmed' );
600
        return $adddress_confirmed;
601
    }
602
603
    private function setup_phone() {
604
        $phone = $this->get_meta( '_wpinv_phone' );
605
        return $phone;
606
    }
607
608
    private function setup_address() {
609
        $address = $this->get_meta( '_wpinv_address', true );
610
        return $address;
611
    }
612
613
    private function setup_city() {
614
        $city = $this->get_meta( '_wpinv_city', true );
615
        return $city;
616
    }
617
618
    private function setup_country() {
619
        $country = $this->get_meta( '_wpinv_country', true );
620
        return $country;
621
    }
622
623
    private function setup_state() {
624
        $state = $this->get_meta( '_wpinv_state', true );
625
        return $state;
626
    }
627
628
    private function setup_zip() {
629
        $zip = $this->get_meta( '_wpinv_zip', true );
630
        return $zip;
631
    }
632
633
    private function setup_user_info() {
634
        $defaults = array(
635
            'user_id'            => $this->user_id,
636
            'first_name'         => $this->first_name,
637
            'last_name'          => $this->last_name,
638
            'email'              => get_the_author_meta( 'email', $this->user_id ),
639
            'phone'              => $this->phone,
640
            'address'            => $this->address,
641
            'city'               => $this->city,
642
            'country'            => $this->country,
643
            'state'              => $this->state,
644
            'zip'                => $this->zip,
645
            'company'            => $this->company,
646
            'vat_number'         => $this->vat_number,
647
            'vat_rate'           => $this->vat_rate,
648
            'adddress_confirmed' => $this->adddress_confirmed,
649
            'discount'           => $this->discounts,
650
        );
651
652
        $user_info = array();
653
        if ( isset( $this->payment_meta['user_info'] ) ) {
654
            $user_info = maybe_unserialize( $this->payment_meta['user_info'] );
655
656
            if ( ! empty( $user_info ) && isset( $user_info['user_id'] ) && $post = get_post( $this->ID ) ) {
657
                $this->user_id = $post->post_author;
658
                $this->email = get_the_author_meta( 'email', $this->user_id );
0 ignored issues
show
$this->user_id of type string is incompatible with the type false|integer expected by parameter $user_id of get_the_author_meta(). ( Ignorable by Annotation )

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

658
                $this->email = get_the_author_meta( 'email', /** @scrutinizer ignore-type */ $this->user_id );
Loading history...
659
660
                $user_info['user_id'] = $this->user_id;
661
                $user_info['email'] = $this->email;
662
                $this->payment_meta['user_id'] = $this->user_id;
663
                $this->payment_meta['email'] = $this->email;
664
            }
665
        }
666
667
        $user_info    = wp_parse_args( $user_info, $defaults );
668
669
        // Get the user, but only if it's been created
670
        $user = get_userdata( $this->user_id );
671
672
        if ( ! empty( $user ) && $user->ID > 0 ) {
673
            if ( empty( $user_info ) ) {
674
                $user_info = array(
675
                    'user_id'    => $user->ID,
676
                    'first_name' => $user->first_name,
677
                    'last_name'  => $user->last_name,
678
                    'email'      => $user->user_email,
679
                    'discount'   => '',
680
                );
681
            } else {
682
                foreach ( $user_info as $key => $value ) {
683
                    if ( ! empty( $value ) ) {
684
                        continue;
685
                    }
686
687
                    switch ( $key ) {
688
                        case 'user_id':
689
                            $user_info[ $key ] = $user->ID;
690
                            break;
691
                        case 'first_name':
692
                            $user_info[ $key ] = $user->first_name;
693
                            break;
694
                        case 'last_name':
695
                            $user_info[ $key ] = $user->last_name;
696
                            break;
697
                        case 'email':
698
                            $user_info[ $key ] = $user->user_email;
699
                            break;
700
                    }
701
                }
702
            }
703
        }
704
705
        return $user_info;
706
    }
707
708
    private function setup_invoice_key() {
709
        $key = $this->get_meta( '_wpinv_key', true );
710
711
        return $key;
712
    }
713
714
    private function setup_invoice_number() {
715
        $number = $this->get_meta( '_wpinv_number', true );
716
717
        if ( ! $number ) {
718
            $number = $this->ID;
719
720
            if ( $this->status == 'auto-draft' ) {
721
                if ( wpinv_sequential_number_active( $this->post_type ) ) {
722
                    $next_number = wpinv_get_next_invoice_number( $this->post_type );
723
                    $number      = $next_number;
724
                }
725
            }
726
727
            $number = wpinv_format_invoice_number( $number, $this->post_type );
728
        }
729
730
        return $number;
731
    }
732
733
    public function save() {}
734
735
    public function add_fee( $args ) {
736
        $default_args = array(
737
            'label'   => '',
738
            'amount'  => 0,
739
            'type'    => 'fee',
740
            'id'      => '',
741
            'no_tax'  => false,
742
            'item_id' => 0,
743
        );
744
745
        $fee = wp_parse_args( $args, $default_args );
746
747
        if ( empty( $fee['label'] ) ) {
748
            return false;
749
        }
750
751
        $fee['id']  = sanitize_title( $fee['label'] );
752
753
        $this->fees[]               = $fee;
754
755
        $added_fee               = $fee;
756
        $added_fee['action']     = 'add';
757
        $this->pending['fees'][] = $added_fee;
758
        reset( $this->fees );
759
760
        $this->increase_fees( $fee['amount'] );
761
        return true;
762
    }
763
764
    public function remove_fee( $key ) {
765
        $removed = false;
766
767
        if ( is_numeric( $key ) ) {
768
            $removed = $this->remove_fee_by( 'index', $key );
769
        }
770
771
        return $removed;
772
    }
773
774
    public function remove_fee_by( $key, $value, $global = false ) {
775
        $allowed_fee_keys = apply_filters(
776
            'wpinv_fee_keys',
777
            array(
778
				'index',
779
				'label',
780
				'amount',
781
				'type',
782
            )
783
        );
784
785
        if ( ! in_array( $key, $allowed_fee_keys ) ) {
786
            return false;
787
        }
788
789
        $removed = false;
790
        if ( 'index' === $key && array_key_exists( $value, $this->fees ) ) {
791
            $removed_fee             = $this->fees[ $value ];
792
            $removed_fee['action']   = 'remove';
793
            $this->pending['fees'][] = $removed_fee;
794
795
            $this->decrease_fees( $removed_fee['amount'] );
796
797
            unset( $this->fees[ $value ] );
798
            $removed = true;
799
        } elseif ( 'index' !== $key ) {
800
            foreach ( $this->fees as $index => $fee ) {
801
                if ( isset( $fee[ $key ] ) && $fee[ $key ] == $value ) {
802
                    $removed_fee             = $fee;
803
                    $removed_fee['action']   = 'remove';
804
                    $this->pending['fees'][] = $removed_fee;
805
806
                    $this->decrease_fees( $removed_fee['amount'] );
807
808
                    unset( $this->fees[ $index ] );
809
                    $removed = true;
810
811
                    if ( false === $global ) {
812
                        break;
813
                    }
814
                }
815
            }
816
        }
817
818
        if ( true === $removed ) {
819
            $this->fees = array_values( $this->fees );
820
        }
821
822
        return $removed;
823
    }
824
825
826
827
    public function add_note( $note = '', $customer_type = false, $added_by_user = false, $system = false ) {
828
        // Bail if no note specified
829
        if ( ! $note ) {
830
            return false;
831
        }
832
833
        if ( empty( $this->ID ) ) {
834
            return false;
835
        }
836
837
        if ( ( ( is_user_logged_in() && wpinv_current_user_can_manage_invoicing() ) || $added_by_user ) && ! $system ) {
838
            $user                 = get_user_by( 'id', get_current_user_id() );
839
            $comment_author       = $user->display_name;
840
            $comment_author_email = $user->user_email;
841
        } else {
842
            $comment_author       = 'System';
843
            $comment_author_email = 'system@';
844
            $comment_author_email .= isset( $_SERVER['HTTP_HOST'] ) ? str_replace( 'www.', '', $_SERVER['HTTP_HOST'] ) : 'noreply.com';
845
            $comment_author_email = sanitize_email( $comment_author_email );
846
        }
847
848
        do_action( 'wpinv_pre_insert_invoice_note', $this->ID, $note, $customer_type );
849
850
        $note_id = wp_insert_comment(
851
            wp_filter_comment(
852
                array(
853
					'comment_post_ID'      => $this->ID,
854
					'comment_content'      => $note,
855
					'comment_agent'        => 'WPInvoicing',
856
					'user_id'              => is_admin() ? get_current_user_id() : 0,
857
					'comment_date'         => current_time( 'mysql' ),
858
					'comment_date_gmt'     => current_time( 'mysql', 1 ),
859
					'comment_approved'     => 1,
860
					'comment_parent'       => 0,
861
					'comment_author'       => $comment_author,
862
					'comment_author_IP'    => wpinv_get_ip(),
863
					'comment_author_url'   => '',
864
					'comment_author_email' => $comment_author_email,
865
					'comment_type'         => 'wpinv_note',
866
                )
867
            )
868
        );
869
870
        do_action( 'wpinv_insert_payment_note', $note_id, $this->ID, $note );
871
872
        if ( $customer_type ) {
873
            add_comment_meta( $note_id, '_wpi_customer_note', 1 );
874
875
            do_action(
876
                'wpinv_new_customer_note',
877
                array(
878
					'invoice_id' => $this->ID,
879
					'user_note'  => $note,
880
                )
881
            );
882
        }
883
884
        return $note_id;
885
    }
886
887
    private function increase_subtotal( $amount = 0.00 ) {
888
        $amount          = (float) $amount;
889
        $this->subtotal += $amount;
890
        $this->subtotal  = wpinv_round_amount( $this->subtotal );
891
892
        $this->recalculate_total();
893
    }
894
895
    private function decrease_subtotal( $amount = 0.00 ) {
896
        $amount          = (float) $amount;
897
        $this->subtotal -= $amount;
898
        $this->subtotal  = wpinv_round_amount( $this->subtotal );
899
900
        if ( $this->subtotal < 0 ) {
901
            $this->subtotal = 0;
902
        }
903
904
        $this->recalculate_total();
905
    }
906
907
    private function increase_fees( $amount = 0.00 ) {
908
        $amount            = (float)$amount;
909
        $this->fees_total += $amount;
910
        $this->fees_total  = wpinv_round_amount( $this->fees_total );
911
912
        $this->recalculate_total();
913
    }
914
915
    private function decrease_fees( $amount = 0.00 ) {
916
        $amount            = (float) $amount;
917
        $this->fees_total -= $amount;
918
        $this->fees_total  = wpinv_round_amount( $this->fees_total );
919
920
        if ( $this->fees_total < 0 ) {
921
            $this->fees_total = 0;
922
        }
923
924
        $this->recalculate_total();
925
    }
926
927
    public function recalculate_total() {
928
        global $wpi_nosave;
929
930
        $this->total = $this->subtotal + $this->tax + $this->fees_total;
931
        $this->total = wpinv_round_amount( $this->total );
932
933
        do_action( 'wpinv_invoice_recalculate_total', $this, $wpi_nosave );
934
    }
935
936
    public function increase_tax( $amount = 0.00 ) {
937
        $amount       = (float) $amount;
938
        $this->tax   += $amount;
939
940
        $this->recalculate_total();
941
    }
942
943
    public function decrease_tax( $amount = 0.00 ) {
944
        $amount     = (float) $amount;
945
        $this->tax -= $amount;
946
947
        if ( $this->tax < 0 ) {
948
            $this->tax = 0;
949
        }
950
951
        $this->recalculate_total();
952
    }
953
954
    public function update_status( $new_status = false, $note = '', $manual = false ) {
955
        $old_status = ! empty( $this->old_status ) ? $this->old_status : get_post_status( $this->ID );
956
957
        if ( $old_status === $new_status && in_array( $new_status, array_keys( wpinv_get_invoice_statuses( true ) ) ) ) {
958
            return false; // Don't permit status changes that aren't changes
959
        }
960
961
        $do_change = apply_filters( 'wpinv_should_update_invoice_status', true, $this->ID, $new_status, $old_status );
962
        $updated = false;
963
964
        if ( $do_change ) {
965
            do_action( 'wpinv_before_invoice_status_change', $this->ID, $new_status, $old_status );
966
967
            $update_post_data                   = array();
968
            $update_post_data['ID']             = $this->ID;
969
            $update_post_data['post_status']    = $new_status;
970
            $update_post_data['edit_date']      = current_time( 'mysql', 0 );
971
            $update_post_data['edit_date_gmt']  = current_time( 'mysql', 1 );
972
973
            $update_post_data = apply_filters( 'wpinv_update_invoice_status_fields', $update_post_data, $this->ID );
974
975
            $updated = wp_update_post( $update_post_data );
976
977
            // Status was changed.
978
            do_action( 'wpinv_status_' . $new_status, $this->ID, $old_status );
979
            do_action( 'wpinv_status_' . $old_status . '_to_' . $new_status, $this->ID, $old_status );
980
            do_action( 'wpinv_update_status', $this->ID, $new_status, $old_status );
981
        }
982
983
        return $updated;
984
    }
985
986
    public function refund() {
987
        $this->old_status        = $this->status;
988
        $this->status            = 'wpi-refunded';
989
        $this->pending['status'] = $this->status;
990
991
        $this->save();
992
    }
993
994
    public function update_meta() {}
995
996
    // get data
997
    public function get_meta( $meta_key = '_wpinv_payment_meta', $single = true ) {
998
        $meta = get_post_meta( $this->ID, $meta_key, $single );
999
1000
        if ( $meta_key === '_wpinv_payment_meta' ) {
1001
1002
            if ( ! is_array( $meta ) ) {
1003
$meta = array();} // we need this to be an array so make sure it is.
1004
1005
            if ( empty( $meta['key'] ) ) {
1006
                $meta['key'] = $this->setup_invoice_key();
1007
            }
1008
1009
            if ( empty( $meta['date'] ) ) {
1010
                $meta['date'] = get_post_field( 'post_date', $this->ID );
1011
            }
1012
        }
1013
1014
        $meta = apply_filters( 'wpinv_get_invoice_meta_' . $meta_key, $meta, $this->ID );
1015
1016
        return apply_filters( 'wpinv_get_invoice_meta', $meta, $this->ID, $meta_key );
1017
    }
1018
1019
    public function get_description() {
1020
        $post = get_post( $this->ID );
1021
1022
        $description = ! empty( $post ) ? $post->post_content : '';
1023
        return apply_filters( 'wpinv_get_description', $description, $this->ID, $this );
1024
    }
1025
1026
    public function get_status( $nicename = false ) {
1027
        if ( ! $nicename ) {
1028
            $status = $this->status;
1029
        } else {
1030
            $status = $this->status_nicename;
1031
        }
1032
1033
        return apply_filters( 'wpinv_get_status', $status, $nicename, $this->ID, $this );
1034
    }
1035
1036
    public function get_cart_details() {
1037
        return apply_filters( 'wpinv_cart_details', $this->cart_details, $this->ID, $this );
1038
    }
1039
1040
    public function get_subtotal( $currency = false ) {
1041
        $subtotal = wpinv_round_amount( $this->subtotal );
1042
1043
        if ( $currency ) {
1044
            $subtotal = wpinv_price( wpinv_format_amount( $subtotal, null, ! $currency ), $this->get_currency() );
1045
        }
1046
1047
        return apply_filters( 'wpinv_get_invoice_subtotal', $subtotal, $this->ID, $this, $currency );
1048
    }
1049
1050
    public function get_total( $currency = false ) {
1051
        if ( $this->is_free_trial() ) {
1052
            $total = wpinv_round_amount( 0 );
1053
        } else {
1054
            $total = wpinv_round_amount( $this->total );
1055
        }
1056
        if ( $currency ) {
1057
            $total = wpinv_price( wpinv_format_amount( $total, null, ! $currency ), $this->get_currency() );
1058
        }
1059
1060
        return apply_filters( 'wpinv_get_invoice_total', $total, $this->ID, $this, $currency );
1061
    }
1062
1063
    public function get_recurring_details() {}
1064
1065
    public function get_final_tax( $currency = false ) {
1066
        $final_total = wpinv_round_amount( $this->tax );
1067
        if ( $currency ) {
1068
            $final_total = wpinv_price( wpinv_format_amount( $final_total, null, ! $currency ), $this->get_currency() );
1069
        }
1070
1071
        return apply_filters( 'wpinv_get_invoice_final_total', $final_total, $this, $currency );
1072
    }
1073
1074
    public function get_discounts( $array = false ) {
1075
        $discounts = $this->discounts;
1076
        if ( $array && $discounts ) {
1077
            $discounts = explode( ',', $discounts );
1078
        }
1079
        return apply_filters( 'wpinv_payment_discounts', $discounts, $this->ID, $this, $array );
1080
    }
1081
1082
    public function get_discount( $currency = false, $dash = false ) {
1083
        if ( ! empty( $this->discounts ) ) {
1084
            global $ajax_cart_details;
1085
            $ajax_cart_details = $this->get_cart_details();
1086
1087
            if ( ! empty( $ajax_cart_details ) && count( $ajax_cart_details ) == count( $this->items ) ) {
1088
                $cart_items = $ajax_cart_details;
1089
            } else {
1090
                $cart_items = $this->items;
1091
            }
1092
1093
            $this->discount = wpinv_get_cart_items_discount_amount( $cart_items, $this->discounts );
1094
        }
1095
        $discount   = wpinv_round_amount( $this->discount );
1096
        $dash       = $dash && $discount > 0 ? '&ndash;' : '';
1097
1098
        if ( $currency ) {
1099
            $discount = wpinv_price( wpinv_format_amount( $discount, null, ! $currency ), $this->get_currency() );
1100
        }
1101
1102
        $discount   = $dash . $discount;
1103
1104
        return apply_filters( 'wpinv_get_invoice_discount', $discount, $this->ID, $this, $currency, $dash );
1105
    }
1106
1107
    public function get_discount_code() {
1108
        return $this->discount_code;
1109
    }
1110
1111
    // Checks if the invoice is taxable. Does not check if taxes are enabled on the site.
1112
    public function is_taxable() {
1113
        return (int) $this->disable_taxes === 0;
1114
    }
1115
1116
    public function get_tax( $currency = false ) {
1117
        $tax = wpinv_round_amount( $this->tax );
1118
1119
        if ( $currency ) {
1120
            $tax = wpinv_price( wpinv_format_amount( $tax, null, ! $currency ), $this->get_currency() );
1121
        }
1122
1123
        if ( ! $this->is_taxable() ) {
1124
            $tax = wpinv_round_amount( 0.00 );
1125
        }
1126
1127
        return apply_filters( 'wpinv_get_invoice_tax', $tax, $this->ID, $this, $currency );
1128
    }
1129
1130
    public function get_fees( $type = 'all' ) {
1131
        $fees    = array();
1132
1133
        if ( ! empty( $this->fees ) && is_array( $this->fees ) ) {
1134
            foreach ( $this->fees as $fee ) {
1135
                if ( 'all' != $type && ! empty( $fee['type'] ) && $type != $fee['type'] ) {
1136
                    continue;
1137
                }
1138
1139
                $fee['label'] = stripslashes( $fee['label'] );
1140
                $fee['amount_display'] = wpinv_price( $fee['amount'], $this->get_currency() );
1141
                $fees[]    = $fee;
1142
            }
1143
        }
1144
1145
        return apply_filters( 'wpinv_get_invoice_fees', $fees, $this->ID, $this );
1146
    }
1147
1148
    public function get_fees_total() {
1149
        $fees_total = (float) 0.00;
1150
1151
        $payment_fees = isset( $this->payment_meta['fees'] ) ? $this->payment_meta['fees'] : array();
1152
        if ( ! empty( $payment_fees ) ) {
1153
            foreach ( $payment_fees as $fee ) {
1154
                $fees_total += (float) $fee['amount'];
1155
            }
1156
        }
1157
1158
        return apply_filters( 'wpinv_get_invoice_fees_total', $fees_total, $this->ID, $this );
1159
1160
    }
1161
1162
    public function get_user_id() {
1163
        return apply_filters( 'wpinv_user_id', $this->user_id, $this->ID, $this );
1164
    }
1165
1166
    public function get_first_name() {
1167
        return apply_filters( 'wpinv_first_name', $this->first_name, $this->ID, $this );
1168
    }
1169
1170
    public function get_last_name() {
1171
        return apply_filters( 'wpinv_last_name', $this->last_name, $this->ID, $this );
1172
    }
1173
1174
    public function get_user_full_name() {
1175
        return apply_filters( 'wpinv_user_full_name', $this->full_name, $this->ID, $this );
1176
    }
1177
1178
    public function get_user_info() {
1179
        return apply_filters( 'wpinv_user_info', $this->user_info, $this->ID, $this );
1180
    }
1181
1182
    public function get_email() {
1183
        return apply_filters( 'wpinv_user_email', $this->email, $this->ID, $this );
1184
    }
1185
1186
    public function get_address() {
1187
        return apply_filters( 'wpinv_address', $this->address, $this->ID, $this );
1188
    }
1189
1190
    public function get_phone() {
1191
        return apply_filters( 'wpinv_phone', $this->phone, $this->ID, $this );
1192
    }
1193
1194
    public function get_number() {
1195
        return apply_filters( 'wpinv_number', $this->number, $this->ID, $this );
1196
    }
1197
1198
    public function get_items() {
1199
        return apply_filters( 'wpinv_payment_meta_items', $this->items, $this->ID, $this );
1200
    }
1201
1202
    public function get_key() {
1203
        return apply_filters( 'wpinv_key', $this->key, $this->ID, $this );
1204
    }
1205
1206
    public function get_transaction_id() {
1207
        return apply_filters( 'wpinv_get_invoice_transaction_id', $this->transaction_id, $this->ID, $this );
1208
    }
1209
1210
    public function get_gateway() {
1211
        return apply_filters( 'wpinv_gateway', $this->gateway, $this->ID, $this );
1212
    }
1213
1214
    public function get_gateway_title() {}
1215
1216
    public function get_currency() {
1217
        return apply_filters( 'wpinv_currency_code', $this->currency, $this->ID, $this );
1218
    }
1219
1220
    public function get_created_date() {
1221
        return apply_filters( 'wpinv_created_date', $this->date, $this->ID, $this );
1222
    }
1223
1224
    public function get_due_date( $display = false ) {
1225
        $due_date = apply_filters( 'wpinv_due_date', $this->due_date, $this->ID, $this );
1226
1227
        if ( ! $display ) {
1228
            return $due_date;
1229
        }
1230
1231
        return getpaid_format_date( $this->due_date );
1232
    }
1233
1234
    public function get_completed_date() {
1235
        return apply_filters( 'wpinv_completed_date', $this->completed_date, $this->ID, $this );
1236
    }
1237
1238
    public function get_invoice_date( $formatted = true ) {
1239
        $date_completed = $this->completed_date;
1240
        $invoice_date   = $date_completed != '' && $date_completed != '0000-00-00 00:00:00' ? $date_completed : '';
1241
1242
        if ( $invoice_date == '' ) {
1243
            $date_created   = $this->date;
1244
            $invoice_date   = $date_created != '' && $date_created != '0000-00-00 00:00:00' ? $date_created : '';
1245
        }
1246
1247
        if ( $formatted && $invoice_date ) {
1248
            $invoice_date   = getpaid_format_date( $invoice_date );
1249
        }
1250
1251
        return apply_filters( 'wpinv_get_invoice_date', $invoice_date, $formatted, $this->ID, $this );
1252
    }
1253
1254
    public function get_ip() {
1255
        return apply_filters( 'wpinv_user_ip', $this->ip, $this->ID, $this );
1256
    }
1257
1258
    public function has_status( $status ) {
1259
        return apply_filters( 'wpinv_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status ) ) || $this->get_status() === $status ? true : false, $this, $status );
1260
    }
1261
1262
    public function add_item() {}
1263
1264
    public function remove_item() {}
1265
1266
    public function update_items() {}
1267
1268
    public function recalculate_totals() {}
1269
1270
    public function needs_payment() {}
1271
1272
    public function get_checkout_payment_url() {}
1273
1274
    public function get_view_url() {}
1275
1276
    public function generate_key( $string = '' ) {
1277
        $auth_key  = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
1278
        return strtolower( md5( $string . date( 'Y-m-d H:i:s' ) . $auth_key . uniqid( 'wpinv', true ) ) );  // Unique key
1279
    }
1280
1281
    public function is_recurring() {
1282
        if ( empty( $this->cart_details ) ) {
1283
            return false;
1284
        }
1285
1286
        $has_subscription = false;
1287
        foreach ( $this->cart_details as $cart_item ) {
1288
            if ( ! empty( $cart_item['id'] ) && wpinv_is_recurring_item( $cart_item['id'] ) ) {
1289
                $has_subscription = true;
1290
                break;
1291
            }
1292
        }
1293
1294
        if ( count( $this->cart_details ) > 1 ) {
1295
            $has_subscription = false;
1296
        }
1297
1298
        return apply_filters( 'wpinv_invoice_has_recurring_item', $has_subscription, $this->cart_details );
1299
    }
1300
1301
    public function is_free_trial() {
1302
        $is_free_trial = false;
1303
1304
        if ( $this->is_parent() && $item = $this->get_recurring( true ) ) {
1305
            if ( ! empty( $item ) && $item->has_free_trial() ) {
1306
                $is_free_trial = true;
1307
            }
1308
        }
1309
1310
        return apply_filters( 'wpinv_invoice_is_free_trial', $is_free_trial, $this->cart_details, $this );
1311
    }
1312
1313
    public function is_initial_free() {}
1314
1315
    public function get_recurring( $object = false ) {
1316
        $item = null;
1317
1318
        if ( empty( $this->cart_details ) ) {
1319
            return $item;
1320
        }
1321
1322
        foreach ( $this->cart_details as $cart_item ) {
1323
            if ( ! empty( $cart_item['id'] ) && wpinv_is_recurring_item( $cart_item['id'] ) ) {
1324
                $item = $cart_item['id'];
1325
                break;
1326
            }
1327
        }
1328
1329
        if ( $object ) {
1330
            $item = $item ? new WPInv_Item( $item ) : null;
1331
1332
            apply_filters( 'wpinv_invoice_get_recurring_item', $item, $this );
1333
        }
1334
1335
        return apply_filters( 'wpinv_invoice_get_recurring_item_id', $item, $this );
1336
    }
1337
1338
    public function get_subscription_name() {}
1339
1340
    public function get_subscription_id() {}
1341
1342
    public function is_parent() {
1343
        return ! empty( $this->parent_invoice );
1344
    }
1345
1346
    public function is_renewal() {}
1347
1348
    public function get_parent_payment() {}
1349
1350
    public function is_paid() {}
1351
1352
    public function is_quote() {}
1353
1354
    public function is_refunded() {}
1355
1356
    public function is_free() {
1357
        $total = (float) wpinv_round_amount( $this->get_total() );
1358
        return $total > 0 && ! $this->is_recurring();
1359
    }
1360
1361
    public function has_vat() {}
1362
1363
    public function refresh_item_ids() {}
1364
1365
    public function get_invoice_quote_type() {}
1366
1367
}
1368