Passed
Pull Request — master (#47)
by Kiran
04:02
created

wpinv-invoice-functions.php ➔ wpinv_get_cart_content_details()   F

Complexity

Conditions 13
Paths 525

Size

Total Lines 74
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 50
nc 525
nop 0
dl 0
loc 74
rs 3.2108
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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'] = '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
    $user_info = wp_parse_args( $invoice_data['user_info'], $default_user_info );
89
    
90 View Code Duplication
    if ( empty( $user_info['first_name'] ) ) {
91
        $user_info['first_name'] = $default_user_info['first_name'];
92
        $user_info['last_name'] = $default_user_info['last_name'];
93
    }
94
    
95
    if ( empty( $user_info['country'] ) ) {
96
        $user_info['country'] = $default_user_info['country'];
97
        $user_info['state'] = $default_user_info['state'];
98
        $user_info['city'] = $default_user_info['city'];
99
        $user_info['address'] = $default_user_info['address'];
100
        $user_info['zip'] = $default_user_info['zip'];
101
        $user_info['phone'] = $default_user_info['phone'];
102
    }
103
    
104
    if ( !empty( $user_info['discount'] ) && !is_array( $user_info['discount'] ) ) {
105
        $user_info['discount'] = (array)$user_info['discount'];
106
    }
107
108
    // Payment details
109
    $payment_details = array();
110
    if ( !empty( $invoice_data['payment_details'] ) ) {
111
        $default_payment_details = array(
112
            'gateway'           => 'manual',
113
            'gateway_title'     => '',
114
            'currency'          => geodir_get_currency_type(),
115
            'transaction_id'    => '',
116
        );
117
        
118
        $payment_details = wp_parse_args( $invoice_data['payment_details'], $default_payment_details );
119
        
120
        if ( empty( $payment_details['gateway'] ) ) {
121
            $payment_details['gateway'] = 'manual';
122
        }
123
        
124
        if ( empty( $payment_details['currency'] ) ) {
125
            $payment_details['currency'] = geodir_get_currency_type();
126
        }
127
        
128
        if ( empty( $payment_details['gateway_title'] ) ) {
129
            $payment_details['gateway_title'] = wpinv_get_gateway_checkout_label( $payment_details['gateway'] );
130
        }
131
    }
132
    
133
    $invoice->set( 'status', ( !empty( $invoice_data['status'] ) ? $invoice_data['status'] : 'pending' ) );
134
    
135
    if ( !empty( $payment_details ) ) {
136
        $invoice->set( 'currency', $payment_details['currency'] );
137
        $invoice->set( 'gateway', $payment_details['gateway'] );
138
        $invoice->set( 'gateway_title', $payment_details['gateway_title'] );
139
        $invoice->set( 'transaction_id', $payment_details['transaction_id'] );
140
    }
141
    
142
    $invoice->set( 'user_info', $user_info );
143
    $invoice->set( 'first_name', $user_info['first_name'] );
144
    $invoice->set( 'last_name', $user_info['last_name'] );
145
    $invoice->set( 'address', $user_info['address'] );
146
    $invoice->set( 'company', $user_info['company'] );
147
    $invoice->set( 'vat_number', $user_info['vat_number'] );
148
    $invoice->set( 'phone', $user_info['phone'] );
149
    $invoice->set( 'city', $user_info['city'] );
150
    $invoice->set( 'country', $user_info['country'] );
151
    $invoice->set( 'state', $user_info['state'] );
152
    $invoice->set( 'zip', $user_info['zip'] );
153
    $invoice->set( 'discounts', $user_info['discount'] );
154
    $invoice->set( 'ip', ( !empty( $invoice_data['ip'] ) ? $invoice_data['ip'] : wpinv_get_ip() ) );
155
    if ( !empty( $invoice_data['invoice_key'] ) ) {
156
        $invoice->set( 'key', $invoice_data['invoice_key'] );
157
    }
158
    $invoice->set( 'mode', ( wpinv_is_test_mode() ? 'test' : 'live' ) );
159
    $invoice->set( 'parent_invoice', ( !empty( $invoice_data['parent'] ) ? absint( $invoice_data['parent'] ) : '' ) );
160
    
161
    if ( !empty( $invoice_data['cart_details'] ) && is_array( $invoice_data['cart_details'] ) ) {
162
        foreach ( $invoice_data['cart_details'] as $key => $item ) {
163
            $item_id        = !empty( $item['id'] ) ? $item['id'] : 0;
164
            $quantity       = !empty( $item['quantity'] ) ? $item['quantity'] : 1;
165
            $name           = !empty( $item['name'] ) ? $item['name'] : '';
166
            $item_price     = isset( $item['item_price'] ) ? $item['item_price'] : '';
167
            
168
            $post_item  = new WPInv_Item( $item_id );
169
            if ( !empty( $post_item ) ) {
170
                $name       = !empty( $name ) ? $name : $post_item->get_name();
171
                $item_price = $item_price !== '' ? $item_price : $post_item->get_price();
172
            } else {
173
                continue;
174
            }
175
            
176
            $args = array(
177
                'name'          => $name,
178
                'quantity'      => $quantity,
179
                'item_price'    => $item_price,
180
                'custom_price'  => isset( $item['custom_price'] ) ? $item['custom_price'] : '',
181
                'tax'           => !empty( $item['tax'] ) ? $item['tax'] : 0.00,
182
                'discount'      => isset( $item['discount'] ) ? $item['discount'] : 0,
183
                'meta'          => isset( $item['meta'] ) ? $item['meta'] : array(),
184
                'fees'          => isset( $item['fees'] ) ? $item['fees'] : array(),
185
            );
186
187
            $invoice->add_item( $item_id, $args );
188
        }
189
    }
190
191
    $invoice->increase_tax( wpinv_get_cart_fee_tax() );
192
193
    if ( isset( $invoice_data['post_date'] ) ) {
194
        $invoice->set( 'date', $invoice_data['post_date'] );
195
    }
196
197
    $number = wpinv_format_invoice_number( $invoice->ID );
198
    $invoice->set( 'number', $number );
199
    update_option( 'wpinv_last_invoice_number', $number );
200
    
201
    $invoice->save();
202
    
203
    // Add notes
204
    if ( !empty( $invoice_data['private_note'] ) ) {
205
        $invoice->add_note( $invoice_data['private_note'] );
206
    }
207
    if ( !empty( $invoice_data['user_note'] ) ) {
208
        $invoice->add_note( $invoice_data['user_note'], true );
209
    }
210
    
211
    do_action( 'wpinv_insert_invoice', $invoice->ID, $invoice_data );
212
213
    if ( ! empty( $invoice->ID ) ) {
214
        global $wpi_userID, $wpinv_ip_address_country;
215
        
216
        $checkout_session = wpinv_get_checkout_session();
217
        
218
        $data_session                   = array();
219
        $data_session['invoice_id']     = $invoice->ID;
220
        $data_session['cart_discounts'] = $invoice->get_discounts( true );
221
        
222
        wpinv_set_checkout_session( $data_session );
223
        
224
        $wpi_userID         = (int)$invoice->get_user_id();
225
        
226
        $_POST['country']   = !empty( $invoice->country ) ? $invoice->country : wpinv_get_default_country();
227
        $_POST['state']     = $invoice->state;
228
229
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
230
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
231
        
232
        $wpinv_ip_address_country = $invoice->country;
233
        
234
        $invoice = $invoice->recalculate_totals( true );
235
        
236
        wpinv_set_checkout_session( $checkout_session );
237
                    
238
        return $invoice;
239
    }
240
    
241
    if ( $wp_error ) {
242
        if ( is_wp_error( $invoice ) ) {
243
            return $invoice;
244
        } else {
245
            return new WP_Error( 'wpinv_insert_invoice_error', __( 'Error in insert invoice.', 'invoicing' ) );
246
        }
247
    } else {
248
        return 0;
249
    }
250
}
251
252
function wpinv_get_invoice( $invoice_id = 0, $cart = false ) {
253
    if ( $cart && empty( $invoice_id ) ) {
254
        $invoice_id = (int)wpinv_get_invoice_cart_id();
255
    }
256
257
    $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...
258
    return $invoice;
259
}
260
261
function wpinv_get_invoice_cart( $invoice_id = 0 ) {
262
    return wpinv_get_invoice( $invoice_id, true );
263
}
264
265
function wpinv_get_invoice_description( $invoice_id = 0 ) {
266
    $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...
267
    return $invoice->get_description();
268
}
269
270
function wpinv_get_invoice_currency_code( $invoice_id = 0 ) {
271
    $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...
272
    return $invoice->get_currency();
273
}
274
275
function wpinv_get_payment_user_email( $invoice_id ) {
276
    $invoice = new WPInv_Invoice( $invoice_id );
277
    return $invoice->get_email();
278
}
279
280
function wpinv_get_user_id( $invoice_id ) {
281
    $invoice = new WPInv_Invoice( $invoice_id );
282
    return $invoice->get_user_id();
283
}
284
285
function wpinv_get_invoice_status( $invoice_id, $return_label = false ) {
286
    $invoice = new WPInv_Invoice( $invoice_id );
287
    
288
    return $invoice->get_status( $return_label );
289
}
290
291
function wpinv_get_payment_gateway( $invoice_id, $return_label = false ) {
292
    $invoice = new WPInv_Invoice( $invoice_id );
293
    
294
    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...
295
}
296
297
function wpinv_get_payment_gateway_name( $invoice_id ) {
298
    $invoice = new WPInv_Invoice( $invoice_id );
299
    
300
    return $invoice->get_gateway_title();
301
}
302
303
function wpinv_get_payment_transaction_id( $invoice_id ) {
304
    $invoice = new WPInv_Invoice( $invoice_id );
305
    
306
    return $invoice->get_transaction_id();
307
}
308
309 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...
310
    global $wpdb;
