Passed
Pull Request — master (#50)
by Kiran
03:42
created

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

Complexity

Conditions 50
Paths > 20000

Size

Total Lines 227
Code Lines 146

Duplication

Lines 4
Ratio 1.76 %

Importance

Changes 0
Metric Value
cc 50
eloc 146
nc 2211875
nop 2
dl 4
loc 227
rs 2
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
        if ( isset( $item['custom_price'] ) && $item['custom_price'] !== '' ) {
717
            $item_price = $item['custom_price'];
718
        } else {
719
            if ( isset( $item['item_price'] ) && $item['item_price'] !== '' && $item['item_price'] !== false ) {
720
                $item_price = $item['item_price'];
721
            } else {
722
                $item_price = wpinv_get_item_price( $item_id );
723
            }
724
        }
725
        $discount           = wpinv_get_cart_item_discount_amount( $item );
726
        $discount           = apply_filters( 'wpinv_get_cart_content_details_item_discount_amount', $discount, $item );
727
        $quantity           = wpinv_get_cart_item_quantity( $item );
728
        $fees               = wpinv_get_cart_fees( 'fee', $item_id );
729
        
730
        $subtotal           = $item_price * $quantity;
731
        $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...
732
        $tax_class          = $wpinv_euvat->get_item_class( $item_id );
733
        $tax                = wpinv_get_cart_item_tax( $item_id, $subtotal - $discount );
734
        
735
        if ( wpinv_prices_include_tax() ) {
736
            $subtotal -= wpinv_round_amount( $tax );
737
        }
738
        
739
        $total              = $subtotal - $discount + $tax;
740
        
741
        // Do not allow totals to go negatve
742
        if( $total < 0 ) {
743
            $total = 0;
744
        }
745
        
746
        $details[ $key ]  = array(
747
            'id'                => $item_id,
748
            'name'              => !empty($item['name']) ? $item['name'] : get_the_title( $item_id ),
749
            'item_price'        => wpinv_round_amount( $item_price ),
750
            'custom_price'      => isset( $item['custom_price'] ) ? $item['custom_price'] : '',
751
            'quantity'          => $quantity,
752
            'discount'          => wpinv_round_amount( $discount ),
753
            'subtotal'          => wpinv_round_amount( $subtotal ),
754
            'tax'               => wpinv_round_amount( $tax ),
755
            'price'             => wpinv_round_amount( $total ),
756
            'vat_rates_class'   => $tax_class,
757
            'vat_rate'          => $tax_rate,
758
            'meta'              => isset( $item['meta'] ) ? $item['meta'] : array(),
759
            'fees'              => $fees,
760
        );
761
        
762
        if ( $wpinv_is_last_cart_item ) {
763
            $wpinv_is_last_cart_item   = false;
764
            $wpinv_flat_discount_total = 0.00;
765
        }
766
    }
767
    
768
    return $details;
