Passed
Pull Request — master (#275)
by Brian
07:08 queued 30s
created

wpinv_checkout_form_get_user()   C

Complexity

Conditions 13
Paths 44

Size

Total Lines 56
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 33
nc 44
nop 1
dl 0
loc 56
rs 6.6166
c 1
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
/**
25
 * Create an invoice
26
 * 
27
 * @param  array         $invoice_data   An array of invoice properties.
28
 * @param  bool          $wp_error       Whether to return false or WP_Error on failure.
29
 * @return mixed         The value 0 or WP_Error on failure. The WPInv_Invoice object on success.
30
 */
31
function wpinv_insert_invoice( $invoice_data = array(), $wp_error = false ) {
32
    if ( empty( $invoice_data ) ) {
33
        return false;
34
    }
35
    
36
    if ( !( !empty( $invoice_data['cart_details'] ) && is_array( $invoice_data['cart_details'] ) ) ) {
37
        return $wp_error ? new WP_Error( 'wpinv_invalid_items', __( 'Invoice must have atleast one item.', 'invoicing' ) ) : 0;
38
    }
39
    
40
    // If no user id is provided, default to the current user id
41
    if ( empty( $invoice_data['user_id'] ) ) {
42
        $invoice_data['user_id'] = get_current_user_id();
43
    }
44
    
45
    $invoice_data['invoice_id'] = !empty( $invoice_data['invoice_id'] ) ? (int)$invoice_data['invoice_id'] : 0;
46
    
47
    if ( empty( $invoice_data['status'] ) ) {
48
        $invoice_data['status'] = 'wpi-pending';
49
    }
50
51
    if ( empty( $invoice_data['post_type'] ) ) {
52
        $invoice_data['post_type'] = 'wpi_invoice';
53
    }
54
    
55
    if ( empty( $invoice_data['ip'] ) ) {
56
        $invoice_data['ip'] = wpinv_get_ip();
57
    }
58
59
    // default invoice args, note that status is checked for validity in wpinv_create_invoice()
60
    $default_args = array(
61
        'invoice_id'    => (int)$invoice_data['invoice_id'],
62
        'user_id'       => (int)$invoice_data['user_id'],
63
        'status'        => $invoice_data['status'],
64
        'post_type'     => $invoice_data['post_type'],
65
    );
66
67
    $invoice = wpinv_create_invoice( $default_args, $invoice_data, true );
68
    if ( is_wp_error( $invoice ) ) {
69
        return $wp_error ? $invoice : 0;
70
    }
71
    
72
    if ( empty( $invoice_data['invoice_id'] ) ) {
73
        //$invoice->add_note( wp_sprintf( __( 'Invoice is created with status %s.', 'invoicing' ), wpinv_status_nicename( $invoice->status ) ) );
74
    }
75
    
76
    // User info
77
    $default_user_info = array(
78
        'user_id'               => '',
79
        'first_name'            => '',
80
        'last_name'             => '',
81
        'email'                 => '',
82
        'company'               => '',
83
        'phone'                 => '',
84
        'address'               => '',
85
        'city'                  => '',
86
        'country'               => wpinv_get_default_country(),
87
        'state'                 => wpinv_get_default_state(),
88
        'zip'                   => '',
89
        'vat_number'            => '',
90
        'vat_rate'              => '',
91
        'adddress_confirmed'    => '',
92
        'discount'              => array(),
93
    );
94
95
    if ( $user_id = (int)$invoice->get_user_id() ) {
0 ignored issues
show
Bug introduced by
The method get_user_id() does not exist on WP_Error. ( Ignorable by Annotation )

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

95
    if ( $user_id = (int)$invoice->/** @scrutinizer ignore-call */ get_user_id() ) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
96
        if ( $user_address = wpinv_get_user_address( $user_id ) ) {
97
            $default_user_info = wp_parse_args( $user_address, $default_user_info );
98
        }
99
    }
100
    
101
    if ( empty( $invoice_data['user_info'] ) ) {
102
        $invoice_data['user_info'] = array();
103
    }
104
    
105
    $user_info = wp_parse_args( $invoice_data['user_info'], $default_user_info );
106
    
107
    if ( empty( $user_info['first_name'] ) ) {
108
        $user_info['first_name'] = $default_user_info['first_name'];
109
        $user_info['last_name'] = $default_user_info['last_name'];
110
    }
111
    
112
    if ( empty( $user_info['country'] ) ) {
113
        $user_info['country'] = $default_user_info['country'];
114
        $user_info['state'] = $default_user_info['state'];
115
        $user_info['city'] = $default_user_info['city'];
116
        $user_info['address'] = $default_user_info['address'];
117
        $user_info['zip'] = $default_user_info['zip'];
118
        $user_info['phone'] = $default_user_info['phone'];
119
    }
120
    
121
    if ( !empty( $user_info['discount'] ) && !is_array( $user_info['discount'] ) ) {
122
        $user_info['discount'] = (array)$user_info['discount'];
123
    }
124
125
    // Payment details
126
    $payment_details = array();
127
    if ( !empty( $invoice_data['payment_details'] ) ) {
128
        $default_payment_details = array(
129
            'gateway'           => 'manual',
130
            'gateway_title'     => '',
131
            'currency'          => wpinv_get_default_country(),
132
            'transaction_id'    => '',
133
        );
134
        
135
        $payment_details = wp_parse_args( $invoice_data['payment_details'], $default_payment_details );
136
        
137
        if ( empty( $payment_details['gateway'] ) ) {
138
            $payment_details['gateway'] = 'manual';
139
        }
140
        
141
        if ( empty( $payment_details['currency'] ) ) {
142
            $payment_details['currency'] = wpinv_get_default_country();
143
        }
144
        
145
        if ( empty( $payment_details['gateway_title'] ) ) {
146
            $payment_details['gateway_title'] = wpinv_get_gateway_checkout_label( $payment_details['gateway'] );
147
        }
148
    }
149
    
150
    $invoice->set( 'status', ( !empty( $invoice_data['status'] ) ? $invoice_data['status'] : 'wpi-pending' ) );
0 ignored issues
show
Bug introduced by
The method set() does not exist on WP_Error. ( Ignorable by Annotation )

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

150
    $invoice->/** @scrutinizer ignore-call */ 
151
              set( 'status', ( !empty( $invoice_data['status'] ) ? $invoice_data['status'] : 'wpi-pending' ) );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
151
    
152
    if ( !empty( $payment_details ) ) {
153
        $invoice->set( 'currency', $payment_details['currency'] );
154
        $invoice->set( 'gateway', $payment_details['gateway'] );
155
        $invoice->set( 'gateway_title', $payment_details['gateway_title'] );
156
        $invoice->set( 'transaction_id', $payment_details['transaction_id'] );
157
    }
158
    
159
    $invoice->set( 'user_info', $user_info );
160
    $invoice->set( 'first_name', $user_info['first_name'] );
161
    $invoice->set( 'last_name', $user_info['last_name'] );
162
    $invoice->set( 'address', $user_info['address'] );
163
    $invoice->set( 'company', $user_info['company'] );
164
    $invoice->set( 'vat_number', $user_info['vat_number'] );
165
    $invoice->set( 'phone', $user_info['phone'] );
166
    $invoice->set( 'city', $user_info['city'] );
167
    $invoice->set( 'country', $user_info['country'] );
168
    $invoice->set( 'state', $user_info['state'] );
169
    $invoice->set( 'zip', $user_info['zip'] );
170
    $invoice->set( 'discounts', $user_info['discount'] );
171
    $invoice->set( 'ip', ( !empty( $invoice_data['ip'] ) ? $invoice_data['ip'] : wpinv_get_ip() ) );
172
    $invoice->set( 'mode', ( wpinv_is_test_mode() ? 'test' : 'live' ) );
173
    $invoice->set( 'parent_invoice', ( !empty( $invoice_data['parent'] ) ? absint( $invoice_data['parent'] ) : '' ) );
174
    
175
    if ( !empty( $invoice_data['cart_details'] ) && is_array( $invoice_data['cart_details'] ) ) {
176
        foreach ( $invoice_data['cart_details'] as $key => $item ) {
177
            $item_id        = !empty( $item['id'] ) ? $item['id'] : 0;
178
            $quantity       = !empty( $item['quantity'] ) ? $item['quantity'] : 1;
179
            $name           = !empty( $item['name'] ) ? $item['name'] : '';
180
            $item_price     = isset( $item['item_price'] ) ? $item['item_price'] : '';
181
            
182
            $post_item  = new WPInv_Item( $item_id );
183
            if ( !empty( $post_item ) ) {
184
                $name       = !empty( $name ) ? $name : $post_item->get_name();
185
                $item_price = $item_price !== '' ? $item_price : $post_item->get_price();
186
            } else {
187
                continue;
188
            }
189
            
190
            $args = array(
191
                'name'          => $name,
192
                'quantity'      => $quantity,
193
                'item_price'    => $item_price,
194
                'custom_price'  => isset( $item['custom_price'] ) ? $item['custom_price'] : '',
195
                'tax'           => !empty( $item['tax'] ) ? $item['tax'] : 0.00,
196
                'discount'      => isset( $item['discount'] ) ? $item['discount'] : 0,
197
                'meta'          => isset( $item['meta'] ) ? $item['meta'] : array(),
198
                'fees'          => isset( $item['fees'] ) ? $item['fees'] : array(),
199
            );
200
201
            $invoice->add_item( $item_id, $args );
0 ignored issues
show
Bug introduced by
The method add_item() does not exist on WP_Error. ( Ignorable by Annotation )

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

201
            $invoice->/** @scrutinizer ignore-call */ 
202
                      add_item( $item_id, $args );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
202
        }
203
    }
204
205
    $invoice->increase_tax( wpinv_get_cart_fee_tax() );
0 ignored issues
show
Bug introduced by
The method increase_tax() does not exist on WP_Error. ( Ignorable by Annotation )

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

205
    $invoice->/** @scrutinizer ignore-call */ 
206
              increase_tax( wpinv_get_cart_fee_tax() );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
206
207
    if ( isset( $invoice_data['post_date'] ) ) {
208
        $invoice->set( 'date', $invoice_data['post_date'] );
209
    }
210
    
211
    // Invoice due date
212
    if ( isset( $invoice_data['due_date'] ) ) {
213
        $invoice->set( 'due_date', $invoice_data['due_date'] );
214
    }
215
    
216
    $invoice->save();
0 ignored issues
show
Bug introduced by
The method save() does not exist on WP_Error. ( Ignorable by Annotation )

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

216
    $invoice->/** @scrutinizer ignore-call */ 
217
              save();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
217
    
218
    // Add notes
219
    if ( !empty( $invoice_data['private_note'] ) ) {
220
        $invoice->add_note( $invoice_data['private_note'] );
0 ignored issues
show
Bug introduced by
The method add_note() does not exist on WP_Error. ( Ignorable by Annotation )

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

220
        $invoice->/** @scrutinizer ignore-call */ 
221
                  add_note( $invoice_data['private_note'] );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
221
    }
222
    if ( !empty( $invoice_data['user_note'] ) ) {
223
        $invoice->add_note( $invoice_data['user_note'], true );
224
    }
225
    
226
    if ( $invoice->is_quote() ) {
0 ignored issues
show
Bug introduced by
The method is_quote() does not exist on WP_Error. ( Ignorable by Annotation )

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

226
    if ( $invoice->/** @scrutinizer ignore-call */ is_quote() ) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
227
228
        if ( isset( $invoice_data['valid_until'] ) ) {
229
            update_post_meta( $invoice->ID, 'wpinv_quote_valid_until', $invoice_data['valid_until'] );
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist on WP_Error.
Loading history...
230
        }
231
        return $invoice;
232
233
    }
234
235
    do_action( 'wpinv_insert_invoice', $invoice->ID, $invoice_data );
236
237
    if ( ! empty( $invoice->ID ) ) {
238
        global $wpi_userID, $wpinv_ip_address_country;
239
        
240
        $checkout_session = wpinv_get_checkout_session();
241
        
242
        $data_session                   = array();
243
        $data_session['invoice_id']     = $invoice->ID;
244
        $data_session['cart_discounts'] = $invoice->get_discounts( true );
0 ignored issues
show
Bug introduced by
The method get_discounts() does not exist on WP_Error. ( Ignorable by Annotation )

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

244
        /** @scrutinizer ignore-call */ 
245
        $data_session['cart_discounts'] = $invoice->get_discounts( true );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
245
        
246
        wpinv_set_checkout_session( $data_session );
247
        
248
        $wpi_userID         = (int)$invoice->get_user_id();
249
        
250
        $_POST['country']   = !empty( $invoice->country ) ? $invoice->country : wpinv_get_default_country();
0 ignored issues
show
Bug introduced by
The property country does not seem to exist on WP_Error.
Loading history...
251
        $_POST['state']     = $invoice->state;
0 ignored issues
show
Bug introduced by
The property state does not seem to exist on WP_Error.
Loading history...
252
253
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
254
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
255
        
256
        $wpinv_ip_address_country = $invoice->country;
257
        
258
        $invoice = $invoice->recalculate_totals( true );
0 ignored issues
show
Bug introduced by
The method recalculate_totals() does not exist on WP_Error. ( Ignorable by Annotation )

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

258
        /** @scrutinizer ignore-call */ 
259
        $invoice = $invoice->recalculate_totals( true );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
259
        
260
        wpinv_set_checkout_session( $checkout_session );
261
                    
262
        return $invoice;
263
    }
264
    
265
    if ( $wp_error ) {
266
        if ( is_wp_error( $invoice ) ) {
267
            return $invoice;
268
        } else {
269
            return new WP_Error( 'wpinv_insert_invoice_error', __( 'Error in insert invoice.', 'invoicing' ) );
270
        }
271
    } else {
272
        return 0;
273
    }
274
}
275
276
function wpinv_update_invoice( $invoice_data = array(), $wp_error = false ) {
277
    $invoice_ID = !empty( $invoice_data['ID'] ) ? absint( $invoice_data['ID'] ) : NULL;
278
279
    if ( !$invoice_ID ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $invoice_ID of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
280
        if ( $wp_error ) {
281
            return new WP_Error( 'invalid_invoice_id', __( 'Invalid invoice ID.', 'invoicing' ) );
282
        }
283
        return 0;
284
    }
285
286
    $invoice = wpinv_get_invoice( $invoice_ID );
287
288
    $recurring_item = $invoice->is_recurring() ? $invoice->get_recurring( true ) : NULL;
289
290
    if ( empty( $invoice->ID ) ) {
291
        if ( $wp_error ) {
292
            return new WP_Error( 'invalid_invoice', __( 'Invalid invoice.', 'invoicing' ) );
293
        }
294
        return 0;
295
    }
296
297
    if ( ! $invoice->has_status( array( 'wpi-pending' ) ) && ! $invoice->is_quote()  ) {
298
        if ( $wp_error ) {
299
            return new WP_Error( 'invalid_invoice_status', __( 'Only invoice with pending payment is allowed to update.', 'invoicing' ) );
300
        }
301
        return 0;
302
    }
303
304
    // Invoice status
305
    if ( !empty( $invoice_data['status'] ) ) {
306
        $invoice->set( 'status', $invoice_data['status'] );
307
    }
308
309
    // Invoice date
310
    if ( !empty( $invoice_data['post_date'] ) ) {
311
        $invoice->set( 'date', $invoice_data['post_date'] );
312
    }
313
314
    // Invoice due date
315
    if ( isset( $invoice_data['due_date'] ) ) {
316
        $invoice->set( 'due_date', $invoice_data['due_date'] );
317
    }
318
319
    // Invoice IP address
320
    if ( !empty( $invoice_data['ip'] ) ) {
321
        $invoice->set( 'ip', $invoice_data['ip'] );
322
    }
323
    
324
    // User info
325
    if ( !empty( $invoice_data['user_info'] ) && is_array( $invoice_data['user_info'] ) ) {
326
        $user_info = wp_parse_args( $invoice_data['user_info'], $invoice->user_info );
327
328
        if ( $discounts = $invoice->get_discounts() ) {
329
            $set_discount = $discounts;
330
        } else {
331
            $set_discount = '';
332
        }
333
334
        // Manage discount
335
        if ( !empty( $invoice_data['user_info']['discount'] ) ) {
336
            // Remove discount
337
            if ( $invoice_data['user_info']['discount'] == 'none' ) {
338
                $set_discount = '';
339
            } else {
340
                $set_discount = $invoice_data['user_info']['discount'];
341
            }
342
343
            $invoice->set( 'discounts', $set_discount );
344
        }
345
346
        $user_info['discount'] = $set_discount;
347
348
        $invoice->set( 'user_info', $user_info );
349
    }
350
351
    if ( !empty( $invoice_data['cart_details'] ) && is_array( $invoice_data['cart_details'] ) && $cart_details = $invoice_data['cart_details'] ) {
352
        $remove_items = !empty( $cart_details['remove_items'] ) && is_array( $cart_details['remove_items'] ) ? $cart_details['remove_items'] : array();
353
354
        if ( !empty( $remove_items[0]['id'] ) ) {
355
            foreach ( $remove_items as $item ) {
356
                $item_id        = !empty( $item['id'] ) ? $item['id'] : 0;
357
                $quantity       = !empty( $item['quantity'] ) ? $item['quantity'] : 1;
358
                if ( empty( $item_id ) ) {
359
                    continue;
360
                }
361
362
                foreach ( $invoice->cart_details as $cart_index => $cart_item ) {
363
                    if ( $item_id == $cart_item['id'] ) {
364
                        $args = array(
365
                            'id'         => $item_id,
366
                            'quantity'   => $quantity,
367
                            'cart_index' => $cart_index
368
                        );
369
370
                        $invoice->remove_item( $item_id, $args );
371
                        break;
372
                    }
373
                }
374
            }
375
        }
376
377
        $add_items = !empty( $cart_details['add_items'] ) && is_array( $cart_details['add_items'] ) ? $cart_details['add_items'] : array();
378
379
        if ( !empty( $add_items[0]['id'] ) ) {
380
            foreach ( $add_items as $item ) {
381
                $item_id        = !empty( $item['id'] ) ? $item['id'] : 0;
382
                $post_item      = new WPInv_Item( $item_id );
383
                if ( empty( $post_item ) ) {
384
                    continue;
385
                }
386
387
                $valid_item = true;
388
                if ( !empty( $recurring_item ) ) {
389
                    if ( $recurring_item->ID != $item_id ) {
390
                        $valid_item = false;
391
                    }
392
                } else if ( wpinv_is_recurring_item( $item_id ) ) {
393
                    $valid_item = false;
394
                }
395
                
396
                if ( !$valid_item ) {
397
                    if ( $wp_error ) {
398
                        return new WP_Error( 'invalid_invoice_item', __( 'You can not add item because recurring item must be paid individually!', 'invoicing' ) );
399
                    }
400
                    return 0;
401
                }
402
403
                $quantity       = !empty( $item['quantity'] ) ? $item['quantity'] : 1;
404
                $name           = !empty( $item['name'] ) ? $item['name'] : $post_item->get_name();
405
                $item_price     = isset( $item['item_price'] ) ? $item['item_price'] : $post_item->get_price();
406
407
                $args = array(
408
                    'name'          => $name,
409
                    'quantity'      => $quantity,
410
                    'item_price'    => $item_price,
411
                    'custom_price'  => isset( $item['custom_price'] ) ? $item['custom_price'] : '',
412
                    'tax'           => !empty( $item['tax'] ) ? $item['tax'] : 0,
413
                    'discount'      => isset( $item['discount'] ) ? $item['discount'] : 0,
414
                    'meta'          => isset( $item['meta'] ) ? $item['meta'] : array(),
415
                    'fees'          => isset( $item['fees'] ) ? $item['fees'] : array(),
416
                );
417
418
                $invoice->add_item( $item_id, $args );
419
            }
420
        }
421
    }
422
    
423
    // Payment details
424
    if ( !empty( $invoice_data['payment_details'] ) && $payment_details = $invoice_data['payment_details'] ) {
425
        if ( !empty( $payment_details['gateway'] ) ) {
426
            $invoice->set( 'gateway', $payment_details['gateway'] );
427
        }
428
429
        if ( !empty( $payment_details['transaction_id'] ) ) {
430
            $invoice->set( 'transaction_id', $payment_details['transaction_id'] );
431
        }
432
    }
433
434
    do_action( 'wpinv_pre_update_invoice', $invoice->ID, $invoice_data );
435
436
    // Parent invoice
437
    if ( !empty( $invoice_data['parent'] ) ) {
438
        $invoice->set( 'parent_invoice', $invoice_data['parent'] );
439
    }
440
441
    // Save invoice data.
442
    $invoice->save();
443
    
444
    if ( empty( $invoice->ID ) || is_wp_error( $invoice ) ) {
445
        if ( $wp_error ) {
446
            if ( is_wp_error( $invoice ) ) {
447
                return $invoice;
448
            } else {
449
                return new WP_Error( 'wpinv_update_invoice_error', __( 'Error in update invoice.', 'invoicing' ) );
450
            }
451
        } else {
452
            return 0;
453
        }
454
    }
455
456
    // Add private note
457
    if ( !empty( $invoice_data['private_note'] ) ) {
458
        $invoice->add_note( $invoice_data['private_note'] );
459
    }
460
461
    // Add user note
462
    if ( !empty( $invoice_data['user_note'] ) ) {
463
        $invoice->add_note( $invoice_data['user_note'], true );
464
    }
465
466
    if ( $invoice->is_quote() ) {
467
468
        if ( isset( $invoice_data['valid_until'] ) ) {
469
            update_post_meta( $invoice->ID, 'wpinv_quote_valid_until', $invoice_data['valid_until'] );
470
        }
471
        return $invoice;
472
473
    }
474
475
    global $wpi_userID, $wpinv_ip_address_country;
476
477
    $checkout_session = wpinv_get_checkout_session();
478
479
    $data_session                   = array();
480
    $data_session['invoice_id']     = $invoice->ID;
481
    $data_session['cart_discounts'] = $invoice->get_discounts( true );
482
483
    wpinv_set_checkout_session( $data_session );
484
485
    $wpi_userID         = (int)$invoice->get_user_id();
486
487
    $_POST['country']   = !empty( $invoice->country ) ? $invoice->country : wpinv_get_default_country();
488
    $_POST['state']     = $invoice->state;
489
490
    $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
491
    $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
492
493
    $wpinv_ip_address_country = $invoice->country;
494
495
    $invoice = $invoice->recalculate_totals( true );
496
497
    do_action( 'wpinv_post_update_invoice', $invoice->ID, $invoice_data );
498
499
    wpinv_set_checkout_session( $checkout_session );
500
501
    return $invoice;
502
}
503
504
function wpinv_get_invoice( $invoice_id = 0, $cart = false ) {
505
    if ( $cart && empty( $invoice_id ) ) {
506
        $invoice_id = (int)wpinv_get_invoice_cart_id();
507
    }
508
509
    $invoice = new WPInv_Invoice( $invoice_id );
510
511
    if ( ! empty( $invoice ) && ! empty( $invoice->ID ) ) {
512
        return $invoice;
513
    }
514
    
515
    return NULL;
516
}
517
518
function wpinv_get_invoice_cart( $invoice_id = 0 ) {
519
    return wpinv_get_invoice( $invoice_id, true );
520
}
521
522
function wpinv_get_invoice_description( $invoice_id = 0 ) {
523
    $invoice = new WPInv_Invoice( $invoice_id );
524
    return $invoice->get_description();
525
}
526
527
function wpinv_get_invoice_currency_code( $invoice_id = 0 ) {
528
    $invoice = new WPInv_Invoice( $invoice_id );
529
    return $invoice->get_currency();
530
}
531
532
function wpinv_get_payment_user_email( $invoice_id ) {
533
    $invoice = new WPInv_Invoice( $invoice_id );
534
    return $invoice->get_email();
535
}
536
537
function wpinv_get_user_id( $invoice_id ) {
538
    $invoice = new WPInv_Invoice( $invoice_id );
539
    return $invoice->get_user_id();
540
}
541
542
function wpinv_get_invoice_status( $invoice_id, $return_label = false ) {
543
    $invoice = new WPInv_Invoice( $invoice_id );
544
    
545
    return $invoice->get_status( $return_label );
546
}
547
548
function wpinv_get_payment_gateway( $invoice_id, $return_label = false ) {
549
    $invoice = new WPInv_Invoice( $invoice_id );
550
    
551
    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. ( Ignorable by Annotation )

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

551
    return $invoice->/** @scrutinizer ignore-call */ get_gateway( $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. Please note the @ignore annotation hint above.

Loading history...
552
}
553
554
function wpinv_get_payment_gateway_name( $invoice_id ) {
555
    $invoice = new WPInv_Invoice( $invoice_id );
556
    
557
    return $invoice->get_gateway_title();
558
}
559
560
function wpinv_get_payment_transaction_id( $invoice_id ) {
561
    $invoice = new WPInv_Invoice( $invoice_id );
562
    
563
    return $invoice->get_transaction_id();
564
}
565
566
function wpinv_get_id_by_transaction_id( $key ) {
567
    global $wpdb;
568
569
    $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 ) );
570
571
    if ( $invoice_id != NULL )
572
        return $invoice_id;
573
574
    return 0;
575
}
576
577
function wpinv_get_invoice_meta( $invoice_id = 0, $meta_key = '_wpinv_payment_meta', $single = true ) {
578
    $invoice = new WPInv_Invoice( $invoice_id );
579
580
    return $invoice->get_meta( $meta_key, $single );
581
}
582
583
function wpinv_update_invoice_meta( $invoice_id = 0, $meta_key = '', $meta_value = '', $prev_value = '' ) {
584
    $invoice = new WPInv_Invoice( $invoice_id );
585
    
586
    return $invoice->update_meta( $meta_key, $meta_value, $prev_value );
587
}
588
589
function wpinv_get_items( $invoice_id = 0 ) {
590
    $invoice            = wpinv_get_invoice( $invoice_id );
591
    
592
    $items              = $invoice->get_items();
593
    $invoice_currency   = $invoice->get_currency();
594
595
    if ( !empty( $items ) && is_array( $items ) ) {
596
        foreach ( $items as $key => $item ) {
597
            $items[$key]['currency'] = $invoice_currency;
598
599
            if ( !isset( $item['subtotal'] ) ) {
600
                $items[$key]['subtotal'] = $items[$key]['amount'] * 1;
601
            }
602
        }
603
    }
604
605
    return apply_filters( 'wpinv_get_items', $items, $invoice_id );
606
}
607
608
function wpinv_get_fees( $invoice_id = 0 ) {
609
    $invoice           = wpinv_get_invoice( $invoice_id );
610
    $fees              = $invoice->get_fees();
611
612
    return apply_filters( 'wpinv_get_fees', $fees, $invoice_id );
613
}
614
615
function wpinv_get_invoice_ip( $invoice_id ) {
616
    $invoice = new WPInv_Invoice( $invoice_id );
617
    return $invoice->get_ip();
618
}
619
620
function wpinv_get_invoice_user_info( $invoice_id ) {
621
    $invoice = new WPInv_Invoice( $invoice_id );
622
    return $invoice->get_user_info();
623
}
624
625
function wpinv_subtotal( $invoice_id = 0, $currency = false ) {
626
    $invoice = new WPInv_Invoice( $invoice_id );
627
628
    return $invoice->get_subtotal( $currency );
629
}
630
631
function wpinv_tax( $invoice_id = 0, $currency = false ) {
632
    $invoice = new WPInv_Invoice( $invoice_id );
633
634
    return $invoice->get_tax( $currency );
635
}
636
637
function wpinv_discount( $invoice_id = 0, $currency = false, $dash = false ) {
638
    $invoice = wpinv_get_invoice( $invoice_id );
639
640
    return $invoice->get_discount( $currency, $dash );
641
}
642
643
function wpinv_discount_code( $invoice_id = 0 ) {
644
    $invoice = new WPInv_Invoice( $invoice_id );
645
646
    return $invoice->get_discount_code();
647
}
648
649
function wpinv_payment_total( $invoice_id = 0, $currency = false ) {
650
    $invoice = new WPInv_Invoice( $invoice_id );
651
652
    return $invoice->get_total( $currency );
653
}
654
655
function wpinv_get_date_created( $invoice_id = 0, $format = '' ) {
656
    $invoice = new WPInv_Invoice( $invoice_id );
657
658
    $format         = !empty( $format ) ? $format : get_option( 'date_format' );
659
    $date_created   = $invoice->get_created_date();
660
    $date_created   = $date_created != '' && $date_created != '0000-00-00 00:00:00' ? date_i18n( $format, strtotime( $date_created ) ) : '';
0 ignored issues
show
Bug introduced by
It seems like $format can also be of type false; however, parameter $format of date_i18n() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

660
    $date_created   = $date_created != '' && $date_created != '0000-00-00 00:00:00' ? date_i18n( /** @scrutinizer ignore-type */ $format, strtotime( $date_created ) ) : '';
Loading history...
661
662
    return $date_created;
663
}
664
665
function wpinv_get_invoice_date( $invoice_id = 0, $format = '', $default = true ) {
666
    $invoice = new WPInv_Invoice( $invoice_id );
667
    
668
    $format         = !empty( $format ) ? $format : get_option( 'date_format' );
669
    $date_completed = $invoice->get_completed_date();
670
    $invoice_date   = $date_completed != '' && $date_completed != '0000-00-00 00:00:00' ? date_i18n( $format, strtotime( $date_completed ) ) : '';
0 ignored issues
show
Bug introduced by
It seems like $format can also be of type false; however, parameter $format of date_i18n() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

670
    $invoice_date   = $date_completed != '' && $date_completed != '0000-00-00 00:00:00' ? date_i18n( /** @scrutinizer ignore-type */ $format, strtotime( $date_completed ) ) : '';
Loading history...
671
    if ( $invoice_date == '' && $default ) {
672
        $invoice_date   = wpinv_get_date_created( $invoice_id, $format );
673
    }
674
675
    return $invoice_date;
676
}
677
678
function wpinv_get_invoice_vat_number( $invoice_id = 0 ) {
679
    $invoice = new WPInv_Invoice( $invoice_id );
680
    
681
    return $invoice->vat_number;
682
}
683
684
function wpinv_insert_payment_note( $invoice_id = 0, $note = '', $user_type = false, $added_by_user = false, $system = false ) {
685
    $invoice = new WPInv_Invoice( $invoice_id );
686
687
    return $invoice->add_note( $note, $user_type, $added_by_user, $system );
688
}
689
690
function wpinv_get_invoice_notes( $invoice_id = 0, $type = '' ) {
691
    global $invoicing;
692
    
693
    if ( empty( $invoice_id ) ) {
694
        return NULL;
695
    }
696
    
697
    $notes = $invoicing->notes->get_invoice_notes( $invoice_id, $type );
698
    
699
    return apply_filters( 'wpinv_invoice_notes', $notes, $invoice_id, $type );
700
}
701
702
function wpinv_get_payment_key( $invoice_id = 0 ) {
703
	$invoice = new WPInv_Invoice( $invoice_id );
704
    return $invoice->get_key();
705
}
706
707
function wpinv_get_invoice_number( $invoice_id = 0 ) {
708
    $invoice = new WPInv_Invoice( $invoice_id );
709
    return $invoice->get_number();
710
}
711
712
function wpinv_get_cart_discountable_subtotal( $code_id ) {
713
    $cart_items = wpinv_get_cart_content_details();
714
    $items      = array();
715
716
    $excluded_items = wpinv_get_discount_excluded_items( $code_id );
717
718
    if( $cart_items ) {
719
720
        foreach( $cart_items as $item ) {
721
722
            if( ! in_array( $item['id'], $excluded_items ) ) {
723
                $items[] =  $item;
724
            }
725
        }
726
    }
727
728
    $subtotal = wpinv_get_cart_items_subtotal( $items );
729
730
    return apply_filters( 'wpinv_get_cart_discountable_subtotal', $subtotal );
731
}
732
733
function wpinv_get_cart_items_subtotal( $items ) {
734
    $subtotal = 0.00;
735
736
    if ( is_array( $items ) && ! empty( $items ) ) {
737
        $prices = wp_list_pluck( $items, 'subtotal' );
738
739
        if( is_array( $prices ) ) {
0 ignored issues
show
introduced by
The condition is_array($prices) is always true.
Loading history...
740
            $subtotal = array_sum( $prices );
741
        } else {
742
            $subtotal = 0.00;
743
        }
744
745
        if( $subtotal < 0 ) {
746
            $subtotal = 0.00;
747
        }
748
    }
749
750
    return apply_filters( 'wpinv_get_cart_items_subtotal', $subtotal );
751
}
752
753
function wpinv_get_cart_subtotal( $items = array() ) {
754
    $items    = !empty( $items ) ? $items : wpinv_get_cart_content_details();
755
    $subtotal = wpinv_get_cart_items_subtotal( $items );
756
757
    return apply_filters( 'wpinv_get_cart_subtotal', $subtotal );
758
}
759
760
function wpinv_cart_subtotal( $items = array() ) {
761
    $price = wpinv_price( wpinv_format_amount( wpinv_get_cart_subtotal( $items ) ) );
762
763
    return $price;
764
}
765
766
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. ( Ignorable by Annotation )

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