311
312
    $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 ) );
313
314
    if ( $invoice_id != NULL )
315
        return $invoice_id;
316
317
    return 0;
318
}
319
320
function wpinv_get_invoice_meta( $invoice_id = 0, $meta_key = '_wpinv_payment_meta', $single = true ) {
321
    $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...
322
323
    return $invoice->get_meta( $meta_key, $single );
324
}
325
326
function wpinv_update_invoice_meta( $invoice_id = 0, $meta_key = '', $meta_value = '', $prev_value = '' ) {
327
    $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...
328
    
329
    return $invoice->update_meta( $meta_key, $meta_value, $prev_value );
330
}
331
332
function wpinv_get_items( $invoice_id = 0 ) {
333
    $invoice            = wpinv_get_invoice( $invoice_id );
334
    
335
    $items              = $invoice->get_items();
336
    $invoice_currency   = $invoice->get_currency();
337
338 View Code Duplication
    if ( !empty( $items ) && is_array( $items ) ) {
339
        foreach ( $items as $key => $item ) {
340
            $items[$key]['currency'] = $invoice_currency;
341
342
            if ( !isset( $cart_item['subtotal'] ) ) {
343
                $items[$key]['subtotal'] = $items[$key]['amount'] * 1;
344
            }
345
        }
346
    }
347
348
    return apply_filters( 'wpinv_get_items', $items, $invoice_id );
349
}
350
351
function wpinv_get_fees( $invoice_id = 0 ) {
352
    $invoice           = wpinv_get_invoice( $invoice_id );
353
    $fees              = $invoice->get_fees();
354
355
    return apply_filters( 'wpinv_get_fees', $fees, $invoice_id );
356
}
357
358
function wpinv_get_invoice_ip( $invoice_id ) {
359
    $invoice = new WPInv_Invoice( $invoice_id );
360
    return $invoice->get_ip();
361
}
362
363
function wpinv_get_invoice_user_info( $invoice_id ) {
364
    $invoice = new WPInv_Invoice( $invoice_id );
365
    return $invoice->get_user_info();
366
}
367
368
function wpinv_subtotal( $invoice_id = 0, $currency = false ) {
369
    $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...
370
371
    return $invoice->get_subtotal( $currency );
372
}
373
374
function wpinv_tax( $invoice_id = 0, $currency = false ) {
375
    $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...
376
377
    return $invoice->get_tax( $currency );
378
}
379
380
function wpinv_discount( $invoice_id = 0, $currency = false, $dash = false ) {
381
    $invoice = wpinv_get_invoice( $invoice_id );
382
383
    return $invoice->get_discount( $currency, $dash );
384
}
385
386
function wpinv_discount_code( $invoice_id = 0 ) {
387
    $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...
388
389
    return $invoice->get_discount_code();
390
}
391
392
function wpinv_payment_total( $invoice_id = 0, $currency = false ) {
393
    $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...
394
395
    return $invoice->get_total( $currency );
396
}
397
398
function wpinv_get_date_created( $invoice_id = 0 ) {
399
    $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...
400
    
401
    $date_created   = $invoice->get_created_date();
402
    $date_created   = $date_created != '' && $date_created != '0000-00-00 00:00:00' ? date_i18n( get_option( 'date_format' ), strtotime( $date_created ) ) : '';
403
404
    return $date_created;
405
}
406
407
function wpinv_get_invoice_date( $invoice_id = 0, $format = '' ) {
408
    $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...
409
    
410
    $format         = !empty( $format ) ? $format : get_option( 'date_format' );
411
    $date_completed = $invoice->get_completed_date();
412
    $invoice_date   = $date_completed != '' && $date_completed != '0000-00-00 00:00:00' ? date_i18n( $format, strtotime( $date_completed ) ) : '';
413
    if ( $invoice_date == '' ) {
414
        $date_created   = $invoice->get_created_date();
415
        $invoice_date   = $date_created != '' && $date_created != '0000-00-00 00:00:00' ? date_i18n( $format, strtotime( $date_created ) ) : '';
416
    }
417
418
    return $invoice_date;
419
}
420
421
function wpinv_get_invoice_vat_number( $invoice_id = 0 ) {
422
    $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...
423
    
424
    return $invoice->vat_number;
425
}
426
427
function wpinv_insert_payment_note( $invoice_id = 0, $note = '', $user_type = false, $added_by_user = false ) {
428
    $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...
429
430
    return $invoice->add_note( $note, $user_type, $added_by_user );
431
}
432
433
function wpinv_get_invoice_notes( $invoice_id = 0, $type = '' ) {
434
    global $invoicing;
435
    
436
    if ( empty( $invoice_id ) ) {
437
        return NULL;
438
    }
439
    
440
    $notes = $invoicing->notes->get_invoice_notes( $invoice_id, $type );
441
    
442
    return apply_filters( 'wpinv_invoice_notes', $notes, $invoice_id, $type );
443
}
444
445
function wpinv_get_payment_key( $invoice_id = 0 ) {
446
	$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...
447
    return $invoice->get_key();
448
}
449
450
function wpinv_get_invoice_number( $invoice_id = 0 ) {
451
    $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...
452
    return $invoice->get_number();
453
}
454
455
function wpinv_get_cart_discountable_subtotal( $code_id ) {
456
    $cart_items = wpinv_get_cart_content_details();
457
    $items      = array();
458
459
    $excluded_items = wpinv_get_discount_excluded_items( $code_id );
460
461
    if( $cart_items ) {
462
463
        foreach( $cart_items as $item ) {
464
465
            if( ! in_array( $item['id'], $excluded_items ) ) {
466
                $items[] =  $item;
467
            }
468
        }
469
    }
470
471
    $subtotal = wpinv_get_cart_items_subtotal( $items );
472
473
    return apply_filters( 'wpinv_get_cart_discountable_subtotal', $subtotal );
474
}
475
476
function wpinv_get_cart_items_subtotal( $items ) {
477
    $subtotal = 0.00;
478
479
    if ( is_array( $items ) && ! empty( $items ) ) {
480
        $prices = wp_list_pluck( $items, 'subtotal' );
481
482
        if( is_array( $prices ) ) {
483
            $subtotal = array_sum( $prices );
484
        } else {
485
            $subtotal = 0.00;
486
        }
487
488
        if( $subtotal < 0 ) {
489
            $subtotal = 0.00;
490
        }
491
    }
492
493
    return apply_filters( 'wpinv_get_cart_items_subtotal', $subtotal );
494
}
495
496
function wpinv_get_cart_subtotal( $items = array() ) {
497
    $items    = !empty( $items ) ? $items : wpinv_get_cart_content_details();
498
    $subtotal = wpinv_get_cart_items_subtotal( $items );
499
500
    return apply_filters( 'wpinv_get_cart_subtotal', $subtotal );
501
}
502
503
function wpinv_cart_subtotal( $items = array() ) {
504
    $price = wpinv_price( wpinv_format_amount( wpinv_get_cart_subtotal( $items ) ) );
505
506
    return $price;
507
}
508
509
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...
510
    $subtotal  = (float)wpinv_get_cart_subtotal( $items );
511
    $discounts = (float)wpinv_get_cart_discounted_amount( $items );
512
    $cart_tax  = (float)wpinv_get_cart_tax( $items );
513
    $fees      = (float)wpinv_get_cart_fee_total();
514
    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...
515
        $total = 0;
516
    } else {
517
        $total     = $subtotal - $discounts + $cart_tax + $fees;
518
    }
519
520
    if ( $total < 0 ) {
521
        $total = 0.00;
522
    }
523
    
524
    $total = (float)apply_filters( 'wpinv_get_cart_total', $total, $items );
525
526
    return wpinv_sanitize_amount( $total );
527
}
528
529
function wpinv_cart_total( $cart_items = array(), $echo = true, $invoice = array() ) {
530
    global $cart_total;
531
    $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...
532
    $total = apply_filters( 'wpinv_cart_total', $total, $cart_items, $invoice );
533
    
534
    $cart_total = $total;
535
536
    if ( !$echo ) {
537
        return $total;
538
    }
539
540
    echo $total;
541
}
542
543 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...
544
    $cart_tax = 0;
545
    $items    = !empty( $items ) ? $items : wpinv_get_cart_content_details();
546
547
    if ( $items ) {
548
        $taxes = wp_list_pluck( $items, 'tax' );
549
550
        if( is_array( $taxes ) ) {
551
            $cart_tax = array_sum( $taxes );
552
        }
553
    }
554
555
    $cart_tax += wpinv_get_cart_fee_tax();