769
}
770
771
function wpinv_get_cart_details( $invoice_id = 0 ) {
772
    global $ajax_cart_details;
773
774
    $invoice      = wpinv_get_invoice_cart( $invoice_id );
775
    $cart_details = !empty( $ajax_cart_details ) ? $ajax_cart_details : $invoice->cart_details;
776
777
    $invoice_currency = $invoice->currency;
778
779 View Code Duplication
    if ( ! empty( $cart_details ) && is_array( $cart_details ) ) {
780
        foreach ( $cart_details as $key => $cart_item ) {
781
            $cart_details[ $key ]['currency'] = $invoice_currency;
782
783
            if ( ! isset( $cart_item['subtotal'] ) ) {
784
                $cart_details[ $key ]['subtotal'] = $cart_item['price'];
785
            }
786
        }
787
    }
788
789
    return apply_filters( 'wpinv_get_cart_details', $cart_details, $invoice_id );
790
}
791
792
function wpinv_record_status_change( $invoice_id, $new_status, $old_status ) {
793
    if ( 'wpi_invoice' != get_post_type( $invoice_id ) ) {
794
        return;
795
    }
796
797
    $invoice    = wpinv_get_invoice( $invoice_id );
798
    
799
    $old_status = wpinv_status_nicename( $old_status );
800
    $new_status = wpinv_status_nicename( $new_status );
801
802
    $status_change = sprintf( __( 'Invoice status changed from %s to %s', 'invoicing' ), $old_status, $new_status );
803
    
804
    // Add note
805
    return $invoice->add_note( $status_change, false, false, true );
806
}
807
add_action( 'wpinv_update_status', 'wpinv_record_status_change', 100, 3 );
808
809
function wpinv_complete_payment( $invoice_id, $new_status, $old_status ) {
810
    global $wpi_has_free_trial;
811
    
812
    $wpi_has_free_trial = false;
813
    
814
    if ( $old_status == 'publish' ) {
815
        return; // Make sure that payments are only paid once
816
    }
817
818
    // Make sure the payment completion is only processed when new status is paid
819
    if ( $new_status != 'publish' ) {
820
        return;
821
    }
822
823
    $invoice = new WPInv_Invoice( $invoice_id );
824
    if ( empty( $invoice ) ) {
825
        return;
826
    }
827
828
    $wpi_has_free_trial = $invoice->is_free_trial();
829
    $completed_date = $invoice->completed_date;
830
    $cart_details   = $invoice->cart_details;
831
832
    do_action( 'wpinv_pre_complete_payment', $invoice_id );
833
834
    if ( is_array( $cart_details ) ) {
835
        // Increase purchase count and earnings
836
        foreach ( $cart_details as $cart_index => $item ) {
837
            // Ensure these actions only run once, ever
838
            if ( empty( $completed_date ) ) {
839
                do_action( 'wpinv_complete_item_payment', $item['id'], $invoice_id, $item, $cart_index );
840
            }
841
        }
842
    }
843
    
844
    // Check for discount codes and increment their use counts
845
    if ( $discounts = $invoice->get_discounts( true ) ) {
846
        if( ! empty( $discounts ) ) {
847
            foreach( $discounts as $code ) {
848
                wpinv_increase_discount_usage( $code );
849
            }
850
        }
851
    }
852
    
853
    // Ensure this action only runs once ever
854
    if( empty( $completed_date ) ) {
855
        // Save the completed date
856
        $invoice->set( 'completed_date', current_time( 'mysql', 0 ) );
857
        $invoice->save();
858
859
        do_action( 'wpinv_complete_payment', $invoice_id );
860
    }
861
862
    // Empty the shopping cart
863
    wpinv_empty_cart();
864
}
865
add_action( 'wpinv_update_status', 'wpinv_complete_payment', 100, 3 );
866
867
function wpinv_update_payment_status( $invoice_id, $new_status = 'publish' ) {    
868
    $invoice = !empty( $invoice_id ) && is_object( $invoice_id ) ? $invoice_id : wpinv_get_invoice( (int)$invoice_id );
869
    
870
    if ( empty( $invoice ) ) {
871
        return false;
872
    }
873
    
874
    return $invoice->update_status( $new_status );
875
}
876
877
function wpinv_cart_has_fees( $type = 'all' ) {
878
    return false;
879
}
880
881
function wpinv_validate_checkout_fields() {    
882
    // Check if there is $_POST
883
    if ( empty( $_POST ) ) {
884
        return false;
885
    }
886
    
887
    // Start an array to collect valid data
888
    $valid_data = array(
889
        'gateway'          => wpinv_checkout_validate_gateway(), // Gateway fallback
890
        'discount'         => wpinv_checkout_validate_discounts(), // Set default discount
891
        'cc_info'          => wpinv_checkout_validate_cc() // Credit card info
892
    );
893
    
894
    // Validate agree to terms
895
    if ( wpinv_get_option( 'show_agree_to_terms', false ) ) {
896
        wpinv_checkout_validate_agree_to_terms();
897
    }
898
    
899
    $valid_data['logged_in_user']   = wpinv_checkout_validate_logged_in_user();
900
    
901
    // Return collected data
902
    return $valid_data;
903
}
904
905
function wpinv_checkout_validate_gateway() {
906
    $gateway = wpinv_get_default_gateway();
907
    
908
    $invoice = wpinv_get_invoice_cart();
909
    $has_subscription = $invoice->is_recurring();
910
    if ( empty( $invoice ) ) {
911
        wpinv_set_error( 'invalid_invoice', __( 'Your cart is empty.', 'invoicing' ) );
912
        return $gateway;
913
    }
914
915
    // Check if a gateway value is present
916
    if ( !empty( $_REQUEST['wpi-gateway'] ) ) {
917
        $gateway = sanitize_text_field( $_REQUEST['wpi-gateway'] );
918
919
        if ( $invoice->is_free() ) {
920
            $gateway = 'manual';
921
        } elseif ( !wpinv_is_gateway_active( $gateway ) ) {
922
            wpinv_set_error( 'invalid_gateway', __( 'The selected payment gateway is not enabled', 'invoicing' ) );
923
        } elseif ( $has_subscription && !wpinv_gateway_support_subscription( $gateway ) ) {
924
            wpinv_set_error( 'invalid_gateway', __( 'The selected payment gateway doesnot support subscription payment', 'invoicing' ) );
925
        }
926
    }
927
928
    if ( $has_subscription && count( wpinv_get_cart_contents() ) > 1 ) {
929
        wpinv_set_error( 'subscription_invalid', __( 'Only one subscription may be purchased through payment per checkout.', 'invoicing' ) );
930
    }
931
932
    return $gateway;
933
}
934
935
function wpinv_checkout_validate_discounts() {
936
    // Retrieve the discount stored in cookies
937
    $discounts = wpinv_get_cart_discounts();
938
    
939
    $error = false;
940
    // If we have discounts, loop through them
941
    if ( ! empty( $discounts ) ) {
942
        foreach ( $discounts as $discount ) {
943
            // Check if valid
944
            if (  !wpinv_is_discount_valid( $discount, get_current_user_id() ) ) {
945
                // Discount is not valid
946
                $error = true;
947
            }
948
        }
949
    } else {
950
        // No discounts
951
        return NULL;
952
    }
953
954
    if ( $error && !wpinv_get_errors() ) {
955
        wpinv_set_error( 'invalid_discount', __( 'Discount code you entered is invalid', 'invoicing' ) );
956
    }
957
958
    return implode( ',', $discounts );
959
}
960
961
function wpinv_checkout_validate_cc() {
962
    $card_data = wpinv_checkout_get_cc_info();
963
964
    // Validate the card zip
965
    if ( !empty( $card_data['wpinv_zip'] ) ) {
966
        if ( !wpinv_checkout_validate_cc_zip( $card_data['wpinv_zip'], $card_data['wpinv_country'] ) ) {
967
            wpinv_set_error( 'invalid_cc_zip', __( 'The zip / postcode you entered for your billing address is invalid', 'invoicing' ) );
968
        }
969
    }
970
971
    // This should validate card numbers at some point too
972
    return $card_data;
973
}
974
975
function wpinv_checkout_get_cc_info() {
976
	$cc_info = array();
977
	$cc_info['card_name']      = isset( $_POST['card_name'] )       ? sanitize_text_field( $_POST['card_name'] )       : '';
978
	$cc_info['card_number']    = isset( $_POST['card_number'] )     ? sanitize_text_field( $_POST['card_number'] )     : '';
979
	$cc_info['card_cvc']       = isset( $_POST['card_cvc'] )        ? sanitize_text_field( $_POST['card_cvc'] )        : '';
980
	$cc_info['card_exp_month'] = isset( $_POST['card_exp_month'] )  ? sanitize_text_field( $_POST['card_exp_month'] )  : '';
981
	$cc_info['card_exp_year']  = isset( $_POST['card_exp_year'] )   ? sanitize_text_field( $_POST['card_exp_year'] )   : '';
982
	$cc_info['card_address']   = isset( $_POST['wpinv_address'] )  ? sanitize_text_field( $_POST['wpinv_address'] ) : '';
983
	$cc_info['card_city']      = isset( $_POST['wpinv_city'] )     ? sanitize_text_field( $_POST['wpinv_city'] )    : '';
984
	$cc_info['card_state']     = isset( $_POST['wpinv_state'] )    ? sanitize_text_field( $_POST['wpinv_state'] )   : '';
985
	$cc_info['card_country']   = isset( $_POST['wpinv_country'] )  ? sanitize_text_field( $_POST['wpinv_country'] ) : '';
986
	$cc_info['card_zip']       = isset( $_POST['wpinv_zip'] )      ? sanitize_text_field( $_POST['wpinv_zip'] )     : '';
987
988
	// Return cc info
989
	return $cc_info;
990
}
991
992
function wpinv_checkout_validate_cc_zip( $zip = 0, $country_code = '' ) {
993
    $ret = false;
994
995
    if ( empty( $zip ) || empty( $country_code ) )
996
        return $ret;
997
998
    $country_code = strtoupper( $country_code );
999
1000
    $zip_regex = array(
1001
        "AD" => "AD\d{3}",
1002
        "AM" => "(37)?\d{4}",
1003
        "AR" => "^([A-Z]{1}\d{4}[A-Z]{3}|[A-Z]{1}\d{4}|\d{4})$",
1004
        "AS" => "96799",
1005
        "AT" => "\d{4}",
1006
        "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})$",
1007
        "AX" => "22\d{3}",
1008
        "AZ" => "\d{4}",
1009
        "BA" => "\d{5}",
1010
        "BB" => "(BB\d{5})?",
1011
        "BD" => "\d{4}",
1012
        "BE" => "^[1-9]{1}[0-9]{3}$",
1013
        "BG" => "\d{4}",
1014
        "BH" => "((1[0-2]|[2-9])\d{2})?",
1015
        "BM" => "[A-Z]{2}[ ]?[A-Z0-9]{2}",
1016
        "BN" => "[A-Z]{2}[ ]?\d{4}",
1017
        "BR" => "\d{5}[\-]?\d{3}",
1018
        "BY" => "\d{6}",
1019
        "CA" => "^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$",
1020
        "CC" => "6799",
1021
        "CH" => "^[1-9][0-9][0-9][0-9]$",
1022
        "CK" => "\d{4}",
1023
        "CL" => "\d{7}",
1024
        "CN" => "\d{6}",
1025
        "CR" => "\d{4,5}|\d{3}-\d{4}",
1026
        "CS" => "\d{5}",
1027
        "CV" => "\d{4}",
1028
        "CX" => "6798",
1029
        "CY" => "\d{4}",
1030
        "CZ" => "\d{3}[ ]?\d{2}",
1031
        "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",
1032
        "DK" => "^([D-d][K-k])?( |-)?[1-9]{1}[0-9]{3}$",
1033
        "DO" => "\d{5}",
1034
        "DZ" => "\d{5}",
1035
        "EC" => "([A-Z]\d{4}[A-Z]|(?:[A-Z]{2})?\d{6})?",
1036
        "EE" => "\d{5}",
1037
        "EG" => "\d{5}",
1038
        "ES" => "^([1-9]{2}|[0-9][1-9]|[1-9][0-9])[0-9]{3}$",
1039
        "ET" => "\d{4}",
1040
        "FI" => "\d{5}",
1041
        "FK" => "FIQQ 1ZZ",
1042
        "FM" => "(9694[1-4])([ \-]\d{4})?",
1043
        "FO" => "\d{3}",
1044
        "FR" => "^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$",
1045
        "GE" => "\d{4}",
1046
        "GF" => "9[78]3\d{2}",
1047
        "GL" => "39\d{2}",
1048
        "GN" => "\d{3}",
1049
        "GP" => "9[78][01]\d{2}",
1050
        "GR" => "\d{3}[ ]?\d{2}",
1051
        "GS" => "SIQQ 1ZZ",
1052
        "GT" => "\d{5}",
1053
        "GU" => "969[123]\d([ \-]\d{4})?",
1054
        "GW" => "\d{4}",
1055
        "HM" => "\d{4}",
1056
        "HN" => "(?:\d{5})?",
1057
        "HR" => "\d{5}",
1058
        "HT" => "\d{4}",
1059
        "HU" => "\d{4}",
1060
        "ID" => "\d{5}",
1061
        "IE" => "((D|DUBLIN)?([1-9]|6[wW]|1[0-8]|2[024]))?",
1062
        "IL" => "\d{5}",
1063
        "IN"=> "^[1-9][0-9][0-9][0-9][0-9][0-9]$", //india
1064
        "IO" => "BBND 1ZZ",
1065
        "IQ" => "\d{5}",
1066
        "IS" => "\d{3}",
1067
        "IT" => "^(V-|I-)?[0-9]{5}$",
1068
        "JO" => "\d{5}",
1069
        "JP" => "\d{3}-\d{4}",
1070
        "KE" => "\d{5}",
1071
        "KG" => "\d{6}",
1072
        "KH" => "\d{5}",
1073
        "KR" => "\d{3}[\-]\d{3}",
1074
        "KW" => "\d{5}",
1075
        "KZ" => "\d{6}",
1076
        "LA" => "\d{5}",
1077
        "LB" => "(\d{4}([ ]?\d{4})?)?",
1078
        "LI" => "(948[5-9])|(949[0-7])",
1079
        "LK" => "\d{5}",
1080
        "LR" => "\d{4}",
1081
        "LS" => "\d{3}",
1082
        "LT" => "\d{5}",
1083
        "LU" => "\d{4}",
1084
        "LV" => "\d{4}",
1085
        "MA" => "\d{5}",
1086
        "MC" => "980\d{2}",
1087
        "MD" => "\d{4}",
1088
        "ME" => "8\d{4}",
1089
        "MG" => "\d{3}",
1090
        "MH" => "969[67]\d([ \-]\d{4})?",
1091
        "MK" => "\d{4}",
1092
        "MN" => "\d{6}",
1093
        "MP" => "9695[012]([ \-]\d{4})?",
1094
        "MQ" => "9[78]2\d{2}",
1095
        "MT" => "[A-Z]{3}[ ]?\d{2,4}",
1096
        "MU" => "(\d{3}[A-Z]{2}\d{3})?",
1097
        "MV" => "\d{5}",
1098
        "MX" => "\d{5}",
1099
        "MY" => "\d{5}",
1100
        "NC" => "988\d{2}",
1101
        "NE" => "\d{4}",
1102
        "NF" => "2899",
1103
        "NG" => "(\d{6})?",
1104
        "NI" => "((\d{4}-)?\d{3}-\d{3}(-\d{1})?)?",
1105
        "NL" => "^[1-9][0-9]{3}\s?([a-zA-Z]{2})?$",
1106
        "NO" => "\d{4}",
1107
        "NP" => "\d{5}",
1108
        "NZ" => "\d{4}",
1109
        "OM" => "(PC )?\d{3}",
1110
        "PF" => "987\d{2}",
1111
        "PG" => "\d{3}",
1112
        "PH" => "\d{4}",
1113
        "PK" => "\d{5}",
1114
        "PL" => "\d{2}-\d{3}",
1115
        "PM" => "9[78]5\d{2}",
1116
        "PN" => "PCRN 1ZZ",
1117
        "PR" => "00[679]\d{2}([ \-]\d{4})?",
1118
        "PT" => "\d{4}([\-]\d{3})?",
1119
        "PW" => "96940",
1120
        "PY" => "\d{4}",
1121
        "RE" => "9[78]4\d{2}",
1122
        "RO" => "\d{6}",
1123
        "RS" => "\d{5}",
1124
        "RU" => "\d{6}",
1125
        "SA" => "\d{5}",
1126
        "SE" => "^(s-|S-){0,1}[0-9]{3}\s?[0-9]{2}$",
1127
        "SG" => "\d{6}",
1128
        "SH" => "(ASCN|STHL) 1ZZ",
1129
        "SI" => "\d{4}",
1130
        "SJ" => "\d{4}",
1131
        "SK" => "\d{3}[ ]?\d{2}",
1132
        "SM" => "4789\d",
1133
        "SN" => "\d{5}",
1134
        "SO" => "\d{5}",
1135
        "SZ" => "[HLMS]\d{3}",
1136
        "TC" => "TKCA 1ZZ",
1137
        "TH" => "\d{5}",
1138
        "TJ" => "\d{6}",
1139
        "TM" => "\d{6}",
1140
        "TN" => "\d{4}",
1141
        "TR" => "\d{5}",
1142
        "TW" => "\d{3}(\d{2})?",
1143
        "UA" => "\d{5}",
1144
        "UK" => "^(GIR|[A-Z]\d[A-Z\d]??|[A-Z]{2}\d[A-Z\d]??)[ ]??(\d[A-Z]{2})$",
1145
        "US" => "^\d{5}([\-]?\d{4})?$",
1146
        "UY" => "\d{5}",
1147
        "UZ" => "\d{6}",
1148
        "VA" => "00120",
1149
        "VE" => "\d{4}",
1150
        "VI" => "008(([0-4]\d)|(5[01]))([ \-]\d{4})?",
1151
        "WF" => "986\d{2}",
1152
        "YT" => "976\d{2}",
1153
        "YU" => "\d{5}",
1154
        "ZA" => "\d{4}",
1155
        "ZM" => "\d{5}"
1156
    );
1157
1158
    if ( ! isset ( $zip_regex[ $country_code ] ) || preg_match( "/" . $zip_regex[ $country_code ] . "/i", $zip ) )
1159
        $ret = true;
1160
1161
    return apply_filters( 'wpinv_is_zip_valid', $ret, $zip, $country_code );
1162
}
1163
1164
function wpinv_checkout_validate_agree_to_terms() {
1165
    // Validate agree to terms
1166
    if ( ! isset( $_POST['wpi_agree_to_terms'] ) || $_POST['wpi_agree_to_terms'] != 1 ) {
1167
        // User did not agree
1168
        wpinv_set_error( 'agree_to_terms', apply_filters( 'wpinv_agree_to_terms_text', __( 'You must agree to the terms of use', 'invoicing' ) ) );
1169
    }
1170
}
1171
1172
function wpinv_checkout_validate_logged_in_user() {
1173
    $user_ID = get_current_user_id();
1174
    
1175
    $valid_user_data = array(
1176
        // Assume there will be errors
1177
        'user_id' => -1
1178
    );
1179
    
1180
    // Verify there is a user_ID
1181
    if ( $user_ID > 0 ) {
1182
        // Get the logged in user data
1183
        $user_data = get_userdata( $user_ID );
1184
        $required_fields  = wpinv_checkout_required_fields();
1185
1186
        // Loop through required fields and show error messages
1187
         if ( !empty( $required_fields ) ) {
1188
            foreach ( $required_fields as $field_name => $value ) {
1189
                if ( in_array( $value, $required_fields ) && empty( $_POST[ 'wpinv_' . $field_name ] ) ) {
1190
                    wpinv_set_error( $value['error_id'], $value['error_message'] );
1191
                }
1192
            }
1193
        }
1194
1195
        // Verify data
1196
        if ( $user_data ) {
1197
            // Collected logged in user data
1198
            $valid_user_data = array(
1199
                'user_id'     => $user_ID,
1200
                'email'       => isset( $_POST['wpinv_email'] ) ? sanitize_email( $_POST['wpinv_email'] ) : $user_data->user_email,
1201
                'first_name'  => isset( $_POST['wpinv_first_name'] ) && ! empty( $_POST['wpinv_first_name'] ) ? sanitize_text_field( $_POST['wpinv_first_name'] ) : $user_data->first_name,
1202
                'last_name'   => isset( $_POST['wpinv_last_name'] ) && ! empty( $_POST['wpinv_last_name']  ) ? sanitize_text_field( $_POST['wpinv_last_name']  ) : $user_data->last_name,
1203
            );
1204
1205
            if ( !empty( $_POST[ 'wpinv_email' ] ) && !is_email( $_POST[ 'wpinv_email' ] ) ) {
1206
                wpinv_set_error( 'invalid_email', __( 'Please enter a valid email address', 'invoicing' ) );
1207
            }
1208
        } else {
1209
            // Set invalid user error
1210
            wpinv_set_error( 'invalid_user', __( 'The user billing information is invalid', 'invoicing' ) );
1211
        }
1212
    }
1213
1214
    // Return user data
1215
    return $valid_user_data;
1216
}
1217
1218
function wpinv_checkout_form_get_user( $valid_data = array() ) {
1219
    // Initialize user
1220
    $user    = false;
1221
    $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...
1222
1223
    /*if ( $is_ajax ) {
1224
        // Do not create or login the user during the ajax submission (check for errors only)
1225
        return true;
1226
    } else */if ( is_user_logged_in() ) {
1227
        // Set the valid user as the logged in collected data
1228
        $user = $valid_data['logged_in_user'];
1229
    }
1230
1231
    // Verify we have an user
1232
    if ( false === $user || empty( $user ) ) {
1233
        // Return false
1234
        return false;
1235
    }
1236
    
1237
    $address_fields = array(
1238
        'first_name',
1239
        'last_name',
1240
        'company',
1241
        'vat_number',
1242
        ///'email',
1243
        'phone',
1244
        'address',
1245
        'city',
1246
        'state',
1247
        'country',
1248
        'zip',
1249
    );
1250
    
1251
    foreach ( $address_fields as $field ) {
1252
        $user[$field]  = !empty( $_POST['wpinv_' . $field] ) ? sanitize_text_field( $_POST['wpinv_' . $field] ) : false;
1253
        
1254
        if ( !empty( $user['user_id'] ) ) {
1255
            update_user_meta( $user['user_id'], '_wpinv_' . $field, $user[$field] );
1256
        }
1257
    }
1258
1259
    // Return valid user
1260
    return $user;
1261
}
1262
1263
function wpinv_set_checkout_session( $invoice_data = array() ) {
1264
    global $wpi_session;
1265
    
1266
    return $wpi_session->set( 'wpinv_checkout', $invoice_data );
1267
}
1268
1269
function wpinv_get_checkout_session() {
1270
	global $wpi_session;
1271
    
1272
    return $wpi_session->get( 'wpinv_checkout' );
1273
}
1274
1275
function wpinv_empty_cart() {
1276
    global $wpi_session;
1277
1278
    // Remove cart contents
1279
    $wpi_session->set( 'wpinv_checkout', NULL );
1280
1281
    // Remove all cart fees
1282
    $wpi_session->set( 'wpi_cart_fees', NULL );
1283
1284
    do_action( 'wpinv_empty_cart' );
1285
}
1286
1287
function wpinv_process_checkout() {
1288
    global $wpinv_euvat, $wpi_checkout_id;
1289
    
1290
    wpinv_clear_errors();
1291
    
1292
    $invoice = wpinv_get_invoice_cart();
1293
    
1294
    $wpi_checkout_id = $invoice->ID;
1295
    
1296
    do_action( 'wpinv_pre_process_checkout' );
1297
    
1298
    if ( !wpinv_get_cart_contents() ) { // Make sure the cart isn't empty
1299
        $valid_data = false;
1300
        wpinv_set_error( 'empty_cart', __( 'Your cart is empty', 'invoicing' ) );
1301
    } else {
1302
        // Validate the form $_POST data
1303
        $valid_data = wpinv_validate_checkout_fields();
1304
        
1305
        // Allow themes and plugins to hook to errors
1306
        do_action( 'wpinv_checkout_error_checks', $valid_data, $_POST );
1307
    }
1308
    
1309
    $is_ajax    = defined( 'DOING_AJAX' ) && DOING_AJAX;
1310
    
1311
    // Validate the user
1312
    $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...
1313
1314
    // Let extensions validate fields after user is logged in if user has used login/registration form
1315
    do_action( 'wpinv_checkout_user_error_checks', $user, $valid_data, $_POST );
1316
    
1317
    if ( false === $valid_data || wpinv_get_errors() || ! $user ) {
1318
        if ( $is_ajax ) {
1319
            do_action( 'wpinv_ajax_checkout_errors' );
1320
            die();
1321
        } else {
1322
            return false;
1323
        }
1324
    }
1325
1326
    if ( $is_ajax ) {
1327
        // Save address fields.
1328
        $address_fields = array( 'first_name', 'last_name', 'phone', 'address', 'city', 'country', 'state', 'zip', 'company' );
1329 View Code Duplication
        foreach ( $address_fields as $field ) {
1330
            if ( isset( $user[$field] ) ) {
1331
                $invoice->set( $field, $user[$field] );
1332
            }
1333
            
1334
            $invoice->save();
1335
        }
1336
1337
        $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...
1338
        $response['data']['subtotal']   = $invoice->get_subtotal();
1339
        $response['data']['subtotalf']  = $invoice->get_subtotal( true );
1340
        $response['data']['discount']   = $invoice->get_discount();
1341
        $response['data']['discountf']  = $invoice->get_discount( true );
1342
        $response['data']['tax']        = $invoice->get_tax();
1343
        $response['data']['taxf']       = $invoice->get_tax( true );
1344
        $response['data']['total']      = $invoice->get_total();
1345
        $response['data']['totalf']     = $invoice->get_total( true );
1346
        
1347
        wp_send_json( $response );
1348
    }
1349
    
1350
    $user_info = array(
1351
        'user_id'        => $user['user_id'],
1352
        'first_name'     => $user['first_name'],
1353
        'last_name'      => $user['last_name'],
1354
        'email'          => $user['email'],
1355
        'company'        => $user['company'],
1356
        'phone'          => $user['phone'],
1357
        'address'        => $user['address'],
1358
        'city'           => $user['city'],
1359
        'country'        => $user['country'],
1360
        'state'          => $user['state'],
1361
        'zip'            => $user['zip'],
1362
    );
1363
    
1364
    $cart_items = wpinv_get_cart_contents();
1365
    $discounts  = wpinv_get_cart_discounts();
1366
    
1367
    // Setup invoice information
1368
    $invoice_data = array(
1369
        'invoice_id'        => !empty( $invoice ) ? $invoice->ID : 0,
1370
        'items'             => $cart_items,
1371
        'cart_discounts'    => $discounts,
1372
        'fees'              => wpinv_get_cart_fees(),        // Any arbitrary fees that have been added to the cart
1373
        'subtotal'          => wpinv_get_cart_subtotal( $cart_items ),    // Amount before taxes and discounts
1374
        'discount'          => wpinv_get_cart_items_discount_amount( $cart_items, $discounts ), // Discounted amount
1375
        'tax'               => wpinv_get_cart_tax( $cart_items ),               // Taxed amount
1376
        'price'             => wpinv_get_cart_total( $cart_items, $discounts ),    // Amount after taxes
1377
        'invoice_key'       => $invoice->get_key() ? $invoice->get_key() : $invoice->generate_key(),
1378
        'user_email'        => $user['email'],
1379
        'date'              => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ),
1380
        'user_info'         => stripslashes_deep( $user_info ),
1381
        'post_data'         => $_POST,
1382
        'cart_details'      => $cart_items,
1383
        'gateway'           => $valid_data['gateway'],
1384
        'card_info'         => $valid_data['cc_info']
1385
    );