766
function wpinv_get_cart_total( $items = array(), /** @scrutinizer ignore-unused */ $discounts = false, $invoice = array() ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
767
    $subtotal  = (float)wpinv_get_cart_subtotal( $items );
768
    $discounts = (float)wpinv_get_cart_discounted_amount( $items );
769
    $cart_tax  = (float)wpinv_get_cart_tax( $items );
770
    $fees      = (float)wpinv_get_cart_fee_total();
771
    if ( !empty( $invoice ) && $invoice->is_free_trial() ) {
772
        $total = 0;
773
    } else {
774
        $total     = $subtotal - $discounts + $cart_tax + $fees;
775
    }
776
777
    if ( $total < 0 ) {
778
        $total = 0.00;
779
    }
780
    
781
    $total = (float)apply_filters( 'wpinv_get_cart_total', $total, $items );
782
783
    return wpinv_sanitize_amount( $total );
784
}
785
786
function wpinv_cart_total( $cart_items = array(), $echo = true, $invoice = array() ) {
787
    global $cart_total;
788
    $total = wpinv_price( wpinv_format_amount( wpinv_get_cart_total( $cart_items, NULL, $invoice ) ) );
789
    $total = apply_filters( 'wpinv_cart_total', $total, $cart_items, $invoice );
790
    
791
    $cart_total = $total;
792
793
    if ( !$echo ) {
794
        return $total;
795
    }
796
797
    echo $total;
798
}
799
800
function wpinv_get_cart_tax( $items = array() ) {
801
    $cart_tax = 0;
802
    $items    = !empty( $items ) ? $items : wpinv_get_cart_content_details();
803
804
    if ( $items ) {
805
        $taxes = wp_list_pluck( $items, 'tax' );
806
807
        if( is_array( $taxes ) ) {
0 ignored issues
show
introduced by
The condition is_array($taxes) is always true.
Loading history...
808
            $cart_tax = array_sum( $taxes );
809
        }
810
    }
811
812
    $cart_tax += wpinv_get_cart_fee_tax();
813
814
    return apply_filters( 'wpinv_get_cart_tax', wpinv_sanitize_amount( $cart_tax ) );
815
}
816
817
function wpinv_cart_tax( $items = array(), $echo = false ) {
818
    $cart_tax = wpinv_get_cart_tax( $items );
819
    $cart_tax = wpinv_price( wpinv_format_amount( $cart_tax ) );
820
821
    $tax = apply_filters( 'wpinv_cart_tax', $cart_tax, $items );
822
823
    if ( !$echo ) {
824
        return $tax;
825
    }
826
827
    echo $tax;
828
}
829
830
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. ( Ignorable by Annotation )

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