556
557
    return apply_filters( 'wpinv_get_cart_tax', wpinv_sanitize_amount( $cart_tax ) );
558
}
559
560 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...
561
    $cart_tax = wpinv_get_cart_tax( $items );
562
    $cart_tax = wpinv_price( wpinv_format_amount( $cart_tax ) );
563
564
    $tax = apply_filters( 'wpinv_cart_tax', $cart_tax, $items );
565
566
    if ( !$echo ) {
567
        return $tax;
568
    }
569
570
    echo $tax;
571
}
572
573
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...
574
    $invoice = wpinv_get_invoice_cart();
575
    $cart_discount_code = !empty( $invoice ) ? $invoice->get_discount_code() : '';
576
    
577
    return apply_filters( 'wpinv_get_cart_discount_code', $cart_discount_code );
578
}
579
580
function wpinv_cart_discount_code( $items = array(), $echo = false ) {
581
    $cart_discount_code = wpinv_get_cart_discount_code( $items );
582
583
    if ( $cart_discount_code != '' ) {
584
        $cart_discount_code = ' (' . $cart_discount_code . ')';
585
    }
586
    
587
    $discount_code = apply_filters( 'wpinv_cart_discount_code', $cart_discount_code, $items );
588
589
    if ( !$echo ) {
590
        return $discount_code;
591
    }
592
593
    echo $discount_code;
594
}
595
596
function wpinv_get_cart_discount( $items = array() ) {
597
    $invoice = wpinv_get_invoice_cart();
598
    $cart_discount = !empty( $invoice ) ? $invoice->get_discount() : 0;
599
    
600
    return apply_filters( 'wpinv_get_cart_discount', wpinv_sanitize_amount( $cart_discount ), $items );
601
}
602
603 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...
604
    $cart_discount = wpinv_get_cart_discount( $items );
605
    $cart_discount = wpinv_price( wpinv_format_amount( $cart_discount ) );
606
607
    $discount = apply_filters( 'wpinv_cart_discount', $cart_discount, $items );
608
609
    if ( !$echo ) {
610
        return $discount;
611
    }
612
613
    echo $discount;