1386
    
1387
    $vat_info   = $wpinv_euvat->current_vat_data();
1388
    if ( is_array( $vat_info ) ) {
1389
        $invoice_data['user_info']['vat_number']        = $vat_info['number'];
1390
        $invoice_data['user_info']['vat_rate']          = wpinv_get_tax_rate($invoice_data['user_info']['country'], $invoice_data['user_info']['state']);
1391
        $invoice_data['user_info']['adddress_confirmed']    = isset($vat_info['adddress_confirmed']) ? $vat_info['adddress_confirmed'] : false;
1392
1393
        // Add the VAT rate to each item in the cart
1394
        foreach( $invoice_data['cart_details'] as $key => $item_data) {
1395
            $rate = wpinv_get_tax_rate($invoice_data['user_info']['country'], $invoice_data['user_info']['state'], $item_data['id']);
1396
            $invoice_data['cart_details'][$key]['vat_rate'] = wpinv_round_amount( $rate, 4 );
1397
        }
1398
    }
1399
    
1400
    // Save vat fields.
1401
    $address_fields = array( 'vat_number', 'vat_rate', 'adddress_confirmed' );
1402 View Code Duplication
    foreach ( $address_fields as $field ) {
1403
        if ( isset( $invoice_data['user_info'][$field] ) ) {
1404
            $invoice->set( $field, $invoice_data['user_info'][$field] );
1405
        }
1406
        
1407
        $invoice->save();
1408
    }