830
function wpinv_get_cart_discount_code( /** @scrutinizer ignore-unused */ $items = array() ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
831
    $invoice = wpinv_get_invoice_cart();
832
    $cart_discount_code = !empty( $invoice ) ? $invoice->get_discount_code() : '';
833
    
834
    return apply_filters( 'wpinv_get_cart_discount_code', $cart_discount_code );
835
}
836
837
function wpinv_cart_discount_code( $items = array(), $echo = false ) {
838
    $cart_discount_code = wpinv_get_cart_discount_code( $items );
839
840
    if ( $cart_discount_code != '' ) {
841
        $cart_discount_code = ' (' . $cart_discount_code . ')';
842
    }
843
    
844
    $discount_code = apply_filters( 'wpinv_cart_discount_code', $cart_discount_code, $items );
845
846
    if ( !$echo ) {
847
        return $discount_code;
848
    }
849
850
    echo $discount_code;
851
}
852
853
function wpinv_get_cart_discount( $items = array() ) {
854
    $invoice = wpinv_get_invoice_cart();
855
    $cart_discount = !empty( $invoice ) ? $invoice->get_discount() : 0;
856
    
857
    return apply_filters( 'wpinv_get_cart_discount', wpinv_sanitize_amount( $cart_discount ), $items );
858
}
859
860
function wpinv_cart_discount( $items = array(), $echo = false ) {
861
    $cart_discount = wpinv_get_cart_discount( $items );
862
    $cart_discount = wpinv_price( wpinv_format_amount( $cart_discount ) );
863
864
    $discount = apply_filters( 'wpinv_cart_discount', $cart_discount, $items );
865
866
    if ( !$echo ) {
867
        return $discount;
868
    }
869
870
    echo $discount;
871
}
872
873
function wpinv_get_cart_fees( $type = 'all', $item_id = 0 ) {
874
    $item = new WPInv_Item( $item_id );
875
    
876
    return $item->get_fees( $type, $item_id );
877
}
878
879
function wpinv_get_cart_fee_total() {
880
    $total  = 0;
881
    $fees = wpinv_get_cart_fees();
882
    
883
    if ( $fees ) {
884
        foreach ( $fees as $fee_id => $fee ) {
885
            $total += $fee['amount'];
886
        }
887
    }
888
889
    return apply_filters( 'wpinv_get_cart_fee_total', $total );
890
}
891
892
function wpinv_get_cart_fee_tax() {
893
    $tax  = 0;
894
    $fees = wpinv_get_cart_fees();
895
896
    if ( $fees ) {
897
        foreach ( $fees as $fee_id => $fee ) {
898
            if( ! empty( $fee['no_tax'] ) ) {
899
                continue;
900
            }
901
902
            $tax += wpinv_calculate_tax( $fee['amount'] );
903
        }
904
    }
905
906
    return apply_filters( 'wpinv_get_cart_fee_tax', $tax );
907
}
908
909
function wpinv_cart_has_recurring_item() {
910
    $cart_items = wpinv_get_cart_contents();
911
    
912
    if ( empty( $cart_items ) ) {
913
        return false;
914
    }
915
    
916
    $has_subscription = false;
917
    foreach( $cart_items as $cart_item ) {
918
        if ( !empty( $cart_item['id'] ) && wpinv_is_recurring_item( $cart_item['id'] )  ) {
919
            $has_subscription = true;
920
            break;
921
        }
922
    }
923
    
924
    return apply_filters( 'wpinv_cart_has_recurring_item', $has_subscription, $cart_items );
925
}
926
927
function wpinv_cart_has_free_trial() {
928
    $invoice = wpinv_get_invoice_cart();
929
    
930
    $free_trial = false;
931
    
932
    if ( !empty( $invoice ) && $invoice->is_free_trial() ) {
933
        $free_trial = true;
934
    }
935
    
936
    return apply_filters( 'wpinv_cart_has_free_trial', $free_trial, $invoice );
937
}
938
939
function wpinv_get_cart_contents() {
940
    $cart_details = wpinv_get_cart_details();
941
    
942
    return apply_filters( 'wpinv_get_cart_contents', $cart_details );
943
}
944
945
function wpinv_get_cart_content_details() {
946
    global $wpinv_euvat, $wpi_current_id, $wpi_item_id, $wpinv_is_last_cart_item, $wpinv_flat_discount_total;
947
    $cart_items = wpinv_get_cart_contents();
948
    
949
    if ( empty( $cart_items ) ) {
950
        return false;
951
    }
952
    $invoice = wpinv_get_invoice_cart();
953
	if ( empty( $invoice ) ) {
954
        return false;
955
    }
956
957
    $details = array();
958
    $length  = count( $cart_items ) - 1;
0 ignored issues
show
Unused Code introduced by
The assignment to $length is dead and can be removed.
Loading history...
959
    
960
    if ( empty( $_POST['country'] ) ) {
961
        $_POST['country'] = $invoice->country;
962
    }
963
    if ( !isset( $_POST['state'] ) ) {
964
        $_POST['state'] = $invoice->state;
965
    }
966
967
    foreach( $cart_items as $key => $item ) {
968
        $item_id            = isset( $item['id'] ) ? sanitize_text_field( $item['id'] ) : '';
969
        if ( empty( $item_id ) ) {
970
            continue;
971
        }
972
        
973
        $wpi_current_id         = $invoice->ID;
974
        $wpi_item_id            = $item_id;
975
        
976
        if ( isset( $item['custom_price'] ) && $item['custom_price'] !== '' ) {
977
            $item_price = $item['custom_price'];
978
        } else {
979
            if ( isset( $item['item_price'] ) && $item['item_price'] !== '' && $item['item_price'] !== false ) {
980
                $item_price = $item['item_price'];
981
            } else {
982
                $item_price = wpinv_get_item_price( $item_id );
983
            }
984
        }
985
        $discount           = wpinv_get_cart_item_discount_amount( $item );
986
        $discount           = apply_filters( 'wpinv_get_cart_content_details_item_discount_amount', $discount, $item );
987
        $quantity           = wpinv_get_cart_item_quantity( $item );
988
        $fees               = wpinv_get_cart_fees( 'fee', $item_id );
989
        
990
        $subtotal           = $item_price * $quantity;
991
        $tax_rate           = wpinv_get_tax_rate( $_POST['country'], $_POST['state'], $wpi_item_id );
992
        $tax_class          = $wpinv_euvat->get_item_class( $item_id );
993
        $tax                = wpinv_get_cart_item_tax( $item_id, $subtotal - $discount );
994
        
995
        if ( wpinv_prices_include_tax() ) {
996
            $subtotal -= wpinv_round_amount( $tax );
997
        }
998
        
999
        $total              = $subtotal - $discount + $tax;
1000
        
1001
        // Do not allow totals to go negatve
1002
        if( $total < 0 ) {
1003
            $total = 0;
1004
        }
1005
        
1006
        $details[ $key ]  = array(
1007
            'id'                => $item_id,
1008
            'name'              => !empty($item['name']) ? $item['name'] : get_the_title( $item_id ),
0 ignored issues
show
Bug introduced by
$item_id of type string is incompatible with the type WP_Post|integer expected by parameter $post of get_the_title(). ( Ignorable by Annotation )

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

1008
            'name'              => !empty($item['name']) ? $item['name'] : get_the_title( /** @scrutinizer ignore-type */ $item_id ),
Loading history...
1009
            'item_price'        => wpinv_round_amount( $item_price ),
1010
            'custom_price'      => isset( $item['custom_price'] ) ? $item['custom_price'] : '',
1011
            'quantity'          => $quantity,
1012
            'discount'          => wpinv_round_amount( $discount ),
1013
            'subtotal'          => wpinv_round_amount( $subtotal ),
1014
            'tax'               => wpinv_round_amount( $tax ),
1015
            'price'             => wpinv_round_amount( $total ),
1016
            'vat_rates_class'   => $tax_class,
1017
            'vat_rate'          => $tax_rate,
1018
            'meta'              => isset( $item['meta'] ) ? $item['meta'] : array(),
1019
            'fees'              => $fees,
1020
        );
1021
        
1022
        if ( $wpinv_is_last_cart_item ) {
1023
            $wpinv_is_last_cart_item   = false;
1024
            $wpinv_flat_discount_total = 0.00;
1025
        }
1026
    }
1027
    
1028
    return $details;
1029
}
1030
1031
function wpinv_get_cart_details( $invoice_id = 0 ) {
1032
    global $ajax_cart_details;
1033
1034
    $invoice      = wpinv_get_invoice_cart( $invoice_id );
1035
    $cart_details = $ajax_cart_details;
1036
    if ( empty( $cart_details ) && ! empty( $invoice->cart_details ) ) {
1037
        $cart_details = $invoice->cart_details;
1038
    }
1039
1040
    if ( ! empty( $cart_details ) && is_array( $cart_details ) ) {
1041
        $invoice_currency = ! empty( $invoice->currency ) ? $invoice->currency : wpinv_get_default_country();
1042
1043
        foreach ( $cart_details as $key => $cart_item ) {
1044
            $cart_details[ $key ]['currency'] = $invoice_currency;
1045
1046
            if ( ! isset( $cart_item['subtotal'] ) ) {
1047
                $cart_details[ $key ]['subtotal'] = $cart_item['price'];
1048
            }
1049
        }
1050
    }
1051
1052
    return apply_filters( 'wpinv_get_cart_details', $cart_details, $invoice_id );
1053
}
1054
1055
function wpinv_record_status_change( $invoice_id, $new_status, $old_status ) {
1056
    if ( 'wpi_invoice' != get_post_type( $invoice_id ) ) {
1057
        return;
1058
    }
1059
1060
    if ( ( $old_status == 'wpi-pending' && $new_status == 'draft' ) || ( $old_status == 'draft' && $new_status == 'wpi-pending' ) ) {
1061
        return;
1062
    }
1063
1064
    $invoice    = wpinv_get_invoice( $invoice_id );
1065
    
1066
    $old_status = wpinv_status_nicename( $old_status );
1067
    $new_status = wpinv_status_nicename( $new_status );
1068
1069
    $status_change = sprintf( __( 'Invoice status changed from %s to %s', 'invoicing' ), $old_status, $new_status );
1070
    
1071
    // Add note
1072
    return $invoice->add_note( $status_change, false, false, true );
1073
}
1074
add_action( 'wpinv_update_status', 'wpinv_record_status_change', 100, 3 );
1075
1076
function wpinv_complete_payment( $invoice_id, $new_status, $old_status ) {
1077
    global $wpi_has_free_trial;
1078
    
1079
    $wpi_has_free_trial = false;
1080
    
1081
    if ( $old_status == 'publish' ) {
1082
        return; // Make sure that payments are only paid once
1083
    }
1084
1085
    // Make sure the payment completion is only processed when new status is paid
1086
    if ( $new_status != 'publish' ) {
1087
        return;
1088
    }
1089
1090
    $invoice = new WPInv_Invoice( $invoice_id );
1091
    if ( empty( $invoice ) ) {
1092
        return;
1093
    }
1094
1095
    $wpi_has_free_trial = $invoice->is_free_trial();
1096
    $completed_date = $invoice->completed_date;
1097
    $cart_details   = $invoice->cart_details;
1098
1099
    do_action( 'wpinv_pre_complete_payment', $invoice_id );
1100
1101
    if ( is_array( $cart_details ) ) {
0 ignored issues
show
introduced by
The condition is_array($cart_details) is always true.
Loading history...
1102
        // Increase purchase count and earnings
1103
        foreach ( $cart_details as $cart_index => $item ) {
1104
            // Ensure these actions only run once, ever
1105
            if ( empty( $completed_date ) ) {
1106
                do_action( 'wpinv_complete_item_payment', $item['id'], $invoice_id, $item, $cart_index );
1107
            }
1108
        }
1109
    }
1110
    
1111
    // Check for discount codes and increment their use counts
1112
    if ( $discounts = $invoice->get_discounts( true ) ) {
1113
        if( ! empty( $discounts ) ) {
1114
            foreach( $discounts as $code ) {
1115
                wpinv_increase_discount_usage( $code );
1116
            }
1117
        }
1118
    }
1119
    
1120
    // Ensure this action only runs once ever
1121
    if( empty( $completed_date ) ) {
1122
        // Save the completed date
1123
        $invoice->set( 'completed_date', current_time( 'mysql', 0 ) );
1124
        $invoice->save();
1125
1126
        do_action( 'wpinv_complete_payment', $invoice_id );
1127
    }
1128
1129
    // Empty the shopping cart
1130
    wpinv_empty_cart();
1131
}
1132
add_action( 'wpinv_update_status', 'wpinv_complete_payment', 100, 3 );
1133
1134
function wpinv_update_payment_status( $invoice_id, $new_status = 'publish' ) {    
1135
    $invoice = !empty( $invoice_id ) && is_object( $invoice_id ) ? $invoice_id : wpinv_get_invoice( (int)$invoice_id );
1136
    
1137
    if ( empty( $invoice ) ) {
1138
        return false;
1139
    }
1140
    
1141
    return $invoice->update_status( $new_status );
1142
}
1143
1144
function wpinv_cart_has_fees( $type = 'all' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

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

1144
function wpinv_cart_has_fees( /** @scrutinizer ignore-unused */ $type = 'all' ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1145
    return false;
1146
}
1147
1148
function wpinv_validate_checkout_fields() {    
1149
    // Check if there is $_POST
1150
    if ( empty( $_POST ) ) {
1151
        return false;
1152
    }
1153
    
1154
    // Start an array to collect valid data
1155
    $valid_data = array(
1156
        'gateway'          => wpinv_checkout_validate_gateway(), // Gateway fallback
1157
        'discount'         => wpinv_checkout_validate_discounts(), // Set default discount
1158
        'cc_info'          => wpinv_checkout_validate_cc() // Credit card info
1159
    );
1160
    
1161
    // Validate agree to terms
1162
    $page = wpinv_get_option( 'tandc_page' );
1163
    if(isset($page) && (int)$page > 0 && apply_filters( 'wpinv_checkout_show_terms', true )){
1164
        // Validate agree to terms
1165
        if ( ! isset( $_POST['wpi_terms'] ) || !$_POST['wpi_terms'] ) {
1166
            wpinv_set_error( 'accept_terms', apply_filters( 'wpinv_accept_terms_error_text', __( 'You must accept terms and conditions', 'invoicing' ) ) );
1167
        }
1168
    }
1169
    
1170
    $valid_data['invoice_user'] = wpinv_checkout_validate_invoice_user();
1171
    $valid_data['current_user'] = wpinv_checkout_validate_current_user();
1172
    
1173
    // Return collected data
1174
    return $valid_data;
1175
}
1176
1177
function wpinv_checkout_validate_gateway() {
1178
    $gateway = wpinv_get_default_gateway();
1179
    
1180
    $invoice = wpinv_get_invoice_cart();
1181
    $has_subscription = $invoice->is_recurring();
1182
    if ( empty( $invoice ) ) {
1183
        wpinv_set_error( 'invalid_invoice', __( 'Your cart is empty.', 'invoicing' ) );
1184
        return $gateway;
1185
    }
1186
1187
    // Check if a gateway value is present
1188
    if ( !empty( $_REQUEST['wpi-gateway'] ) ) {
1189
        $gateway = sanitize_text_field( $_REQUEST['wpi-gateway'] );
1190
1191
        if ( $invoice->is_free() ) {
1192
            $gateway = 'manual';
1193
        } elseif ( !wpinv_is_gateway_active( $gateway ) ) {
1194
            wpinv_set_error( 'invalid_gateway', __( 'The selected payment gateway is not enabled', 'invoicing' ) );
1195
        } elseif ( $has_subscription && !wpinv_gateway_support_subscription( $gateway ) ) {
1196
            wpinv_set_error( 'invalid_gateway', __( 'The selected payment gateway doesnot support subscription payment', 'invoicing' ) );
1197
        }
1198
    }
1199
1200
    if ( $has_subscription && count( wpinv_get_cart_contents() ) > 1 ) {
1201
        wpinv_set_error( 'subscription_invalid', __( 'Only one subscription may be purchased through payment per checkout.', 'invoicing' ) );
1202
    }
1203
1204
    return $gateway;
1205
}
1206
1207
function wpinv_checkout_validate_discounts() {
1208
    global $wpi_cart;
1209
    
1210
    // Retrieve the discount stored in cookies
1211
    $discounts = wpinv_get_cart_discounts();
1212
    
1213
    $error = false;
1214
    // If we have discounts, loop through them
1215
    if ( ! empty( $discounts ) ) {
1216
        foreach ( $discounts as $discount ) {
1217
            // Check if valid
1218
            if (  !wpinv_is_discount_valid( $discount, (int)$wpi_cart->get_user_id() ) ) {
1219
                // Discount is not valid
1220
                $error = true;
1221
            }
1222
        }
1223
    } else {
1224
        // No discounts
1225
        return NULL;
1226
    }
1227
1228
    if ( $error && !wpinv_get_errors() ) {
1229
        wpinv_set_error( 'invalid_discount', __( 'Discount code you entered is invalid', 'invoicing' ) );
1230
    }
1231
1232
    return implode( ',', $discounts );
1233
}
1234
1235
function wpinv_checkout_validate_cc() {
1236
    $card_data = wpinv_checkout_get_cc_info();
1237
1238
    // Validate the card zip
1239
    if ( !empty( $card_data['wpinv_zip'] ) ) {
1240
        if ( !wpinv_checkout_validate_cc_zip( $card_data['wpinv_zip'], $card_data['wpinv_country'] ) ) {
1241
            wpinv_set_error( 'invalid_cc_zip', __( 'The zip / postcode you entered for your billing address is invalid', 'invoicing' ) );
1242
        }
1243
    }
1244
1245
    // This should validate card numbers at some point too
1246
    return $card_data;
1247
}
1248
1249
function wpinv_checkout_get_cc_info() {
1250
	$cc_info = array();
1251
	$cc_info['card_name']      = isset( $_POST['card_name'] )       ? sanitize_text_field( $_POST['card_name'] )       : '';
1252
	$cc_info['card_number']    = isset( $_POST['card_number'] )     ? sanitize_text_field( $_POST['card_number'] )     : '';
1253
	$cc_info['card_cvc']       = isset( $_POST['card_cvc'] )        ? sanitize_text_field( $_POST['card_cvc'] )        : '';
1254
	$cc_info['card_exp_month'] = isset( $_POST['card_exp_month'] )  ? sanitize_text_field( $_POST['card_exp_month'] )  : '';
1255
	$cc_info['card_exp_year']  = isset( $_POST['card_exp_year'] )   ? sanitize_text_field( $_POST['card_exp_year'] )   : '';
1256
	$cc_info['card_address']   = isset( $_POST['wpinv_address'] )  ? sanitize_text_field( $_POST['wpinv_address'] ) : '';
1257
	$cc_info['card_city']      = isset( $_POST['wpinv_city'] )     ? sanitize_text_field( $_POST['wpinv_city'] )    : '';
1258
	$cc_info['card_state']     = isset( $_POST['wpinv_state'] )    ? sanitize_text_field( $_POST['wpinv_state'] )   : '';
1259
	$cc_info['card_country']   = isset( $_POST['wpinv_country'] )  ? sanitize_text_field( $_POST['wpinv_country'] ) : '';
1260
	$cc_info['card_zip']       = isset( $_POST['wpinv_zip'] )      ? sanitize_text_field( $_POST['wpinv_zip'] )     : '';
1261
1262
	// Return cc info
1263
	return $cc_info;
1264
}
1265
1266
function wpinv_checkout_validate_cc_zip( $zip = 0, $country_code = '' ) {
1267
    $ret = false;
1268
1269
    if ( empty( $zip ) || empty( $country_code ) )
1270
        return $ret;
1271
1272
    $country_code = strtoupper( $country_code );
1273
1274
    $zip_regex = array(
1275
        "AD" => "AD\d{3}",
1276
        "AM" => "(37)?\d{4}",
1277
        "AR" => "^([A-Z]{1}\d{4}[A-Z]{3}|[A-Z]{1}\d{4}|\d{4})$",
1278
        "AS" => "96799",
1279
        "AT" => "\d{4}",
1280
        "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})$",
1281
        "AX" => "22\d{3}",
1282
        "AZ" => "\d{4}",
1283
        "BA" => "\d{5}",
1284
        "BB" => "(BB\d{5})?",
1285
        "BD" => "\d{4}",
1286
        "BE" => "^[1-9]{1}[0-9]{3}$",
1287
        "BG" => "\d{4}",
1288
        "BH" => "((1[0-2]|[2-9])\d{2})?",
1289
        "BM" => "[A-Z]{2}[ ]?[A-Z0-9]{2}",
1290
        "BN" => "[A-Z]{2}[ ]?\d{4}",
1291
        "BR" => "\d{5}[\-]?\d{3}",
1292
        "BY" => "\d{6}",
1293
        "CA" => "^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$",
1294
        "CC" => "6799",
1295
        "CH" => "^[1-9][0-9][0-9][0-9]$",
1296
        "CK" => "\d{4}",
1297
        "CL" => "\d{7}",
1298
        "CN" => "\d{6}",
1299
        "CR" => "\d{4,5}|\d{3}-\d{4}",
1300
        "CS" => "\d{5}",
1301
        "CV" => "\d{4}",
1302
        "CX" => "6798",
1303
        "CY" => "\d{4}",
1304
        "CZ" => "\d{3}[ ]?\d{2}",
1305
        "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",
1306
        "DK" => "^([D-d][K-k])?( |-)?[1-9]{1}[0-9]{3}$",
1307
        "DO" => "\d{5}",
1308
        "DZ" => "\d{5}",
1309
        "EC" => "([A-Z]\d{4}[A-Z]|(?:[A-Z]{2})?\d{6})?",
1310
        "EE" => "\d{5}",
1311
        "EG" => "\d{5}",
1312
        "ES" => "^([1-9]{2}|[0-9][1-9]|[1-9][0-9])[0-9]{3}$",
1313
        "ET" => "\d{4}",
1314
        "FI" => "\d{5}",
1315
        "FK" => "FIQQ 1ZZ",
1316
        "FM" => "(9694[1-4])([ \-]\d{4})?",
1317
        "FO" => "\d{3}",
1318
        "FR" => "^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$",
1319
        "GE" => "\d{4}",
1320
        "GF" => "9[78]3\d{2}",
1321
        "GL" => "39\d{2}",
1322
        "GN" => "\d{3}",
1323
        "GP" => "9[78][01]\d{2}",
1324
        "GR" => "\d{3}[ ]?\d{2}",
1325
        "GS" => "SIQQ 1ZZ",
1326
        "GT" => "\d{5}",
1327
        "GU" => "969[123]\d([ \-]\d{4})?",
1328
        "GW" => "\d{4}",
1329
        "HM" => "\d{4}",
1330
        "HN" => "(?:\d{5})?",
1331
        "HR" => "\d{5}",
1332
        "HT" => "\d{4}",
1333
        "HU" => "\d{4}",
1334
        "ID" => "\d{5}",
1335
        "IE" => "((D|DUBLIN)?([1-9]|6[wW]|1[0-8]|2[024]))?",
1336
        "IL" => "\d{5}",
1337
        "IN"=> "^[1-9][0-9][0-9][0-9][0-9][0-9]$", //india
1338
        "IO" => "BBND 1ZZ",
1339
        "IQ" => "\d{5}",
1340
        "IS" => "\d{3}",
1341
        "IT" => "^(V-|I-)?[0-9]{5}$",
1342
        "JO" => "\d{5}",
1343
        "JP" => "\d{3}-\d{4}",
1344
        "KE" => "\d{5}",
1345
        "KG" => "\d{6}",
1346
        "KH" => "\d{5}",
1347
        "KR" => "\d{3}[\-]\d{3}",
1348
        "KW" => "\d{5}",
1349
        "KZ" => "\d{6}",
1350
        "LA" => "\d{5}",
1351
        "LB" => "(\d{4}([ ]?\d{4})?)?",
1352
        "LI" => "(948[5-9])|(949[0-7])",
1353
        "LK" => "\d{5}",
1354
        "LR" => "\d{4}",
1355
        "LS" => "\d{3}",
1356
        "LT" => "\d{5}",
1357
        "LU" => "\d{4}",
1358
        "LV" => "\d{4}",
1359
        "MA" => "\d{5}",
1360
        "MC" => "980\d{2}",
1361
        "MD" => "\d{4}",
1362
        "ME" => "8\d{4}",
1363
        "MG" => "\d{3}",
1364
        "MH" => "969[67]\d([ \-]\d{4})?",
1365
        "MK" => "\d{4}",
1366
        "MN" => "\d{6}",
1367
        "MP" => "9695[012]([ \-]\d{4})?",
1368
        "MQ" => "9[78]2\d{2}",
1369
        "MT" => "[A-Z]{3}[ ]?\d{2,4}",
1370
        "MU" => "(\d{3}[A-Z]{2}\d{3})?",
1371
        "MV" => "\d{5}",
1372
        "MX" => "\d{5}",
1373
        "MY" => "\d{5}",
1374
        "NC" => "988\d{2}",
1375
        "NE" => "\d{4}",
1376
        "NF" => "2899",
1377
        "NG" => "(\d{6})?",
1378
        "NI" => "((\d{4}-)?\d{3}-\d{3}(-\d{1})?)?",
1379
        "NL" => "^[1-9][0-9]{3}\s?([a-zA-Z]{2})?$",
1380
        "NO" => "\d{4}",
1381
        "NP" => "\d{5}",
1382
        "NZ" => "\d{4}",
1383
        "OM" => "(PC )?\d{3}",
1384
        "PF" => "987\d{2}",
1385
        "PG" => "\d{3}",
1386
        "PH" => "\d{4}",
1387
        "PK" => "\d{5}",
1388
        "PL" => "\d{2}-\d{3}",
1389
        "PM" => "9[78]5\d{2}",
1390
        "PN" => "PCRN 1ZZ",
1391
        "PR" => "00[679]\d{2}([ \-]\d{4})?",
1392
        "PT" => "\d{4}([\-]\d{3})?",
1393
        "PW" => "96940",
1394
        "PY" => "\d{4}",
1395
        "RE" => "9[78]4\d{2}",
1396
        "RO" => "\d{6}",
1397
        "RS" => "\d{5}",
1398
        "RU" => "\d{6}",
1399
        "SA" => "\d{5}",
1400
        "SE" => "^(s-|S-){0,1}[0-9]{3}\s?[0-9]{2}$",
1401
        "SG" => "\d{6}",
1402
        "SH" => "(ASCN|STHL) 1ZZ",
1403
        "SI" => "\d{4}",
1404
        "SJ" => "\d{4}",
1405
        "SK" => "\d{3}[ ]?\d{2}",
1406
        "SM" => "4789\d",
1407
        "SN" => "\d{5}",
1408
        "SO" => "\d{5}",
1409
        "SZ" => "[HLMS]\d{3}",
1410
        "TC" => "TKCA 1ZZ",
1411
        "TH" => "\d{5}",
1412
        "TJ" => "\d{6}",
1413
        "TM" => "\d{6}",
1414
        "TN" => "\d{4}",
1415
        "TR" => "\d{5}",
1416
        "TW" => "\d{3}(\d{2})?",
1417
        "UA" => "\d{5}",
1418
        "UK" => "^(GIR|[A-Z]\d[A-Z\d]??|[A-Z]{2}\d[A-Z\d]??)[ ]??(\d[A-Z]{2})$",
1419
        "US" => "^\d{5}([\-]?\d{4})?$",
1420
        "UY" => "\d{5}",
1421
        "UZ" => "\d{6}",
1422
        "VA" => "00120",
1423
        "VE" => "\d{4}",
1424
        "VI" => "008(([0-4]\d)|(5[01]))([ \-]\d{4})?",
1425
        "WF" => "986\d{2}",
1426
        "YT" => "976\d{2}",
1427
        "YU" => "\d{5}",
1428
        "ZA" => "\d{4}",
1429
        "ZM" => "\d{5}"
1430
    );
1431
1432
    if ( ! isset ( $zip_regex[ $country_code ] ) || preg_match( "/" . $zip_regex[ $country_code ] . "/i", $zip ) )
1433
        $ret = true;
1434
1435
    return apply_filters( 'wpinv_is_zip_valid', $ret, $zip, $country_code );
1436
}
1437
1438
function wpinv_checkout_validate_agree_to_terms() {
1439
    // Validate agree to terms
1440
    if ( ! isset( $_POST['wpi_agree_to_terms'] ) || $_POST['wpi_agree_to_terms'] != 1 ) {
1441
        // User did not agree
1442
        wpinv_set_error( 'agree_to_terms', apply_filters( 'wpinv_agree_to_terms_text', __( 'You must agree to the terms of use', 'invoicing' ) ) );
1443
    }
1444
}
1445
1446
function wpinv_checkout_validate_invoice_user() {
1447
    global $wpi_cart, $user_ID;
1448
1449
    if(empty($wpi_cart)){
1450
        $wpi_cart = wpinv_get_invoice_cart();
1451
    }
1452
1453
    $invoice_user = (int)$wpi_cart->get_user_id();
1454
    $valid_user_data = array(
1455
        'user_id' => $invoice_user
1456
    );
1457
1458
    // If guest checkout allowed
1459
    if ( !wpinv_require_login_to_checkout() ) {
1460
        return $valid_user_data;
1461
    }
1462
    
1463
    // Verify there is a user_ID
1464
    if ( $user_ID == $invoice_user ) {
1465
        // Get the logged in user data
1466
        $user_data = get_userdata( $user_ID );
1467
        $required_fields  = wpinv_checkout_required_fields();
1468
1469
        // Loop through required fields and show error messages
1470
         if ( !empty( $required_fields ) ) {
1471
            foreach ( $required_fields as $field_name => $value ) {
1472
                if ( empty( $_POST[ $field_name ] ) ) {
1473
                    wpinv_set_error( $value['error_id'], $value['error_message'] );
1474
                }
1475
            }
1476
        }
1477
1478
        // Verify data
1479
        if ( $user_data ) {
1480
            // Collected logged in user data
1481
            $valid_user_data = array(
1482
                'user_id'     => $user_ID,
1483
                'email'       => isset( $_POST['wpinv_email'] ) ? sanitize_email( $_POST['wpinv_email'] ) : $user_data->user_email,
1484
                'first_name'  => isset( $_POST['wpinv_first_name'] ) && ! empty( $_POST['wpinv_first_name'] ) ? sanitize_text_field( $_POST['wpinv_first_name'] ) : $user_data->first_name,
1485
                'last_name'   => isset( $_POST['wpinv_last_name'] ) && ! empty( $_POST['wpinv_last_name']  ) ? sanitize_text_field( $_POST['wpinv_last_name']  ) : $user_data->last_name,
1486
            );
1487
1488
            if ( !empty( $_POST[ 'wpinv_email' ] ) && !is_email( $_POST[ 'wpinv_email' ] ) ) {
1489
                wpinv_set_error( 'invalid_email', __( 'Please enter a valid email address', 'invoicing' ) );
1490
            }
1491
        } else {
1492
            // Set invalid user error
1493
            wpinv_set_error( 'invalid_user', __( 'The user billing information is invalid', 'invoicing' ) );
1494
        }
1495
    } else {
1496
        // Set invalid user error
1497
        wpinv_set_error( 'invalid_user_id', __( 'The invoice user id does not exist', 'invoicing' ) );
1498
    }
1499
1500
    // Return user data
1501
    return $valid_user_data;
1502
}
1503
1504
function wpinv_checkout_validate_current_user() {
1505
    global $wpi_cart;
1506
1507
    $data = array();
1508
    
1509
    if ( is_user_logged_in() ) {
1510
        if ( !wpinv_require_login_to_checkout() || ( wpinv_require_login_to_checkout() && (int)$wpi_cart->get_user_id() === (int)get_current_user_id() ) ) {
1511
            $data['user_id'] = (int)get_current_user_id();
1512
        } else {
1513
            wpinv_set_error( 'logged_in_only', __( 'You are not allowed to pay for this invoice', 'invoicing' ) );
1514
        }
1515
    } else {
1516
        // If guest checkout allowed
1517
        if ( !wpinv_require_login_to_checkout() ) {
1518
            $data['user_id'] = 0;
1519
        } else {
1520
            wpinv_set_error( 'logged_in_only', __( 'You must be logged in to pay for this invoice', 'invoicing' ) );
1521
        }
1522
    }
1523
1524
    return $data;
1525
}
1526
1527
function wpinv_checkout_form_get_user( $valid_data = array() ) {
1528
1529
    if ( !empty( $valid_data['current_user']['user_id'] ) ) {
1530
        $user = $valid_data['current_user'];
1531
    } else {
1532
        // Set the valid invoice user
1533
        $user = $valid_data['invoice_user'];
1534
    }
1535
1536
    // Verify invoice have an user
1537
    if ( false === $user || empty( $user ) ) {
1538
        return false;
1539
    }
1540
1541
    // Save checkout fields.
1542
    $checkout_fields = wpinv_get_checkout_fields();
1543
    foreach ( $checkout_fields as $field ) {
1544
        $key           = $field['key'];
1545
        $name          = $field['name'];
1546
        $meta_key      = '_wpinv_' . $key;
1547
        $value         = isset( $_POST[ $name ] ) ? sanitize_text_field( $_POST[ $name ] ) : null;
1548
        $user[ $key ]  = $value;
1549
1550
        if ( ! empty( $user['user_id'] ) && ! empty( $valid_data['current_user']['user_id'] ) && $valid_data['current_user']['user_id'] == $valid_data['invoice_user']['user_id'] ) {
1551
1552
            if ( isset( $_POST[ $name ] ) ) {
1553
                update_user_meta( $user['user_id'], $meta_key, $value );
1554
            } else {
1555
                $user[ $key ] = get_user_meta( $user['user_id'], $meta_key, true );
1556
            }
1557
1558
        }
1559
    }
1560
1561
    // Ensure some fields are set; for backwards compatibilty.
1562
    $address_fields = array(
1563
        'first_name',
1564
        'last_name',
1565
        'company',
1566
        'vat_number',
1567
        'phone',
1568
        'address',
1569
        'city',
1570
        'state',
1571
        'country',
1572
        'zip',
1573
    );
1574
1575
    foreach ( $address_fields as $field ) {
1576
        if ( ! isset( $user[$field] ) && ! empty( $user['user_id'] ) ) {
1577
            $user[$field] = get_user_meta( $user['user_id'], '_wpinv_' . $field, true );
1578
        }
1579
    }
1580
1581
    // Return valid user
1582
    return $user;
1583
}
1584
1585
function wpinv_set_checkout_session( $invoice_data = array() ) {
1586
    global $wpi_session;
1587
    
1588
    return $wpi_session->set( 'wpinv_checkout', $invoice_data );
1589
}
1590
1591
function wpinv_get_checkout_session() {
1592
	global $wpi_session;
1593
    
1594
    return $wpi_session->get( 'wpinv_checkout' );
1595
}
1596
1597
function wpinv_empty_cart() {
1598
    global $wpi_session;
1599
1600
    // Remove cart contents
1601
    $wpi_session->set( 'wpinv_checkout', NULL );
1602
1603
    // Remove all cart fees
1604
    $wpi_session->set( 'wpi_cart_fees', NULL );
1605
1606
    do_action( 'wpinv_empty_cart' );
1607
}
1608
1609
/**
1610
 * Returns user info fields.
1611
 */
1612
function wpinv_get_userinfo_fields() {
1613
    return wp_list_pluck( wpinv_get_checkout_fields(), 'key' );
1614
}
1615
1616
/**
1617
 * Returns the default address fields.
1618
 * 
1619
 * This are usually added directly to the invoice object and the user_info key of the invoice object.
1620
 */
1621
function wpinv_get_default_address_fields() {
1622
    return array( 'first_name', 'last_name', 'phone', 'address', 'city', 'country', 'state', 'zip', 'company' );
1623
}
1624
1625
/**
1626
 * Returns the custom address fields which are only accessible via the user_info key of the invoice object.
1627
 */
1628
function wpinv_get_custom_userinfo_fields() {
1629
    return array_diff( wpinv_get_userinfo_fields(), wpinv_get_default_address_fields() );
1630
}
1631
1632
function wpinv_process_checkout() {
1633
    global $wpinv_euvat, $wpi_checkout_id, $wpi_cart;
1634
    
1635
    wpinv_clear_errors();
1636
    
1637
    $invoice = wpinv_get_invoice_cart();
1638
    if ( empty( $invoice ) ) {
1639
        return false;
1640
    }
1641
    
1642
    $wpi_cart = $invoice;
1643
    
1644
    $wpi_checkout_id = $invoice->ID;
1645
    
1646
    do_action( 'wpinv_pre_process_checkout' );
1647
    
1648
    if ( !wpinv_get_cart_contents() ) { // Make sure the cart isn't empty
1649
        $valid_data = false;
1650
        wpinv_set_error( 'empty_cart', __( 'Your cart is empty', 'invoicing' ) );
1651
    } else {
1652
        // Validate the form $_POST data
1653
        $valid_data = wpinv_validate_checkout_fields();
1654
1655
        // Allow themes and plugins to hook to errors
1656
        do_action( 'wpinv_checkout_error_checks', $valid_data, $_POST );
1657
    }
1658
1659
    $is_ajax    = defined( 'DOING_AJAX' ) && DOING_AJAX;
1660
    
1661
    // Validate the user
1662
    $user = wpinv_checkout_form_get_user( $valid_data );
1663
1664
    // Let extensions validate fields after user is logged in if user has used login/registration form
1665
    do_action( 'wpinv_checkout_user_error_checks', $user, $valid_data, $_POST );
1666
    
1667
    if ( false === $valid_data || wpinv_get_errors() || ! $user ) {
1668
        if ( $is_ajax ) {
1669
            do_action( 'wpinv_ajax_checkout_errors' );
1670
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1671
        } else {
1672
            return false;
1673
        }
1674
    }
1675
1676
    if ( $is_ajax ) {
1677
1678
        // Save address fields.
1679
        $address_fields = wpinv_get_default_address_fields();
1680
        $custom_fields  = wpinv_get_custom_userinfo_fields();
1681
        $address_fields = $address_fields + $custom_fields;
1682
1683
        foreach ( $address_fields as $field ) {
1684
            if ( isset( $user[$field] ) ) {
1685
                $invoice->set( $field, $user[$field] );
1686
            }
1687
1688
            $invoice->save();
1689
        }
1690
1691
        $response['success']            = true;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$response was never initialized. Although not strictly required by PHP, it is generally a good practice to add $response = array(); before regardless.
Loading history...
1692
        $response['data']['subtotal']   = $invoice->get_subtotal();
1693
        $response['data']['subtotalf']  = $invoice->get_subtotal( true );
1694
        $response['data']['discount']   = $invoice->get_discount();
1695
        $response['data']['discountf']  = $invoice->get_discount( true );
1696
        $response['data']['tax']        = $invoice->get_tax();
1697
        $response['data']['taxf']       = $invoice->get_tax( true );
1698
        $response['data']['total']      = $invoice->get_total();
1699
        $response['data']['totalf']     = $invoice->get_total( true );
1700
	    $response['data']['free']       = $invoice->is_free() && ( ! ( (float) $response['data']['total'] > 0 ) || $invoice->is_free_trial() ) ? true : false;
1701
1702
        wp_send_json( $response );
1703
    }
1704
    
1705
    $user_info = array(
1706
        'user_id'        => $user['user_id'],
1707
        'first_name'     => $user['first_name'],
1708
        'last_name'      => $user['last_name'],
1709
        'email'          => $invoice->get_email(),
1710
        'company'        => $user['company'],
1711
        'phone'          => $user['phone'],
1712
        'address'        => $user['address'],
1713
        'city'           => $user['city'],
1714
        'country'        => $user['country'],
1715
        'state'          => $user['state'],
1716
        'zip'            => $user['zip'],
1717
    );
1718
    
1719
    foreach ( $custom_fields as $field ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $custom_fields does not seem to be defined for all execution paths leading up to this point.
Loading history...
1720
        $user_info[ $field ] = $user[ $field ];
1721
    }
1722
1723
    $cart_items = wpinv_get_cart_contents();
1724
    $discounts  = wpinv_get_cart_discounts();
1725
    
1726
    // Setup invoice information
1727
    $invoice_data = array(
1728
        'invoice_id'        => !empty( $invoice ) ? $invoice->ID : 0,
1729
        'items'             => $cart_items,
1730
        'cart_discounts'    => $discounts,
1731
        'fees'              => wpinv_get_cart_fees(),        // Any arbitrary fees that have been added to the cart
1732
        'subtotal'          => wpinv_get_cart_subtotal( $cart_items ),    // Amount before taxes and discounts
1733
        'discount'          => wpinv_get_cart_items_discount_amount( $cart_items, $discounts ), // Discounted amount
1734
        'tax'               => wpinv_get_cart_tax( $cart_items ),               // Taxed amount
1735
        'price'             => wpinv_get_cart_total( $cart_items, $discounts ),    // Amount after taxes
1736
        'invoice_key'       => $invoice->get_key() ? $invoice->get_key() : $invoice->generate_key(),
1737
        'user_email'        => $invoice->get_email(),
1738
        'date'              => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ),
1739
        'user_info'         => stripslashes_deep( $user_info ),
1740
        'post_data'         => $_POST,
1741
        'cart_details'      => $cart_items,
1742
        'gateway'           => $valid_data['gateway'],
1743
        'card_info'         => $valid_data['cc_info']
1744
    );
1745
    
1746
    $vat_info   = $wpinv_euvat->current_vat_data();
1747
    if ( is_array( $vat_info ) ) {
1748
        $invoice_data['user_info']['vat_number']        = $vat_info['number'];
1749
        $invoice_data['user_info']['vat_rate']          = wpinv_get_tax_rate($invoice_data['user_info']['country'], $invoice_data['user_info']['state']);
1750
        $invoice_data['user_info']['adddress_confirmed']    = isset($vat_info['adddress_confirmed']) ? $vat_info['adddress_confirmed'] : false;
1751
1752
        // Add the VAT rate to each item in the cart
1753
        foreach( $invoice_data['cart_details'] as $key => $item_data) {
1754
            $rate = wpinv_get_tax_rate($invoice_data['user_info']['country'], $invoice_data['user_info']['state'], $item_data['id']);
1755
            $invoice_data['cart_details'][$key]['vat_rate'] = wpinv_round_amount( $rate, 4 );
1756
        }
1757
    }
1758
    
1759
    // Save vat fields.
1760
    $address_fields = array( 'vat_number', 'vat_rate', 'adddress_confirmed' );
1761
    foreach ( $address_fields as $field ) {
1762
        if ( isset( $invoice_data['user_info'][$field] ) ) {
1763
            $invoice->set( $field, $invoice_data['user_info'][$field] );
1764
        }
1765
1766
        $invoice->save();
1767
    }
1768
1769
    // Add the user data for hooks
1770
    $valid_data['user'] = $user;
1771
    
1772
    // Allow themes and plugins to hook before the gateway
1773
    do_action( 'wpinv_checkout_before_gateway', $_POST, $user_info, $valid_data );
1774
    
1775
    // If the total amount in the cart is 0, send to the manual gateway. This emulates a free invoice
1776
    if ( !$invoice_data['price'] ) {
1777
        // Revert to manual
1778
        $invoice_data['gateway'] = 'manual';
1779
        $_POST['wpi-gateway'] = 'manual';
1780
    }
1781
    
1782
    // Allow the invoice data to be modified before it is sent to the gateway
1783
    $invoice_data = apply_filters( 'wpinv_data_before_gateway', $invoice_data, $valid_data );
1784
    
1785
    if ( $invoice_data['price'] && $invoice_data['gateway'] == 'manual' ) {
1786
        $mode = 'test';
1787
    } else {
1788
        $mode = wpinv_is_test_mode( $invoice_data['gateway'] ) ? 'test' : 'live';
1789
    }
1790
    
1791
    // Setup the data we're storing in the purchase session
1792
    $session_data = $invoice_data;
1793
    // Make sure credit card numbers are never stored in sessions
1794
    if ( !empty( $session_data['card_info']['card_number'] ) ) {
1795
        unset( $session_data['card_info']['card_number'] );
1796
    }
1797
    
1798
    // Used for showing item links to non logged-in users after purchase, and for other plugins needing purchase data.
1799
    wpinv_set_checkout_session( $invoice_data );
1800
    
1801
    // Set gateway
1802
    $invoice->update_meta( '_wpinv_gateway', $invoice_data['gateway'] );
1803
    $invoice->update_meta( '_wpinv_mode', $mode );
1804
    $invoice->update_meta( '_wpinv_checkout', date_i18n( 'Y-m-d H:i:s', current_time( 'timestamp' ) ) );
1805
    
1806
    do_action( 'wpinv_checkout_before_send_to_gateway', $invoice, $invoice_data );
1807
1808
    // Send info to the gateway for payment processing
1809
    wpinv_send_to_gateway( $invoice_data['gateway'], $invoice_data );
1810
    die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1811
}
1812
add_action( 'wpinv_payment', 'wpinv_process_checkout' );
1813
1814
function wpinv_get_invoices( $args ) {
1815
    $args = wp_parse_args( $args, array(
1816
        'status'   => array_keys( wpinv_get_invoice_statuses() ),
1817
        'type'     => 'wpi_invoice',
1818
        'parent'   => null,
1819
        'user'     => null,
1820
        'email'    => '',
1821
        'limit'    => get_option( 'posts_per_page' ),
1822
        'offset'   => null,
1823
        'page'     => 1,
1824
        'exclude'  => array(),
1825
        'orderby'  => 'date',
1826
        'order'    => 'DESC',
1827
        'return'   => 'objects',
1828
        'paginate' => false,
1829
    ) );
1830
    
1831
    // Handle some BW compatibility arg names where wp_query args differ in naming.
1832
    $map_legacy = array(
1833
        'numberposts'    => 'limit',
1834
        'post_type'      => 'type',
1835
        'post_status'    => 'status',
1836
        'post_parent'    => 'parent',
1837
        'author'         => 'user',
1838
        'posts_per_page' => 'limit',
1839
        'paged'          => 'page',
1840
    );
1841
1842
    foreach ( $map_legacy as $from => $to ) {
1843
        if ( isset( $args[ $from ] ) ) {
1844
            $args[ $to ] = $args[ $from ];
1845
        }
1846
    }
1847
1848
    if ( get_query_var( 'paged' ) )
1849
        $args['page'] = get_query_var('paged');
1850
    else if ( get_query_var( 'page' ) )
1851
        $args['page'] = get_query_var( 'page' );
1852
    else if ( !empty( $args[ 'page' ] ) )
1853
        $args['page'] = $args[ 'page' ];
1854
    else
1855
        $args['page'] = 1;
1856
1857
    /**
1858
     * Generate WP_Query args. This logic will change if orders are moved to
1859
     * custom tables in the future.
1860
     */
1861
    $wp_query_args = array(
1862
        'post_type'      => 'wpi_invoice',
1863
        'post_status'    => $args['status'],
1864
        'posts_per_page' => $args['limit'],
1865
        'meta_query'     => array(),
1866
        'date_query'     => !empty( $args['date_query'] ) ? $args['date_query'] : array(),
1867
        'fields'         => 'ids',
1868
        'orderby'        => $args['orderby'],
1869
        'order'          => $args['order'],
1870
    );
1871
    
1872
    if ( !empty( $args['user'] ) ) {
1873
        $wp_query_args['author'] = absint( $args['user'] );
1874
    }
1875
1876
    if ( ! is_null( $args['parent'] ) ) {
1877
        $wp_query_args['post_parent'] = absint( $args['parent'] );
1878
    }
1879
1880
    if ( ! is_null( $args['offset'] ) ) {
1881
        $wp_query_args['offset'] = absint( $args['offset'] );
1882
    } else {
1883
        $wp_query_args['paged'] = absint( $args['page'] );
1884
    }
1885
1886
    if ( ! empty( $args['exclude'] ) ) {
1887
        $wp_query_args['post__not_in'] = array_map( 'absint', $args['exclude'] );
1888
    }
1889
1890
    if ( ! $args['paginate' ] ) {
1891
        $wp_query_args['no_found_rows'] = true;
1892
    }
1893
1894
    $wp_query_args = apply_filters('wpinv_get_invoices_args', $wp_query_args, $args);
1895
1896
    // Get results.
1897
    $invoices = new WP_Query( $wp_query_args );
1898
1899
    if ( 'objects' === $args['return'] ) {
1900
        $return = array_map( 'wpinv_get_invoice', $invoices->posts );
1901
    } elseif ( 'self' === $args['return'] ) {
1902
        return $invoices;
1903
    } else {
1904
        $return = $invoices->posts;
1905
    }
1906
1907
    if ( $args['paginate' ] ) {
1908
        return (object) array(
1909
            'invoices'      => $return,
1910
            'total'         => $invoices->found_posts,
1911
            'max_num_pages' => $invoices->max_num_pages,
1912
        );
1913
    } else {
1914
        return $return;
1915
    }
1916
}
1917
1918
function wpinv_get_user_invoices_columns() {
1919
    $columns = array(
1920
            'invoice-number'  => array( 'title' => __( 'ID', 'invoicing' ), 'class' => 'text-left' ),
1921
            'created-date'    => array( 'title' => __( 'Created Date', 'invoicing' ), 'class' => 'text-left' ),
1922
            'payment-date'    => array( 'title' => __( 'Payment Date', 'invoicing' ), 'class' => 'text-left' ),
1923
            'invoice-status'  => array( 'title' => __( 'Status', 'invoicing' ), 'class' => 'text-center' ),
1924
            'invoice-total'   => array( 'title' => __( 'Total', 'invoicing' ), 'class' => 'text-right' ),
1925
            'invoice-actions' => array( 'title' => '&nbsp;', 'class' => 'text-center' ),
1926
        );
1927
1928
    return apply_filters( 'wpinv_user_invoices_columns', $columns );
1929
}
1930
1931
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. ( Ignorable by Annotation )

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