614
}
615
616
function wpinv_get_cart_fees( $type = 'all', $item_id = 0 ) {
617
    $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...
618
    
619
    return $item->get_fees( $type, $item_id );
620
}
621
622
function wpinv_get_cart_fee_total() {
623
    $total  = 0;
624
    $fees = wpinv_get_cart_fees();
625
    
626
    if ( $fees ) {
627
        foreach ( $fees as $fee_id => $fee ) {
628
            $total += $fee['amount'];
629
        }
630
    }
631
632
    return apply_filters( 'wpinv_get_cart_fee_total', $total );
633
}
634
635
function wpinv_get_cart_fee_tax() {
636
    $tax  = 0;
637
    $fees = wpinv_get_cart_fees();
638
639
    if ( $fees ) {
640
        foreach ( $fees as $fee_id => $fee ) {
641
            if( ! empty( $fee['no_tax'] ) ) {
642
                continue;
643
            }
644
645
            $tax += wpinv_calculate_tax( $fee['amount'] );
646
        }
647
    }
648
649
    return apply_filters( 'wpinv_get_cart_fee_tax', $tax );
650
}
651
652
function wpinv_cart_has_recurring_item() {
653
    $cart_items = wpinv_get_cart_contents();
654
    
655
    if ( empty( $cart_items ) ) {
656
        return false;
657
    }
658
    
659
    $has_subscription = false;
660 View Code Duplication
    foreach( $cart_items as $cart_item ) {
661
        if ( !empty( $cart_item['id'] ) && wpinv_is_recurring_item( $cart_item['id'] )  ) {
662
            $has_subscription = true;
663
            break;
664
        }
665
    }
666
    
667
    return apply_filters( 'wpinv_cart_has_recurring_item', $has_subscription, $cart_items );
668
}
669
670
function wpinv_cart_has_free_trial() {
671
    $invoice = wpinv_get_invoice_cart();
672
    
673
    $free_trial = false;
674
    
675
    if ( !empty( $invoice ) && $invoice->is_free_trial() ) {
676
        $free_trial = true;
677
    }
678
    
679
    return apply_filters( 'wpinv_cart_has_free_trial', $free_trial, $invoice );
680
}
681
682
function wpinv_get_cart_contents() {
683
    $cart_details = wpinv_get_cart_details();
684
    
685
    return apply_filters( 'wpinv_get_cart_contents', $cart_details );
686
}
687
688
function wpinv_get_cart_content_details() {
689
    global $wpinv_euvat, $wpi_current_id, $wpi_item_id, $wpinv_is_last_cart_item, $wpinv_flat_discount_total;
690
    $cart_items = wpinv_get_cart_contents();
691
    
692
    if ( empty( $cart_items ) ) {
693
        return false;
694
    }
695
    $invoice = wpinv_get_invoice_cart();
696
697
    $details = array();
698
    $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...
699
    
700
    if ( empty( $_POST['country'] ) ) {
701
        $_POST['country'] = $invoice->country;
702
    }
703
    if ( !isset( $_POST['state'] ) ) {
704
        $_POST['state'] = $invoice->state;
705
    }
706
707
    foreach( $cart_items as $key => $item ) {
708
        $item_id            = isset( $item['id'] ) ? sanitize_text_field( $item['id'] ) : '';
709
        if ( empty( $item_id ) ) {
710
            continue;
711
        }
712
        
713
        $wpi_current_id         = $invoice->ID;
714
        $wpi_item_id            = $item_id;
715
        
716
        $item_price         = wpinv_get_item_price( $item_id );
717
        $discount           = wpinv_get_cart_item_discount_amount( $item );
718
        $discount           = apply_filters( 'wpinv_get_cart_content_details_item_discount_amount', $discount, $item );
719
        $quantity           = wpinv_get_cart_item_quantity( $item );
720
        $fees               = wpinv_get_cart_fees( 'fee', $item_id );
721
        
722
        $subtotal           = $item_price * $quantity;
723
        $tax_rate           = wpinv_get_tax_rate( $_POST['country'], $_POST['state'], $wpi_item_id );
0 ignored issues
show
Documentation introduced by
$_POST['state'] is of type string, but the function expects a boolean.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
724
        $tax_class          = $wpinv_euvat->get_item_class( $item_id );
725
        $tax                = wpinv_get_cart_item_tax( $item_id, $subtotal - $discount );
726
        
727
        if ( wpinv_prices_include_tax() ) {
728
            $subtotal -= wpinv_round_amount( $tax );
729
        }
730
        
731
        $total              = $subtotal - $discount + $tax;
732
        
733
        // Do not allow totals to go negatve
734
        if( $total < 0 ) {
735
            $total = 0;
736
        }
737
        
738
        $details[ $key ]  = array(
739
            'id'                => $item_id,
740
            'name'              => !empty($item['name']) ? $item['name'] : get_the_title( $item_id ),
741
            'item_price'        => wpinv_round_amount( $item_price ),
742
            'custom_price'      => isset( $item['custom_price'] ) ? $item['custom_price'] : '',
743
            'quantity'          => $quantity,
744
            'discount'          => wpinv_round_amount( $discount ),
745
            'subtotal'          => wpinv_round_amount( $subtotal ),
746
            'tax'               => wpinv_round_amount( $tax ),
747
            'price'             => wpinv_round_amount( $total ),
748
            'vat_rates_class'   => $tax_class,
749
            'vat_rate'          => $tax_rate,
750
            'meta'              => isset( $item['meta'] ) ? $item['meta'] : array(),
751
            'fees'              => $fees,
752
        );
753
        
754
        if ( $wpinv_is_last_cart_item ) {
755
            $wpinv_is_last_cart_item   = false;
756
            $wpinv_flat_discount_total = 0.00;
757
        }
758
    }
759
    
760
    return $details;
761
}
762
763
function wpinv_get_cart_details( $invoice_id = 0 ) {
764
    global $ajax_cart_details;
765
766
    $invoice      = wpinv_get_invoice_cart( $invoice_id );
767
    $cart_details = !empty( $ajax_cart_details ) ? $ajax_cart_details : $invoice->cart_details;
768
769
    $invoice_currency = $invoice->currency;
770
771 View Code Duplication
    if ( ! empty( $cart_details ) && is_array( $cart_details ) ) {
772
        foreach ( $cart_details as $key => $cart_item ) {
773
            $cart_details[ $key ]['currency'] = $invoice_currency;
774
775
            if ( ! isset( $cart_item['subtotal'] ) ) {
776
                $cart_details[ $key ]['subtotal'] = $cart_item['price'];
777
            }
778
        }
779
    }
780
781
    return apply_filters( 'wpinv_get_cart_details', $cart_details, $invoice_id );
782
}
783
784
function wpinv_record_status_change( $invoice_id, $new_status, $old_status ) {
785
    $invoice    = wpinv_get_invoice( $invoice_id );
786
    
787
    $old_status = wpinv_status_nicename( $old_status );
788
    $new_status = wpinv_status_nicename( $new_status );
789
790
    $status_change = sprintf( __( 'Invoice status changed from %s to %s', 'invoicing' ), $old_status, $new_status );
791
    
792
    // Add note
793
    return $invoice->add_note( $status_change, 0 );
0 ignored issues
show
Documentation introduced by
0 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...
794
}
795
add_action( 'wpinv_update_status', 'wpinv_record_status_change', 100, 3 );
796
797
function wpinv_complete_payment( $invoice_id, $new_status, $old_status ) {
798
    global $wpi_has_free_trial;
799
    
800
    $wpi_has_free_trial = false;
801
    
802
    if ( $old_status == 'publish' ) {
803
        return; // Make sure that payments are only paid once
804
    }
805
806
    // Make sure the payment completion is only processed when new status is paid
807
    if ( $new_status != 'publish' ) {
808
        return;
809
    }
810
811
    $invoice = new WPInv_Invoice( $invoice_id );
812
    if ( empty( $invoice ) ) {
813
        return;
814
    }
815
816
    $wpi_has_free_trial = $invoice->is_free_trial();
817
    $completed_date = $invoice->completed_date;
818
    $cart_details   = $invoice->cart_details;
819
820
    do_action( 'wpinv_pre_complete_payment', $invoice_id );
821
822
    if ( is_array( $cart_details ) ) {
823
        // Increase purchase count and earnings
824
        foreach ( $cart_details as $cart_index => $item ) {
825
            // Ensure these actions only run once, ever
826
            if ( empty( $completed_date ) ) {
827
                do_action( 'wpinv_complete_item_payment', $item['id'], $invoice_id, $item, $cart_index );
828
            }
829
        }
830
    }
831
    
832
    // Check for discount codes and increment their use counts
833
    if ( $discounts = $invoice->get_discounts( true ) ) {
834
        if( ! empty( $discounts ) ) {
835
            foreach( $discounts as $code ) {
836
                wpinv_increase_discount_usage( $code );
837
            }
838
        }
839
    }
840
    
841
    // Ensure this action only runs once ever
842
    if( empty( $completed_date ) ) {
843
        // Save the completed date
844
        $invoice->set( 'completed_date', current_time( 'mysql', 0 ) );
845
        $invoice->save();
846
847
        do_action( 'wpinv_complete_payment', $invoice_id );
848
    }
849
850
    // Empty the shopping cart
851
    wpinv_empty_cart();
852
}
853
add_action( 'wpinv_update_status', 'wpinv_complete_payment', 100, 3 );
854
855
function wpinv_update_payment_status( $invoice_id, $new_status = 'publish' ) {    
856
    $invoice = !empty( $invoice_id ) && is_object( $invoice_id ) ? $invoice_id : wpinv_get_invoice( (int)$invoice_id );
857
    
858
    if ( empty( $invoice ) ) {
859
        return false;
860
    }
861
    
862
    return $invoice->update_status( $new_status );
863
}
864
865
function wpinv_cart_has_fees( $type = 'all' ) {
866
    return false;
867
}
868
869
function wpinv_validate_checkout_fields() {    
870
    // Check if there is $_POST
871
    if ( empty( $_POST ) ) {
872
        return false;
873
    }
874
    
875
    // Start an array to collect valid data
876
    $valid_data = array(
877
        'gateway'          => wpinv_checkout_validate_gateway(), // Gateway fallback
878
        'discount'         => wpinv_checkout_validate_discounts(), // Set default discount
879
        'cc_info'          => wpinv_checkout_validate_cc() // Credit card info
880
    );
881
    
882
    // Validate agree to terms
883
    if ( wpinv_get_option( 'show_agree_to_terms', false ) ) {
884
        wpinv_checkout_validate_agree_to_terms();
885
    }
886
    
887
    $valid_data['logged_in_user']   = wpinv_checkout_validate_logged_in_user();
888
    
889
    // Return collected data
890
    return $valid_data;
891
}
892
893
function wpinv_checkout_validate_gateway() {
894
    $gateway = wpinv_get_default_gateway();
895
    
896
    $invoice = wpinv_get_invoice_cart();
897
    $has_subscription = $invoice->is_recurring();
898
    if ( empty( $invoice ) ) {
899
        wpinv_set_error( 'invalid_invoice', __( 'Your cart is empty.', 'invoicing' ) );
900
        return $gateway;
901
    }
902
903
    // Check if a gateway value is present
904
    if ( !empty( $_REQUEST['wpi-gateway'] ) ) {
905
        $gateway = sanitize_text_field( $_REQUEST['wpi-gateway'] );
906
907
        if ( $invoice->is_free() ) {
908
            $gateway = 'manual';
909
        } elseif ( !wpinv_is_gateway_active( $gateway ) ) {
910
            wpinv_set_error( 'invalid_gateway', __( 'The selected payment gateway is not enabled', 'invoicing' ) );
911
        } elseif ( $has_subscription && !wpinv_gateway_support_subscription( $gateway ) ) {
912
            wpinv_set_error( 'invalid_gateway', __( 'The selected payment gateway doesnot support subscription payment', 'invoicing' ) );
913
        }
914
    }
915
916
    if ( $has_subscription && count( wpinv_get_cart_contents() ) > 1 ) {
917
        wpinv_set_error( 'subscription_invalid', __( 'Only one subscription may be purchased through payment per checkout.', 'invoicing' ) );
918
    }
919
920
    return $gateway;
921
}
922
923
function wpinv_checkout_validate_discounts() {
924
    // Retrieve the discount stored in cookies
925
    $discounts = wpinv_get_cart_discounts();
926
    
927
    $error = false;
928
    // If we have discounts, loop through them
929
    if ( ! empty( $discounts ) ) {
930
        foreach ( $discounts as $discount ) {
931
            // Check if valid
932
            if (  !wpinv_is_discount_valid( $discount, get_current_user_id() ) ) {
933
                // Discount is not valid
934
                $error = true;
935
            }
936
        }
937
    } else {
938
        // No discounts
939
        return NULL;
940
    }
941
942
    if ( $error && !wpinv_get_errors() ) {
943
        wpinv_set_error( 'invalid_discount', __( 'Discount code you entered is invalid', 'invoicing' ) );
944
    }
945
946
    return implode( ',', $discounts );
947
}
948
949
function wpinv_checkout_validate_cc() {
950
    $card_data = wpinv_checkout_get_cc_info();
951
952
    // Validate the card zip
953
    if ( !empty( $card_data['wpinv_zip'] ) ) {
954
        if ( !wpinv_checkout_validate_cc_zip( $card_data['wpinv_zip'], $card_data['wpinv_country'] ) ) {
955
            wpinv_set_error( 'invalid_cc_zip', __( 'The zip / postcode you entered for your billing address is invalid', 'invoicing' ) );
956
        }
957
    }
958
959
    // This should validate card numbers at some point too
960
    return $card_data;
961
}
962
963
function wpinv_checkout_get_cc_info() {
964
	$cc_info = array();
965
	$cc_info['card_name']      = isset( $_POST['card_name'] )       ? sanitize_text_field( $_POST['card_name'] )       : '';
966
	$cc_info['card_number']    = isset( $_POST['card_number'] )     ? sanitize_text_field( $_POST['card_number'] )     : '';
967
	$cc_info['card_cvc']       = isset( $_POST['card_cvc'] )        ? sanitize_text_field( $_POST['card_cvc'] )        : '';
968
	$cc_info['card_exp_month'] = isset( $_POST['card_exp_month'] )  ? sanitize_text_field( $_POST['card_exp_month'] )  : '';
969
	$cc_info['card_exp_year']  = isset( $_POST['card_exp_year'] )   ? sanitize_text_field( $_POST['card_exp_year'] )   : '';
970
	$cc_info['card_address']   = isset( $_POST['wpinv_address'] )  ? sanitize_text_field( $_POST['wpinv_address'] ) : '';
971
	$cc_info['card_city']      = isset( $_POST['wpinv_city'] )     ? sanitize_text_field( $_POST['wpinv_city'] )    : '';
972
	$cc_info['card_state']     = isset( $_POST['wpinv_state'] )    ? sanitize_text_field( $_POST['wpinv_state'] )   : '';
973
	$cc_info['card_country']   = isset( $_POST['wpinv_country'] )  ? sanitize_text_field( $_POST['wpinv_country'] ) : '';
974
	$cc_info['card_zip']       = isset( $_POST['wpinv_zip'] )      ? sanitize_text_field( $_POST['wpinv_zip'] )     : '';
975
976
	// Return cc info
977
	return $cc_info;
978
}
979
980
function wpinv_checkout_validate_cc_zip( $zip = 0, $country_code = '' ) {
981
    $ret = false;
982
983
    if ( empty( $zip ) || empty( $country_code ) )
984
        return $ret;
985
986
    $country_code = strtoupper( $country_code );
987
988
    $zip_regex = array(
989
        "AD" => "AD\d{3}",
990
        "AM" => "(37)?\d{4}",
991
        "AR" => "^([A-Z]{1}\d{4}[A-Z]{3}|[A-Z]{1}\d{4}|\d{4})$",
992
        "AS" => "96799",
993
        "AT" => "\d{4}",
994
        "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})$",
995
        "AX" => "22\d{3}",
996
        "AZ" => "\d{4}",
997
        "BA" => "\d{5}",
998
        "BB" => "(BB\d{5})?",
999
        "BD" => "\d{4}",
1000
        "BE" => "^[1-9]{1}[0-9]{3}$",
1001
        "BG" => "\d{4}",
1002
        "BH" => "((1[0-2]|[2-9])\d{2})?",
1003
        "BM" => "[A-Z]{2}[ ]?[A-Z0-9]{2}",
1004
        "BN" => "[A-Z]{2}[ ]?\d{4}",
1005
        "BR" => "\d{5}[\-]?\d{3}",
1006
        "BY" => "\d{6}",
1007
        "CA" => "^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$",
1008
        "CC" => "6799",
1009
        "CH" => "^[1-9][0-9][0-9][0-9]$",
1010
        "CK" => "\d{4}",
1011
        "CL" => "\d{7}",
1012
        "CN" => "\d{6}",
1013
        "CR" => "\d{4,5}|\d{3}-\d{4}",
1014
        "CS" => "\d{5}",
1015
        "CV" => "\d{4}",
1016
        "CX" => "6798",
1017
        "CY" => "\d{4}",
1018
        "CZ" => "\d{3}[ ]?\d{2}",
1019
        "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",
1020
        "DK" => "^([D-d][K-k])?( |-)?[1-9]{1}[0-9]{3}$",
1021
        "DO" => "\d{5}",
1022
        "DZ" => "\d{5}",
1023
        "EC" => "([A-Z]\d{4}[A-Z]|(?:[A-Z]{2})?\d{6})?",
1024
        "EE" => "\d{5}",
1025
        "EG" => "\d{5}",
1026
        "ES" => "^([1-9]{2}|[0-9][1-9]|[1-9][0-9])[0-9]{3}$",
1027
        "ET" => "\d{4}",
1028
        "FI" => "\d{5}",
1029
        "FK" => "FIQQ 1ZZ",
1030
        "FM" => "(9694[1-4])([ \-]\d{4})?",
1031
        "FO" => "\d{3}",
1032
        "FR" => "^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$",
1033
        "GE" => "\d{4}",
1034
        "GF" => "9[78]3\d{2}",
1035
        "GL" => "39\d{2}",
1036
        "GN" => "\d{3}",
1037
        "GP" => "9[78][01]\d{2}",
1038
        "GR" => "\d{3}[ ]?\d{2}",
1039
        "GS" => "SIQQ 1ZZ",
1040
        "GT" => "\d{5}",
1041
        "GU" => "969[123]\d([ \-]\d{4})?",
1042
        "GW" => "\d{4}",
1043
        "HM" => "\d{4}",
1044
        "HN" => "(?:\d{5})?",
1045
        "HR" => "\d{5}",
1046
        "HT" => "\d{4}",
1047
        "HU" => "\d{4}",
1048
        "ID" => "\d{5}",
1049
        "IE" => "((D|DUBLIN)?([1-9]|6[wW]|1[0-8]|2[024]))?",
1050
        "IL" => "\d{5}",
1051
        "IN"=> "^[1-9][0-9][0-9][0-9][0-9][0-9]$", //india
1052
        "IO" => "BBND 1ZZ",
1053
        "IQ" => "\d{5}",
1054
        "IS" => "\d{3}",
1055
        "IT" => "^(V-|I-)?[0-9]{5}$",
1056
        "JO" => "\d{5}",
1057
        "JP" => "\d{3}-\d{4}",
1058
        "KE" => "\d{5}",
1059
        "KG" => "\d{6}",
1060
        "KH" => "\d{5}",
1061
        "KR" => "\d{3}[\-]\d{3}",
1062
        "KW" => "\d{5}",
1063
        "KZ" => "\d{6}",
1064
        "LA" => "\d{5}",
1065
        "LB" => "(\d{4}([ ]?\d{4})?)?",
1066
        "LI" => "(948[5-9])|(949[0-7])",
1067
        "LK" => "\d{5}",
1068
        "LR" => "\d{4}",
1069
        "LS" => "\d{3}",
1070
        "LT" => "\d{5}",
1071
        "LU" => "\d{4}",
1072
        "LV" => "\d{4}",
1073
        "MA" => "\d{5}",
1074
        "MC" => "980\d{2}",
1075
        "MD" => "\d{4}",
1076
        "ME" => "8\d{4}",
1077
        "MG" => "\d{3}",
1078
        "MH" => "969[67]\d([ \-]\d{4})?",
1079
        "MK" => "\d{4}",
1080
        "MN" => "\d{6}",
1081
        "MP" => "9695[012]([ \-]\d{4})?",
1082
        "MQ" => "9[78]2\d{2}",
1083
        "MT" => "[A-Z]{3}[ ]?\d{2,4}",
1084
        "MU" => "(\d{3}[A-Z]{2}\d{3})?",
1085
        "MV" => "\d{5}",
1086
        "MX" => "\d{5}",
1087
        "MY" => "\d{5}",
1088
        "NC" => "988\d{2}",
1089
        "NE" => "\d{4}",
1090
        "NF" => "2899",
1091
        "NG" => "(\d{6})?",
1092
        "NI" => "((\d{4}-)?\d{3}-\d{3}(-\d{1})?)?",
1093
        "NL" => "^[1-9][0-9]{3}\s?([a-zA-Z]{2})?$",
1094
        "NO" => "\d{4}",
1095
        "NP" => "\d{5}",
1096
        "NZ" => "\d{4}",
1097
        "OM" => "(PC )?\d{3}",
1098
        "PF" => "987\d{2}",
1099
        "PG" => "\d{3}",
1100
        "PH" => "\d{4}",
1101
        "PK" => "\d{5}",
1102
        "PL" => "\d{2}-\d{3}",
1103
        "PM" => "9[78]5\d{2}",
1104
        "PN" => "PCRN 1ZZ",
1105
        "PR" => "00[679]\d{2}([ \-]\d{4})?",
1106
        "PT" => "\d{4}([\-]\d{3})?",
1107
        "PW" => "96940",
1108
        "PY" => "\d{4}",
1109
        "RE" => "9[78]4\d{2}",
1110
        "RO" => "\d{6}",
1111
        "RS" => "\d{5}",
1112
        "RU" => "\d{6}",
1113
        "SA" => "\d{5}",
1114
        "SE" => "^(s-|S-){0,1}[0-9]{3}\s?[0-9]{2}$",
1115
        "SG" => "\d{6}",
1116
        "SH" => "(ASCN|STHL) 1ZZ",
1117
        "SI" => "\d{4}",
1118
        "SJ" => "\d{4}",
1119
        "SK" => "\d{3}[ ]?\d{2}",
1120
        "SM" => "4789\d",
1121
        "SN" => "\d{5}",
1122
        "SO" => "\d{5}",
1123
        "SZ" => "[HLMS]\d{3}",
1124
        "TC" => "TKCA 1ZZ",
1125
        "TH" => "\d{5}",
1126
        "TJ" => "\d{6}",
1127
        "TM" => "\d{6}",
1128
        "TN" => "\d{4}",
1129
        "TR" => "\d{5}",
1130
        "TW" => "\d{3}(\d{2})?",
1131
        "UA" => "\d{5}",
1132
        "UK" => "^(GIR|[A-Z]\d[A-Z\d]??|[A-Z]{2}\d[A-Z\d]??)[ ]??(\d[A-Z]{2})$",
1133
        "US" => "^\d{5}([\-]?\d{4})?$",
1134
        "UY" => "\d{5}",
1135
        "UZ" => "\d{6}",
1136
        "VA" => "00120",
1137
        "VE" => "\d{4}",
1138
        "VI" => "008(([0-4]\d)|(5[01]))([ \-]\d{4})?",
1139
        "WF" => "986\d{2}",
1140
        "YT" => "976\d{2}",
1141
        "YU" => "\d{5}",
1142
        "ZA" => "\d{4}",
1143
        "ZM" => "\d{5}"
1144
    );
1145
1146
    if ( ! isset ( $zip_regex[ $country_code ] ) || preg_match( "/" . $zip_regex[ $country_code ] . "/i", $zip ) )
1147
        $ret = true;
1148
1149
    return apply_filters( 'wpinv_is_zip_valid', $ret, $zip, $country_code );
1150
}
1151
1152
function wpinv_checkout_validate_agree_to_terms() {
1153
    // Validate agree to terms
1154
    if ( ! isset( $_POST['wpi_agree_to_terms'] ) || $_POST['wpi_agree_to_terms'] != 1 ) {
1155
        // User did not agree
1156
        wpinv_set_error( 'agree_to_terms', apply_filters( 'wpinv_agree_to_terms_text', __( 'You must agree to the terms of use', 'invoicing' ) ) );
1157
    }
1158
}
1159
1160
function wpinv_checkout_validate_logged_in_user() {
1161
    $user_ID = get_current_user_id();
1162
    
1163
    $valid_user_data = array(
1164
        // Assume there will be errors
1165
        'user_id' => -1
1166
    );
1167
    
1168
    // Verify there is a user_ID
1169
    if ( $user_ID > 0 ) {
1170
        // Get the logged in user data
1171
        $user_data = get_userdata( $user_ID );
1172
        $required_fields  = wpinv_checkout_required_fields();
1173
1174
        // Loop through required fields and show error messages
1175
         if ( !empty( $required_fields ) ) {
1176
            foreach ( $required_fields as $field_name => $value ) {
1177
                if ( in_array( $value, $required_fields ) && empty( $_POST[ 'wpinv_' . $field_name ] ) ) {
1178
                    wpinv_set_error( $value['error_id'], $value['error_message'] );
1179
                }
1180
            }
1181
        }
1182
1183
        // Verify data
1184
        if ( $user_data ) {
1185
            // Collected logged in user data
1186
            $valid_user_data = array(
1187
                'user_id'     => $user_ID,
1188
                'email'       => isset( $_POST['wpinv_email'] ) ? sanitize_email( $_POST['wpinv_email'] ) : $user_data->user_email,
1189
                'first_name'  => isset( $_POST['wpinv_first_name'] ) && ! empty( $_POST['wpinv_first_name'] ) ? sanitize_text_field( $_POST['wpinv_first_name'] ) : $user_data->first_name,
1190
                'last_name'   => isset( $_POST['wpinv_last_name'] ) && ! empty( $_POST['wpinv_last_name']  ) ? sanitize_text_field( $_POST['wpinv_last_name']  ) : $user_data->last_name,
1191
            );
1192
1193
            if ( !empty( $_POST[ 'wpinv_email' ] ) && !is_email( $_POST[ 'wpinv_email' ] ) ) {
1194
                wpinv_set_error( 'invalid_email', __( 'Please enter a valid email address', 'invoicing' ) );
1195
            }
1196
        } else {
1197
            // Set invalid user error
1198
            wpinv_set_error( 'invalid_user', __( 'The user billing information is invalid', 'invoicing' ) );
1199
        }
1200
    }
1201
1202
    // Return user data
1203
    return $valid_user_data;
1204
}
1205
1206
function wpinv_checkout_form_get_user( $valid_data = array() ) {
1207
    // Initialize user
1208
    $user    = false;
1209
    $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...
1210
1211
    /*if ( $is_ajax ) {
1212
        // Do not create or login the user during the ajax submission (check for errors only)
1213
        return true;
1214
    } else */if ( is_user_logged_in() ) {
1215
        // Set the valid user as the logged in collected data
1216
        $user = $valid_data['logged_in_user'];
1217
    }
1218
1219
    // Verify we have an user
1220
    if ( false === $user || empty( $user ) ) {
1221
        // Return false
1222
        return false;
1223
    }
1224
    
1225
    $address_fields = array(
1226
        'first_name',
1227
        'last_name',
1228
        'company',
1229
        'vat_number',
1230
        ///'email',
1231
        'phone',
1232
        'address',
1233
        'city',
1234
        'state',
1235
        'country',
1236
        'zip',
1237
    );
1238
    
1239
    foreach ( $address_fields as $field ) {
1240
        $user[$field]  = !empty( $_POST['wpinv_' . $field] ) ? sanitize_text_field( $_POST['wpinv_' . $field] ) : false;
1241
        
1242
        if ( !empty( $user['user_id'] ) ) {
1243
            update_user_meta( $user['user_id'], '_wpinv_' . $field, $user[$field] );
1244
        }
1245
    }
1246
1247
    // Return valid user
1248
    return $user;
1249
}
1250
1251
function wpinv_set_checkout_session( $invoice_data = array() ) {
1252
    global $wpi_session;
1253
    
1254
    return $wpi_session->set( 'wpinv_checkout', $invoice_data );
1255
}
1256
1257
function wpinv_get_checkout_session() {
1258
	global $wpi_session;
1259
    
1260
    return $wpi_session->get( 'wpinv_checkout' );
1261
}
1262
1263
function wpinv_empty_cart() {
1264
    global $wpi_session;
1265
1266
    // Remove cart contents
1267
    $wpi_session->set( 'wpinv_checkout', NULL );
1268
1269
    // Remove all cart fees
1270
    $wpi_session->set( 'wpi_cart_fees', NULL );
1271
1272
    do_action( 'wpinv_empty_cart' );
1273
}
1274
1275
function wpinv_process_checkout() {
1276
    global $wpinv_euvat, $wpi_checkout_id;
1277
    
1278
    wpinv_clear_errors();
1279
    
1280
    $invoice = wpinv_get_invoice_cart();
1281
    
1282
    $wpi_checkout_id = $invoice->ID;
1283
    
1284
    do_action( 'wpinv_pre_process_checkout' );
1285
    
1286
    if ( !wpinv_get_cart_contents() ) { // Make sure the cart isn't empty
1287
        $valid_data = false;
1288
        wpinv_set_error( 'empty_cart', __( 'Your cart is empty', 'invoicing' ) );
1289
    } else {
1290
        // Validate the form $_POST data
1291
        $valid_data = wpinv_validate_checkout_fields();
1292
        
1293
        // Allow themes and plugins to hook to errors
1294
        do_action( 'wpinv_checkout_error_checks', $valid_data, $_POST );
1295
    }
1296
    
1297
    $is_ajax    = defined( 'DOING_AJAX' ) && DOING_AJAX;
1298
    
1299
    // Validate the user
1300
    $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...
1301
1302
    // Let extensions validate fields after user is logged in if user has used login/registration form
1303
    do_action( 'wpinv_checkout_user_error_checks', $user, $valid_data, $_POST );
1304
    
1305
    if ( false === $valid_data || wpinv_get_errors() || ! $user ) {
1306
        if ( $is_ajax ) {
1307
            do_action( 'wpinv_ajax_checkout_errors' );
1308
            die();
1309
        } else {
1310
            return false;
1311
        }
1312
    }
1313
1314
    if ( $is_ajax ) {
1315
        // Save address fields.
1316
        $address_fields = array( 'first_name', 'last_name', 'phone', 'address', 'city', 'country', 'state', 'zip', 'company' );
1317 View Code Duplication
        foreach ( $address_fields as $field ) {
1318
            if ( isset( $user[$field] ) ) {
1319
                $invoice->set( $field, $user[$field] );
1320
            }
1321
            
1322
            $invoice->save();
1323
        }
1324
1325
        $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...
1326
        $response['data']['subtotal']   = $invoice->get_subtotal();
1327
        $response['data']['subtotalf']  = $invoice->get_subtotal( true );
1328
        $response['data']['discount']   = $invoice->get_discount();
1329
        $response['data']['discountf']  = $invoice->get_discount( true );
1330
        $response['data']['tax']        = $invoice->get_tax();
1331
        $response['data']['taxf']       = $invoice->get_tax( true );
1332
        $response['data']['total']      = $invoice->get_total();
1333
        $response['data']['totalf']     = $invoice->get_total( true );
1334
        
1335
        wp_send_json( $response );
1336
    }
1337
    
1338
    $user_info = array(
1339
        'user_id'        => $user['user_id'],
1340
        'first_name'     => $user['first_name'],
1341
        'last_name'      => $user['last_name'],
1342
        'email'          => $user['email'],
1343
        'company'        => $user['company'],
1344
        'phone'          => $user['phone'],
1345
        'address'        => $user['address'],
1346
        'city'           => $user['city'],
1347
        'country'        => $user['country'],
1348
        'state'          => $user['state'],
1349
        'zip'            => $user['zip'],
1350
    );
1351
    
1352
    $cart_items = wpinv_get_cart_contents();
1353
    $discounts  = wpinv_get_cart_discounts();
1354
    
1355
    // Setup invoice information
1356
    $invoice_data = array(
1357
        'invoice_id'        => !empty( $invoice ) ? $invoice->ID : 0,
1358
        'items'             => $cart_items,
1359
        'cart_discounts'    => $discounts,
1360
        'fees'              => wpinv_get_cart_fees(),        // Any arbitrary fees that have been added to the cart
1361
        'subtotal'          => wpinv_get_cart_subtotal( $cart_items ),    // Amount before taxes and discounts
1362
        'discount'          => wpinv_get_cart_items_discount_amount( $cart_items, $discounts ), // Discounted amount
1363
        'tax'               => wpinv_get_cart_tax( $cart_items ),               // Taxed amount
1364
        'price'             => wpinv_get_cart_total( $cart_items, $discounts ),    // Amount after taxes
1365
        'invoice_key'       => $invoice->get_key() ? $invoice->get_key() : $invoice->generate_key(),
1366
        'user_email'        => $user['email'],
1367
        'date'              => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ),
1368
        'user_info'         => stripslashes_deep( $user_info ),
1369
        'post_data'         => $_POST,
1370
        'cart_details'      => $cart_items,
1371
        'gateway'           => $valid_data['gateway'],
1372
        'card_info'         => $valid_data['cc_info']
1373
    );