1409
1410
    // Add the user data for hooks
1411
    $valid_data['user'] = $user;
1412
    
1413
    // Allow themes and plugins to hook before the gateway
1414
    do_action( 'wpinv_checkout_before_gateway', $_POST, $user_info, $valid_data );
1415
    
1416
    // If the total amount in the cart is 0, send to the manual gateway. This emulates a free invoice
1417
    if ( !$invoice_data['price'] ) {
1418
        // Revert to manual
1419
        $invoice_data['gateway'] = 'manual';
1420
        $_POST['wpi-gateway'] = 'manual';
1421
    }
1422
    
1423
    // Allow the invoice data to be modified before it is sent to the gateway
1424
    $invoice_data = apply_filters( 'wpinv_data_before_gateway', $invoice_data, $valid_data );
1425
    
1426
    // Setup the data we're storing in the purchase session
1427
    $session_data = $invoice_data;
1428
    // Make sure credit card numbers are never stored in sessions
1429
    if ( !empty( $session_data['card_info']['card_number'] ) ) {
1430
        unset( $session_data['card_info']['card_number'] );
1431
    }
1432
    
1433
    // Used for showing item links to non logged-in users after purchase, and for other plugins needing purchase data.
1434
    wpinv_set_checkout_session( $invoice_data );