1931
function wpinv_payment_receipt( $atts, /** @scrutinizer ignore-unused */ $content = null ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1932
    global $wpinv_receipt_args;
1933
1934
    $wpinv_receipt_args = shortcode_atts( array(
1935
        'error'           => __( 'Sorry, trouble retrieving payment receipt.', 'invoicing' ),
1936
        'price'           => true,
1937
        'discount'        => true,
1938
        'items'           => true,
1939
        'date'            => true,
1940
        'notes'           => true,
1941
        'invoice_key'     => false,
1942
        'payment_method'  => true,
1943
        'invoice_id'      => true
1944
    ), $atts, 'wpinv_receipt' );
1945
1946
    $session = wpinv_get_checkout_session();
1947
    if ( isset( $_GET['invoice_key'] ) ) {
1948
        $invoice_key = urldecode( $_GET['invoice_key'] );
1949
    } else if ( $session && isset( $session['invoice_key'] ) ) {
1950
        $invoice_key = $session['invoice_key'];
1951
    } elseif ( isset( $wpinv_receipt_args['invoice_key'] ) && $wpinv_receipt_args['invoice_key'] ) {
1952
        $invoice_key = $wpinv_receipt_args['invoice_key'];
1953
    } else if ( isset( $_GET['invoice-id'] ) ) {
1954
        $invoice_key = wpinv_get_payment_key( (int)$_GET['invoice-id'] );
1955
    }
1956
1957
    // No key found
1958
    if ( ! isset( $invoice_key ) ) {
1959
        return '<p class="alert alert-error">' . $wpinv_receipt_args['error'] . '</p>';
1960
    }
1961
1962
    $invoice_id    = wpinv_get_invoice_id_by_key( $invoice_key );
0 ignored issues
show
Unused Code introduced by
The assignment to $invoice_id is dead and can be removed.
Loading history...
1963
    $user_can_view = wpinv_can_view_receipt( $invoice_key );
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $invoice_key does not seem to be defined for all execution paths leading up to this point.
Loading history...
1964
    if ( $user_can_view && isset( $_GET['invoice-id'] ) ) {
1965
        $invoice_id     = (int)$_GET['invoice-id'];
1966
        $user_can_view  = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? true : false;
1967
    }
1968
1969
    // Key was provided, but user is logged out. Offer them the ability to login and view the receipt
1970
    if ( ! $user_can_view && ! empty( $invoice_key ) && ! is_user_logged_in() ) {
1971
        // login redirect
1972
        return '<p class="alert alert-error">' . __( 'You are not allowed to access this section', 'invoicing' ) . '</p>';
1973
    }
1974
1975
    if ( ! apply_filters( 'wpinv_user_can_view_receipt', $user_can_view, $wpinv_receipt_args ) ) {
1976
        return '<p class="alert alert-error">' . $wpinv_receipt_args['error'] . '</p>';
1977
    }
1978
1979
    ob_start();
1980
1981
    wpinv_get_template_part( 'wpinv-invoice-receipt' );
1982
1983
    $display = ob_get_clean();
1984
1985
    return $display;
1986
}
1987
1988
function wpinv_get_invoice_id_by_key( $key ) {
1989
	global $wpdb;
1990
1991
	$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 ) );