1374
    
1375
    $vat_info   = $wpinv_euvat->current_vat_data();
1376
    if ( is_array( $vat_info ) ) {
1377
        $invoice_data['user_info']['vat_number']        = $vat_info['number'];
1378
        $invoice_data['user_info']['vat_rate']          = wpinv_get_tax_rate($invoice_data['user_info']['country'], $invoice_data['user_info']['state']);
1379
        $invoice_data['user_info']['adddress_confirmed']    = isset($vat_info['adddress_confirmed']) ? $vat_info['adddress_confirmed'] : false;
1380
1381
        // Add the VAT rate to each item in the cart
1382
        foreach( $invoice_data['cart_details'] as $key => $item_data) {
1383
            $rate = wpinv_get_tax_rate($invoice_data['user_info']['country'], $invoice_data['user_info']['state'], $item_data['id']);
1384
            $invoice_data['cart_details'][$key]['vat_rate'] = wpinv_round_amount( $rate, 4 );
1385
        }
1386
    }
1387
    
1388
    // Save vat fields.
1389
    $address_fields = array( 'vat_number', 'vat_rate', 'adddress_confirmed' );
1390 View Code Duplication
    foreach ( $address_fields as $field ) {
1391
        if ( isset( $invoice_data['user_info'][$field] ) ) {
1392
            $invoice->set( $field, $invoice_data['user_info'][$field] );
1393
        }
1394
        
1395
        $invoice->save();
1396
    }