1435
    
1436
    // Set gateway
1437
    $invoice->update_meta( '_wpinv_gateway', $invoice_data['gateway'] );
1438
    $invoice->update_meta( '_wpinv_mode', ( wpinv_is_test_mode( $invoice_data['gateway'] ) ? 'test' : 'live' ) );
1439
    $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...
1440
    
1441
    do_action( 'wpinv_checkout_before_send_to_gateway', $invoice, $invoice_data );
1442
1443
    // Send info to the gateway for payment processing
1444
    wpinv_send_to_gateway( $invoice_data['gateway'], $invoice_data );
1445
    die();
1446
}
1447
add_action( 'wpinv_payment', 'wpinv_process_checkout' );
1448
1449
function wpinv_get_invoices( $args ) {
1450
    $args = wp_parse_args( $args, array(
1451
        'status'   => array_keys( wpinv_get_invoice_statuses() ),
1452
        'type'     => 'wpi_invoice',
1453
        'parent'   => null,
1454
        'user'     => null,
1455
        'email'    => '',
1456
        'limit'    => get_option( 'posts_per_page' ),
1457
        'offset'   => null,
1458
        'page'     => 1,
1459
        'exclude'  => array(),
1460
        'orderby'  => 'date',
1461
        'order'    => 'DESC',
1462
        'return'   => 'objects',
1463
        'paginate' => false,
1464
    ) );
1465
    
1466
    // Handle some BW compatibility arg names where wp_query args differ in naming.
1467
    $map_legacy = array(
1468
        'numberposts'    => 'limit',
1469
        'post_type'      => 'type',
1470
        'post_status'    => 'status',
1471
        'post_parent'    => 'parent',
1472
        'author'         => 'user',
1473
        'posts_per_page' => 'limit',
1474
        'paged'          => 'page',
1475
    );
1476
1477
    foreach ( $map_legacy as $from => $to ) {
1478
        if ( isset( $args[ $from ] ) ) {
1479
            $args[ $to ] = $args[ $from ];
1480
        }
1481
    }
1482
1483
    $wpinv_cpt = isset( $_REQUEST[ 'wpinv-cpt' ] ) ? $_REQUEST[ 'wpinv-cpt' ] : '';
1484
1485
    if ( get_query_var( 'paged' ) && 'wpi_invoice' == $wpinv_cpt )
1486
        $args['page'] = get_query_var('paged');
1487
    else if ( get_query_var( 'page' )  && 'wpi_invoice' == $wpinv_cpt )
1488
        $args['page'] = get_query_var( 'page' );
1489
    else if ( !empty( $args[ 'page' ] ) )
1490
        $args['page'] = $args[ 'page' ];
1491
    else
1492
        $args['page'] = 1;
1493
1494
    /**
1495
     * Generate WP_Query args. This logic will change if orders are moved to
1496
     * custom tables in the future.
1497
     */
1498
    $wp_query_args = array(
1499
        'post_type'      => 'wpi_invoice',
1500
        'post_status'    => $args['status'],
1501
        'posts_per_page' => $args['limit'],
1502
        'meta_query'     => array(),
1503
        'date_query'     => !empty( $args['date_query'] ) ? $args['date_query'] : array(),
1504
        'fields'         => 'ids',
1505
        'orderby'        => $args['orderby'],
1506
        'order'          => $args['order'],
1507
    );
1508
    
1509
    if ( !empty( $args['user'] ) ) {
1510
        $wp_query_args['author'] = absint( $args['user'] );
1511
    }
1512
1513
    if ( ! is_null( $args['parent'] ) ) {
1514
        $wp_query_args['post_parent'] = absint( $args['parent'] );
1515
    }
1516
1517
    if ( ! is_null( $args['offset'] ) ) {
1518
        $wp_query_args['offset'] = absint( $args['offset'] );
1519
    } else {
1520
        $wp_query_args['paged'] = absint( $args['page'] );
1521
    }
1522
1523
    if ( ! empty( $args['exclude'] ) ) {
1524
        $wp_query_args['post__not_in'] = array_map( 'absint', $args['exclude'] );
1525
    }
1526
1527
    if ( ! $args['paginate' ] ) {
1528
        $wp_query_args['no_found_rows'] = true;
1529
    }
1530
1531
    // Get results.
1532
    $invoices = new WP_Query( $wp_query_args );
1533
1534
    if ( 'objects' === $args['return'] ) {
1535
        $return = array_map( 'wpinv_get_invoice', $invoices->posts );
1536
    } elseif ( 'self' === $args['return'] ) {
1537
        return $invoices;
1538
    } else {
1539
        $return = $invoices->posts;
1540
    }
1541
1542
    if ( $args['paginate' ] ) {
1543
        return (object) array(
1544
            'invoices'      => $return,
1545
            'total'         => $invoices->found_posts,
1546
            'max_num_pages' => $invoices->max_num_pages,
1547
        );
1548
    } else {
1549
        return $return;
1550
    }
1551
}
1552
1553
function wpinv_get_user_invoices_columns() {
1554
    $columns = array(
1555
            'invoice-number'  => array( 'title' => __( 'ID', 'invoicing' ), 'class' => 'text-left' ),
1556
            'invoice-date'    => array( 'title' => __( 'Date', 'invoicing' ), 'class' => 'text-left' ),
1557
            'invoice-status'  => array( 'title' => __( 'Status', 'invoicing' ), 'class' => 'text-center' ),
1558
            'invoice-total'   => array( 'title' => __( 'Total', 'invoicing' ), 'class' => 'text-right' ),
1559
            'invoice-actions' => array( 'title' => '&nbsp;', 'class' => 'text-center' ),
1560
        );
1561
1562
    return apply_filters( 'wpinv_user_invoices_columns', $columns );
1563
}
1564
1565
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...
1566
    global $wpinv_receipt_args;
