Passed
Pull Request — master (#153)
by Kiran
04:16
created

wpinv-invoice-functions.php ➔ wpinv_record_status_change()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 10
nc 3
nop 3
dl 0
loc 19
rs 8.8571
c 0
b 0
f 0
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( 'Do NOT access this file directly: ' . basename( __FILE__ ) );
12
}
13
14
function wpinv_get_invoice_cart_id() {
15
    $wpinv_checkout = wpinv_get_checkout_session();
16
    
17
    if ( !empty( $wpinv_checkout['invoice_id'] ) ) {
18
        return $wpinv_checkout['invoice_id'];
19
    }
20
    
21
    return NULL;
22
}
23
24
function wpinv_insert_invoice( $invoice_data = array(), $wp_error = false ) {
25
    if ( empty( $invoice_data ) ) {
26
        return false;
27
    }
28
    
29
    if ( !( !empty( $invoice_data['cart_details'] ) && is_array( $invoice_data['cart_details'] ) ) ) {
30
        return $wp_error ? new WP_Error( 'wpinv_invalid_items', __( 'Invoice must have atleast on item.', 'invoicing' ) ) : 0;
31
    }
32
    
33
    if ( empty( $invoice_data['user_id'] ) ) {
34
        $invoice_data['user_id'] = get_current_user_id();
35
    }
36
    
37
    $invoice_data['invoice_id'] = !empty( $invoice_data['invoice_id'] ) ? (int)$invoice_data['invoice_id'] : 0;
38
    
39
    if ( empty( $invoice_data['status'] ) ) {
40
        $invoice_data['status'] = 'wpi-pending';
41
    }
42
    
43
    if ( empty( $invoice_data['ip'] ) ) {
44
        $invoice_data['ip'] = wpinv_get_ip();
45
    }
46
47
    // default invoice args, note that status is checked for validity in wpinv_create_invoice()
48
    $default_args = array(
49
        'invoice_id'    => (int)$invoice_data['invoice_id'],
50
        'user_id'       => (int)$invoice_data['user_id'],
51
        'status'        => $invoice_data['status'],
52
    );
53
54
    $invoice = wpinv_create_invoice( $default_args, $invoice_data, true );
55
    if ( is_wp_error( $invoice ) ) {
56
        return $wp_error ? $invoice : 0;
57
    }
58
    
59
    if ( empty( $invoice_data['invoice_id'] ) ) {
60
        //$invoice->add_note( wp_sprintf( __( 'Invoice is created with status %s.', 'invoicing' ), wpinv_status_nicename( $invoice->status ) ) );
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
61
    }
62
    
63
    // User info
64
    $default_user_info = array(
65
        'user_id'               => '',
66
        'first_name'            => '',
67
        'last_name'             => '',
68
        'email'                 => '',
69
        'company'               => '',
70
        'phone'                 => '',
71
        'address'               => '',
72
        'city'                  => '',
73
        'country'               => wpinv_get_default_country(),
74
        'state'                 => wpinv_get_default_state(),
75
        'zip'                   => '',
76
        'vat_number'            => '',
77
        'vat_rate'              => '',
78
        'adddress_confirmed'    => '',
79
        'discount'              => array(),
80
    );
81
82
    if ( $user_id = (int)$invoice->get_user_id() ) {
83
        if ( $user_address = wpinv_get_user_address( $user_id ) ) {
84
            $default_user_info = wp_parse_args( $user_address, $default_user_info );
85
        }
86
    }
87
    
88
    if ( empty( $invoice_data['user_info'] ) ) {
89
        $invoice_data['user_info'] = array();
90
    }
91
    
92
    $user_info = wp_parse_args( $invoice_data['user_info'], $default_user_info );
93
    
94 View Code Duplication
    if ( empty( $user_info['first_name'] ) ) {
95
        $user_info['first_name'] = $default_user_info['first_name'];
96
        $user_info['last_name'] = $default_user_info['last_name'];
97
    }
98
    
99
    if ( empty( $user_info['country'] ) ) {
100
        $user_info['country'] = $default_user_info['country'];
101
        $user_info['state'] = $default_user_info['state'];
102
        $user_info['city'] = $default_user_info['city'];
103
        $user_info['address'] = $default_user_info['address'];
104
        $user_info['zip'] = $default_user_info['zip'];
105
        $user_info['phone'] = $default_user_info['phone'];
106
    }
107
    
108
    if ( !empty( $user_info['discount'] ) && !is_array( $user_info['discount'] ) ) {
109
        $user_info['discount'] = (array)$user_info['discount'];
110
    }
111
112
    // Payment details
113
    $payment_details = array();
114
    if ( !empty( $invoice_data['payment_details'] ) ) {
115
        $default_payment_details = array(
116
            'gateway'           => 'manual',
117
            'gateway_title'     => '',
118
            'currency'          => wpinv_get_default_country(),
119
            'transaction_id'    => '',
120
        );
121
        
122
        $payment_details = wp_parse_args( $invoice_data['payment_details'], $default_payment_details );
123
        
124
        if ( empty( $payment_details['gateway'] ) ) {
125
            $payment_details['gateway'] = 'manual';
126
        }
127
        
128
        if ( empty( $payment_details['currency'] ) ) {
129
            $payment_details['currency'] = wpinv_get_default_country();
130
        }
131
        
132
        if ( empty( $payment_details['gateway_title'] ) ) {
133
            $payment_details['gateway_title'] = wpinv_get_gateway_checkout_label( $payment_details['gateway'] );
134
        }
135
    }
136
    
137
    $invoice->set( 'status', ( !empty( $invoice_data['status'] ) ? $invoice_data['status'] : 'wpi-pending' ) );
138
    
139
    if ( !empty( $payment_details ) ) {
140
        $invoice->set( 'currency', $payment_details['currency'] );
141
        $invoice->set( 'gateway', $payment_details['gateway'] );
142
        $invoice->set( 'gateway_title', $payment_details['gateway_title'] );
143
        $invoice->set( 'transaction_id', $payment_details['transaction_id'] );
144
    }
145
    
146
    $invoice->set( 'user_info', $user_info );
147
    $invoice->set( 'first_name', $user_info['first_name'] );
148
    $invoice->set( 'last_name', $user_info['last_name'] );
149
    $invoice->set( 'address', $user_info['address'] );
150
    $invoice->set( 'company', $user_info['company'] );
151
    $invoice->set( 'vat_number', $user_info['vat_number'] );
152
    $invoice->set( 'phone', $user_info['phone'] );
153
    $invoice->set( 'city', $user_info['city'] );
154
    $invoice->set( 'country', $user_info['country'] );
155
    $invoice->set( 'state', $user_info['state'] );
156
    $invoice->set( 'zip', $user_info['zip'] );
157
    $invoice->set( 'discounts', $user_info['discount'] );
158
    $invoice->set( 'ip', ( !empty( $invoice_data['ip'] ) ? $invoice_data['ip'] : wpinv_get_ip() ) );
159
    $invoice->set( 'mode', ( wpinv_is_test_mode() ? 'test' : 'live' ) );
160
    $invoice->set( 'parent_invoice', ( !empty( $invoice_data['parent'] ) ? absint( $invoice_data['parent'] ) : '' ) );
161
    
162
    if ( !empty( $invoice_data['cart_details'] ) && is_array( $invoice_data['cart_details'] ) ) {
163
        foreach ( $invoice_data['cart_details'] as $key => $item ) {
164
            $item_id        = !empty( $item['id'] ) ? $item['id'] : 0;
165
            $quantity       = !empty( $item['quantity'] ) ? $item['quantity'] : 1;
166
            $name           = !empty( $item['name'] ) ? $item['name'] : '';
167
            $item_price     = isset( $item['item_price'] ) ? $item['item_price'] : '';
168
            
169
            $post_item  = new WPInv_Item( $item_id );
170
            if ( !empty( $post_item ) ) {
171
                $name       = !empty( $name ) ? $name : $post_item->get_name();
172
                $item_price = $item_price !== '' ? $item_price : $post_item->get_price();
173
            } else {
174
                continue;
175
            }
176
            
177
            $args = array(
178
                'name'          => $name,
179
                'quantity'      => $quantity,
180
                'item_price'    => $item_price,
181
                'custom_price'  => isset( $item['custom_price'] ) ? $item['custom_price'] : '',
182
                'tax'           => !empty( $item['tax'] ) ? $item['tax'] : 0.00,
183
                'discount'      => isset( $item['discount'] ) ? $item['discount'] : 0,
184
                'meta'          => isset( $item['meta'] ) ? $item['meta'] : array(),
185
                'fees'          => isset( $item['fees'] ) ? $item['fees'] : array(),
186
            );
187
188
            $invoice->add_item( $item_id, $args );
189
        }
190
    }
191
192
    $invoice->increase_tax( wpinv_get_cart_fee_tax() );
193
194
    if ( isset( $invoice_data['post_date'] ) ) {
195
        $invoice->set( 'date', $invoice_data['post_date'] );
196
    }
197
    
198
    // Invoice due date
199
    if ( isset( $invoice_data['due_date'] ) ) {
200
        $invoice->set( 'due_date', $invoice_data['due_date'] );
201
    }
202
    
203
    $invoice->save();
204
    
205
    // Add notes
206
    if ( !empty( $invoice_data['private_note'] ) ) {
207
        $invoice->add_note( $invoice_data['private_note'] );
208
    }
209
    if ( !empty( $invoice_data['user_note'] ) ) {
210
        $invoice->add_note( $invoice_data['user_note'], true );
211
    }
212
    
213
    do_action( 'wpinv_insert_invoice', $invoice->ID, $invoice_data );
214
215
    if ( ! empty( $invoice->ID ) ) {
216
        global $wpi_userID, $wpinv_ip_address_country;
217
        
218
        $checkout_session = wpinv_get_checkout_session();
219
        
220
        $data_session                   = array();
221
        $data_session['invoice_id']     = $invoice->ID;
222
        $data_session['cart_discounts'] = $invoice->get_discounts( true );
223
        
224
        wpinv_set_checkout_session( $data_session );
225
        
226
        $wpi_userID         = (int)$invoice->get_user_id();
227
        
228
        $_POST['country']   = !empty( $invoice->country ) ? $invoice->country : wpinv_get_default_country();
229
        $_POST['state']     = $invoice->state;
230
231
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
232
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
233
        
234
        $wpinv_ip_address_country = $invoice->country;
235
        
236
        $invoice = $invoice->recalculate_totals( true );
237
        
238
        wpinv_set_checkout_session( $checkout_session );
239
                    
240
        return $invoice;
241
    }
242
    
243 View Code Duplication
    if ( $wp_error ) {
244
        if ( is_wp_error( $invoice ) ) {
245
            return $invoice;
246
        } else {
247
            return new WP_Error( 'wpinv_insert_invoice_error', __( 'Error in insert invoice.', 'invoicing' ) );
248
        }
249
    } else {
250
        return 0;
251
    }
252
}
253
254
function wpinv_update_invoice( $invoice_data = array(), $wp_error = false ) {
255
    $invoice_ID = !empty( $invoice_data['ID'] ) ? absint( $invoice_data['ID'] ) : NULL;
256
257
    if ( !$invoice_ID ) {
258
        if ( $wp_error ) {
259
            return new WP_Error( 'invalid_invoice_id', __( 'Invalid invoice ID.', 'invoicing' ) );
260
        }
261
        return 0;
262
    }
263
264
    $invoice = wpinv_get_invoice( $invoice_ID );
265
266
    $recurring_item = $invoice->is_recurring() ? $invoice->get_recurring( true ) : NULL;
267
268
    if ( empty( $invoice->ID ) ) {
269
        if ( $wp_error ) {
270
            return new WP_Error( 'invalid_invoice', __( 'Invalid invoice.', 'invoicing' ) );
271
        }
272
        return 0;
273
    }
274
275
    if ( !$invoice->has_status( array( 'wpi-pending' ) ) ) {
276
        if ( $wp_error ) {
277
            return new WP_Error( 'invalid_invoice_status', __( 'Only invoice with pending payment is allowed to update.', 'invoicing' ) );
278
        }
279
        return 0;
280
    }
281
282
    // Invoice status
283
    if ( !empty( $invoice_data['status'] ) ) {
284
        $invoice->set( 'status', $invoice_data['status'] );
285
    }
286
287
    // Invoice date
288
    if ( !empty( $invoice_data['post_date'] ) ) {
289
        $invoice->set( 'date', $invoice_data['post_date'] );
290
    }
291
292
    // Invoice due date
293
    if ( isset( $invoice_data['due_date'] ) ) {
294
        $invoice->set( 'due_date', $invoice_data['due_date'] );
295
    }
296
297
    // Invoice IP address
298
    if ( !empty( $invoice_data['ip'] ) ) {
299
        $invoice->set( 'ip', $invoice_data['ip'] );
300
    }
301
    
302
    // User info
303
    if ( !empty( $invoice_data['user_info'] ) && is_array( $invoice_data['user_info'] ) ) {
304
        $user_info = wp_parse_args( $invoice_data['user_info'], $invoice->user_info );
305
306
        if ( $discounts = $invoice->get_discounts() ) {
307
            $set_discount = $discounts;
308
        } else {
309
            $set_discount = '';
310
        }
311
312
        // Manage discount
313
        if ( !empty( $invoice_data['user_info']['discount'] ) ) {
314
            // Remove discount
315
            if ( $invoice_data['user_info']['discount'] == 'none' ) {
316
                $set_discount = '';
317
            } else {
318
                $set_discount = $invoice_data['user_info']['discount'];
319
            }
320
321
            $invoice->set( 'discounts', $set_discount );
322
        }
323
324
        $user_info['discount'] = $set_discount;
325
326
        $invoice->set( 'user_info', $user_info );
327
    }
328
329
    if ( !empty( $invoice_data['cart_details'] ) && is_array( $invoice_data['cart_details'] ) && $cart_details = $invoice_data['cart_details'] ) {
330
        $remove_items = !empty( $cart_details['remove_items'] ) && is_array( $cart_details['remove_items'] ) ? $cart_details['remove_items'] : array();
331
332
        if ( !empty( $remove_items[0]['id'] ) ) {
333
            foreach ( $remove_items as $item ) {
334
                $item_id        = !empty( $item['id'] ) ? $item['id'] : 0;
335
                $quantity       = !empty( $item['quantity'] ) ? $item['quantity'] : 1;
336
                if ( empty( $item_id ) ) {
337
                    continue;
338
                }
339
340
                foreach ( $invoice->cart_details as $cart_index => $cart_item ) {
341
                    if ( $item_id == $cart_item['id'] ) {
342
                        $args = array(
343
                            'id'         => $item_id,
344
                            'quantity'   => $quantity,
345
                            'cart_index' => $cart_index
346
                        );
347
348
                        $invoice->remove_item( $item_id, $args );
349
                        break;
350
                    }
351
                }
352
            }
353
        }
354
355
        $add_items = !empty( $cart_details['add_items'] ) && is_array( $cart_details['add_items'] ) ? $cart_details['add_items'] : array();
356
357
        if ( !empty( $add_items[0]['id'] ) ) {
358
            foreach ( $add_items as $item ) {
359
                $item_id        = !empty( $item['id'] ) ? $item['id'] : 0;
360
                $post_item      = new WPInv_Item( $item_id );
361
                if ( empty( $post_item ) ) {
362
                    continue;
363
                }
364
365
                $valid_item = true;
366 View Code Duplication
                if ( !empty( $recurring_item ) ) {
367
                    if ( $recurring_item->ID != $item_id ) {
368
                        $valid_item = false;
369
                    }
370
                } else if ( wpinv_is_recurring_item( $item_id ) ) {
371
                    $valid_item = false;
372
                }
373
                
374
                if ( !$valid_item ) {
375
                    if ( $wp_error ) {
376
                        return new WP_Error( 'invalid_invoice_item', __( 'You can not add item because recurring item must be paid individually!', 'invoicing' ) );
377
                    }
378
                    return 0;
379
                }
380
381
                $quantity       = !empty( $item['quantity'] ) ? $item['quantity'] : 1;
382
                $name           = !empty( $item['name'] ) ? $item['name'] : $post_item->get_name();
383
                $item_price     = isset( $item['item_price'] ) ? $item['item_price'] : $post_item->get_price();
384
385
                $args = array(
386
                    'name'          => $name,
387
                    'quantity'      => $quantity,
388
                    'item_price'    => $item_price,
389
                    'custom_price'  => isset( $item['custom_price'] ) ? $item['custom_price'] : '',
390
                    'tax'           => !empty( $item['tax'] ) ? $item['tax'] : 0,
391
                    'discount'      => isset( $item['discount'] ) ? $item['discount'] : 0,
392
                    'meta'          => isset( $item['meta'] ) ? $item['meta'] : array(),
393
                    'fees'          => isset( $item['fees'] ) ? $item['fees'] : array(),
394
                );
395
396
                $invoice->add_item( $item_id, $args );
397
            }
398
        }
399
    }
400
    
401
    // Payment details
402
    if ( !empty( $invoice_data['payment_details'] ) && $payment_details = $invoice_data['payment_details'] ) {
403
        if ( !empty( $payment_details['gateway'] ) ) {
404
            $invoice->set( 'gateway', $payment_details['gateway'] );
405
        }
406
407
        if ( !empty( $payment_details['transaction_id'] ) ) {
408
            $invoice->set( 'transaction_id', $payment_details['transaction_id'] );
409
        }
410
    }
411
412
    do_action( 'wpinv_pre_update_invoice', $invoice->ID, $invoice_data );
413
414
    // Parent invoice
415
    if ( !empty( $invoice_data['parent'] ) ) {
416
        $invoice->set( 'parent_invoice', $invoice_data['parent'] );
417
    }
418
419
    // Save invoice data.
420
    $invoice->save();
421
    
422
    if ( empty( $invoice->ID ) || is_wp_error( $invoice ) ) {
423 View Code Duplication
        if ( $wp_error ) {
424
            if ( is_wp_error( $invoice ) ) {
425
                return $invoice;
426
            } else {
427
                return new WP_Error( 'wpinv_update_invoice_error', __( 'Error in update invoice.', 'invoicing' ) );
428
            }
429
        } else {
430
            return 0;
431
        }
432
    }
433
434
    // Add private note
435
    if ( !empty( $invoice_data['private_note'] ) ) {
436
        $invoice->add_note( $invoice_data['private_note'] );
437
    }
438
439
    // Add user note
440
    if ( !empty( $invoice_data['user_note'] ) ) {
441
        $invoice->add_note( $invoice_data['user_note'], true );
442
    }
443
444
    global $wpi_userID, $wpinv_ip_address_country;
445
446
    $checkout_session = wpinv_get_checkout_session();
447
448
    $data_session                   = array();
449
    $data_session['invoice_id']     = $invoice->ID;
450
    $data_session['cart_discounts'] = $invoice->get_discounts( true );
451
452
    wpinv_set_checkout_session( $data_session );
453
454
    $wpi_userID         = (int)$invoice->get_user_id();
455
456
    $_POST['country']   = !empty( $invoice->country ) ? $invoice->country : wpinv_get_default_country();
457
    $_POST['state']     = $invoice->state;
458
459
    $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
460
    $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
461
462
    $wpinv_ip_address_country = $invoice->country;
463
464
    $invoice = $invoice->recalculate_totals( true );
465
466
    do_action( 'wpinv_post_update_invoice', $invoice->ID, $invoice_data );
467
468
    wpinv_set_checkout_session( $checkout_session );
469
470
    return $invoice;
471
}
472
473
function wpinv_get_invoice( $invoice_id = 0, $cart = false ) {
474
    if ( $cart && empty( $invoice_id ) ) {
475
        $invoice_id = (int)wpinv_get_invoice_cart_id();
476
    }
477
478
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
479
480
    if ( ! empty( $invoice ) && ! empty( $invoice->ID ) ) {
481
        return $invoice;
482
    }
483
    
484
    return NULL;
485
}
486
487
function wpinv_get_invoice_cart( $invoice_id = 0 ) {
488
    return wpinv_get_invoice( $invoice_id, true );
489
}
490
491
function wpinv_get_invoice_description( $invoice_id = 0 ) {
492
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
493
    return $invoice->get_description();
494
}
495
496
function wpinv_get_invoice_currency_code( $invoice_id = 0 ) {
497
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
498
    return $invoice->get_currency();
499
}
500
501
function wpinv_get_payment_user_email( $invoice_id ) {
502
    $invoice = new WPInv_Invoice( $invoice_id );
503
    return $invoice->get_email();
504
}
505
506
function wpinv_get_user_id( $invoice_id ) {
507
    $invoice = new WPInv_Invoice( $invoice_id );
508
    return $invoice->get_user_id();
509
}
510
511
function wpinv_get_invoice_status( $invoice_id, $return_label = false ) {
512
    $invoice = new WPInv_Invoice( $invoice_id );
513
    
514
    return $invoice->get_status( $return_label );
515
}
516
517
function wpinv_get_payment_gateway( $invoice_id, $return_label = false ) {
518
    $invoice = new WPInv_Invoice( $invoice_id );
519
    
520
    return $invoice->get_gateway( $return_label );
0 ignored issues
show
Unused Code introduced by
The call to WPInv_Invoice::get_gateway() has too many arguments starting with $return_label.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
521
}
522
523
function wpinv_get_payment_gateway_name( $invoice_id ) {
524
    $invoice = new WPInv_Invoice( $invoice_id );
525
    
526
    return $invoice->get_gateway_title();
527
}
528
529
function wpinv_get_payment_transaction_id( $invoice_id ) {
530
    $invoice = new WPInv_Invoice( $invoice_id );
531
    
532
    return $invoice->get_transaction_id();
533
}
534
535 View Code Duplication
function wpinv_get_id_by_transaction_id( $key ) {
0 ignored issues
show
Duplication introduced by
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...
536
    global $wpdb;
537
538
    $invoice_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wpinv_transaction_id' AND meta_value = %s LIMIT 1", $key ) );
539
540
    if ( $invoice_id != NULL )
541
        return $invoice_id;
542
543
    return 0;
544
}
545
546
function wpinv_get_invoice_meta( $invoice_id = 0, $meta_key = '_wpinv_payment_meta', $single = true ) {
547
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
548
549
    return $invoice->get_meta( $meta_key, $single );
550
}
551
552
function wpinv_update_invoice_meta( $invoice_id = 0, $meta_key = '', $meta_value = '', $prev_value = '' ) {
553
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
554
    
555
    return $invoice->update_meta( $meta_key, $meta_value, $prev_value );
556
}
557
558
function wpinv_get_items( $invoice_id = 0 ) {
559
    $invoice            = wpinv_get_invoice( $invoice_id );
560
    
561
    $items              = $invoice->get_items();
562
    $invoice_currency   = $invoice->get_currency();
563
564
    if ( !empty( $items ) && is_array( $items ) ) {
565
        foreach ( $items as $key => $item ) {
566
            $items[$key]['currency'] = $invoice_currency;
567
568
            if ( !isset( $cart_item['subtotal'] ) ) {
569
                $items[$key]['subtotal'] = $items[$key]['amount'] * 1;
570
            }
571
        }
572
    }
573
574
    return apply_filters( 'wpinv_get_items', $items, $invoice_id );
575
}
576
577
function wpinv_get_fees( $invoice_id = 0 ) {
578
    $invoice           = wpinv_get_invoice( $invoice_id );
579
    $fees              = $invoice->get_fees();
580
581
    return apply_filters( 'wpinv_get_fees', $fees, $invoice_id );
582
}
583
584
function wpinv_get_invoice_ip( $invoice_id ) {
585
    $invoice = new WPInv_Invoice( $invoice_id );
586
    return $invoice->get_ip();
587
}
588
589
function wpinv_get_invoice_user_info( $invoice_id ) {
590
    $invoice = new WPInv_Invoice( $invoice_id );
591
    return $invoice->get_user_info();
592
}
593
594
function wpinv_subtotal( $invoice_id = 0, $currency = false ) {
595
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
596
597
    return $invoice->get_subtotal( $currency );
598
}
599
600
function wpinv_tax( $invoice_id = 0, $currency = false ) {
601
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
602
603
    return $invoice->get_tax( $currency );
604
}
605
606
function wpinv_discount( $invoice_id = 0, $currency = false, $dash = false ) {
607
    $invoice = wpinv_get_invoice( $invoice_id );
608
609
    return $invoice->get_discount( $currency, $dash );
610
}
611
612
function wpinv_discount_code( $invoice_id = 0 ) {
613
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
614
615
    return $invoice->get_discount_code();
616
}
617
618
function wpinv_payment_total( $invoice_id = 0, $currency = false ) {
619
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
620
621
    return $invoice->get_total( $currency );
622
}
623
624
function wpinv_get_date_created( $invoice_id = 0, $format = '' ) {
625
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
626
627
    $format         = !empty( $format ) ? $format : get_option( 'date_format' );
628
    $date_created   = $invoice->get_created_date();
629
    $date_created   = $date_created != '' && $date_created != '0000-00-00 00:00:00' ? date_i18n( $format, strtotime( $date_created ) ) : '';
630
631
    return $date_created;
632
}
633
634
function wpinv_get_invoice_date( $invoice_id = 0, $format = '', $default = true ) {
635
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
636
    
637
    $format         = !empty( $format ) ? $format : get_option( 'date_format' );
638
    $date_completed = $invoice->get_completed_date();
639
    $invoice_date   = $date_completed != '' && $date_completed != '0000-00-00 00:00:00' ? date_i18n( $format, strtotime( $date_completed ) ) : '';
640
    if ( $invoice_date == '' && $default ) {
641
        $invoice_date   = wpinv_get_date_created( $invoice_id, $format );
642
    }
643
644
    return $invoice_date;
645
}
646
647
function wpinv_get_invoice_vat_number( $invoice_id = 0 ) {
648
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
649
    
650
    return $invoice->vat_number;
651
}
652
653
function wpinv_insert_payment_note( $invoice_id = 0, $note = '', $user_type = false, $added_by_user = false, $system = false ) {
654
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
655
656
    return $invoice->add_note( $note, $user_type, $added_by_user, $system );
657
}
658
659
function wpinv_get_invoice_notes( $invoice_id = 0, $type = '' ) {
660
    global $invoicing;
661
    
662
    if ( empty( $invoice_id ) ) {
663
        return NULL;
664
    }
665
    
666
    $notes = $invoicing->notes->get_invoice_notes( $invoice_id, $type );
667
    
668
    return apply_filters( 'wpinv_invoice_notes', $notes, $invoice_id, $type );
669
}
670
671
function wpinv_get_payment_key( $invoice_id = 0 ) {
672
	$invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
673
    return $invoice->get_key();
674
}
675
676
function wpinv_get_invoice_number( $invoice_id = 0 ) {
677
    $invoice = new WPInv_Invoice( $invoice_id );
0 ignored issues
show
Documentation introduced by
$invoice_id is of type integer, 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...
678
    return $invoice->get_number();
679
}
680
681
function wpinv_get_cart_discountable_subtotal( $code_id ) {
682
    $cart_items = wpinv_get_cart_content_details();
683
    $items      = array();
684
685
    $excluded_items = wpinv_get_discount_excluded_items( $code_id );
686
687
    if( $cart_items ) {
688
689
        foreach( $cart_items as $item ) {
690
691
            if( ! in_array( $item['id'], $excluded_items ) ) {
692
                $items[] =  $item;
693
            }
694
        }
695
    }
696
697
    $subtotal = wpinv_get_cart_items_subtotal( $items );
698
699
    return apply_filters( 'wpinv_get_cart_discountable_subtotal', $subtotal );
700
}
701
702
function wpinv_get_cart_items_subtotal( $items ) {
703
    $subtotal = 0.00;
704
705
    if ( is_array( $items ) && ! empty( $items ) ) {
706
        $prices = wp_list_pluck( $items, 'subtotal' );
707
708
        if( is_array( $prices ) ) {
709
            $subtotal = array_sum( $prices );
710
        } else {
711
            $subtotal = 0.00;
712
        }
713
714
        if( $subtotal < 0 ) {
715
            $subtotal = 0.00;
716
        }
717
    }
718
719
    return apply_filters( 'wpinv_get_cart_items_subtotal', $subtotal );
720
}
721
722
function wpinv_get_cart_subtotal( $items = array() ) {
723
    $items    = !empty( $items ) ? $items : wpinv_get_cart_content_details();
724
    $subtotal = wpinv_get_cart_items_subtotal( $items );
725
726
    return apply_filters( 'wpinv_get_cart_subtotal', $subtotal );
727
}
728
729
function wpinv_cart_subtotal( $items = array() ) {
730
    $price = wpinv_price( wpinv_format_amount( wpinv_get_cart_subtotal( $items ) ) );
731
732
    return $price;
733
}
734
735
function wpinv_get_cart_total( $items = array(), $discounts = false, $invoice = array() ) {
0 ignored issues
show
Unused Code introduced by
The parameter $discounts 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...
736
    $subtotal  = (float)wpinv_get_cart_subtotal( $items );
737
    $discounts = (float)wpinv_get_cart_discounted_amount( $items );
738
    $cart_tax  = (float)wpinv_get_cart_tax( $items );
739
    $fees      = (float)wpinv_get_cart_fee_total();
740
    if ( !empty( $invoice ) && $invoice->is_free_trial() ) {
0 ignored issues
show
Bug introduced by
The method is_free_trial cannot be called on $invoice (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
741
        $total = 0;
742
    } else {
743
        $total     = $subtotal - $discounts + $cart_tax + $fees;
744
    }
745
746
    if ( $total < 0 ) {
747
        $total = 0.00;
748
    }
749
    
750
    $total = (float)apply_filters( 'wpinv_get_cart_total', $total, $items );
751
752
    return wpinv_sanitize_amount( $total );
753
}
754
755
function wpinv_cart_total( $cart_items = array(), $echo = true, $invoice = array() ) {
756
    global $cart_total;
757
    $total = wpinv_price( wpinv_format_amount( wpinv_get_cart_total( $cart_items, NULL, $invoice ) ) );
0 ignored issues
show
Documentation introduced by
NULL is of type null, 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...
758
    $total = apply_filters( 'wpinv_cart_total', $total, $cart_items, $invoice );
759
    
760
    $cart_total = $total;
761
762
    if ( !$echo ) {
763
        return $total;
764
    }
765
766
    echo $total;
767
}
768
769 View Code Duplication
function wpinv_get_cart_tax( $items = array() ) {
0 ignored issues
show
Duplication introduced by
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...
770
    $cart_tax = 0;
771
    $items    = !empty( $items ) ? $items : wpinv_get_cart_content_details();
772
773
    if ( $items ) {
774
        $taxes = wp_list_pluck( $items, 'tax' );
775
776
        if( is_array( $taxes ) ) {
777
            $cart_tax = array_sum( $taxes );
778
        }
779
    }
780
781
    $cart_tax += wpinv_get_cart_fee_tax();
782
783
    return apply_filters( 'wpinv_get_cart_tax', wpinv_sanitize_amount( $cart_tax ) );
784
}
785
786 View Code Duplication
function wpinv_cart_tax( $items = array(), $echo = false ) {
0 ignored issues
show
Duplication introduced by
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...
787
    $cart_tax = wpinv_get_cart_tax( $items );
788
    $cart_tax = wpinv_price( wpinv_format_amount( $cart_tax ) );
789
790
    $tax = apply_filters( 'wpinv_cart_tax', $cart_tax, $items );
791
792
    if ( !$echo ) {
793
        return $tax;
794
    }
795
796
    echo $tax;
797
}
798
799
function wpinv_get_cart_discount_code( $items = array() ) {
0 ignored issues
show
Unused Code introduced by
The parameter $items 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...
800
    $invoice = wpinv_get_invoice_cart();
801
    $cart_discount_code = !empty( $invoice ) ? $invoice->get_discount_code() : '';
802
    
803
    return apply_filters( 'wpinv_get_cart_discount_code', $cart_discount_code );
804
}
805
806
function wpinv_cart_discount_code( $items = array(), $echo = false ) {
807
    $cart_discount_code = wpinv_get_cart_discount_code( $items );
808
809
    if ( $cart_discount_code != '' ) {
810
        $cart_discount_code = ' (' . $cart_discount_code . ')';
811
    }
812
    
813
    $discount_code = apply_filters( 'wpinv_cart_discount_code', $cart_discount_code, $items );
814
815
    if ( !$echo ) {
816
        return $discount_code;
817
    }
818
819
    echo $discount_code;
820
}
821
822
function wpinv_get_cart_discount( $items = array() ) {
823
    $invoice = wpinv_get_invoice_cart();
824
    $cart_discount = !empty( $invoice ) ? $invoice->get_discount() : 0;
825
    
826
    return apply_filters( 'wpinv_get_cart_discount', wpinv_sanitize_amount( $cart_discount ), $items );
827
}
828
829 View Code Duplication
function wpinv_cart_discount( $items = array(), $echo = false ) {
0 ignored issues
show
Duplication introduced by
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...
830
    $cart_discount = wpinv_get_cart_discount( $items );
831
    $cart_discount = wpinv_price( wpinv_format_amount( $cart_discount ) );
832
833
    $discount = apply_filters( 'wpinv_cart_discount', $cart_discount, $items );
834
835
    if ( !$echo ) {
836
        return $discount;
837
    }
838
839
    echo $discount;
840
}
841
842
function wpinv_get_cart_fees( $type = 'all', $item_id = 0 ) {
843
    $item = new WPInv_Item( $item_id );
0 ignored issues
show
Documentation introduced by
$item_id is of type integer, 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...
844
    
845
    return $item->get_fees( $type, $item_id );
846
}
847
848
function wpinv_get_cart_fee_total() {
849
    $total  = 0;
850
    $fees = wpinv_get_cart_fees();
851
    
852
    if ( $fees ) {
853
        foreach ( $fees as $fee_id => $fee ) {
854
            $total += $fee['amount'];
855
        }
856
    }
857
858
    return apply_filters( 'wpinv_get_cart_fee_total', $total );
859
}
860
861
function wpinv_get_cart_fee_tax() {
862
    $tax  = 0;
863
    $fees = wpinv_get_cart_fees();
864
865
    if ( $fees ) {
866
        foreach ( $fees as $fee_id => $fee ) {
867
            if( ! empty( $fee['no_tax'] ) ) {
868
                continue;
869
            }
870
871
            $tax += wpinv_calculate_tax( $fee['amount'] );
872
        }
873
    }
874
875
    return apply_filters( 'wpinv_get_cart_fee_tax', $tax );
876
}
877
878
function wpinv_cart_has_recurring_item() {
879
    $cart_items = wpinv_get_cart_contents();
880
    
881
    if ( empty( $cart_items ) ) {
882
        return false;
883
    }
884
    
885
    $has_subscription = false;
886 View Code Duplication
    foreach( $cart_items as $cart_item ) {
887
        if ( !empty( $cart_item['id'] ) && wpinv_is_recurring_item( $cart_item['id'] )  ) {
888
            $has_subscription = true;
889
            break;
890
        }
891
    }
892
    
893
    return apply_filters( 'wpinv_cart_has_recurring_item', $has_subscription, $cart_items );
894
}
895
896
function wpinv_cart_has_free_trial() {
897
    $invoice = wpinv_get_invoice_cart();
898
    
899
    $free_trial = false;
900
    
901
    if ( !empty( $invoice ) && $invoice->is_free_trial() ) {
902
        $free_trial = true;
903
    }
904
    
905
    return apply_filters( 'wpinv_cart_has_free_trial', $free_trial, $invoice );
906
}
907
908
function wpinv_get_cart_contents() {
909
    $cart_details = wpinv_get_cart_details();
910
    
911
    return apply_filters( 'wpinv_get_cart_contents', $cart_details );
912
}
913
914
function wpinv_get_cart_content_details() {
915
    global $wpinv_euvat, $wpi_current_id, $wpi_item_id, $wpinv_is_last_cart_item, $wpinv_flat_discount_total;
916
    $cart_items = wpinv_get_cart_contents();
917
    
918
    if ( empty( $cart_items ) ) {
919
        return false;
920
    }
921
    $invoice = wpinv_get_invoice_cart();
922
923
    $details = array();
924
    $length  = count( $cart_items ) - 1;
0 ignored issues
show
Unused Code introduced by
$length is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
925
    
926
    if ( empty( $_POST['country'] ) ) {
927
        $_POST['country'] = $invoice->country;
928
    }
929
    if ( !isset( $_POST['state'] ) ) {
930
        $_POST['state'] = $invoice->state;
931
    }
932
933
    foreach( $cart_items as $key => $item ) {
934
        $item_id            = isset( $item['id'] ) ? sanitize_text_field( $item['id'] ) : '';
935
        if ( empty( $item_id ) ) {
936
            continue;
937
        }
938
        
939
        $wpi_current_id         = $invoice->ID;
940
        $wpi_item_id            = $item_id;
941
        
942
        if ( isset( $item['custom_price'] ) && $item['custom_price'] !== '' ) {
943
            $item_price = $item['custom_price'];
944
        } else {
945
            if ( isset( $item['item_price'] ) && $item['item_price'] !== '' && $item['item_price'] !== false ) {
946
                $item_price = $item['item_price'];
947
            } else {
948
                $item_price = wpinv_get_item_price( $item_id );
949
            }
950
        }
951
        $discount           = wpinv_get_cart_item_discount_amount( $item );
952
        $discount           = apply_filters( 'wpinv_get_cart_content_details_item_discount_amount', $discount, $item );
953
        $quantity           = wpinv_get_cart_item_quantity( $item );
954
        $fees               = wpinv_get_cart_fees( 'fee', $item_id );
955
        
956
        $subtotal           = $item_price * $quantity;
957
        $tax_rate           = wpinv_get_tax_rate( $_POST['country'], $_POST['state'], $wpi_item_id );
958
        $tax_class          = $wpinv_euvat->get_item_class( $item_id );
959
        $tax                = wpinv_get_cart_item_tax( $item_id, $subtotal - $discount );
960
        
961
        if ( wpinv_prices_include_tax() ) {
962
            $subtotal -= wpinv_round_amount( $tax );
963
        }
964
        
965
        $total              = $subtotal - $discount + $tax;
966
        
967
        // Do not allow totals to go negatve
968
        if( $total < 0 ) {
969
            $total = 0;
970
        }
971
        
972
        $details[ $key ]  = array(
973
            'id'                => $item_id,
974
            'name'              => !empty($item['name']) ? $item['name'] : get_the_title( $item_id ),
975
            'item_price'        => wpinv_round_amount( $item_price ),
976
            'custom_price'      => isset( $item['custom_price'] ) ? $item['custom_price'] : '',
977
            'quantity'          => $quantity,
978
            'discount'          => wpinv_round_amount( $discount ),
979
            'subtotal'          => wpinv_round_amount( $subtotal ),
980
            'tax'               => wpinv_round_amount( $tax ),
981
            'price'             => wpinv_round_amount( $total ),
982
            'vat_rates_class'   => $tax_class,
983
            'vat_rate'          => $tax_rate,
984
            'meta'              => isset( $item['meta'] ) ? $item['meta'] : array(),
985
            'fees'              => $fees,
986
        );
987
        
988
        if ( $wpinv_is_last_cart_item ) {
989
            $wpinv_is_last_cart_item   = false;
990
            $wpinv_flat_discount_total = 0.00;
991
        }
992
    }
993
    
994
    return $details;
995
}
996
997
function wpinv_get_cart_details( $invoice_id = 0 ) {
998
    global $ajax_cart_details;
999
1000
    $invoice      = wpinv_get_invoice_cart( $invoice_id );
1001
    $cart_details = $ajax_cart_details;
1002
    if ( empty( $cart_details ) && ! empty( $invoice->cart_details ) ) {
1003
        $cart_details = $invoice->cart_details;
1004
    }
1005
1006
    if ( ! empty( $cart_details ) && is_array( $cart_details ) ) {
1007
        $invoice_currency = ! empty( $invoice->currency ) ? $invoice->currency : wpinv_get_default_country();
1008
1009
        foreach ( $cart_details as $key => $cart_item ) {
1010
            $cart_details[ $key ]['currency'] = $invoice_currency;
1011
1012
            if ( ! isset( $cart_item['subtotal'] ) ) {
1013
                $cart_details[ $key ]['subtotal'] = $cart_item['price'];
1014
            }
1015
        }
1016
    }
1017
1018
    return apply_filters( 'wpinv_get_cart_details', $cart_details, $invoice_id );
1019
}
1020
1021
function wpinv_record_status_change( $invoice_id, $new_status, $old_status ) {
1022
    if ( 'wpi_invoice' != get_post_type( $invoice_id ) ) {
1023
        return;
1024
    }
1025
1026
    if ( ( $old_status == 'wpi-pending' && $new_status == 'draft' ) || ( $old_status == 'draft' && $new_status == 'wpi-pending' ) ) {
1027
        return;
1028
    }
1029
1030
    $invoice    = wpinv_get_invoice( $invoice_id );
1031
    
1032
    $old_status = wpinv_status_nicename( $old_status );
1033
    $new_status = wpinv_status_nicename( $new_status );
1034
1035
    $status_change = sprintf( __( 'Invoice status changed from %s to %s', 'invoicing' ), $old_status, $new_status );
1036
    
1037
    // Add note
1038
    return $invoice->add_note( $status_change, false, false, true );
1039
}
1040
add_action( 'wpinv_update_status', 'wpinv_record_status_change', 100, 3 );
1041
1042
function wpinv_complete_payment( $invoice_id, $new_status, $old_status ) {
1043
    global $wpi_has_free_trial;
1044
    
1045
    $wpi_has_free_trial = false;
1046
    
1047
    if ( $old_status == 'publish' ) {
1048
        return; // Make sure that payments are only paid once
1049
    }
1050
1051
    // Make sure the payment completion is only processed when new status is paid
1052
    if ( $new_status != 'publish' ) {
1053
        return;
1054
    }
1055
1056
    $invoice = new WPInv_Invoice( $invoice_id );
1057
    if ( empty( $invoice ) ) {
1058
        return;
1059
    }
1060
1061
    $wpi_has_free_trial = $invoice->is_free_trial();
1062
    $completed_date = $invoice->completed_date;
1063
    $cart_details   = $invoice->cart_details;
1064
1065
    do_action( 'wpinv_pre_complete_payment', $invoice_id );
1066
1067
    if ( is_array( $cart_details ) ) {
1068
        // Increase purchase count and earnings
1069
        foreach ( $cart_details as $cart_index => $item ) {
1070
            // Ensure these actions only run once, ever
1071
            if ( empty( $completed_date ) ) {
1072
                do_action( 'wpinv_complete_item_payment', $item['id'], $invoice_id, $item, $cart_index );
1073
            }
1074
        }
1075
    }
1076
    
1077
    // Check for discount codes and increment their use counts
1078
    if ( $discounts = $invoice->get_discounts( true ) ) {
1079
        if( ! empty( $discounts ) ) {
1080
            foreach( $discounts as $code ) {
1081
                wpinv_increase_discount_usage( $code );
1082
            }
1083
        }
1084
    }
1085
    
1086
    // Ensure this action only runs once ever
1087
    if( empty( $completed_date ) ) {
1088
        // Save the completed date
1089
        $invoice->set( 'completed_date', current_time( 'mysql', 0 ) );
1090
        $invoice->save();
1091
1092
        do_action( 'wpinv_complete_payment', $invoice_id );
1093
    }
1094
1095
    // Empty the shopping cart
1096
    wpinv_empty_cart();
1097
}
1098
add_action( 'wpinv_update_status', 'wpinv_complete_payment', 100, 3 );
1099
1100
function wpinv_update_payment_status( $invoice_id, $new_status = 'publish' ) {    
1101
    $invoice = !empty( $invoice_id ) && is_object( $invoice_id ) ? $invoice_id : wpinv_get_invoice( (int)$invoice_id );
1102
    
1103
    if ( empty( $invoice ) ) {
1104
        return false;
1105
    }
1106
    
1107
    return $invoice->update_status( $new_status );
1108
}
1109
1110
function wpinv_cart_has_fees( $type = 'all' ) {
1111
    return false;
1112
}
1113
1114
function wpinv_validate_checkout_fields() {    
1115
    // Check if there is $_POST
1116
    if ( empty( $_POST ) ) {
1117
        return false;
1118
    }
1119
    
1120
    // Start an array to collect valid data
1121
    $valid_data = array(
1122
        'gateway'          => wpinv_checkout_validate_gateway(), // Gateway fallback
1123
        'discount'         => wpinv_checkout_validate_discounts(), // Set default discount
1124
        'cc_info'          => wpinv_checkout_validate_cc() // Credit card info
1125
    );
1126
    
1127
    // Validate agree to terms
1128
    if ( wpinv_get_option( 'show_agree_to_terms', false ) ) {
1129
        wpinv_checkout_validate_agree_to_terms();
1130
    }
1131
    
1132
    $valid_data['invoice_user'] = wpinv_checkout_validate_invoice_user();
1133
    $valid_data['current_user'] = wpinv_checkout_validate_current_user();
1134
    
1135
    // Return collected data
1136
    return $valid_data;
1137
}
1138
1139
function wpinv_checkout_validate_gateway() {
1140
    $gateway = wpinv_get_default_gateway();
1141
    
1142
    $invoice = wpinv_get_invoice_cart();
1143
    $has_subscription = $invoice->is_recurring();
1144
    if ( empty( $invoice ) ) {
1145
        wpinv_set_error( 'invalid_invoice', __( 'Your cart is empty.', 'invoicing' ) );
1146
        return $gateway;
1147
    }
1148
1149
    // Check if a gateway value is present
1150
    if ( !empty( $_REQUEST['wpi-gateway'] ) ) {
1151
        $gateway = sanitize_text_field( $_REQUEST['wpi-gateway'] );
1152
1153
        if ( $invoice->is_free() ) {
1154
            $gateway = 'manual';
1155
        } elseif ( !wpinv_is_gateway_active( $gateway ) ) {
1156
            wpinv_set_error( 'invalid_gateway', __( 'The selected payment gateway is not enabled', 'invoicing' ) );
1157
        } elseif ( $has_subscription && !wpinv_gateway_support_subscription( $gateway ) ) {
1158
            wpinv_set_error( 'invalid_gateway', __( 'The selected payment gateway doesnot support subscription payment', 'invoicing' ) );
1159
        }
1160
    }
1161
1162
    if ( $has_subscription && count( wpinv_get_cart_contents() ) > 1 ) {
1163
        wpinv_set_error( 'subscription_invalid', __( 'Only one subscription may be purchased through payment per checkout.', 'invoicing' ) );
1164
    }
1165
1166
    return $gateway;
1167
}
1168
1169
function wpinv_checkout_validate_discounts() {
1170
    global $wpi_cart;
1171
    
1172
    // Retrieve the discount stored in cookies
1173
    $discounts = wpinv_get_cart_discounts();
1174
    
1175
    $error = false;
1176
    // If we have discounts, loop through them
1177
    if ( ! empty( $discounts ) ) {
1178
        foreach ( $discounts as $discount ) {
1179
            // Check if valid
1180
            if (  !wpinv_is_discount_valid( $discount, (int)$wpi_cart->get_user_id() ) ) {
1181
                // Discount is not valid
1182
                $error = true;
1183
            }
1184
        }
1185
    } else {
1186
        // No discounts
1187
        return NULL;
1188
    }
1189
1190
    if ( $error && !wpinv_get_errors() ) {
1191
        wpinv_set_error( 'invalid_discount', __( 'Discount code you entered is invalid', 'invoicing' ) );
1192
    }
1193
1194
    return implode( ',', $discounts );
1195
}
1196
1197
function wpinv_checkout_validate_cc() {
1198
    $card_data = wpinv_checkout_get_cc_info();
1199
1200
    // Validate the card zip
1201
    if ( !empty( $card_data['wpinv_zip'] ) ) {
1202
        if ( !wpinv_checkout_validate_cc_zip( $card_data['wpinv_zip'], $card_data['wpinv_country'] ) ) {
1203
            wpinv_set_error( 'invalid_cc_zip', __( 'The zip / postcode you entered for your billing address is invalid', 'invoicing' ) );
1204
        }
1205
    }
1206
1207
    // This should validate card numbers at some point too
1208
    return $card_data;
1209
}
1210
1211
function wpinv_checkout_get_cc_info() {
1212
	$cc_info = array();
1213
	$cc_info['card_name']      = isset( $_POST['card_name'] )       ? sanitize_text_field( $_POST['card_name'] )       : '';
1214
	$cc_info['card_number']    = isset( $_POST['card_number'] )     ? sanitize_text_field( $_POST['card_number'] )     : '';
1215
	$cc_info['card_cvc']       = isset( $_POST['card_cvc'] )        ? sanitize_text_field( $_POST['card_cvc'] )        : '';
1216
	$cc_info['card_exp_month'] = isset( $_POST['card_exp_month'] )  ? sanitize_text_field( $_POST['card_exp_month'] )  : '';
1217
	$cc_info['card_exp_year']  = isset( $_POST['card_exp_year'] )   ? sanitize_text_field( $_POST['card_exp_year'] )   : '';
1218
	$cc_info['card_address']   = isset( $_POST['wpinv_address'] )  ? sanitize_text_field( $_POST['wpinv_address'] ) : '';
1219
	$cc_info['card_city']      = isset( $_POST['wpinv_city'] )     ? sanitize_text_field( $_POST['wpinv_city'] )    : '';
1220
	$cc_info['card_state']     = isset( $_POST['wpinv_state'] )    ? sanitize_text_field( $_POST['wpinv_state'] )   : '';
1221
	$cc_info['card_country']   = isset( $_POST['wpinv_country'] )  ? sanitize_text_field( $_POST['wpinv_country'] ) : '';
1222
	$cc_info['card_zip']       = isset( $_POST['wpinv_zip'] )      ? sanitize_text_field( $_POST['wpinv_zip'] )     : '';
1223
1224
	// Return cc info
1225
	return $cc_info;
1226
}
1227
1228
function wpinv_checkout_validate_cc_zip( $zip = 0, $country_code = '' ) {
1229
    $ret = false;
1230
1231
    if ( empty( $zip ) || empty( $country_code ) )
1232
        return $ret;
1233
1234
    $country_code = strtoupper( $country_code );
1235
1236
    $zip_regex = array(
1237
        "AD" => "AD\d{3}",
1238
        "AM" => "(37)?\d{4}",
1239
        "AR" => "^([A-Z]{1}\d{4}[A-Z]{3}|[A-Z]{1}\d{4}|\d{4})$",
1240
        "AS" => "96799",
1241
        "AT" => "\d{4}",
1242
        "AU" => "^(0[289][0-9]{2})|([1345689][0-9]{3})|(2[0-8][0-9]{2})|(290[0-9])|(291[0-4])|(7[0-4][0-9]{2})|(7[8-9][0-9]{2})$",
1243
        "AX" => "22\d{3}",
1244
        "AZ" => "\d{4}",
1245
        "BA" => "\d{5}",
1246
        "BB" => "(BB\d{5})?",
1247
        "BD" => "\d{4}",
1248
        "BE" => "^[1-9]{1}[0-9]{3}$",
1249
        "BG" => "\d{4}",
1250
        "BH" => "((1[0-2]|[2-9])\d{2})?",
1251
        "BM" => "[A-Z]{2}[ ]?[A-Z0-9]{2}",
1252
        "BN" => "[A-Z]{2}[ ]?\d{4}",
1253
        "BR" => "\d{5}[\-]?\d{3}",
1254
        "BY" => "\d{6}",
1255
        "CA" => "^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$",
1256
        "CC" => "6799",
1257
        "CH" => "^[1-9][0-9][0-9][0-9]$",
1258
        "CK" => "\d{4}",
1259
        "CL" => "\d{7}",
1260
        "CN" => "\d{6}",
1261
        "CR" => "\d{4,5}|\d{3}-\d{4}",
1262
        "CS" => "\d{5}",
1263
        "CV" => "\d{4}",
1264
        "CX" => "6798",
1265
        "CY" => "\d{4}",
1266
        "CZ" => "\d{3}[ ]?\d{2}",
1267
        "DE" => "\b((?:0[1-46-9]\d{3})|(?:[1-357-9]\d{4})|(?:[4][0-24-9]\d{3})|(?:[6][013-9]\d{3}))\b",
1268
        "DK" => "^([D-d][K-k])?( |-)?[1-9]{1}[0-9]{3}$",
1269
        "DO" => "\d{5}",
1270
        "DZ" => "\d{5}",
1271
        "EC" => "([A-Z]\d{4}[A-Z]|(?:[A-Z]{2})?\d{6})?",
1272
        "EE" => "\d{5}",
1273
        "EG" => "\d{5}",
1274
        "ES" => "^([1-9]{2}|[0-9][1-9]|[1-9][0-9])[0-9]{3}$",
1275
        "ET" => "\d{4}",
1276
        "FI" => "\d{5}",
1277
        "FK" => "FIQQ 1ZZ",
1278
        "FM" => "(9694[1-4])([ \-]\d{4})?",
1279
        "FO" => "\d{3}",
1280
        "FR" => "^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$",
1281
        "GE" => "\d{4}",
1282
        "GF" => "9[78]3\d{2}",
1283
        "GL" => "39\d{2}",
1284
        "GN" => "\d{3}",
1285
        "GP" => "9[78][01]\d{2}",
1286
        "GR" => "\d{3}[ ]?\d{2}",
1287
        "GS" => "SIQQ 1ZZ",
1288
        "GT" => "\d{5}",
1289
        "GU" => "969[123]\d([ \-]\d{4})?",
1290
        "GW" => "\d{4}",
1291
        "HM" => "\d{4}",
1292
        "HN" => "(?:\d{5})?",
1293
        "HR" => "\d{5}",
1294
        "HT" => "\d{4}",
1295
        "HU" => "\d{4}",
1296
        "ID" => "\d{5}",
1297
        "IE" => "((D|DUBLIN)?([1-9]|6[wW]|1[0-8]|2[024]))?",
1298
        "IL" => "\d{5}",
1299
        "IN"=> "^[1-9][0-9][0-9][0-9][0-9][0-9]$", //india
1300
        "IO" => "BBND 1ZZ",
1301
        "IQ" => "\d{5}",
1302
        "IS" => "\d{3}",
1303
        "IT" => "^(V-|I-)?[0-9]{5}$",
1304
        "JO" => "\d{5}",
1305
        "JP" => "\d{3}-\d{4}",
1306
        "KE" => "\d{5}",
1307
        "KG" => "\d{6}",
1308
        "KH" => "\d{5}",
1309
        "KR" => "\d{3}[\-]\d{3}",
1310
        "KW" => "\d{5}",
1311
        "KZ" => "\d{6}",
1312
        "LA" => "\d{5}",
1313
        "LB" => "(\d{4}([ ]?\d{4})?)?",
1314
        "LI" => "(948[5-9])|(949[0-7])",
1315
        "LK" => "\d{5}",
1316
        "LR" => "\d{4}",
1317
        "LS" => "\d{3}",
1318
        "LT" => "\d{5}",
1319
        "LU" => "\d{4}",
1320
        "LV" => "\d{4}",
1321
        "MA" => "\d{5}",
1322
        "MC" => "980\d{2}",
1323
        "MD" => "\d{4}",
1324
        "ME" => "8\d{4}",
1325
        "MG" => "\d{3}",
1326
        "MH" => "969[67]\d([ \-]\d{4})?",
1327
        "MK" => "\d{4}",
1328
        "MN" => "\d{6}",
1329
        "MP" => "9695[012]([ \-]\d{4})?",
1330
        "MQ" => "9[78]2\d{2}",
1331
        "MT" => "[A-Z]{3}[ ]?\d{2,4}",
1332
        "MU" => "(\d{3}[A-Z]{2}\d{3})?",
1333
        "MV" => "\d{5}",
1334
        "MX" => "\d{5}",
1335
        "MY" => "\d{5}",
1336
        "NC" => "988\d{2}",
1337
        "NE" => "\d{4}",
1338
        "NF" => "2899",
1339
        "NG" => "(\d{6})?",
1340
        "NI" => "((\d{4}-)?\d{3}-\d{3}(-\d{1})?)?",
1341
        "NL" => "^[1-9][0-9]{3}\s?([a-zA-Z]{2})?$",
1342
        "NO" => "\d{4}",
1343
        "NP" => "\d{5}",
1344
        "NZ" => "\d{4}",
1345
        "OM" => "(PC )?\d{3}",
1346
        "PF" => "987\d{2}",
1347
        "PG" => "\d{3}",
1348
        "PH" => "\d{4}",
1349
        "PK" => "\d{5}",
1350
        "PL" => "\d{2}-\d{3}",
1351
        "PM" => "9[78]5\d{2}",
1352
        "PN" => "PCRN 1ZZ",
1353
        "PR" => "00[679]\d{2}([ \-]\d{4})?",
1354
        "PT" => "\d{4}([\-]\d{3})?",
1355
        "PW" => "96940",
1356
        "PY" => "\d{4}",
1357
        "RE" => "9[78]4\d{2}",
1358
        "RO" => "\d{6}",
1359
        "RS" => "\d{5}",
1360
        "RU" => "\d{6}",
1361
        "SA" => "\d{5}",
1362
        "SE" => "^(s-|S-){0,1}[0-9]{3}\s?[0-9]{2}$",
1363
        "SG" => "\d{6}",
1364
        "SH" => "(ASCN|STHL) 1ZZ",
1365
        "SI" => "\d{4}",
1366
        "SJ" => "\d{4}",
1367
        "SK" => "\d{3}[ ]?\d{2}",
1368
        "SM" => "4789\d",
1369
        "SN" => "\d{5}",
1370
        "SO" => "\d{5}",
1371
        "SZ" => "[HLMS]\d{3}",
1372
        "TC" => "TKCA 1ZZ",
1373
        "TH" => "\d{5}",
1374
        "TJ" => "\d{6}",
1375
        "TM" => "\d{6}",
1376
        "TN" => "\d{4}",
1377
        "TR" => "\d{5}",
1378
        "TW" => "\d{3}(\d{2})?",
1379
        "UA" => "\d{5}",
1380
        "UK" => "^(GIR|[A-Z]\d[A-Z\d]??|[A-Z]{2}\d[A-Z\d]??)[ ]??(\d[A-Z]{2})$",
1381
        "US" => "^\d{5}([\-]?\d{4})?$",
1382
        "UY" => "\d{5}",
1383
        "UZ" => "\d{6}",
1384
        "VA" => "00120",
1385
        "VE" => "\d{4}",
1386
        "VI" => "008(([0-4]\d)|(5[01]))([ \-]\d{4})?",
1387
        "WF" => "986\d{2}",
1388
        "YT" => "976\d{2}",
1389
        "YU" => "\d{5}",
1390
        "ZA" => "\d{4}",
1391
        "ZM" => "\d{5}"
1392
    );
1393
1394
    if ( ! isset ( $zip_regex[ $country_code ] ) || preg_match( "/" . $zip_regex[ $country_code ] . "/i", $zip ) )
1395
        $ret = true;
1396
1397
    return apply_filters( 'wpinv_is_zip_valid', $ret, $zip, $country_code );
1398
}
1399
1400
function wpinv_checkout_validate_agree_to_terms() {
1401
    // Validate agree to terms
1402
    if ( ! isset( $_POST['wpi_agree_to_terms'] ) || $_POST['wpi_agree_to_terms'] != 1 ) {
1403
        // User did not agree
1404
        wpinv_set_error( 'agree_to_terms', apply_filters( 'wpinv_agree_to_terms_text', __( 'You must agree to the terms of use', 'invoicing' ) ) );
1405
    }
1406
}
1407
1408
function wpinv_checkout_validate_invoice_user() {
1409
    global $wpi_cart;
1410
    
1411
    $valid_user_data = array(
1412
        'user_id' => -1
1413
    );
1414
    
1415
    // Verify there is a user_ID
1416
    if ( $user_ID = (int)$wpi_cart->get_user_id() ) {
1417
        // Get the logged in user data
1418
        $user_data = get_userdata( $user_ID );
1419
        $required_fields  = wpinv_checkout_required_fields();
1420
1421
        // Loop through required fields and show error messages
1422
         if ( !empty( $required_fields ) ) {
1423
            foreach ( $required_fields as $field_name => $value ) {
1424
                if ( in_array( $value, $required_fields ) && empty( $_POST[ 'wpinv_' . $field_name ] ) ) {
1425
                    wpinv_set_error( $value['error_id'], $value['error_message'] );
1426
                }
1427
            }
1428
        }
1429
1430
        // Verify data
1431
        if ( $user_data ) {
1432
            // Collected logged in user data
1433
            $valid_user_data = array(
1434
                'user_id'     => $user_ID,
1435
                'email'       => isset( $_POST['wpinv_email'] ) ? sanitize_email( $_POST['wpinv_email'] ) : $user_data->user_email,
1436
                'first_name'  => isset( $_POST['wpinv_first_name'] ) && ! empty( $_POST['wpinv_first_name'] ) ? sanitize_text_field( $_POST['wpinv_first_name'] ) : $user_data->first_name,
1437
                'last_name'   => isset( $_POST['wpinv_last_name'] ) && ! empty( $_POST['wpinv_last_name']  ) ? sanitize_text_field( $_POST['wpinv_last_name']  ) : $user_data->last_name,
1438
            );
1439
1440
            if ( !empty( $_POST[ 'wpinv_email' ] ) && !is_email( $_POST[ 'wpinv_email' ] ) ) {
1441
                wpinv_set_error( 'invalid_email', __( 'Please enter a valid email address', 'invoicing' ) );
1442
            }
1443
        } else {
1444
            // Set invalid user error
1445
            wpinv_set_error( 'invalid_user', __( 'The user billing information is invalid', 'invoicing' ) );
1446
        }
1447
    } else {
1448
        // Set invalid user error
1449
        wpinv_set_error( 'invalid_user_id', __( 'The invalid invoice user id', 'invoicing' ) );
1450
    }
1451
1452
    // Return user data
1453
    return $valid_user_data;
1454
}
1455
1456
function wpinv_checkout_validate_current_user() {
1457
    global $wpi_cart;
1458
1459
    $data = array();
1460
    
1461
    if ( is_user_logged_in() ) {
1462
        if ( !wpinv_require_login_to_checkout() || ( wpinv_require_login_to_checkout() && (int)$wpi_cart->get_user_id() === (int)get_current_user_id() ) ) {
1463
            $data['user_id'] = (int)get_current_user_id();
1464
        } else {
1465
            wpinv_set_error( 'logged_in_only', __( 'You are not allowed to pay for this invoice', 'invoicing' ) );
1466
        }
1467
    } else {
1468
        // If guest checkout allowed
1469
        if ( !wpinv_require_login_to_checkout() ) {
1470
            $data['user_id'] = 0;
1471
        } else {
1472
            wpinv_set_error( 'logged_in_only', __( 'You must be logged in to pay for this invoice', 'invoicing' ) );
1473
        }
1474
    }
1475
1476
    return $data;
1477
}
1478
1479
function wpinv_checkout_form_get_user( $valid_data = array() ) {
1480
    // Initialize user
1481
    $user    = false;
0 ignored issues
show
Unused Code introduced by
$user is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1482
    $is_ajax = defined( 'DOING_AJAX' ) && DOING_AJAX;
0 ignored issues
show
Unused Code introduced by
$is_ajax is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1483
1484
    if ( empty( $valid_data['current_user'] ) ) {
1485
        $user = false;
1486
    } else {
1487
        // Set the valid invoice user
1488
        $user = $valid_data['invoice_user'];
1489
    }
1490
1491
    // Verify invoice have an user
1492
    if ( false === $user || empty( $user ) ) {
1493
        return false;
1494
    }
1495
1496
    $address_fields = array(
1497
        'first_name',
1498
        'last_name',
1499
        'company',
1500
        'vat_number',
1501
        'phone',
1502
        'address',
1503
        'city',
1504
        'state',
1505
        'country',
1506
        'zip',
1507
    );
1508
    
1509
    foreach ( $address_fields as $field ) {
1510
        $user[$field]  = !empty( $_POST['wpinv_' . $field] ) ? sanitize_text_field( $_POST['wpinv_' . $field] ) : false;
1511
        
1512
        if ( !empty( $user['user_id'] ) && !empty( $valid_data['current_user']['user_id'] ) && $valid_data['current_user']['user_id'] == $user['user_id'] ) {
1513
            update_user_meta( $user['user_id'], '_wpinv_' . $field, $user[$field] );
1514
        }
1515
    }
1516
1517
    // Return valid user
1518
    return $user;
1519
}
1520
1521
function wpinv_set_checkout_session( $invoice_data = array() ) {
1522
    global $wpi_session;
1523
    
1524
    return $wpi_session->set( 'wpinv_checkout', $invoice_data );
1525
}
1526
1527
function wpinv_get_checkout_session() {
1528
	global $wpi_session;
1529
    
1530
    return $wpi_session->get( 'wpinv_checkout' );
1531
}
1532
1533
function wpinv_empty_cart() {
1534
    global $wpi_session;
1535
1536
    // Remove cart contents
1537
    $wpi_session->set( 'wpinv_checkout', NULL );
1538
1539
    // Remove all cart fees
1540
    $wpi_session->set( 'wpi_cart_fees', NULL );
1541
1542
    do_action( 'wpinv_empty_cart' );
1543
}
1544
1545
function wpinv_process_checkout() {
1546
    global $wpinv_euvat, $wpi_checkout_id, $wpi_cart;
1547
    
1548
    wpinv_clear_errors();
1549
    
1550
    $invoice = wpinv_get_invoice_cart();
1551
    $wpi_cart = $invoice;
1552
    
1553
    $wpi_checkout_id = $invoice->ID;
1554
    
1555
    do_action( 'wpinv_pre_process_checkout' );
1556
    
1557
    if ( !wpinv_get_cart_contents() ) { // Make sure the cart isn't empty
1558
        $valid_data = false;
1559
        wpinv_set_error( 'empty_cart', __( 'Your cart is empty', 'invoicing' ) );
1560
    } else {
1561
        // Validate the form $_POST data
1562
        $valid_data = wpinv_validate_checkout_fields();
1563
        
1564
        // Allow themes and plugins to hook to errors
1565
        do_action( 'wpinv_checkout_error_checks', $valid_data, $_POST );
1566
    }
1567
    
1568
    $is_ajax    = defined( 'DOING_AJAX' ) && DOING_AJAX;
1569
    
1570
    // Validate the user
1571
    $user = wpinv_checkout_form_get_user( $valid_data );
0 ignored issues
show
Security Bug introduced by
It seems like $valid_data can also be of type false; however, wpinv_checkout_form_get_user() does only seem to accept array, did you maybe forget to handle an error condition?
Loading history...
1572
1573
    // Let extensions validate fields after user is logged in if user has used login/registration form
1574
    do_action( 'wpinv_checkout_user_error_checks', $user, $valid_data, $_POST );
1575
    
1576
    if ( false === $valid_data || wpinv_get_errors() || ! $user ) {
1577
        if ( $is_ajax ) {
1578
            do_action( 'wpinv_ajax_checkout_errors' );
1579
            die();
1580
        } else {
1581
            return false;
1582
        }
1583
    }
1584
1585
    if ( $is_ajax ) {
1586
        // Save address fields.
1587
        $address_fields = array( 'first_name', 'last_name', 'phone', 'address', 'city', 'country', 'state', 'zip', 'company' );
1588 View Code Duplication
        foreach ( $address_fields as $field ) {
1589
            if ( isset( $user[$field] ) ) {
1590
                $invoice->set( $field, $user[$field] );
1591
            }
1592
            
1593
            $invoice->save();
1594
        }
1595
1596
        $response['success']            = true;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$response was never initialized. Although not strictly required by PHP, it is generally a good practice to add $response = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1597
        $response['data']['subtotal']   = $invoice->get_subtotal();
1598
        $response['data']['subtotalf']  = $invoice->get_subtotal( true );
1599
        $response['data']['discount']   = $invoice->get_discount();
1600
        $response['data']['discountf']  = $invoice->get_discount( true );
1601
        $response['data']['tax']        = $invoice->get_tax();
1602
        $response['data']['taxf']       = $invoice->get_tax( true );
1603
        $response['data']['total']      = $invoice->get_total();
1604
        $response['data']['totalf']     = $invoice->get_total( true );
1605
        
1606
        wp_send_json( $response );
1607
    }
1608
    
1609
    $user_info = array(
1610
        'user_id'        => $user['user_id'],
1611
        'first_name'     => $user['first_name'],
1612
        'last_name'      => $user['last_name'],
1613
        'email'          => $user['email'],
1614
        'company'        => $user['company'],
1615
        'phone'          => $user['phone'],
1616
        'address'        => $user['address'],
1617
        'city'           => $user['city'],
1618
        'country'        => $user['country'],
1619
        'state'          => $user['state'],
1620
        'zip'            => $user['zip'],
1621
    );
1622
    
1623
    $cart_items = wpinv_get_cart_contents();
1624
    $discounts  = wpinv_get_cart_discounts();
1625
    
1626
    // Setup invoice information
1627
    $invoice_data = array(
1628
        'invoice_id'        => !empty( $invoice ) ? $invoice->ID : 0,
1629
        'items'             => $cart_items,
1630
        'cart_discounts'    => $discounts,
1631
        'fees'              => wpinv_get_cart_fees(),        // Any arbitrary fees that have been added to the cart
1632
        'subtotal'          => wpinv_get_cart_subtotal( $cart_items ),    // Amount before taxes and discounts
1633
        'discount'          => wpinv_get_cart_items_discount_amount( $cart_items, $discounts ), // Discounted amount
1634
        'tax'               => wpinv_get_cart_tax( $cart_items ),               // Taxed amount
1635
        'price'             => wpinv_get_cart_total( $cart_items, $discounts ),    // Amount after taxes
1636
        'invoice_key'       => $invoice->get_key() ? $invoice->get_key() : $invoice->generate_key(),
1637
        'user_email'        => $user['email'],
1638
        'date'              => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ),
1639
        'user_info'         => stripslashes_deep( $user_info ),
1640
        'post_data'         => $_POST,
1641
        'cart_details'      => $cart_items,
1642
        'gateway'           => $valid_data['gateway'],
1643
        'card_info'         => $valid_data['cc_info']
1644
    );
1645
    
1646
    $vat_info   = $wpinv_euvat->current_vat_data();
1647
    if ( is_array( $vat_info ) ) {
1648
        $invoice_data['user_info']['vat_number']        = $vat_info['number'];
1649
        $invoice_data['user_info']['vat_rate']          = wpinv_get_tax_rate($invoice_data['user_info']['country'], $invoice_data['user_info']['state']);
1650
        $invoice_data['user_info']['adddress_confirmed']    = isset($vat_info['adddress_confirmed']) ? $vat_info['adddress_confirmed'] : false;
1651
1652
        // Add the VAT rate to each item in the cart
1653
        foreach( $invoice_data['cart_details'] as $key => $item_data) {
1654
            $rate = wpinv_get_tax_rate($invoice_data['user_info']['country'], $invoice_data['user_info']['state'], $item_data['id']);
1655
            $invoice_data['cart_details'][$key]['vat_rate'] = wpinv_round_amount( $rate, 4 );
1656
        }
1657
    }
1658
    
1659
    // Save vat fields.
1660
    $address_fields = array( 'vat_number', 'vat_rate', 'adddress_confirmed' );
1661 View Code Duplication
    foreach ( $address_fields as $field ) {
1662
        if ( isset( $invoice_data['user_info'][$field] ) ) {
1663
            $invoice->set( $field, $invoice_data['user_info'][$field] );
1664
        }
1665
        
1666
        $invoice->save();
1667
    }
1668
1669
    // Add the user data for hooks
1670
    $valid_data['user'] = $user;
1671
    
1672
    // Allow themes and plugins to hook before the gateway
1673
    do_action( 'wpinv_checkout_before_gateway', $_POST, $user_info, $valid_data );
1674
    
1675
    // If the total amount in the cart is 0, send to the manual gateway. This emulates a free invoice
1676
    if ( !$invoice_data['price'] ) {
1677
        // Revert to manual
1678
        $invoice_data['gateway'] = 'manual';
1679
        $_POST['wpi-gateway'] = 'manual';
1680
    }
1681
    
1682
    // Allow the invoice data to be modified before it is sent to the gateway
1683
    $invoice_data = apply_filters( 'wpinv_data_before_gateway', $invoice_data, $valid_data );
1684
    
1685
    if ( $invoice_data['price'] && $invoice_data['gateway'] == 'manual' ) {
1686
        $mode = 'test';
1687
    } else {
1688
        $mode = wpinv_is_test_mode( $invoice_data['gateway'] ) ? 'test' : 'live';
1689
    }
1690
    
1691
    // Setup the data we're storing in the purchase session
1692
    $session_data = $invoice_data;
1693
    // Make sure credit card numbers are never stored in sessions
1694
    if ( !empty( $session_data['card_info']['card_number'] ) ) {
1695
        unset( $session_data['card_info']['card_number'] );
1696
    }
1697
    
1698
    // Used for showing item links to non logged-in users after purchase, and for other plugins needing purchase data.
1699
    wpinv_set_checkout_session( $invoice_data );
1700
    
1701
    // Set gateway
1702
    $invoice->update_meta( '_wpinv_gateway', $invoice_data['gateway'] );
1703
    $invoice->update_meta( '_wpinv_mode', $mode );
1704
    $invoice->update_meta( '_wpinv_checkout', date_i18n( 'Y-m-d H:i:s', current_time( 'timestamp' ) ) );
1705
    
1706
    do_action( 'wpinv_checkout_before_send_to_gateway', $invoice, $invoice_data );
1707
1708
    // Send info to the gateway for payment processing
1709
    wpinv_send_to_gateway( $invoice_data['gateway'], $invoice_data );
1710
    die();
1711
}
1712
add_action( 'wpinv_payment', 'wpinv_process_checkout' );
1713
1714
function wpinv_get_invoices( $args ) {
1715
    $args = wp_parse_args( $args, array(
1716
        'status'   => array_keys( wpinv_get_invoice_statuses() ),
1717
        'type'     => 'wpi_invoice',
1718
        'parent'   => null,
1719
        'user'     => null,
1720
        'email'    => '',
1721
        'limit'    => get_option( 'posts_per_page' ),
1722
        'offset'   => null,
1723
        'page'     => 1,
1724
        'exclude'  => array(),
1725
        'orderby'  => 'date',
1726
        'order'    => 'DESC',
1727
        'return'   => 'objects',
1728
        'paginate' => false,
1729
    ) );
1730
    
1731
    // Handle some BW compatibility arg names where wp_query args differ in naming.
1732
    $map_legacy = array(
1733
        'numberposts'    => 'limit',
1734
        'post_type'      => 'type',
1735
        'post_status'    => 'status',
1736
        'post_parent'    => 'parent',
1737
        'author'         => 'user',
1738
        'posts_per_page' => 'limit',
1739
        'paged'          => 'page',
1740
    );
1741
1742
    foreach ( $map_legacy as $from => $to ) {
1743
        if ( isset( $args[ $from ] ) ) {
1744
            $args[ $to ] = $args[ $from ];
1745
        }
1746
    }
1747
1748
    if ( get_query_var( 'paged' ) )
1749
        $args['page'] = get_query_var('paged');
1750
    else if ( get_query_var( 'page' ) )
1751
        $args['page'] = get_query_var( 'page' );
1752
    else if ( !empty( $args[ 'page' ] ) )
1753
        $args['page'] = $args[ 'page' ];
1754
    else
1755
        $args['page'] = 1;
1756
1757
    /**
1758
     * Generate WP_Query args. This logic will change if orders are moved to
1759
     * custom tables in the future.
1760
     */
1761
    $wp_query_args = array(
1762
        'post_type'      => 'wpi_invoice',
1763
        'post_status'    => $args['status'],
1764
        'posts_per_page' => $args['limit'],
1765
        'meta_query'     => array(),
1766
        'date_query'     => !empty( $args['date_query'] ) ? $args['date_query'] : array(),
1767
        'fields'         => 'ids',
1768
        'orderby'        => $args['orderby'],
1769
        'order'          => $args['order'],
1770
    );
1771
    
1772
    if ( !empty( $args['user'] ) ) {
1773
        $wp_query_args['author'] = absint( $args['user'] );
1774
    }
1775
1776
    if ( ! is_null( $args['parent'] ) ) {
1777
        $wp_query_args['post_parent'] = absint( $args['parent'] );
1778
    }
1779
1780
    if ( ! is_null( $args['offset'] ) ) {
1781
        $wp_query_args['offset'] = absint( $args['offset'] );
1782
    } else {
1783
        $wp_query_args['paged'] = absint( $args['page'] );
1784
    }
1785
1786
    if ( ! empty( $args['exclude'] ) ) {
1787
        $wp_query_args['post__not_in'] = array_map( 'absint', $args['exclude'] );
1788
    }
1789
1790
    if ( ! $args['paginate' ] ) {
1791
        $wp_query_args['no_found_rows'] = true;
1792
    }
1793
1794
    // Get results.
1795
    $invoices = new WP_Query( $wp_query_args );
1796
1797
    if ( 'objects' === $args['return'] ) {
1798
        $return = array_map( 'wpinv_get_invoice', $invoices->posts );
1799
    } elseif ( 'self' === $args['return'] ) {
1800
        return $invoices;
1801
    } else {
1802
        $return = $invoices->posts;
1803
    }
1804
1805
    if ( $args['paginate' ] ) {
1806
        return (object) array(
1807
            'invoices'      => $return,
1808
            'total'         => $invoices->found_posts,
1809
            'max_num_pages' => $invoices->max_num_pages,
1810
        );
1811
    } else {
1812
        return $return;
1813
    }
1814
}
1815
1816
function wpinv_get_user_invoices_columns() {
1817
    $columns = array(
1818
            'invoice-number'  => array( 'title' => __( 'ID', 'invoicing' ), 'class' => 'text-left' ),
1819
            'created-date'    => array( 'title' => __( 'Created Date', 'invoicing' ), 'class' => 'text-left' ),
1820
            'payment-date'    => array( 'title' => __( 'Payment Date', 'invoicing' ), 'class' => 'text-left' ),
1821
            'invoice-status'  => array( 'title' => __( 'Status', 'invoicing' ), 'class' => 'text-center' ),
1822
            'invoice-total'   => array( 'title' => __( 'Total', 'invoicing' ), 'class' => 'text-right' ),
1823
            'invoice-actions' => array( 'title' => '&nbsp;', 'class' => 'text-center' ),
1824
        );
1825
1826
    return apply_filters( 'wpinv_user_invoices_columns', $columns );
1827
}
1828
1829
function wpinv_payment_receipt( $atts, $content = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $content 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...
1830
    global $wpinv_receipt_args;
1831
1832
    $wpinv_receipt_args = shortcode_atts( array(
1833
        'error'           => __( 'Sorry, trouble retrieving payment receipt.', 'invoicing' ),
1834
        'price'           => true,
1835
        'discount'        => true,
1836
        'items'           => true,
1837
        'date'            => true,
1838
        'notes'           => true,
1839
        'invoice_key'     => false,
1840
        'payment_method'  => true,
1841
        'invoice_id'      => true
1842
    ), $atts, 'wpinv_receipt' );
1843
1844
    $session = wpinv_get_checkout_session();
1845
    if ( isset( $_GET['invoice_key'] ) ) {
1846
        $invoice_key = urldecode( $_GET['invoice_key'] );
1847
    } else if ( $session && isset( $session['invoice_key'] ) ) {
1848
        $invoice_key = $session['invoice_key'];
1849
    } elseif ( isset( $wpinv_receipt_args['invoice_key'] ) && $wpinv_receipt_args['invoice_key'] ) {
1850
        $invoice_key = $wpinv_receipt_args['invoice_key'];
1851
    } else if ( isset( $_GET['invoice-id'] ) ) {
1852
        $invoice_key = wpinv_get_payment_key( (int)$_GET['invoice-id'] );
1853
    }
1854
1855
    // No key found
1856
    if ( ! isset( $invoice_key ) ) {
1857
        return '<p class="alert alert-error">' . $wpinv_receipt_args['error'] . '</p>';
1858
    }
1859
1860
    $invoice_id    = wpinv_get_invoice_id_by_key( $invoice_key );
0 ignored issues
show
Unused Code introduced by
$invoice_id is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1861
    $user_can_view = wpinv_can_view_receipt( $invoice_key );
1862 View Code Duplication
    if ( $user_can_view && isset( $_GET['invoice-id'] ) ) {
1863
        $invoice_id     = (int)$_GET['invoice-id'];
0 ignored issues
show
Unused Code introduced by
$invoice_id is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1864
        $user_can_view  = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? true : false;
1865
    }
1866
1867
    // Key was provided, but user is logged out. Offer them the ability to login and view the receipt
1868
    if ( ! $user_can_view && ! empty( $invoice_key ) && ! is_user_logged_in() ) {
1869
        // login redirect
1870
        return '<p class="alert alert-error">' . __( 'You are not allowed to access this section', 'invoicing' ) . '</p>';
1871
    }
1872
1873
    if ( ! apply_filters( 'wpinv_user_can_view_receipt', $user_can_view, $wpinv_receipt_args ) ) {
1874
        return '<p class="alert alert-error">' . $wpinv_receipt_args['error'] . '</p>';
1875
    }
1876
1877
    ob_start();
1878
1879
    wpinv_get_template_part( 'wpinv-invoice-receipt' );
1880
1881
    $display = ob_get_clean();
1882
1883
    return $display;
1884
}
1885
1886 View Code Duplication
function wpinv_get_invoice_id_by_key( $key ) {
0 ignored issues
show
Duplication introduced by
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...
1887
	global $wpdb;
1888
1889
	$invoice_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wpinv_key' AND meta_value = %s LIMIT 1", $key ) );
1890
1891
	if ( $invoice_id != NULL )
1892
		return $invoice_id;
1893
1894
	return 0;
1895
}
1896
1897
function wpinv_can_view_receipt( $invoice_key = '' ) {
1898
	$return = false;
1899
1900
	if ( empty( $invoice_key ) ) {
1901
		return $return;
1902
	}
1903
1904
	global $wpinv_receipt_args;
1905
1906
	$wpinv_receipt_args['id'] = wpinv_get_invoice_id_by_key( $invoice_key );
1907
	if ( isset( $_GET['invoice-id'] ) ) {
1908
		$wpinv_receipt_args['id'] = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? (int)$_GET['invoice-id'] : 0;
1909
	}
1910
1911
	if ( empty( $wpinv_receipt_args['id'] ) ) {
1912
		return $return;
1913
	}
1914
1915
	$invoice = wpinv_get_invoice( $wpinv_receipt_args['id'] );
1916
	if ( !( !empty( $invoice->ID ) && $invoice->get_key() === $invoice_key ) ) {
1917
		return $return;
1918
	}
1919
1920
	if ( is_user_logged_in() ) {
1921
		if ( (int)$invoice->get_user_id() === (int) get_current_user_id() ) {
1922
			$return = true;
1923
		}
1924
	}
1925
1926
	$session = wpinv_get_checkout_session();
1927
	if ( isset( $_GET['invoice_key'] ) || ( $session && isset( $session['invoice_key'] ) ) ) {
1928
		$check_key = isset( $_GET['invoice_key'] ) ? $_GET['invoice_key'] : $session['invoice_key'];
1929
1930
		if ( wpinv_require_login_to_checkout() ) {
1931
			$return = $return && $check_key === $invoice_key;
1932
		} else {
1933
			$return = $check_key === $invoice_key;
1934
		}
1935
	}
1936
1937
	return (bool) apply_filters( 'wpinv_can_view_receipt', $return, $invoice_key );
1938
}
1939
1940
function wpinv_pay_for_invoice() {
1941
    global $wpinv_euvat;
1942
    
1943
    if ( isset( $_GET['invoice_key'] ) ) {
1944
        $checkout_uri   = wpinv_get_checkout_uri();
1945
        $invoice_key    = sanitize_text_field( $_GET['invoice_key'] );
1946
        
1947
        if ( empty( $invoice_key ) ) {
1948
            wpinv_set_error( 'invalid_invoice', __( 'Invoice not found', 'invoicing' ) );
1949
            wp_redirect( $checkout_uri );
1950
            wpinv_die();
1951
        }
1952
        
1953
        do_action( 'wpinv_check_pay_for_invoice', $invoice_key );
1954
1955
        $invoice_id    = wpinv_get_invoice_id_by_key( $invoice_key );
1956
        $user_can_view = wpinv_can_view_receipt( $invoice_key );
1957 View Code Duplication
        if ( $user_can_view && isset( $_GET['invoice-id'] ) ) {
1958
            $invoice_id     = (int)$_GET['invoice-id'];
1959
            $user_can_view  = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? true : false;
1960
        }
1961
        
1962
        if ( $invoice_id && $user_can_view && ( $invoice = wpinv_get_invoice( $invoice_id ) ) ) {
1963
            if ( $invoice->needs_payment() ) {
1964
                $data                   = array();
1965
                $data['invoice_id']     = $invoice_id;
1966
                $data['cart_discounts'] = $invoice->get_discounts( true );
1967
                
1968
                wpinv_set_checkout_session( $data );
1969
                
1970
                if ( wpinv_get_option( 'vat_ip_country_default' ) ) {
1971
                    $_POST['country']   = $wpinv_euvat->get_country_by_ip();
1972
                    $_POST['state']     = $_POST['country'] == $invoice->country ? $invoice->state : '';
1973
                    
1974
                    wpinv_recalculate_tax( true );
1975
                }
1976
                
1977
            } else {
1978
                $checkout_uri = $invoice->get_view_url();
1979
            }
1980
        } else {
1981
            wpinv_set_error( 'invalid_invoice', __( 'You are not allowed to view this invoice', 'invoicing' ) );
1982
            
1983
            $checkout_uri = is_user_logged_in() ? wpinv_get_history_page_uri() : wp_login_url( get_permalink() );
1984
        }
1985
        
1986
        wp_redirect( $checkout_uri );
1987
        wpinv_die();
1988
    }
1989
}
1990
add_action( 'wpinv_pay_for_invoice', 'wpinv_pay_for_invoice' );
1991
1992
function wpinv_handle_pay_via_invoice_link( $invoice_key ) {
1993
    if ( !empty( $invoice_key ) && !empty( $_REQUEST['_wpipay'] ) && !is_user_logged_in() && $invoice_id = wpinv_get_invoice_id_by_key( $invoice_key ) ) {
1994
        if ( $invoice = wpinv_get_invoice( $invoice_id ) ) {
1995
            $user_id = $invoice->get_user_id();
1996
            $secret = sanitize_text_field( $_GET['_wpipay'] );
1997
            
1998
            if ( $secret === md5( $user_id . '::' . $invoice->get_email() . '::' . $invoice_key ) ) { // valid invoice link
1999
                $redirect_to = remove_query_arg( '_wpipay', get_permalink() );
2000
                
2001
                wpinv_guest_redirect( $redirect_to, $user_id );
2002
                wpinv_die();
2003
            }
2004
        }
2005
    }
2006
}
2007
add_action( 'wpinv_check_pay_for_invoice', 'wpinv_handle_pay_via_invoice_link' );
2008
2009
function wpinv_set_payment_transaction_id( $invoice_id = 0, $transaction_id = '' ) {
2010
    $invoice_id = is_object( $invoice_id ) && !empty( $invoice_id->ID ) ? $invoice_id : $invoice_id;
2011
    
2012
    if ( empty( $invoice_id ) && $invoice_id > 0 ) {
2013
        return false;
2014
    }
2015
    
2016
    if ( empty( $transaction_id ) ) {
2017
        $transaction_id = $invoice_id;
2018
    }
2019
2020
    $transaction_id = apply_filters( 'wpinv_set_payment_transaction_id', $transaction_id, $invoice_id );
2021
    
2022
    return wpinv_update_invoice_meta( $invoice_id, '_wpinv_transaction_id', $transaction_id );
2023
}
2024
2025
function wpinv_invoice_status_label( $status, $status_display = '' ) {
2026
    if ( empty( $status_display ) ) {
2027
        $status_display = wpinv_status_nicename( $status );
2028
    }
2029
    
2030
    switch ( $status ) {
2031
        case 'publish' :
2032
        case 'wpi-renewal' :
2033
            $class = 'label-success';
2034
        break;
2035
        case 'wpi-pending' :
2036
            $class = 'label-primary';
2037
        break;
2038
        case 'wpi-processing' :
2039
            $class = 'label-warning';
2040
        break;
2041
        case 'wpi-onhold' :
2042
            $class = 'label-info';
2043
        break;
2044
        case 'wpi-cancelled' :
2045
        case 'wpi-failed' :
2046
            $class = 'label-danger';
2047
        break;
2048
        default:
2049
            $class = 'label-default';
2050
        break;
2051
    }
2052
    
2053
    $label = '<span class="label label-inv-' . $status . ' ' . $class . '">' . $status_display . '</span>';
2054
    
2055
    return apply_filters( 'wpinv_invoice_status_label', $label, $status, $status_display );
2056
}
2057
2058
function wpinv_format_invoice_number( $number, $type = '' ) {
2059
    $check = apply_filters( 'wpinv_pre_format_invoice_number', null, $number, $type );
2060
    if ( null !== $check ) {
2061
        return $check;
2062
    }
2063
2064
    if ( !empty( $number ) && !is_numeric( $number ) ) {
2065
        return $number;
2066
    }
2067
2068
    $padd  = wpinv_get_option( 'invoice_number_padd' );
2069
    $prefix  = wpinv_get_option( 'invoice_number_prefix' );
2070
    $postfix = wpinv_get_option( 'invoice_number_postfix' );
2071
    
2072
    $padd = absint( $padd );
2073
    $formatted_number = absint( $number );
2074
    
2075
    if ( $padd > 0 ) {
2076
        $formatted_number = zeroise( $formatted_number, $padd );
2077
    }    
2078
2079
    $formatted_number = $prefix . $formatted_number . $postfix;
2080
2081
    return apply_filters( 'wpinv_format_invoice_number', $formatted_number, $number, $prefix, $postfix, $padd );
2082
}
2083
2084
function wpinv_get_next_invoice_number( $type = '' ) {
2085
    $check = apply_filters( 'wpinv_get_pre_next_invoice_number', null, $type );
2086
    if ( null !== $check ) {
2087
        return $check;
2088
    }
2089
    
2090
    if ( !wpinv_sequential_number_active() ) {
2091
        return false;
2092
    }
2093
2094
    $number = $last_number = get_option( 'wpinv_last_invoice_number' );
2095
    $start  = wpinv_get_option( 'invoice_sequence_start' );
2096
    if ( !absint( $start ) > 0 ) {
2097
        $start = 1;
2098
    }
2099
    $increment_number = true;
2100
    $save_number = false;
2101
2102
    if ( !empty( $number ) && !is_numeric( $number ) && $number == wpinv_format_invoice_number( $number ) ) {
2103
        $number = wpinv_clean_invoice_number( $number );
2104
    }
2105
2106
    if ( empty( $number ) ) {
2107
        if ( !( $last_number === 0 || $last_number === '0' ) ) {
2108
            $last_invoice = wpinv_get_invoices( array( 'limit' => 1, 'order' => 'DESC', 'orderby' => 'ID', 'return' => 'posts', 'fields' => 'ids', 'status' => array_keys( wpinv_get_invoice_statuses( true, true ) ) ) );
2109
2110
            if ( !empty( $last_invoice[0] ) && $invoice_number = wpinv_get_invoice_number( $last_invoice[0] ) ) {
2111
                if ( is_numeric( $invoice_number ) ) {
2112
                    $number = $invoice_number;
2113
                } else {
2114
                    $number = wpinv_clean_invoice_number( $invoice_number );
2115
                }
2116
            }
2117
2118 View Code Duplication
            if ( empty( $number ) ) {
2119
                $increment_number = false;
2120
                $number = $start;
2121
                $save_number = ( $number - 1 );
2122
            } else {
2123
                $save_number = $number;
2124
            }
2125
        }
2126
    }
2127
2128 View Code Duplication
    if ( $start > $number ) {
2129
        $increment_number = false;
2130
        $number = $start;
2131
        $save_number = ( $number - 1 );
2132
    }
2133
2134
    if ( $save_number !== false ) {
2135
        update_option( 'wpinv_last_invoice_number', $save_number );
2136
    }
2137
    
2138
    $increment_number = apply_filters( 'wpinv_increment_payment_number', $increment_number, $number );
2139
2140
    if ( $increment_number ) {
2141
        $number++;
2142
    }
2143
2144
    return apply_filters( 'wpinv_get_next_invoice_number', $number );
2145
}
2146
2147
function wpinv_clean_invoice_number( $number, $type = '' ) {
2148
    $check = apply_filters( 'wpinv_pre_clean_invoice_number', null, $number, $type );
2149
    if ( null !== $check ) {
2150
        return $check;
2151
    }
2152
    
2153
    $prefix  = wpinv_get_option( 'invoice_number_prefix' );
2154
    $postfix = wpinv_get_option( 'invoice_number_postfix' );
2155
2156
    $number = preg_replace( '/' . $prefix . '/', '', $number, 1 );
2157
2158
    $length      = strlen( $number );
2159
    $postfix_pos = strrpos( $number, $postfix );
2160
    
2161
    if ( false !== $postfix_pos ) {
2162
        $number      = substr_replace( $number, '', $postfix_pos, $length );
2163
    }
2164
2165
    $number = intval( $number );
2166
2167
    return apply_filters( 'wpinv_clean_invoice_number', $number, $prefix, $postfix );
2168
}
2169
2170
function wpinv_save_number_post_saved( $post_ID, $post, $update ) {
2171
    global $wpdb;
2172
2173
    if ( !$update && !get_post_meta( $post_ID, '_wpinv_number', true ) ) {
2174
        wpinv_update_invoice_number( $post_ID, $post->post_status != 'auto-draft', $post->post_type );
2175
    }
2176
2177
    if ( !$update ) {
2178
        $wpdb->update( $wpdb->posts, array( 'post_name' => wpinv_generate_post_name( $post_ID ) ), array( 'ID' => $post_ID ) );
2179
        clean_post_cache( $post_ID );
2180
    }
2181
}
2182
add_action( 'save_post_wpi_invoice', 'wpinv_save_number_post_saved', 1, 3 );
2183
2184
function wpinv_save_number_post_updated( $post_ID, $post_after, $post_before ) {
2185
    if ( !empty( $post_after->post_type ) && $post_after->post_type == 'wpi_invoice' && $post_before->post_status == 'auto-draft' && $post_after->post_status != $post_before->post_status ) {
2186
        wpinv_update_invoice_number( $post_ID, true, $post_after->post_type );
2187
    }
2188
}
2189
add_action( 'post_updated', 'wpinv_save_number_post_updated', 1, 3 );
2190
2191
function wpinv_update_invoice_number( $post_ID, $save_sequential = false, $type = '' ) {
2192
    global $wpdb;
2193
    
2194
    $check = apply_filters( 'wpinv_pre_update_invoice_number', null, $post_ID, $save_sequential, $type );
2195
    if ( null !== $check ) {
2196
        return $check;
2197
    }
2198
2199
    if ( wpinv_sequential_number_active() ) {
2200
        $number = wpinv_get_next_invoice_number();
2201
2202
        if ( $save_sequential ) {
2203
            update_option( 'wpinv_last_invoice_number', $number );
2204
        }
2205
    } else {
2206
        $number = $post_ID;
2207
    }
2208
2209
    $number = wpinv_format_invoice_number( $number );
2210
2211
    update_post_meta( $post_ID, '_wpinv_number', $number );
2212
2213
    $wpdb->update( $wpdb->posts, array( 'post_title' => $number ), array( 'ID' => $post_ID ) );
2214
2215
    clean_post_cache( $post_ID );
2216
2217
    return $number;
2218
}
2219
2220
function wpinv_post_name_prefix( $post_type = 'wpi_invoice' ) {
2221
    return apply_filters( 'wpinv_post_name_prefix', 'inv-', $post_type );
2222
}
2223
2224
function wpinv_generate_post_name( $post_ID ) {
2225
    $prefix = wpinv_post_name_prefix( get_post_type( $post_ID ) );
2226
    $post_name = sanitize_title( $prefix . $post_ID );
2227
2228
    return apply_filters( 'wpinv_generate_post_name', $post_name, $post_ID, $prefix );
2229
}
2230
2231
function wpinv_is_invoice_viewed( $invoice_id ) {
2232
    if ( empty( $invoice_id ) ) {
2233
        return false;
2234
    }
2235
2236
    $viewed_meta = get_post_meta( $invoice_id, '_wpinv_is_viewed', true );
2237
2238
    if ( isset($viewed_meta) && 1 == $viewed_meta ) {
2239
        $is_viewed = true;
2240
    } else {
2241
        $is_viewed = false;
2242
    }
2243
2244
    return apply_filters( 'wpinv_is_invoice_viewed', $is_viewed, $invoice_id );
2245
}
2246
2247
function wpinv_mark_invoice_viewed() {
2248
2249
    if ( isset( $_GET['invoice_key'] ) ) {
2250
        $invoice_key = urldecode($_GET['invoice_key']);
2251
2252
        $invoice_id = wpinv_get_invoice_id_by_key($invoice_key);
2253
        $invoice = new WPInv_Invoice($invoice_id);
2254
2255
        if(!$invoice_id){
2256
            return;
2257
        }
2258
2259
        if( is_user_logged_in()){
2260
            $current_user = wp_get_current_user();
2261
            if(!current_user_can('administrator') && $current_user->user_email == $invoice->get_email()){
2262
                update_post_meta($invoice_id,'_wpinv_is_viewed', 1);
2263
            }
2264
        } else {
2265
            update_post_meta($invoice_id,'_wpinv_is_viewed', 1);
2266
        }
2267
    }
2268
2269
}
2270
add_action( 'init', 'wpinv_mark_invoice_viewed' );
2271
2272
function wpinv_get_subscription( $invoice ) {
2273
    if ( empty( $invoice ) ) {
2274
        return false;
2275
    }
2276
    
2277
    if ( ! is_object( $invoice ) && is_scalar( $invoice ) ) {
2278
        $invoice = wpinv_get_invoice( $invoice );
2279
    }
2280
    
2281
    if ( !( is_object( $invoice ) && ! empty( $invoice->ID ) && $invoice->is_recurring() ) ) {
2282
        return false;
2283
    }
2284
    
2285
    $invoice_id = ! empty( $invoice->parent_invoice ) ? $invoice->parent_invoice : $invoice->ID;
2286
    
2287
    $subs_db    = new WPInv_Subscriptions_DB;
2288
    $subs       = $subs_db->get_subscriptions( array( 'parent_payment_id' => $invoice_id, 'number' => 1 ) );
2289
    
2290
    if ( ! empty( $subs ) ) {
2291
        return reset( $subs );
2292
    }
2293
    
2294
    return false;
2295
}
2296
2297
function wpinv_filter_posts_clauses( $clauses, $wp_query ) {
2298
    global $wpdb;
2299
2300
    if ( ! empty( $wp_query->query_vars['orderby'] ) && $wp_query->query_vars['orderby'] == 'invoice_date' ) {
2301
        if ( !empty( $clauses['join'] ) ) {
2302
            $clauses['join'] .= " ";
2303
        }
2304
2305
        if ( !empty( $clauses['fields'] ) ) {
2306
            $clauses['fields'] .= ", ";
2307
        }
2308
2309
        $clauses['join'] .= "LEFT JOIN {$wpdb->postmeta} ON ( {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_wpinv_completed_date' )";
2310
        $clauses['fields'] .= "IF( {$wpdb->postmeta}.meta_value, {$wpdb->postmeta}.meta_value, {$wpdb->posts}.post_date ) AS invoice_date";
2311
        $clauses['orderby'] = "invoice_date DESC, {$wpdb->posts}.post_date DESC, {$wpdb->posts}.ID DESC";
2312
    }
2313
2314
    return $clauses;
2315
}
2316
add_filter( 'posts_clauses', 'wpinv_filter_posts_clauses', 10, 2 );