1397
1398
    // Add the user data for hooks
1399
    $valid_data['user'] = $user;
1400
    
1401
    // Allow themes and plugins to hook before the gateway
1402
    do_action( 'wpinv_checkout_before_gateway', $_POST, $user_info, $valid_data );
1403
    
1404
    // If the total amount in the cart is 0, send to the manual gateway. This emulates a free invoice
1405
    if ( !$invoice_data['price'] ) {
1406
        // Revert to manual
1407
        $invoice_data['gateway'] = 'manual';
1408
        $_POST['wpi-gateway'] = 'manual';
1409
    }
1410
    
1411
    // Allow the invoice data to be modified before it is sent to the gateway
1412
    $invoice_data = apply_filters( 'wpinv_data_before_gateway', $invoice_data, $valid_data );
1413
    
1414
    // Setup the data we're storing in the purchase session
1415
    $session_data = $invoice_data;
1416
    // Make sure credit card numbers are never stored in sessions
1417
    if ( !empty( $session_data['card_info']['card_number'] ) ) {
1418
        unset( $session_data['card_info']['card_number'] );
1419
    }
1420
    
1421
    // Used for showing item links to non logged-in users after purchase, and for other plugins needing purchase data.
1422
    wpinv_set_checkout_session( $invoice_data );