1567
1568
    $wpinv_receipt_args = shortcode_atts( array(
1569
        'error'           => __( 'Sorry, trouble retrieving payment receipt.', 'invoicing' ),
1570
        'price'           => true,
1571
        'discount'        => true,
1572
        'items'           => true,
1573
        'date'            => true,
1574
        'notes'           => true,
1575
        'invoice_key'     => false,
1576
        'payment_method'  => true,
1577
        'invoice_id'      => true
1578
    ), $atts, 'wpinv_receipt' );
1579
1580
    $session = wpinv_get_checkout_session();
1581
    if ( isset( $_GET['invoice_key'] ) ) {
1582
        $invoice_key = urldecode( $_GET['invoice_key'] );
1583
    } else if ( $session && isset( $session['invoice_key'] ) ) {
1584
        $invoice_key = $session['invoice_key'];
1585
    } elseif ( isset( $wpinv_receipt_args['invoice_key'] ) && $wpinv_receipt_args['invoice_key'] ) {
1586
        $invoice_key = $wpinv_receipt_args['invoice_key'];
1587
    } else if ( isset( $_GET['invoice-id'] ) ) {
1588
        $invoice_key = wpinv_get_payment_key( (int)$_GET['invoice-id'] );
1589
    }
1590
1591
    // No key found