1992
1993
	if ( $invoice_id != NULL )
1994
		return $invoice_id;
1995
1996
	return 0;
1997
}
1998
1999
function wpinv_can_view_receipt( $invoice_key = '' ) {
2000
	$return = false;
2001
2002
	if ( empty( $invoice_key ) ) {
2003
		return $return;
2004
	}
2005
2006
	global $wpinv_receipt_args;
2007
2008
	$wpinv_receipt_args['id'] = wpinv_get_invoice_id_by_key( $invoice_key );
2009
	if ( isset( $_GET['invoice-id'] ) ) {
2010
		$wpinv_receipt_args['id'] = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? (int)$_GET['invoice-id'] : 0;
2011
	}
2012
2013
	if ( empty( $wpinv_receipt_args['id'] ) ) {
2014
		return $return;
2015
	}
2016
2017
	$invoice = wpinv_get_invoice( $wpinv_receipt_args['id'] );
2018
	if ( !( !empty( $invoice->ID ) && $invoice->get_key() === $invoice_key ) ) {
2019
		return $return;
2020
	}
2021
2022
	if ( is_user_logged_in() ) {
2023
		if ( (int)$invoice->get_user_id() === (int) get_current_user_id() ) {
2024
			$return = true;
2025
		}
2026
	}
2027
2028
	$session = wpinv_get_checkout_session();
2029
	if ( isset( $_GET['invoice_key'] ) || ( $session && isset( $session['invoice_key'] ) ) ) {
2030
		$check_key = isset( $_GET['invoice_key'] ) ? $_GET['invoice_key'] : $session['invoice_key'];
2031
2032
		if ( wpinv_require_login_to_checkout() ) {
2033
			$return = $return && $check_key === $invoice_key;
2034
		} else {
2035
			$return = $check_key === $invoice_key;
2036
		}
2037
	}
2038
2039
	return (bool) apply_filters( 'wpinv_can_view_receipt', $return, $invoice_key );
2040
}
2041
2042
function wpinv_pay_for_invoice() {
2043
    global $wpinv_euvat;
2044
    
2045
    if ( isset( $_GET['invoice_key'] ) ) {
2046
        $checkout_uri   = wpinv_get_checkout_uri();
2047
        $invoice_key    = sanitize_text_field( $_GET['invoice_key'] );
2048
        
2049
        if ( empty( $invoice_key ) ) {
2050
            wpinv_set_error( 'invalid_invoice', __( 'Invoice not found', 'invoicing' ) );
2051
            wp_redirect( $checkout_uri );
0 ignored issues
show
Bug introduced by
It seems like $checkout_uri can also be of type false; however, parameter $location of wp_redirect() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

2051
            wp_redirect( /** @scrutinizer ignore-type */ $checkout_uri );
Loading history...
2052
            exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2053
        }
2054
        
2055
        do_action( 'wpinv_check_pay_for_invoice', $invoice_key );
2056
2057
        $invoice_id    = wpinv_get_invoice_id_by_key( $invoice_key );
2058
        $user_can_view = wpinv_can_view_receipt( $invoice_key );
2059
        if ( $user_can_view && isset( $_GET['invoice-id'] ) ) {
2060
            $invoice_id     = (int)$_GET['invoice-id'];
2061
            $user_can_view  = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? true : false;
2062
        }
2063
        
2064
        if ( $invoice_id && $user_can_view && ( $invoice = wpinv_get_invoice( $invoice_id ) ) ) {
2065
            if ( $invoice->needs_payment() ) {
2066
                $data                   = array();
2067
                $data['invoice_id']     = $invoice_id;
2068
                $data['cart_discounts'] = $invoice->get_discounts( true );
2069
                
2070
                wpinv_set_checkout_session( $data );
2071
                
2072
                if ( wpinv_get_option( 'vat_ip_country_default' ) ) {
2073
                    $_POST['country']   = $wpinv_euvat->get_country_by_ip();
2074
                    $_POST['state']     = $_POST['country'] == $invoice->country ? $invoice->state : '';
2075
                    
2076
                    wpinv_recalculate_tax( true );
2077
                }
2078
                
2079
            } else {
2080
                $checkout_uri = $invoice->get_view_url();
2081
            }
2082
        } else {
2083
            wpinv_set_error( 'invalid_invoice', __( 'You are not allowed to view this invoice', 'invoicing' ) );
2084
            
2085
            $checkout_uri = is_user_logged_in() ? wpinv_get_history_page_uri() : wp_login_url( get_permalink() );
0 ignored issues
show
Bug introduced by
It seems like get_permalink() can also be of type false; however, parameter $redirect of wp_login_url() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

2085
            $checkout_uri = is_user_logged_in() ? wpinv_get_history_page_uri() : wp_login_url( /** @scrutinizer ignore-type */ get_permalink() );
Loading history...
2086
        }
2087
        
2088
        if(wp_redirect( $checkout_uri )){
2089
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2090
        };
2091
        wpinv_die();
2092
    }
2093
}
2094
add_action( 'wpinv_pay_for_invoice', 'wpinv_pay_for_invoice' );
2095
2096
function wpinv_handle_pay_via_invoice_link( $invoice_key ) {
2097
    if ( !empty( $invoice_key ) && !empty( $_REQUEST['_wpipay'] ) && !is_user_logged_in() && $invoice_id = wpinv_get_invoice_id_by_key( $invoice_key ) ) {
2098
        if ( $invoice = wpinv_get_invoice( $invoice_id ) ) {
2099
            $user_id = $invoice->get_user_id();
2100
            $secret = sanitize_text_field( $_GET['_wpipay'] );
2101
            
2102
            if ( $secret === md5( $user_id . '::' . $invoice->get_email() . '::' . $invoice_key ) ) { // valid invoice link
2103
                $redirect_to = remove_query_arg( '_wpipay', get_permalink() );
2104
                
2105
                wpinv_guest_redirect( $redirect_to, $user_id );
2106
                exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2107
            }
2108
        }
2109
    }
2110
}
2111
add_action( 'wpinv_check_pay_for_invoice', 'wpinv_handle_pay_via_invoice_link' );
2112
2113
function wpinv_set_payment_transaction_id( $invoice_id = 0, $transaction_id = '' ) {
2114
    $invoice_id = is_object( $invoice_id ) && !empty( $invoice_id->ID ) ? $invoice_id : $invoice_id;
2115
    
2116
    if ( empty( $invoice_id ) && $invoice_id > 0 ) {
2117
        return false;
2118
    }
2119
    
2120
    if ( empty( $transaction_id ) ) {
2121
        $transaction_id = $invoice_id;
2122
    }
2123
2124
    $transaction_id = apply_filters( 'wpinv_set_payment_transaction_id', $transaction_id, $invoice_id );
2125
    
2126
    return wpinv_update_invoice_meta( $invoice_id, '_wpinv_transaction_id', $transaction_id );
2127
}
2128
2129
function wpinv_invoice_status_label( $status, $status_display = '' ) {
2130
    if ( empty( $status_display ) ) {
2131
        $status_display = wpinv_status_nicename( $status );
2132
    }
2133
    
2134
    switch ( $status ) {
2135
        case 'publish' :
2136
        case 'wpi-renewal' :
2137
            $class = 'label-success';
2138
        break;
2139
        case 'wpi-pending' :
2140
            $class = 'label-primary';
2141
        break;
2142
        case 'wpi-processing' :
2143
            $class = 'label-warning';
2144
        break;
2145
        case 'wpi-onhold' :
2146
            $class = 'label-info';
2147
        break;
2148
        case 'wpi-cancelled' :
2149
        case 'wpi-failed' :
2150
            $class = 'label-danger';
2151
        break;
2152
        default:
2153
            $class = 'label-default';
2154
        break;
2155
    }
2156
    
2157
    $label = '<span class="label label-inv-' . $status . ' ' . $class . '">' . $status_display . '</span>';
2158
    
2159
    return apply_filters( 'wpinv_invoice_status_label', $label, $status, $status_display );
2160
}
2161
2162
function wpinv_format_invoice_number( $number, $type = '' ) {
2163
    $check = apply_filters( 'wpinv_pre_format_invoice_number', null, $number, $type );
2164
    if ( null !== $check ) {
2165
        return $check;
2166
    }
2167
2168
    if ( !empty( $number ) && !is_numeric( $number ) ) {
2169
        return $number;
2170
    }
2171
2172
    $padd  = wpinv_get_option( 'invoice_number_padd' );
2173
    $prefix  = wpinv_get_option( 'invoice_number_prefix' );
2174
    $postfix = wpinv_get_option( 'invoice_number_postfix' );
2175
    
2176
    $padd = absint( $padd );
2177
    $formatted_number = absint( $number );
2178
    
2179
    if ( $padd > 0 ) {
2180
        $formatted_number = zeroise( $formatted_number, $padd );
2181
    }    
2182
2183
    $formatted_number = $prefix . $formatted_number . $postfix;
0 ignored issues
show
Bug introduced by
Are you sure $postfix of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

2183
    $formatted_number = $prefix . $formatted_number . /** @scrutinizer ignore-type */ $postfix;
Loading history...
Bug introduced by
Are you sure $prefix of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

2183
    $formatted_number = /** @scrutinizer ignore-type */ $prefix . $formatted_number . $postfix;
Loading history...
2184
2185
    return apply_filters( 'wpinv_format_invoice_number', $formatted_number, $number, $prefix, $postfix, $padd );
2186
}
2187
2188
function wpinv_get_next_invoice_number( $type = '' ) {
2189
    $check = apply_filters( 'wpinv_get_pre_next_invoice_number', null, $type );
2190
    if ( null !== $check ) {
2191
        return $check;
2192
    }
2193
    
2194
    if ( !wpinv_sequential_number_active() ) {
2195
        return false;
2196
    }
2197
2198
    $number = $last_number = get_option( 'wpinv_last_invoice_number', 0 );
2199
    $start  = wpinv_get_option( 'invoice_sequence_start', 1 );
2200
    if ( !absint( $start ) > 0 ) {
2201
        $start = 1;
2202
    }
2203
    $increment_number = true;
2204
    $save_number = false;
2205
2206
    if ( !empty( $number ) && !is_numeric( $number ) && $number == wpinv_format_invoice_number( $number ) ) {
2207
        $number = wpinv_clean_invoice_number( $number );
2208
    }
2209
2210
    if ( empty( $number ) ) {
2211
        if ( !( $last_number === 0 || $last_number === '0' ) ) {
2212
            $last_invoice = wpinv_get_invoices( array( 'limit' => 1, 'order' => 'DESC', 'orderby' => 'ID', 'return' => 'posts', 'fields' => 'ids', 'status' => array_keys( wpinv_get_invoice_statuses( true, true ) ) ) );
2213
2214
            if ( !empty( $last_invoice[0] ) && $invoice_number = wpinv_get_invoice_number( $last_invoice[0] ) ) {
2215
                if ( is_numeric( $invoice_number ) ) {
2216
                    $number = $invoice_number;
2217
                } else {
2218
                    $number = wpinv_clean_invoice_number( $invoice_number );
2219
                }
2220
            }
2221
2222
            if ( empty( $number ) ) {
2223
                $increment_number = false;
2224
                $number = $start;
2225
                $save_number = ( $number - 1 );
2226
            } else {
2227
                $save_number = $number;
2228
            }
2229
        }
2230
    }
2231
2232
    if ( $start > $number ) {
2233
        $increment_number = false;
2234
        $number = $start;
2235
        $save_number = ( $number - 1 );
2236
    }
2237
2238
    if ( $save_number !== false ) {
2239
        update_option( 'wpinv_last_invoice_number', $save_number );
2240
    }
2241
    
2242
    $increment_number = apply_filters( 'wpinv_increment_payment_number', $increment_number, $number );
2243
2244
    if ( $increment_number ) {
2245
        $number++;
2246
    }
2247
2248
    return apply_filters( 'wpinv_get_next_invoice_number', $number );
2249
}
2250
2251
function wpinv_clean_invoice_number( $number, $type = '' ) {
2252
    $check = apply_filters( 'wpinv_pre_clean_invoice_number', null, $number, $type );
2253
    if ( null !== $check ) {
2254
        return $check;
2255
    }
2256
    
2257
    $prefix  = wpinv_get_option( 'invoice_number_prefix' );
2258
    $postfix = wpinv_get_option( 'invoice_number_postfix' );
2259
2260
    $number = preg_replace( '/' . $prefix . '/', '', $number, 1 );
0 ignored issues
show
Bug introduced by
Are you sure $prefix of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

2260
    $number = preg_replace( '/' . /** @scrutinizer ignore-type */ $prefix . '/', '', $number, 1 );
Loading history...
2261
2262
    $length      = strlen( $number );
2263
    $postfix_pos = strrpos( $number, $postfix );
0 ignored issues
show
Bug introduced by
It seems like $postfix can also be of type false; however, parameter $needle of strrpos() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

2263
    $postfix_pos = strrpos( $number, /** @scrutinizer ignore-type */ $postfix );
Loading history...
2264
    
2265
    if ( false !== $postfix_pos ) {
2266
        $number      = substr_replace( $number, '', $postfix_pos, $length );
2267
    }
2268
2269
    $number = intval( $number );
2270
2271
    return apply_filters( 'wpinv_clean_invoice_number', $number, $prefix, $postfix );
2272
}
2273
2274
function wpinv_save_number_post_saved( $post_ID, $post, $update ) {
2275
    global $wpdb;
2276
2277
    if ( !$update && !get_post_meta( $post_ID, '_wpinv_number', true ) ) {
2278
        wpinv_update_invoice_number( $post_ID, $post->post_status != 'auto-draft', $post->post_type );
2279
    }
2280
2281
    if ( !$update ) {
2282
        $wpdb->update( $wpdb->posts, array( 'post_name' => wpinv_generate_post_name( $post_ID ) ), array( 'ID' => $post_ID ) );
2283
        clean_post_cache( $post_ID );
2284
    }
2285
}
2286
add_action( 'save_post_wpi_invoice', 'wpinv_save_number_post_saved', 1, 3 );
2287
2288
function wpinv_save_number_post_updated( $post_ID, $post_after, $post_before ) {
2289
    if ( !empty( $post_after->post_type ) && $post_after->post_type == 'wpi_invoice' && $post_before->post_status == 'auto-draft' && $post_after->post_status != $post_before->post_status ) {
2290
        wpinv_update_invoice_number( $post_ID, true, $post_after->post_type );
2291
    }
2292
}
2293
add_action( 'post_updated', 'wpinv_save_number_post_updated', 1, 3 );
2294
2295
function wpinv_update_invoice_number( $post_ID, $save_sequential = false, $type = '' ) {
2296
    global $wpdb;
2297
    
2298
    $check = apply_filters( 'wpinv_pre_update_invoice_number', null, $post_ID, $save_sequential, $type );
2299
    if ( null !== $check ) {
2300
        return $check;
2301
    }
2302
2303
    if ( wpinv_sequential_number_active() ) {
2304
        $number = wpinv_get_next_invoice_number();
2305
2306
        if ( $save_sequential ) {
2307
            update_option( 'wpinv_last_invoice_number', $number );
2308
        }
2309
    } else {
2310
        $number = $post_ID;
2311
    }
2312
2313
    $number = wpinv_format_invoice_number( $number );
2314
2315
    update_post_meta( $post_ID, '_wpinv_number', $number );
2316
2317
    $wpdb->update( $wpdb->posts, array( 'post_title' => $number ), array( 'ID' => $post_ID ) );
2318
2319
    clean_post_cache( $post_ID );
2320
2321
    return $number;
2322
}
2323
2324
function wpinv_post_name_prefix( $post_type = 'wpi_invoice' ) {
2325
    return apply_filters( 'wpinv_post_name_prefix', 'inv-', $post_type );
2326
}
2327
2328
function wpinv_generate_post_name( $post_ID ) {
2329
    $prefix = wpinv_post_name_prefix( get_post_type( $post_ID ) );
2330
    $post_name = sanitize_title( $prefix . $post_ID );
2331
2332
    return apply_filters( 'wpinv_generate_post_name', $post_name, $post_ID, $prefix );
2333
}
2334
2335
function wpinv_is_invoice_viewed( $invoice_id ) {
2336
    if ( empty( $invoice_id ) ) {
2337
        return false;
2338
    }
2339
2340
    $viewed_meta = get_post_meta( $invoice_id, '_wpinv_is_viewed', true );
2341
2342
    return apply_filters( 'wpinv_is_invoice_viewed', 1 === (int)$viewed_meta, $invoice_id );
2343
}
2344
2345
function wpinv_mark_invoice_viewed() {
2346
2347
    if ( isset( $_GET['invoice_key'] ) || is_singular( 'wpi_invoice' ) || is_singular( 'wpi_quote' ) ) {
2348
        $invoice_key = isset( $_GET['invoice_key'] ) ? urldecode($_GET['invoice_key']) : '';
2349
	    global $post;
2350
2351
        if(!empty($invoice_key)){
2352
	        $invoice_id = wpinv_get_invoice_id_by_key($invoice_key);
2353
        } else if(!empty( $post ) && ($post->post_type == 'wpi_invoice' || $post->post_type == 'wpi_quote')) {
2354
			$invoice_id = $post->ID;
2355
        } else {
2356
        	return;
2357
        }
2358
2359
        $invoice = new WPInv_Invoice($invoice_id);
2360
2361
        if(!$invoice_id){
2362
            return;
2363
        }
2364
2365
	    if ( is_user_logged_in() ) {
2366
		    if ( (int)$invoice->get_user_id() === get_current_user_id() ) {
2367
			    update_post_meta($invoice_id,'_wpinv_is_viewed', 1);
2368
		    } else if ( !wpinv_require_login_to_checkout() && isset( $_GET['invoice_key'] ) && $_GET['invoice_key'] === $invoice->get_key() ) {
2369
			    update_post_meta($invoice_id,'_wpinv_is_viewed', 1);
2370
		    }
2371
	    } else {
2372
		    if ( !wpinv_require_login_to_checkout() && isset( $_GET['invoice_key'] ) && $_GET['invoice_key'] === $invoice->get_key() ) {
2373
			    update_post_meta($invoice_id,'_wpinv_is_viewed', 1);
2374
		    }
2375
	    }
2376
    }
2377
2378
}
2379
add_action( 'template_redirect', 'wpinv_mark_invoice_viewed' );
2380
2381
function wpinv_get_subscription( $invoice, $by_parent = false ) {
2382
    if ( empty( $invoice ) ) {
2383
        return false;
2384
    }
2385
    
2386
    if ( ! is_object( $invoice ) && is_scalar( $invoice ) ) {
2387
        $invoice = wpinv_get_invoice( $invoice );
2388
    }
2389
    
2390
    if ( !( is_object( $invoice ) && ! empty( $invoice->ID ) && $invoice->is_recurring() ) ) {
2391
        return false;
2392
    }
2393
    
2394
    $invoice_id = ! $by_parent && ! empty( $invoice->parent_invoice ) ? $invoice->parent_invoice : $invoice->ID;
2395
    
2396
    $subs_db    = new WPInv_Subscriptions_DB;
2397
    $subs       = $subs_db->get_subscriptions( array( 'parent_payment_id' => $invoice_id, 'number' => 1 ) );
2398
    
2399
    if ( ! empty( $subs ) ) {
2400
        return reset( $subs );
2401
    }
2402
    
2403
    return false;
2404
}
2405
2406
function wpinv_filter_posts_clauses( $clauses, $wp_query ) {
2407
    global $wpdb;
2408
2409
    if ( ! empty( $wp_query->query_vars['orderby'] ) && $wp_query->query_vars['orderby'] == 'invoice_date' ) {
2410
        if ( !empty( $clauses['join'] ) ) {
2411
            $clauses['join'] .= " ";
2412
        }
2413
2414
        if ( !empty( $clauses['fields'] ) ) {
2415
            $clauses['fields'] .= ", ";
2416
        }
2417
2418
        $clauses['join'] .= "LEFT JOIN {$wpdb->postmeta} ON ( {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_wpinv_completed_date' )";
2419
        $clauses['fields'] .= "IF( {$wpdb->postmeta}.meta_value, {$wpdb->postmeta}.meta_value, {$wpdb->posts}.post_date ) AS invoice_date";
2420
        $clauses['orderby'] = "invoice_date DESC, {$wpdb->posts}.post_date DESC, {$wpdb->posts}.ID DESC";
2421
    }
2422
2423
    return $clauses;
2424
}
2425
add_filter( 'posts_clauses', 'wpinv_filter_posts_clauses', 10, 2 );