1423
    
1424
    // Set gateway
1425
    $invoice->update_meta( '_wpinv_gateway', $invoice_data['gateway'] );
1426
    $invoice->update_meta( '_wpinv_mode', ( wpinv_is_test_mode( $invoice_data['gateway'] ) ? 'test' : 'live' ) );
1427
    $invoice->update_meta( '_wpinv_checkout', true );
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string.

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...
1428
    
1429
    do_action( 'wpinv_checkout_before_send_to_gateway', $invoice, $invoice_data );
1430
1431
    // Send info to the gateway for payment processing
1432
    wpinv_send_to_gateway( $invoice_data['gateway'], $invoice_data );
1433
    die();
1434
}
1435
add_action( 'wpinv_payment', 'wpinv_process_checkout' );
1436
1437
function wpinv_get_invoices( $args ) {
1438
    $args = wp_parse_args( $args, array(
1439
        'status'   => array_keys( wpinv_get_invoice_statuses() ),
1440
        'type'     => 'wpi_invoice',
1441
        'parent'   => null,
1442
        'user'     => null,
1443
        'email'    => '',
1444
        'limit'    => get_option( 'posts_per_page' ),
1445
        'offset'   => null,
1446
        'page'     => 1,
1447
        'exclude'  => array(),
1448
        'orderby'  => 'date',
1449
        'order'    => 'DESC',
1450
        'return'   => 'objects',
1451
        'paginate' => false,
1452
    ) );
1453
    
1454
    // Handle some BW compatibility arg names where wp_query args differ in naming.
1455
    $map_legacy = array(
1456
        'numberposts'    => 'limit',
1457
        'post_type'      => 'type',
1458
        'post_status'    => 'status',
1459
        'post_parent'    => 'parent',
1460
        'author'         => 'user',
1461
        'posts_per_page' => 'limit',
1462
        'paged'          => 'page',
1463
    );
1464
1465
    foreach ( $map_legacy as $from => $to ) {
1466
        if ( isset( $args[ $from ] ) ) {
1467
            $args[ $to ] = $args[ $from ];
1468
        }
1469
    }
1470
    
1471
    if ( get_query_var( 'paged' ) )
1472
        $args['page'] = get_query_var('paged');
1473
    else if ( get_query_var( 'page' ) )
1474
        $args['page'] = get_query_var( 'page' );
1475
    else if ( !empty( $args[ 'page' ] ) )
1476
        $args['page'] = $args[ 'page' ];
1477
    else
1478
        $args['page'] = 1;
1479
1480
    /**
1481
     * Generate WP_Query args. This logic will change if orders are moved to
1482
     * custom tables in the future.
1483
     */
1484
    $wp_query_args = array(
1485
        'post_type'      => 'wpi_invoice',
1486
        'post_status'    => $args['status'],
1487
        'posts_per_page' => $args['limit'],
1488
        'meta_query'     => array(),
1489
        'date_query'     => !empty( $args['date_query'] ) ? $args['date_query'] : array(),
1490
        'fields'         => 'ids',
1491
        'orderby'        => $args['orderby'],
1492
        'order'          => $args['order'],
1493
    );
1494
    
1495
    if ( !empty( $args['user'] ) ) {
1496
        $wp_query_args['author'] = absint( $args['user'] );
1497
    }
1498
1499
    if ( ! is_null( $args['parent'] ) ) {
1500
        $wp_query_args['post_parent'] = absint( $args['parent'] );
1501
    }
1502
1503
    if ( ! is_null( $args['offset'] ) ) {
1504
        $wp_query_args['offset'] = absint( $args['offset'] );
1505
    } else {
1506
        $wp_query_args['paged'] = absint( $args['page'] );
1507
    }
1508
1509
    if ( ! empty( $args['exclude'] ) ) {
1510
        $wp_query_args['post__not_in'] = array_map( 'absint', $args['exclude'] );
1511
    }
1512
1513
    if ( ! $args['paginate' ] ) {
1514
        $wp_query_args['no_found_rows'] = true;
1515
    }
1516
1517
    // Get results.
1518
    $invoices = new WP_Query( $wp_query_args );
1519
1520
    if ( 'objects' === $args['return'] ) {
1521
        $return = array_map( 'wpinv_get_invoice', $invoices->posts );
1522
    } elseif ( 'self' === $args['return'] ) {
1523
        return $invoices;
1524
    } else {
1525
        $return = $invoices->posts;
1526
    }
1527
1528
    if ( $args['paginate' ] ) {
1529
        return (object) array(
1530
            'invoices'      => $return,
1531
            'total'         => $invoices->found_posts,
1532
            'max_num_pages' => $invoices->max_num_pages,
1533
        );
1534
    } else {
1535
        return $return;
1536
    }
1537
}
1538
1539
function wpinv_get_user_invoices_columns() {
1540
    $columns = array(
1541
            'invoice-number'  => array( 'title' => __( 'ID', 'invoicing' ), 'class' => 'text-left' ),
1542
            'invoice-date'    => array( 'title' => __( 'Date', 'invoicing' ), 'class' => 'text-left' ),
1543
            'invoice-status'  => array( 'title' => __( 'Status', 'invoicing' ), 'class' => 'text-center' ),
1544
            'invoice-total'   => array( 'title' => __( 'Total', 'invoicing' ), 'class' => 'text-right' ),
1545
            'invoice-actions' => array( 'title' => '&nbsp;', 'class' => 'text-center' ),
1546
        );
1547
1548
    return apply_filters( 'wpinv_user_invoices_columns', $columns );
1549
}
1550
1551
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...
1552
    global $wpinv_receipt_args;
1553
1554
    $wpinv_receipt_args = shortcode_atts( array(
1555
        'error'           => __( 'Sorry, trouble retrieving payment receipt.', 'invoicing' ),
1556
        'price'           => true,
1557
        'discount'        => true,
1558
        'items'           => true,
1559
        'date'            => true,
1560
        'notes'           => true,
1561
        'invoice_key'     => false,
1562
        'payment_method'  => true,
1563
        'invoice_id'      => true
1564
    ), $atts, 'wpinv_receipt' );
1565
1566
    $session = wpinv_get_checkout_session();
1567
    if ( isset( $_GET['invoice_key'] ) ) {
1568
        $invoice_key = urldecode( $_GET['invoice_key'] );
1569
    } else if ( $session && isset( $session['invoice_key'] ) ) {
1570
        $invoice_key = $session['invoice_key'];
1571
    } elseif ( isset( $wpinv_receipt_args['invoice_key'] ) && $wpinv_receipt_args['invoice_key'] ) {
1572
        $invoice_key = $wpinv_receipt_args['invoice_key'];
1573
    } else if ( isset( $_GET['invoice-id'] ) ) {
1574
        $invoice_key = wpinv_get_payment_key( (int)$_GET['invoice-id'] );
1575
    }
1576
1577
    // No key found
1578
    if ( ! isset( $invoice_key ) ) {
1579
        return '<p class="alert alert-error">' . $wpinv_receipt_args['error'] . '</p>';
1580
    }
1581
1582
    $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...
1583
    $user_can_view = wpinv_can_view_receipt( $invoice_key );