1592
    if ( ! isset( $invoice_key ) ) {
1593
        return '<p class="alert alert-error">' . $wpinv_receipt_args['error'] . '</p>';
1594
    }
1595
1596
    $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...
1597
    $user_can_view = wpinv_can_view_receipt( $invoice_key );
1598 View Code Duplication
    if ( $user_can_view && isset( $_GET['invoice-id'] ) ) {
1599
        $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...
1600
        $user_can_view  = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? true : false;
1601
    }
1602
1603
    // Key was provided, but user is logged out. Offer them the ability to login and view the receipt
1604
    if ( ! $user_can_view && ! empty( $invoice_key ) && ! is_user_logged_in() ) {
1605
        // login redirect
1606
        return '<p class="alert alert-error">' . __( 'You are not allowed to access this section', 'invoicing' ) . '</p>';
1607
    }
1608
1609
    if ( ! apply_filters( 'wpinv_user_can_view_receipt', $user_can_view, $wpinv_receipt_args ) ) {
1610
        return '<p class="alert alert-error">' . $wpinv_receipt_args['error'] . '</p>';
1611
    }
1612
1613
    ob_start();
1614
1615
    wpinv_get_template_part( 'wpinv-invoice-receipt' );
1616
1617
    $display = ob_get_clean();
1618
1619
    return $display;
1620
}
1621
1622 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...
1623
	global $wpdb;
1624
1625
	$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 ) );
1626
1627
	if ( $invoice_id != NULL )
1628
		return $invoice_id;
1629
1630
	return 0;