1584 View Code Duplication
    if ( $user_can_view && isset( $_GET['invoice-id'] ) ) {
1585
        $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...
1586
        $user_can_view  = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? true : false;
1587
    }
1588
1589
    // Key was provided, but user is logged out. Offer them the ability to login and view the receipt
1590
    if ( ! $user_can_view && ! empty( $invoice_key ) && ! is_user_logged_in() ) {
1591
        // login redirect
1592
        return '<p class="alert alert-error">' . __( 'You are not allowed to access this section', 'invoicing' ) . '</p>';
1593
    }
1594
1595
    if ( ! apply_filters( 'wpinv_user_can_view_receipt', $user_can_view, $wpinv_receipt_args ) ) {
1596
        return '<p class="alert alert-error">' . $wpinv_receipt_args['error'] . '</p>';
1597
    }
1598
1599
    ob_start();
1600
1601
    wpinv_get_template_part( 'wpinv-invoice-receipt' );
1602
1603
    $display = ob_get_clean();
1604
1605
    return $display;
1606
}
1607
1608 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...
1609
	global $wpdb;
1610
1611
	$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 ) );
1612
1613
	if ( $invoice_id != NULL )
1614
		return $invoice_id;
1615
1616
	return 0;
1617
}
1618
1619
function wpinv_can_view_receipt( $invoice_key = '' ) {
1620
	$return = false;
1621
1622
	if ( empty( $invoice_key ) ) {
1623
		return $return;
1624
	}
1625
1626
	global $wpinv_receipt_args;
1627
1628
	$wpinv_receipt_args['id'] = wpinv_get_invoice_id_by_key( $invoice_key );
1629
	if ( isset( $_GET['invoice-id'] ) ) {
1630
		$wpinv_receipt_args['id'] = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? (int)$_GET['invoice-id'] : 0;
1631
	}
1632
1633
	$user_id = (int) wpinv_get_user_id( $wpinv_receipt_args['id'] );
1634
    $invoice_meta = wpinv_get_invoice_meta( $wpinv_receipt_args['id'] );
1635
1636
	if ( is_user_logged_in() ) {
1637
		if ( $user_id === (int) get_current_user_id() ) {
1638
			$return = true;
1639
		}
1640
	}
1641
1642
	$session = wpinv_get_checkout_session();
1643
	if ( ! empty( $session ) && ! is_user_logged_in() ) {
1644
		if ( $session['invoice_key'] === $invoice_meta['key'] ) {
1645
			$return = true;
1646
		}
1647
	}
1648
1649
	return (bool) apply_filters( 'wpinv_can_view_receipt', $return, $invoice_key );
1650
}
1651
1652
function wpinv_pay_for_invoice() {
1653
    global $wpinv_euvat;
1654
    
1655
    if ( isset( $_GET['invoice_key'] ) ) {
1656
        $checkout_uri   = wpinv_get_checkout_uri();
1657
        $invoice_key    = sanitize_text_field( $_GET['invoice_key'] );
1658
        
1659
        if ( empty( $invoice_key ) ) {
1660
            wpinv_set_error( 'invalid_invoice', __( 'Invoice not found', 'invoicing' ) );
1661
            wp_redirect( $checkout_uri );
1662
            wpinv_die();
1663
        }
1664
        
1665
        do_action( 'wpinv_check_pay_for_invoice', $invoice_key );
1666
1667
        $invoice_id    = wpinv_get_invoice_id_by_key( $invoice_key );
1668
        $user_can_view = wpinv_can_view_receipt( $invoice_key );
1669 View Code Duplication
        if ( $user_can_view && isset( $_GET['invoice-id'] ) ) {
1670
            $invoice_id     = (int)$_GET['invoice-id'];
1671
            $user_can_view  = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? true : false;
1672
        }
1673
        
1674
        if ( $invoice_id && $user_can_view && ( $invoice = wpinv_get_invoice( $invoice_id ) ) ) {
1675
            if ( $invoice->needs_payment() ) {
1676
                $data                   = array();
1677
                $data['invoice_id']     = $invoice_id;
1678
                $data['cart_discounts'] = $invoice->get_discounts( true );
1679
                
1680
                wpinv_set_checkout_session( $data );
1681
                
1682
                if ( wpinv_get_option( 'vat_ip_country_default' ) ) {
1683
                    $_POST['country']   = $wpinv_euvat->get_country_by_ip();
1684
                    $_POST['state']     = $_POST['country'] == $invoice->country ? $invoice->state : '';
1685
                    
1686
                    wpinv_recalculate_tax( true );
1687
                }
1688
                
1689
            } else {
1690
                $checkout_uri = $invoice->get_view_url();
1691
            }
1692
        } else {
1693
            wpinv_set_error( 'invalid_invoice', __( 'You are not allowed to view this invoice', 'invoicing' ) );
1694
            
1695
            $checkout_uri = is_user_logged_in() ? wpinv_get_history_page_uri() : wp_login_url( get_permalink() );
1696
        }
1697
        
1698
        wp_redirect( $checkout_uri );
1699
        wpinv_die();
1700
    }
1701
}
1702
add_action( 'wpinv_pay_for_invoice', 'wpinv_pay_for_invoice' );
1703
1704
function wpinv_handle_pay_via_invoice_link( $invoice_key ) {
1705
    if ( !empty( $invoice_key ) && !empty( $_REQUEST['_wpipay'] ) && !is_user_logged_in() && $invoice_id = wpinv_get_invoice_id_by_key( $invoice_key ) ) {
1706
        if ( $invoice = wpinv_get_invoice( $invoice_id ) ) {
1707
            $user_id = $invoice->get_user_id();
1708
            $secret = sanitize_text_field( $_GET['_wpipay'] );
1709
            
1710 View Code Duplication
            if ( $secret === md5( $user_id . '::' . $invoice->get_email() . '::' . $invoice_key ) ) { // valid invoice link
1711
                $redirect_to = remove_query_arg( '_wpipay', get_permalink() );
1712
                
1713
                wpinv_guest_redirect( $redirect_to, $user_id );
1714
                wpinv_die();
1715
            }
1716
        }
1717
    }
1718
}
1719
add_action( 'wpinv_check_pay_for_invoice', 'wpinv_handle_pay_via_invoice_link' );
1720
1721
function wpinv_set_payment_transaction_id( $invoice_id = 0, $transaction_id = '' ) {
1722
    $invoice_id = is_object( $invoice_id ) && !empty( $invoice_id->ID ) ? $invoice_id : $invoice_id;
1723
    
1724
    if ( empty( $invoice_id ) && $invoice_id > 0 ) {
1725
        return false;
1726
    }
1727
    
1728
    if ( empty( $transaction_id ) ) {
1729
        $transaction_id = $invoice_id;
1730
    }
1731
1732
    $transaction_id = apply_filters( 'wpinv_set_payment_transaction_id', $transaction_id, $invoice_id );
1733
    
1734
    return wpinv_update_invoice_meta( $invoice_id, '_wpinv_transaction_id', $transaction_id );
1735
}
1736
1737
function wpinv_invoice_status_label( $status, $status_display = '' ) {
1738
    if ( empty( $status_display ) ) {
1739
        $status_display = wpinv_status_nicename( $status );
1740
    }
1741
    
1742
    switch ( $status ) {
1743
        case 'publish' :
1744
        case 'wpi-renewal' :
1745
            $class = 'label-success';
1746
        break;
1747
        case 'pending' :
1748
            $class = 'label-primary';
1749
        break;
1750
        case 'wpi-processing' :
1751
            $class = 'label-warning';
1752
        break;
1753
        case 'wpi-onhold' :
1754
            $class = 'label-info';
1755
        break;
1756
        case 'wpi-cancelled' :
1757
        case 'wpi-failed' :
1758
            $class = 'label-danger';
1759
        break;
1760
        default:
1761
            $class = 'label-default';
1762
        break;
1763
    }
1764
    
1765
    $label = '<span class="label label-inv-' . $status . ' ' . $class . '">' . $status_display . '</span>';
1766
    
1767
    return apply_filters( 'wpinv_invoice_status_label', $label, $status, $status_display );
1768
}
1769
1770
function wpinv_format_invoice_number( $number ) {
1771
    $padd  = wpinv_get_option( 'invoice_number_padd' );
1772
    
1773
    // TODO maintain old invoice numbers if invoice number settings not saved. Should be removed before stable release.
1774
    if ( $padd === '' || $padd === false || $padd === NULL ) {
1775
        return wp_sprintf( __( 'WPINV-%d', 'invoicing' ), $number );
1776
    }
1777
    
1778
    $prefix  = wpinv_get_option( 'invoice_number_prefix' );
1779
    $postfix = wpinv_get_option( 'invoice_number_postfix' );
1780
    
1781
    $padd = absint( $padd );
1782
    $formatted_number = absint( $number );
1783
    
1784
    if ( $padd > 0 ) {
1785
        $formatted_number = zeroise( $formatted_number, $padd );
1786
    }    
1787
1788
    $formatted_number = $prefix . $formatted_number . $postfix;
1789
1790
    return apply_filters( 'wpinv_format_invoice_number', $formatted_number, $number, $prefix, $postfix, $padd );
1791
}