1631
}
1632
1633
function wpinv_can_view_receipt( $invoice_key = '' ) {
1634
	$return = false;
1635
1636
	if ( empty( $invoice_key ) ) {
1637
		return $return;
1638
	}
1639
1640
	global $wpinv_receipt_args;
1641
1642
	$wpinv_receipt_args['id'] = wpinv_get_invoice_id_by_key( $invoice_key );
1643
	if ( isset( $_GET['invoice-id'] ) ) {
1644
		$wpinv_receipt_args['id'] = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? (int)$_GET['invoice-id'] : 0;
1645
	}
1646
1647
	$user_id = (int) wpinv_get_user_id( $wpinv_receipt_args['id'] );
1648
    $invoice_meta = wpinv_get_invoice_meta( $wpinv_receipt_args['id'] );
1649
1650
	if ( is_user_logged_in() ) {
1651
		if ( $user_id === (int) get_current_user_id() ) {
1652
			$return = true;
1653
		}
1654
	}
1655
1656
	$session = wpinv_get_checkout_session();
1657
	if ( ! empty( $session ) && ! is_user_logged_in() ) {
1658
		if ( $session['invoice_key'] === $invoice_meta['key'] ) {
1659
			$return = true;
1660
		}
1661
	}
1662
1663
	return (bool) apply_filters( 'wpinv_can_view_receipt', $return, $invoice_key );
1664
}
1665
1666
function wpinv_pay_for_invoice() {
1667
    global $wpinv_euvat;
1668
    
1669
    if ( isset( $_GET['invoice_key'] ) ) {
1670
        $checkout_uri   = wpinv_get_checkout_uri();
1671
        $invoice_key    = sanitize_text_field( $_GET['invoice_key'] );
1672
        
1673
        if ( empty( $invoice_key ) ) {
1674
            wpinv_set_error( 'invalid_invoice', __( 'Invoice not found', 'invoicing' ) );
1675
            wp_redirect( $checkout_uri );
1676
            wpinv_die();
1677
        }
1678
        
1679
        do_action( 'wpinv_check_pay_for_invoice', $invoice_key );
1680
1681
        $invoice_id    = wpinv_get_invoice_id_by_key( $invoice_key );
1682
        $user_can_view = wpinv_can_view_receipt( $invoice_key );
1683 View Code Duplication
        if ( $user_can_view && isset( $_GET['invoice-id'] ) ) {
1684
            $invoice_id     = (int)$_GET['invoice-id'];
1685
            $user_can_view  = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? true : false;
1686
        }
1687
        
1688
        if ( $invoice_id && $user_can_view && ( $invoice = wpinv_get_invoice( $invoice_id ) ) ) {
1689
            if ( $invoice->needs_payment() ) {
1690
                $data                   = array();
1691
                $data['invoice_id']     = $invoice_id;
1692
                $data['cart_discounts'] = $invoice->get_discounts( true );
1693
                
1694
                wpinv_set_checkout_session( $data );
1695
                
1696
                if ( wpinv_get_option( 'vat_ip_country_default' ) ) {
1697
                    $_POST['country']   = $wpinv_euvat->get_country_by_ip();
1698
                    $_POST['state']     = $_POST['country'] == $invoice->country ? $invoice->state : '';
1699
                    
1700
                    wpinv_recalculate_tax( true );
1701
                }
1702
                
1703
            } else {
1704
                $checkout_uri = $invoice->get_view_url();
1705
            }
1706
        } else {
1707
            wpinv_set_error( 'invalid_invoice', __( 'You are not allowed to view this invoice', 'invoicing' ) );
1708
            
1709
            $checkout_uri = is_user_logged_in() ? wpinv_get_history_page_uri() : wp_login_url( get_permalink() );
1710
        }
1711
        
1712
        wp_redirect( $checkout_uri );
1713
        wpinv_die();
1714
    }
1715
}
1716
add_action( 'wpinv_pay_for_invoice', 'wpinv_pay_for_invoice' );
1717
1718
function wpinv_handle_pay_via_invoice_link( $invoice_key ) {
1719
    if ( !empty( $invoice_key ) && !empty( $_REQUEST['_wpipay'] ) && !is_user_logged_in() && $invoice_id = wpinv_get_invoice_id_by_key( $invoice_key ) ) {
1720
        if ( $invoice = wpinv_get_invoice( $invoice_id ) ) {
1721
            $user_id = $invoice->get_user_id();
1722
            $secret = sanitize_text_field( $_GET['_wpipay'] );
1723
            
1724 View Code Duplication
            if ( $secret === md5( $user_id . '::' . $invoice->get_email() . '::' . $invoice_key ) ) { // valid invoice link
1725
                $redirect_to = remove_query_arg( '_wpipay', get_permalink() );
1726
                
1727
                wpinv_guest_redirect( $redirect_to, $user_id );
1728
                wpinv_die();
1729
            }
1730
        }
1731
    }
1732
}
1733
add_action( 'wpinv_check_pay_for_invoice', 'wpinv_handle_pay_via_invoice_link' );
1734
1735
function wpinv_set_payment_transaction_id( $invoice_id = 0, $transaction_id = '' ) {
1736
    $invoice_id = is_object( $invoice_id ) && !empty( $invoice_id->ID ) ? $invoice_id : $invoice_id;
1737
    
1738
    if ( empty( $invoice_id ) && $invoice_id > 0 ) {
1739
        return false;
1740
    }
1741
    
1742
    if ( empty( $transaction_id ) ) {
1743
        $transaction_id = $invoice_id;
1744
    }
1745
1746
    $transaction_id = apply_filters( 'wpinv_set_payment_transaction_id', $transaction_id, $invoice_id );
1747
    
1748
    return wpinv_update_invoice_meta( $invoice_id, '_wpinv_transaction_id', $transaction_id );
1749
}
1750
1751
function wpinv_invoice_status_label( $status, $status_display = '' ) {
1752
    if ( empty( $status_display ) ) {
1753
        $status_display = wpinv_status_nicename( $status );
1754
    }
1755
    
1756
    switch ( $status ) {
1757
        case 'publish' :
1758
        case 'wpi-renewal' :
1759
            $class = 'label-success';
1760
        break;
1761
        case 'pending' :
1762
            $class = 'label-primary';
1763
        break;
1764
        case 'wpi-processing' :
1765
            $class = 'label-warning';
1766
        break;
1767
        case 'wpi-onhold' :
1768
            $class = 'label-info';
1769
        break;
1770
        case 'wpi-cancelled' :
1771
        case 'wpi-failed' :
1772
            $class = 'label-danger';
1773
        break;
1774
        default:
1775
            $class = 'label-default';
1776
        break;
1777
    }
1778
    
1779
    $label = '<span class="label label-inv-' . $status . ' ' . $class . '">' . $status_display . '</span>';
1780
    
1781
    return apply_filters( 'wpinv_invoice_status_label', $label, $status, $status_display );
1782
}
1783
1784
function wpinv_format_invoice_number( $number ) {
1785
    $padd  = wpinv_get_option( 'invoice_number_padd' );
1786
    
1787
    // TODO maintain old invoice numbers if invoice number settings not saved. Should be removed before stable release.
1788
    if ( $padd === '' || $padd === false || $padd === NULL ) {
1789
        return wp_sprintf( __( 'WPINV-%d', 'invoicing' ), $number );
1790
    }
1791
    
1792
    $prefix  = wpinv_get_option( 'invoice_number_prefix' );
1793
    $postfix = wpinv_get_option( 'invoice_number_postfix' );
1794
    
1795
    $padd = absint( $padd );
1796
    $formatted_number = absint( $number );
1797
    
1798
    if ( $padd > 0 ) {
1799
        $formatted_number = zeroise( $formatted_number, $padd );
1800
    }    
1801
1802
    $formatted_number = $prefix . $formatted_number . $postfix;
1803
1804
    return apply_filters( 'wpinv_format_invoice_number', $formatted_number, $number, $prefix, $postfix, $padd );
1805
}