Passed
Branch master (50908e)
by Stiofan
07:01
created

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

Complexity

Conditions 51
Paths > 20000

Size

Total Lines 230

Duplication

Lines 13
Ratio 5.65 %

Importance

Changes 0
Metric Value
cc 51
nc 4423715
nop 2
dl 13
loc 230
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Contains functions related to Invoicing plugin.
4
 *
5
 * @since 1.0.0
6
 * @package Invoicing
7
 */
8
 
9
// MUST have WordPress.
10
if ( !defined( 'WPINC' ) ) {
11
    exit( 'Do NOT access this file directly: ' . basename( __FILE__ ) );
12
}
13
14
function wpinv_get_invoice_cart_id() {
15
    $wpinv_checkout = wpinv_get_checkout_session();
16
    
17
    if ( !empty( $wpinv_checkout['invoice_id'] ) ) {
18
        return $wpinv_checkout['invoice_id'];
19
    }
20
    
21
    return NULL;
22
}
23
24
/**
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['ip'] ) ) {
52
        $invoice_data['ip'] = wpinv_get_ip();
53
    }
54
55
    // default invoice args, note that status is checked for validity in wpinv_create_invoice()
56
    $default_args = array(
57
        'invoice_id'    => (int)$invoice_data['invoice_id'],
58
        'user_id'       => (int)$invoice_data['user_id'],
59
        'status'        => $invoice_data['status'],
60
    );
61
62
    $invoice = wpinv_create_invoice( $default_args, $invoice_data, true );
63
    if ( is_wp_error( $invoice ) ) {
64
        return $wp_error ? $invoice : 0;
65
    }
66
    
67
    if ( empty( $invoice_data['invoice_id'] ) ) {
68
        //$invoice->add_note( wp_sprintf( __( 'Invoice is created with status %s.', 'invoicing' ), wpinv_status_nicename( $invoice->status ) ) );
69
    }
70
    
71
    // User info
72
    $default_user_info = array(
73
        'user_id'               => '',
74
        'first_name'            => '',
75
        'last_name'             => '',
76
        'email'                 => '',
77
        'company'               => '',
78
        'phone'                 => '',
79
        'address'               => '',
80
        'city'                  => '',
81
        'country'               => wpinv_get_default_country(),
82
        'state'                 => wpinv_get_default_state(),
83
        'zip'                   => '',
84
        'vat_number'            => '',
85
        'vat_rate'              => '',
86
        'adddress_confirmed'    => '',
87
        'discount'              => array(),
88
    );
89
90
    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

90
    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...
91
        if ( $user_address = wpinv_get_user_address( $user_id ) ) {
92
            $default_user_info = wp_parse_args( $user_address, $default_user_info );
93
        }
94
    }
95
    
96
    if ( empty( $invoice_data['user_info'] ) ) {
97
        $invoice_data['user_info'] = array();
98
    }
99
    
100
    $user_info = wp_parse_args( $invoice_data['user_info'], $default_user_info );
101
    
102
    if ( empty( $user_info['first_name'] ) ) {
103
        $user_info['first_name'] = $default_user_info['first_name'];
104
        $user_info['last_name'] = $default_user_info['last_name'];
105
    }
106
    
107
    if ( empty( $user_info['country'] ) ) {
108
        $user_info['country'] = $default_user_info['country'];
109
        $user_info['state'] = $default_user_info['state'];
110
        $user_info['city'] = $default_user_info['city'];
111
        $user_info['address'] = $default_user_info['address'];
112
        $user_info['zip'] = $default_user_info['zip'];
113
        $user_info['phone'] = $default_user_info['phone'];
114
    }
115
    
116
    if ( !empty( $user_info['discount'] ) && !is_array( $user_info['discount'] ) ) {
117
        $user_info['discount'] = (array)$user_info['discount'];
118
    }
119
120
    // Payment details
121
    $payment_details = array();
122
    if ( !empty( $invoice_data['payment_details'] ) ) {
123
        $default_payment_details = array(
124
            'gateway'           => 'manual',
125
            'gateway_title'     => '',
126
            'currency'          => wpinv_get_default_country(),
127
            'transaction_id'    => '',
128
        );
129
        
130
        $payment_details = wp_parse_args( $invoice_data['payment_details'], $default_payment_details );
131
        
132
        if ( empty( $payment_details['gateway'] ) ) {
133
            $payment_details['gateway'] = 'manual';
134
        }
135
        
136
        if ( empty( $payment_details['currency'] ) ) {
137
            $payment_details['currency'] = wpinv_get_default_country();
138
        }
139
        
140
        if ( empty( $payment_details['gateway_title'] ) ) {
141
            $payment_details['gateway_title'] = wpinv_get_gateway_checkout_label( $payment_details['gateway'] );
142
        }
143
    }
144
    
145
    $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

145
    $invoice->/** @scrutinizer ignore-call */ 
146
              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...
146
    
147
    if ( !empty( $payment_details ) ) {
148
        $invoice->set( 'currency', $payment_details['currency'] );
149
        $invoice->set( 'gateway', $payment_details['gateway'] );
150
        $invoice->set( 'gateway_title', $payment_details['gateway_title'] );
151
        $invoice->set( 'transaction_id', $payment_details['transaction_id'] );
152
    }
153
    
154
    $invoice->set( 'user_info', $user_info );
155
    $invoice->set( 'first_name', $user_info['first_name'] );
156
    $invoice->set( 'last_name', $user_info['last_name'] );
157
    $invoice->set( 'address', $user_info['address'] );
158
    $invoice->set( 'company', $user_info['company'] );
159
    $invoice->set( 'vat_number', $user_info['vat_number'] );
160
    $invoice->set( 'phone', $user_info['phone'] );
161
    $invoice->set( 'city', $user_info['city'] );
162
    $invoice->set( 'country', $user_info['country'] );
163
    $invoice->set( 'state', $user_info['state'] );
164
    $invoice->set( 'zip', $user_info['zip'] );
165
    $invoice->set( 'discounts', $user_info['discount'] );
166
    $invoice->set( 'ip', ( !empty( $invoice_data['ip'] ) ? $invoice_data['ip'] : wpinv_get_ip() ) );
167
    $invoice->set( 'mode', ( wpinv_is_test_mode() ? 'test' : 'live' ) );
168
    $invoice->set( 'parent_invoice', ( !empty( $invoice_data['parent'] ) ? absint( $invoice_data['parent'] ) : '' ) );
169
    
170
    if ( !empty( $invoice_data['cart_details'] ) && is_array( $invoice_data['cart_details'] ) ) {
171
        foreach ( $invoice_data['cart_details'] as $key => $item ) {
172
            $item_id        = !empty( $item['id'] ) ? $item['id'] : 0;
173
            $quantity       = !empty( $item['quantity'] ) ? $item['quantity'] : 1;
174
            $name           = !empty( $item['name'] ) ? $item['name'] : '';
175
            $item_price     = isset( $item['item_price'] ) ? $item['item_price'] : '';
176
            
177
            $post_item  = new WPInv_Item( $item_id );
178
            if ( !empty( $post_item ) ) {
179
                $name       = !empty( $name ) ? $name : $post_item->get_name();
180
                $item_price = $item_price !== '' ? $item_price : $post_item->get_price();
181
            } else {
182
                continue;
183
            }
184
            
185
            $args = array(
186
                'name'          => $name,
187
                'quantity'      => $quantity,
188
                'item_price'    => $item_price,
189
                'custom_price'  => isset( $item['custom_price'] ) ? $item['custom_price'] : '',
190
                'tax'           => !empty( $item['tax'] ) ? $item['tax'] : 0.00,
191
                'discount'      => isset( $item['discount'] ) ? $item['discount'] : 0,
192
                'meta'          => isset( $item['meta'] ) ? $item['meta'] : array(),
193
                'fees'          => isset( $item['fees'] ) ? $item['fees'] : array(),
194
            );
195
196
            $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

196
            $invoice->/** @scrutinizer ignore-call */ 
197
                      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...
197
        }
198
    }
199
200
    $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

200
    $invoice->/** @scrutinizer ignore-call */ 
201
              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...
201
202
    if ( isset( $invoice_data['post_date'] ) ) {
203
        $invoice->set( 'date', $invoice_data['post_date'] );
204
    }
205
    
206
    // Invoice due date
207
    if ( isset( $invoice_data['due_date'] ) ) {
208
        $invoice->set( 'due_date', $invoice_data['due_date'] );
209
    }
210
    
211
    $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

211
    $invoice->/** @scrutinizer ignore-call */ 
212
              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...
212
    
213
    // Add notes
214
    if ( !empty( $invoice_data['private_note'] ) ) {
215
        $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

215
        $invoice->/** @scrutinizer ignore-call */ 
216
                  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...
216
    }
217
    if ( !empty( $invoice_data['user_note'] ) ) {
218
        $invoice->add_note( $invoice_data['user_note'], true );
219
    }
220
    
221
    do_action( 'wpinv_insert_invoice', $invoice->ID, $invoice_data );
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist on WP_Error.
Loading history...
222
223
    if ( ! empty( $invoice->ID ) ) {
224
        global $wpi_userID, $wpinv_ip_address_country;
225
        
226
        $checkout_session = wpinv_get_checkout_session();
227
        
228
        $data_session                   = array();
229
        $data_session['invoice_id']     = $invoice->ID;
230
        $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

230
        /** @scrutinizer ignore-call */ 
231
        $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...
231
        
232
        wpinv_set_checkout_session( $data_session );
233
        
234
        $wpi_userID         = (int)$invoice->get_user_id();
235
        
236
        $_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...
237
        $_POST['state']     = $invoice->state;
0 ignored issues
show
Bug introduced by
The property state does not seem to exist on WP_Error.
Loading history...
238
239
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
240
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
241
        
242
        $wpinv_ip_address_country = $invoice->country;
243
        
244
        $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

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

528
    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...
529
}
530
531
function wpinv_get_payment_gateway_name( $invoice_id ) {
532
    $invoice = new WPInv_Invoice( $invoice_id );
533
    
534
    return $invoice->get_gateway_title();
535
}
536
537
function wpinv_get_payment_transaction_id( $invoice_id ) {
538
    $invoice = new WPInv_Invoice( $invoice_id );
539
    
540
    return $invoice->get_transaction_id();
541
}
542
543
function wpinv_get_id_by_transaction_id( $key ) {
544
    global $wpdb;
545
546
    $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 ) );
547
548
    if ( $invoice_id != NULL )
549
        return $invoice_id;
550
551
    return 0;
552
}
553
554
function wpinv_get_invoice_meta( $invoice_id = 0, $meta_key = '_wpinv_payment_meta', $single = true ) {
555
    $invoice = new WPInv_Invoice( $invoice_id );
556
557
    return $invoice->get_meta( $meta_key, $single );
558
}
559
560
function wpinv_update_invoice_meta( $invoice_id = 0, $meta_key = '', $meta_value = '', $prev_value = '' ) {
561
    $invoice = new WPInv_Invoice( $invoice_id );
562
    
563
    return $invoice->update_meta( $meta_key, $meta_value, $prev_value );
564
}
565
566
function wpinv_get_items( $invoice_id = 0 ) {
567
    $invoice            = wpinv_get_invoice( $invoice_id );
568
    
569
    $items              = $invoice->get_items();
570
    $invoice_currency   = $invoice->get_currency();
571
572
    if ( !empty( $items ) && is_array( $items ) ) {
573
        foreach ( $items as $key => $item ) {
574
            $items[$key]['currency'] = $invoice_currency;
575
576
            if ( !isset( $item['subtotal'] ) ) {
577
                $items[$key]['subtotal'] = $items[$key]['amount'] * 1;
578
            }
579
        }
580
    }
581
582
    return apply_filters( 'wpinv_get_items', $items, $invoice_id );
583
}
584
585
function wpinv_get_fees( $invoice_id = 0 ) {
586
    $invoice           = wpinv_get_invoice( $invoice_id );
587
    $fees              = $invoice->get_fees();
588
589
    return apply_filters( 'wpinv_get_fees', $fees, $invoice_id );
590
}
591
592
function wpinv_get_invoice_ip( $invoice_id ) {
593
    $invoice = new WPInv_Invoice( $invoice_id );
594
    return $invoice->get_ip();
595
}
596
597
function wpinv_get_invoice_user_info( $invoice_id ) {
598
    $invoice = new WPInv_Invoice( $invoice_id );
599
    return $invoice->get_user_info();
600
}
601
602
function wpinv_subtotal( $invoice_id = 0, $currency = false ) {
603
    $invoice = new WPInv_Invoice( $invoice_id );
604
605
    return $invoice->get_subtotal( $currency );
606
}
607
608
function wpinv_tax( $invoice_id = 0, $currency = false ) {
609
    $invoice = new WPInv_Invoice( $invoice_id );
610
611
    return $invoice->get_tax( $currency );
612
}
613
614
function wpinv_discount( $invoice_id = 0, $currency = false, $dash = false ) {
615
    $invoice = wpinv_get_invoice( $invoice_id );
616
617
    return $invoice->get_discount( $currency, $dash );
618
}
619
620
function wpinv_discount_code( $invoice_id = 0 ) {
621
    $invoice = new WPInv_Invoice( $invoice_id );
622
623
    return $invoice->get_discount_code();
624
}
625
626
function wpinv_payment_total( $invoice_id = 0, $currency = false ) {
627
    $invoice = new WPInv_Invoice( $invoice_id );
628
629
    return $invoice->get_total( $currency );
630
}
631
632
function wpinv_get_date_created( $invoice_id = 0, $format = '' ) {
633
    $invoice = new WPInv_Invoice( $invoice_id );
634
635
    $format         = !empty( $format ) ? $format : get_option( 'date_format' );
636
    $date_created   = $invoice->get_created_date();
637
    $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

637
    $date_created   = $date_created != '' && $date_created != '0000-00-00 00:00:00' ? date_i18n( /** @scrutinizer ignore-type */ $format, strtotime( $date_created ) ) : '';
Loading history...
638
639
    return $date_created;
640
}
641
642
function wpinv_get_invoice_date( $invoice_id = 0, $format = '', $default = true ) {
643
    $invoice = new WPInv_Invoice( $invoice_id );
644
    
645
    $format         = !empty( $format ) ? $format : get_option( 'date_format' );
646
    $date_completed = $invoice->get_completed_date();
647
    $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

647
    $invoice_date   = $date_completed != '' && $date_completed != '0000-00-00 00:00:00' ? date_i18n( /** @scrutinizer ignore-type */ $format, strtotime( $date_completed ) ) : '';
Loading history...
648
    if ( $invoice_date == '' && $default ) {
649
        $invoice_date   = wpinv_get_date_created( $invoice_id, $format );
650
    }
651
652
    return $invoice_date;
653
}
654
655
function wpinv_get_invoice_vat_number( $invoice_id = 0 ) {
656
    $invoice = new WPInv_Invoice( $invoice_id );
657
    
658
    return $invoice->vat_number;
659
}
660
661
function wpinv_insert_payment_note( $invoice_id = 0, $note = '', $user_type = false, $added_by_user = false, $system = false ) {
662
    $invoice = new WPInv_Invoice( $invoice_id );
663
664
    return $invoice->add_note( $note, $user_type, $added_by_user, $system );
665
}
666
667
function wpinv_get_invoice_notes( $invoice_id = 0, $type = '' ) {
668
    global $invoicing;
669
    
670
    if ( empty( $invoice_id ) ) {
671
        return NULL;
672
    }
673
    
674
    $notes = $invoicing->notes->get_invoice_notes( $invoice_id, $type );
675
    
676
    return apply_filters( 'wpinv_invoice_notes', $notes, $invoice_id, $type );
677
}
678
679
function wpinv_get_payment_key( $invoice_id = 0 ) {
680
	$invoice = new WPInv_Invoice( $invoice_id );
681
    return $invoice->get_key();
682
}
683
684
function wpinv_get_invoice_number( $invoice_id = 0 ) {
685
    $invoice = new WPInv_Invoice( $invoice_id );
686
    return $invoice->get_number();
687
}
688
689
function wpinv_get_cart_discountable_subtotal( $code_id ) {
690
    $cart_items = wpinv_get_cart_content_details();
691
    $items      = array();
692
693
    $excluded_items = wpinv_get_discount_excluded_items( $code_id );
694
695
    if( $cart_items ) {
696
697
        foreach( $cart_items as $item ) {
698
699
            if( ! in_array( $item['id'], $excluded_items ) ) {
700
                $items[] =  $item;
701
            }
702
        }
703
    }
704
705
    $subtotal = wpinv_get_cart_items_subtotal( $items );
706
707
    return apply_filters( 'wpinv_get_cart_discountable_subtotal', $subtotal );
708
}
709
710
function wpinv_get_cart_items_subtotal( $items ) {
711
    $subtotal = 0.00;
712
713
    if ( is_array( $items ) && ! empty( $items ) ) {
714
        $prices = wp_list_pluck( $items, 'subtotal' );
715
716
        if( is_array( $prices ) ) {
0 ignored issues
show
introduced by
The condition is_array($prices) is always true.
Loading history...
717
            $subtotal = array_sum( $prices );
718
        } else {
719
            $subtotal = 0.00;
720
        }
721
722
        if( $subtotal < 0 ) {
723
            $subtotal = 0.00;
724
        }
725
    }
726
727
    return apply_filters( 'wpinv_get_cart_items_subtotal', $subtotal );
728
}
729
730
function wpinv_get_cart_subtotal( $items = array() ) {
731
    $items    = !empty( $items ) ? $items : wpinv_get_cart_content_details();
732
    $subtotal = wpinv_get_cart_items_subtotal( $items );
733
734
    return apply_filters( 'wpinv_get_cart_subtotal', $subtotal );
735
}
736
737
function wpinv_cart_subtotal( $items = array() ) {
738
    $price = wpinv_price( wpinv_format_amount( wpinv_get_cart_subtotal( $items ) ) );
739
740
    return $price;
741
}
742
743
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

743
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...
744
    $subtotal  = (float)wpinv_get_cart_subtotal( $items );
745
    $discounts = (float)wpinv_get_cart_discounted_amount( $items );
746
    $cart_tax  = (float)wpinv_get_cart_tax( $items );
747
    $fees      = (float)wpinv_get_cart_fee_total();
748
    if ( !empty( $invoice ) && $invoice->is_free_trial() ) {
749
        $total = 0;
750
    } else {
751
        $total     = $subtotal - $discounts + $cart_tax + $fees;
752
    }
753
754
    if ( $total < 0 ) {
755
        $total = 0.00;
756
    }
757
    
758
    $total = (float)apply_filters( 'wpinv_get_cart_total', $total, $items );
759
760
    return wpinv_sanitize_amount( $total );
761
}
762
763
function wpinv_cart_total( $cart_items = array(), $echo = true, $invoice = array() ) {
764
    global $cart_total;
765
    $total = wpinv_price( wpinv_format_amount( wpinv_get_cart_total( $cart_items, NULL, $invoice ) ) );
766
    $total = apply_filters( 'wpinv_cart_total', $total, $cart_items, $invoice );
767
    
768
    $cart_total = $total;
769
770
    if ( !$echo ) {
771
        return $total;
772
    }
773
774
    echo $total;
775
}
776
777
function wpinv_get_cart_tax( $items = array() ) {
778
    $cart_tax = 0;
779
    $items    = !empty( $items ) ? $items : wpinv_get_cart_content_details();
780
781
    if ( $items ) {
782
        $taxes = wp_list_pluck( $items, 'tax' );
783
784
        if( is_array( $taxes ) ) {
0 ignored issues
show
introduced by
The condition is_array($taxes) is always true.
Loading history...
785
            $cart_tax = array_sum( $taxes );
786
        }
787
    }
788
789
    $cart_tax += wpinv_get_cart_fee_tax();
790
791
    return apply_filters( 'wpinv_get_cart_tax', wpinv_sanitize_amount( $cart_tax ) );
792
}
793
794
function wpinv_cart_tax( $items = array(), $echo = false ) {
795
    $cart_tax = wpinv_get_cart_tax( $items );
796
    $cart_tax = wpinv_price( wpinv_format_amount( $cart_tax ) );
797
798
    $tax = apply_filters( 'wpinv_cart_tax', $cart_tax, $items );
799
800
    if ( !$echo ) {
801
        return $tax;
802
    }
803
804
    echo $tax;
805
}
806
807
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

807
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...
808
    $invoice = wpinv_get_invoice_cart();
809
    $cart_discount_code = !empty( $invoice ) ? $invoice->get_discount_code() : '';
810
    
811
    return apply_filters( 'wpinv_get_cart_discount_code', $cart_discount_code );
812
}
813
814
function wpinv_cart_discount_code( $items = array(), $echo = false ) {
815
    $cart_discount_code = wpinv_get_cart_discount_code( $items );
816
817
    if ( $cart_discount_code != '' ) {
818
        $cart_discount_code = ' (' . $cart_discount_code . ')';
819
    }
820
    
821
    $discount_code = apply_filters( 'wpinv_cart_discount_code', $cart_discount_code, $items );
822
823
    if ( !$echo ) {
824
        return $discount_code;
825
    }
826
827
    echo $discount_code;
828
}
829
830
function wpinv_get_cart_discount( $items = array() ) {
831
    $invoice = wpinv_get_invoice_cart();
832
    $cart_discount = !empty( $invoice ) ? $invoice->get_discount() : 0;
833
    
834
    return apply_filters( 'wpinv_get_cart_discount', wpinv_sanitize_amount( $cart_discount ), $items );
835
}
836
837
function wpinv_cart_discount( $items = array(), $echo = false ) {
838
    $cart_discount = wpinv_get_cart_discount( $items );
839
    $cart_discount = wpinv_price( wpinv_format_amount( $cart_discount ) );
840
841
    $discount = apply_filters( 'wpinv_cart_discount', $cart_discount, $items );
842
843
    if ( !$echo ) {
844
        return $discount;
845
    }
846
847
    echo $discount;
848
}
849
850
function wpinv_get_cart_fees( $type = 'all', $item_id = 0 ) {
851
    $item = new WPInv_Item( $item_id );
852
    
853
    return $item->get_fees( $type, $item_id );
854
}
855
856
function wpinv_get_cart_fee_total() {
857
    $total  = 0;
858
    $fees = wpinv_get_cart_fees();
859
    
860
    if ( $fees ) {
861
        foreach ( $fees as $fee_id => $fee ) {
862
            $total += $fee['amount'];
863
        }
864
    }
865
866
    return apply_filters( 'wpinv_get_cart_fee_total', $total );
867
}
868
869
function wpinv_get_cart_fee_tax() {
870
    $tax  = 0;
871
    $fees = wpinv_get_cart_fees();
872
873
    if ( $fees ) {
874
        foreach ( $fees as $fee_id => $fee ) {
875
            if( ! empty( $fee['no_tax'] ) ) {
876
                continue;
877
            }
878
879
            $tax += wpinv_calculate_tax( $fee['amount'] );
880
        }
881
    }
882
883
    return apply_filters( 'wpinv_get_cart_fee_tax', $tax );
884
}
885
886
function wpinv_cart_has_recurring_item() {
887
    $cart_items = wpinv_get_cart_contents();
888
    
889
    if ( empty( $cart_items ) ) {
890
        return false;
891
    }
892
    
893
    $has_subscription = false;
894
    foreach( $cart_items as $cart_item ) {
895
        if ( !empty( $cart_item['id'] ) && wpinv_is_recurring_item( $cart_item['id'] )  ) {
896
            $has_subscription = true;
897
            break;
898
        }
899
    }
900
    
901
    return apply_filters( 'wpinv_cart_has_recurring_item', $has_subscription, $cart_items );
902
}
903
904
function wpinv_cart_has_free_trial() {
905
    $invoice = wpinv_get_invoice_cart();
906
    
907
    $free_trial = false;
908
    
909
    if ( !empty( $invoice ) && $invoice->is_free_trial() ) {
910
        $free_trial = true;
911
    }
912
    
913
    return apply_filters( 'wpinv_cart_has_free_trial', $free_trial, $invoice );
914
}
915
916
function wpinv_get_cart_contents() {
917
    $cart_details = wpinv_get_cart_details();
918
    
919
    return apply_filters( 'wpinv_get_cart_contents', $cart_details );
920
}
921
922
function wpinv_get_cart_content_details() {
923
    global $wpinv_euvat, $wpi_current_id, $wpi_item_id, $wpinv_is_last_cart_item, $wpinv_flat_discount_total;
924
    $cart_items = wpinv_get_cart_contents();
925
    
926
    if ( empty( $cart_items ) ) {
927
        return false;
928
    }
929
    $invoice = wpinv_get_invoice_cart();
930
	if ( empty( $invoice ) ) {
931
        return false;
932
    }
933
934
    $details = array();
935
    $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...
936
    
937
    if ( empty( $_POST['country'] ) ) {
938
        $_POST['country'] = $invoice->country;
939
    }
940
    if ( !isset( $_POST['state'] ) ) {
941
        $_POST['state'] = $invoice->state;
942
    }
943
944
    foreach( $cart_items as $key => $item ) {
945
        $item_id            = isset( $item['id'] ) ? sanitize_text_field( $item['id'] ) : '';
946
        if ( empty( $item_id ) ) {
947
            continue;
948
        }
949
        
950
        $wpi_current_id         = $invoice->ID;
951
        $wpi_item_id            = $item_id;
952
        
953
        if ( isset( $item['custom_price'] ) && $item['custom_price'] !== '' ) {
954
            $item_price = $item['custom_price'];
955
        } else {
956
            if ( isset( $item['item_price'] ) && $item['item_price'] !== '' && $item['item_price'] !== false ) {
957
                $item_price = $item['item_price'];
958
            } else {
959
                $item_price = wpinv_get_item_price( $item_id );
960
            }
961
        }
962
        $discount           = wpinv_get_cart_item_discount_amount( $item );
963
        $discount           = apply_filters( 'wpinv_get_cart_content_details_item_discount_amount', $discount, $item );
964
        $quantity           = wpinv_get_cart_item_quantity( $item );
965
        $fees               = wpinv_get_cart_fees( 'fee', $item_id );
966
        
967
        $subtotal           = $item_price * $quantity;
968
        $tax_rate           = wpinv_get_tax_rate( $_POST['country'], $_POST['state'], $wpi_item_id );
969
        $tax_class          = $wpinv_euvat->get_item_class( $item_id );
970
        $tax                = wpinv_get_cart_item_tax( $item_id, $subtotal - $discount );
971
        
972
        if ( wpinv_prices_include_tax() ) {
973
            $subtotal -= wpinv_round_amount( $tax );
974
        }
975
        
976
        $total              = $subtotal - $discount + $tax;
977
        
978
        // Do not allow totals to go negatve
979
        if( $total < 0 ) {
980
            $total = 0;
981
        }
982
        
983
        $details[ $key ]  = array(
984
            'id'                => $item_id,
985
            '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

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

1121
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...
1122
    return false;
1123
}
1124
1125
function wpinv_validate_checkout_fields() {    
1126
    // Check if there is $_POST
1127
    if ( empty( $_POST ) ) {
1128
        return false;
1129
    }
1130
    
1131
    // Start an array to collect valid data
1132
    $valid_data = array(
1133
        'gateway'          => wpinv_checkout_validate_gateway(), // Gateway fallback
1134
        'discount'         => wpinv_checkout_validate_discounts(), // Set default discount
1135
        'cc_info'          => wpinv_checkout_validate_cc() // Credit card info
1136
    );
1137
    
1138
    // Validate agree to terms
1139
    $page = wpinv_get_option( 'tandc_page' );
1140
    if(isset($page) && (int)$page > 0 && apply_filters( 'wpinv_checkout_show_terms', true )){
1141
        // Validate agree to terms
1142
        if ( ! isset( $_POST['wpi_terms'] ) || !$_POST['wpi_terms'] ) {
1143
            wpinv_set_error( 'accept_terms', apply_filters( 'wpinv_accept_terms_error_text', __( 'You must accept terms and conditions', 'invoicing' ) ) );
1144
        }
1145
    }
1146
    
1147
    $valid_data['invoice_user'] = wpinv_checkout_validate_invoice_user();
1148
    $valid_data['current_user'] = wpinv_checkout_validate_current_user();
1149
    
1150
    // Return collected data
1151
    return $valid_data;
1152
}
1153
1154
function wpinv_checkout_validate_gateway() {
1155
    $gateway = wpinv_get_default_gateway();
1156
    
1157
    $invoice = wpinv_get_invoice_cart();
1158
    $has_subscription = $invoice->is_recurring();
1159
    if ( empty( $invoice ) ) {
1160
        wpinv_set_error( 'invalid_invoice', __( 'Your cart is empty.', 'invoicing' ) );
1161
        return $gateway;
1162
    }
1163
1164
    // Check if a gateway value is present
1165
    if ( !empty( $_REQUEST['wpi-gateway'] ) ) {
1166
        $gateway = sanitize_text_field( $_REQUEST['wpi-gateway'] );
1167
1168
        if ( $invoice->is_free() ) {
1169
            $gateway = 'manual';
1170
        } elseif ( !wpinv_is_gateway_active( $gateway ) ) {
1171
            wpinv_set_error( 'invalid_gateway', __( 'The selected payment gateway is not enabled', 'invoicing' ) );
1172
        } elseif ( $has_subscription && !wpinv_gateway_support_subscription( $gateway ) ) {
1173
            wpinv_set_error( 'invalid_gateway', __( 'The selected payment gateway doesnot support subscription payment', 'invoicing' ) );
1174
        }
1175
    }
1176
1177
    if ( $has_subscription && count( wpinv_get_cart_contents() ) > 1 ) {
1178
        wpinv_set_error( 'subscription_invalid', __( 'Only one subscription may be purchased through payment per checkout.', 'invoicing' ) );
1179
    }
1180
1181
    return $gateway;
1182
}
1183
1184
function wpinv_checkout_validate_discounts() {
1185
    global $wpi_cart;
1186
    
1187
    // Retrieve the discount stored in cookies
1188
    $discounts = wpinv_get_cart_discounts();
1189
    
1190
    $error = false;
1191
    // If we have discounts, loop through them
1192
    if ( ! empty( $discounts ) ) {
1193
        foreach ( $discounts as $discount ) {
1194
            // Check if valid
1195
            if (  !wpinv_is_discount_valid( $discount, (int)$wpi_cart->get_user_id() ) ) {
1196
                // Discount is not valid
1197
                $error = true;
1198
            }
1199
        }
1200
    } else {
1201
        // No discounts
1202
        return NULL;
1203
    }
1204
1205
    if ( $error && !wpinv_get_errors() ) {
1206
        wpinv_set_error( 'invalid_discount', __( 'Discount code you entered is invalid', 'invoicing' ) );
1207
    }
1208
1209
    return implode( ',', $discounts );
1210
}
1211
1212
function wpinv_checkout_validate_cc() {
1213
    $card_data = wpinv_checkout_get_cc_info();
1214
1215
    // Validate the card zip
1216
    if ( !empty( $card_data['wpinv_zip'] ) ) {
1217
        if ( !wpinv_checkout_validate_cc_zip( $card_data['wpinv_zip'], $card_data['wpinv_country'] ) ) {
1218
            wpinv_set_error( 'invalid_cc_zip', __( 'The zip / postcode you entered for your billing address is invalid', 'invoicing' ) );
1219
        }
1220
    }
1221
1222
    // This should validate card numbers at some point too
1223
    return $card_data;
1224
}
1225
1226
function wpinv_checkout_get_cc_info() {
1227
	$cc_info = array();
1228
	$cc_info['card_name']      = isset( $_POST['card_name'] )       ? sanitize_text_field( $_POST['card_name'] )       : '';
1229
	$cc_info['card_number']    = isset( $_POST['card_number'] )     ? sanitize_text_field( $_POST['card_number'] )     : '';
1230
	$cc_info['card_cvc']       = isset( $_POST['card_cvc'] )        ? sanitize_text_field( $_POST['card_cvc'] )        : '';
1231
	$cc_info['card_exp_month'] = isset( $_POST['card_exp_month'] )  ? sanitize_text_field( $_POST['card_exp_month'] )  : '';
1232
	$cc_info['card_exp_year']  = isset( $_POST['card_exp_year'] )   ? sanitize_text_field( $_POST['card_exp_year'] )   : '';
1233
	$cc_info['card_address']   = isset( $_POST['wpinv_address'] )  ? sanitize_text_field( $_POST['wpinv_address'] ) : '';
1234
	$cc_info['card_city']      = isset( $_POST['wpinv_city'] )     ? sanitize_text_field( $_POST['wpinv_city'] )    : '';
1235
	$cc_info['card_state']     = isset( $_POST['wpinv_state'] )    ? sanitize_text_field( $_POST['wpinv_state'] )   : '';
1236
	$cc_info['card_country']   = isset( $_POST['wpinv_country'] )  ? sanitize_text_field( $_POST['wpinv_country'] ) : '';
1237
	$cc_info['card_zip']       = isset( $_POST['wpinv_zip'] )      ? sanitize_text_field( $_POST['wpinv_zip'] )     : '';
1238
1239
	// Return cc info
1240
	return $cc_info;
1241
}
1242
1243
function wpinv_checkout_validate_cc_zip( $zip = 0, $country_code = '' ) {
1244
    $ret = false;
1245
1246
    if ( empty( $zip ) || empty( $country_code ) )
1247
        return $ret;
1248
1249
    $country_code = strtoupper( $country_code );
1250
1251
    $zip_regex = array(
1252
        "AD" => "AD\d{3}",
1253
        "AM" => "(37)?\d{4}",
1254
        "AR" => "^([A-Z]{1}\d{4}[A-Z]{3}|[A-Z]{1}\d{4}|\d{4})$",
1255
        "AS" => "96799",
1256
        "AT" => "\d{4}",
1257
        "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})$",
1258
        "AX" => "22\d{3}",
1259
        "AZ" => "\d{4}",
1260
        "BA" => "\d{5}",
1261
        "BB" => "(BB\d{5})?",
1262
        "BD" => "\d{4}",
1263
        "BE" => "^[1-9]{1}[0-9]{3}$",
1264
        "BG" => "\d{4}",
1265
        "BH" => "((1[0-2]|[2-9])\d{2})?",
1266
        "BM" => "[A-Z]{2}[ ]?[A-Z0-9]{2}",
1267
        "BN" => "[A-Z]{2}[ ]?\d{4}",
1268
        "BR" => "\d{5}[\-]?\d{3}",
1269
        "BY" => "\d{6}",
1270
        "CA" => "^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$",
1271
        "CC" => "6799",
1272
        "CH" => "^[1-9][0-9][0-9][0-9]$",
1273
        "CK" => "\d{4}",
1274
        "CL" => "\d{7}",
1275
        "CN" => "\d{6}",
1276
        "CR" => "\d{4,5}|\d{3}-\d{4}",
1277
        "CS" => "\d{5}",
1278
        "CV" => "\d{4}",
1279
        "CX" => "6798",
1280
        "CY" => "\d{4}",
1281
        "CZ" => "\d{3}[ ]?\d{2}",
1282
        "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",
1283
        "DK" => "^([D-d][K-k])?( |-)?[1-9]{1}[0-9]{3}$",
1284
        "DO" => "\d{5}",
1285
        "DZ" => "\d{5}",
1286
        "EC" => "([A-Z]\d{4}[A-Z]|(?:[A-Z]{2})?\d{6})?",
1287
        "EE" => "\d{5}",
1288
        "EG" => "\d{5}",
1289
        "ES" => "^([1-9]{2}|[0-9][1-9]|[1-9][0-9])[0-9]{3}$",
1290
        "ET" => "\d{4}",
1291
        "FI" => "\d{5}",
1292
        "FK" => "FIQQ 1ZZ",
1293
        "FM" => "(9694[1-4])([ \-]\d{4})?",
1294
        "FO" => "\d{3}",
1295
        "FR" => "^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$",
1296
        "GE" => "\d{4}",
1297
        "GF" => "9[78]3\d{2}",
1298
        "GL" => "39\d{2}",
1299
        "GN" => "\d{3}",
1300
        "GP" => "9[78][01]\d{2}",
1301
        "GR" => "\d{3}[ ]?\d{2}",
1302
        "GS" => "SIQQ 1ZZ",
1303
        "GT" => "\d{5}",
1304
        "GU" => "969[123]\d([ \-]\d{4})?",
1305
        "GW" => "\d{4}",
1306
        "HM" => "\d{4}",
1307
        "HN" => "(?:\d{5})?",
1308
        "HR" => "\d{5}",
1309
        "HT" => "\d{4}",
1310
        "HU" => "\d{4}",
1311
        "ID" => "\d{5}",
1312
        "IE" => "((D|DUBLIN)?([1-9]|6[wW]|1[0-8]|2[024]))?",
1313
        "IL" => "\d{5}",
1314
        "IN"=> "^[1-9][0-9][0-9][0-9][0-9][0-9]$", //india
1315
        "IO" => "BBND 1ZZ",
1316
        "IQ" => "\d{5}",
1317
        "IS" => "\d{3}",
1318
        "IT" => "^(V-|I-)?[0-9]{5}$",
1319
        "JO" => "\d{5}",
1320
        "JP" => "\d{3}-\d{4}",
1321
        "KE" => "\d{5}",
1322
        "KG" => "\d{6}",
1323
        "KH" => "\d{5}",
1324
        "KR" => "\d{3}[\-]\d{3}",
1325
        "KW" => "\d{5}",
1326
        "KZ" => "\d{6}",
1327
        "LA" => "\d{5}",
1328
        "LB" => "(\d{4}([ ]?\d{4})?)?",
1329
        "LI" => "(948[5-9])|(949[0-7])",
1330
        "LK" => "\d{5}",
1331
        "LR" => "\d{4}",
1332
        "LS" => "\d{3}",
1333
        "LT" => "\d{5}",
1334
        "LU" => "\d{4}",
1335
        "LV" => "\d{4}",
1336
        "MA" => "\d{5}",
1337
        "MC" => "980\d{2}",
1338
        "MD" => "\d{4}",
1339
        "ME" => "8\d{4}",
1340
        "MG" => "\d{3}",
1341
        "MH" => "969[67]\d([ \-]\d{4})?",
1342
        "MK" => "\d{4}",
1343
        "MN" => "\d{6}",
1344
        "MP" => "9695[012]([ \-]\d{4})?",
1345
        "MQ" => "9[78]2\d{2}",
1346
        "MT" => "[A-Z]{3}[ ]?\d{2,4}",
1347
        "MU" => "(\d{3}[A-Z]{2}\d{3})?",
1348
        "MV" => "\d{5}",
1349
        "MX" => "\d{5}",
1350
        "MY" => "\d{5}",
1351
        "NC" => "988\d{2}",
1352
        "NE" => "\d{4}",
1353
        "NF" => "2899",
1354
        "NG" => "(\d{6})?",
1355
        "NI" => "((\d{4}-)?\d{3}-\d{3}(-\d{1})?)?",
1356
        "NL" => "^[1-9][0-9]{3}\s?([a-zA-Z]{2})?$",
1357
        "NO" => "\d{4}",
1358
        "NP" => "\d{5}",
1359
        "NZ" => "\d{4}",
1360
        "OM" => "(PC )?\d{3}",
1361
        "PF" => "987\d{2}",
1362
        "PG" => "\d{3}",
1363
        "PH" => "\d{4}",
1364
        "PK" => "\d{5}",
1365
        "PL" => "\d{2}-\d{3}",
1366
        "PM" => "9[78]5\d{2}",
1367
        "PN" => "PCRN 1ZZ",
1368
        "PR" => "00[679]\d{2}([ \-]\d{4})?",
1369
        "PT" => "\d{4}([\-]\d{3})?",
1370
        "PW" => "96940",
1371
        "PY" => "\d{4}",
1372
        "RE" => "9[78]4\d{2}",
1373
        "RO" => "\d{6}",
1374
        "RS" => "\d{5}",
1375
        "RU" => "\d{6}",
1376
        "SA" => "\d{5}",
1377
        "SE" => "^(s-|S-){0,1}[0-9]{3}\s?[0-9]{2}$",
1378
        "SG" => "\d{6}",
1379
        "SH" => "(ASCN|STHL) 1ZZ",
1380
        "SI" => "\d{4}",
1381
        "SJ" => "\d{4}",
1382
        "SK" => "\d{3}[ ]?\d{2}",
1383
        "SM" => "4789\d",
1384
        "SN" => "\d{5}",
1385
        "SO" => "\d{5}",
1386
        "SZ" => "[HLMS]\d{3}",
1387
        "TC" => "TKCA 1ZZ",
1388
        "TH" => "\d{5}",
1389
        "TJ" => "\d{6}",
1390
        "TM" => "\d{6}",
1391
        "TN" => "\d{4}",
1392
        "TR" => "\d{5}",
1393
        "TW" => "\d{3}(\d{2})?",
1394
        "UA" => "\d{5}",
1395
        "UK" => "^(GIR|[A-Z]\d[A-Z\d]??|[A-Z]{2}\d[A-Z\d]??)[ ]??(\d[A-Z]{2})$",
1396
        "US" => "^\d{5}([\-]?\d{4})?$",
1397
        "UY" => "\d{5}",
1398
        "UZ" => "\d{6}",
1399
        "VA" => "00120",
1400
        "VE" => "\d{4}",
1401
        "VI" => "008(([0-4]\d)|(5[01]))([ \-]\d{4})?",
1402
        "WF" => "986\d{2}",
1403
        "YT" => "976\d{2}",
1404
        "YU" => "\d{5}",
1405
        "ZA" => "\d{4}",
1406
        "ZM" => "\d{5}"
1407
    );
1408
1409
    if ( ! isset ( $zip_regex[ $country_code ] ) || preg_match( "/" . $zip_regex[ $country_code ] . "/i", $zip ) )
1410
        $ret = true;
1411
1412
    return apply_filters( 'wpinv_is_zip_valid', $ret, $zip, $country_code );
1413
}
1414
1415
function wpinv_checkout_validate_agree_to_terms() {
1416
    // Validate agree to terms
1417
    if ( ! isset( $_POST['wpi_agree_to_terms'] ) || $_POST['wpi_agree_to_terms'] != 1 ) {
1418
        // User did not agree
1419
        wpinv_set_error( 'agree_to_terms', apply_filters( 'wpinv_agree_to_terms_text', __( 'You must agree to the terms of use', 'invoicing' ) ) );
1420
    }
1421
}
1422
1423
function wpinv_checkout_validate_invoice_user() {
1424
    global $wpi_cart, $user_ID;
1425
1426
    if(empty($wpi_cart)){
1427
        $wpi_cart = wpinv_get_invoice_cart();
1428
    }
1429
1430
    $invoice_user = (int)$wpi_cart->get_user_id();
1431
    $valid_user_data = array(
1432
        'user_id' => $invoice_user
1433
    );
1434
1435
    // If guest checkout allowed
1436
    if ( !wpinv_require_login_to_checkout() ) {
1437
        return $valid_user_data;
1438
    }
1439
    
1440
    // Verify there is a user_ID
1441
    if ( $user_ID == $invoice_user ) {
1442
        // Get the logged in user data
1443
        $user_data = get_userdata( $user_ID );
1444
        $required_fields  = wpinv_checkout_required_fields();
1445
1446
        // Loop through required fields and show error messages
1447
         if ( !empty( $required_fields ) ) {
1448
            foreach ( $required_fields as $field_name => $value ) {
1449
                if ( in_array( $value, $required_fields ) && empty( $_POST[ 'wpinv_' . $field_name ] ) ) {
1450
                    wpinv_set_error( $value['error_id'], $value['error_message'] );
1451
                }
1452
            }
1453
        }
1454
1455
        // Verify data
1456
        if ( $user_data ) {
1457
            // Collected logged in user data
1458
            $valid_user_data = array(
1459
                'user_id'     => $user_ID,
1460
                'email'       => isset( $_POST['wpinv_email'] ) ? sanitize_email( $_POST['wpinv_email'] ) : $user_data->user_email,
1461
                'first_name'  => isset( $_POST['wpinv_first_name'] ) && ! empty( $_POST['wpinv_first_name'] ) ? sanitize_text_field( $_POST['wpinv_first_name'] ) : $user_data->first_name,
1462
                'last_name'   => isset( $_POST['wpinv_last_name'] ) && ! empty( $_POST['wpinv_last_name']  ) ? sanitize_text_field( $_POST['wpinv_last_name']  ) : $user_data->last_name,
1463
            );
1464
1465
            if ( !empty( $_POST[ 'wpinv_email' ] ) && !is_email( $_POST[ 'wpinv_email' ] ) ) {
1466
                wpinv_set_error( 'invalid_email', __( 'Please enter a valid email address', 'invoicing' ) );
1467
            }
1468
        } else {
1469
            // Set invalid user error
1470
            wpinv_set_error( 'invalid_user', __( 'The user billing information is invalid', 'invoicing' ) );
1471
        }
1472
    } else {
1473
        // Set invalid user error
1474
        wpinv_set_error( 'invalid_user_id', __( 'The invalid invoice user id', 'invoicing' ) );
1475
    }
1476
1477
    // Return user data
1478
    return $valid_user_data;
1479
}
1480
1481
function wpinv_checkout_validate_current_user() {
1482
    global $wpi_cart;
1483
1484
    $data = array();
1485
    
1486
    if ( is_user_logged_in() ) {
1487
        if ( !wpinv_require_login_to_checkout() || ( wpinv_require_login_to_checkout() && (int)$wpi_cart->get_user_id() === (int)get_current_user_id() ) ) {
1488
            $data['user_id'] = (int)get_current_user_id();
1489
        } else {
1490
            wpinv_set_error( 'logged_in_only', __( 'You are not allowed to pay for this invoice', 'invoicing' ) );
1491
        }
1492
    } else {
1493
        // If guest checkout allowed
1494
        if ( !wpinv_require_login_to_checkout() ) {
1495
            $data['user_id'] = 0;
1496
        } else {
1497
            wpinv_set_error( 'logged_in_only', __( 'You must be logged in to pay for this invoice', 'invoicing' ) );
1498
        }
1499
    }
1500
1501
    return $data;
1502
}
1503
1504
function wpinv_checkout_form_get_user( $valid_data = array() ) {
1505
1506
    if ( !empty( $valid_data['current_user']['user_id'] ) ) {
1507
        $user = $valid_data['current_user'];
1508
    } else {
1509
        // Set the valid invoice user
1510
        $user = $valid_data['invoice_user'];
1511
    }
1512
1513
    // Verify invoice have an user
1514
    if ( false === $user || empty( $user ) ) {
1515
        return false;
1516
    }
1517
1518
    $address_fields = array(
1519
        'first_name',
1520
        'last_name',
1521
        'company',
1522
        'vat_number',
1523
        'phone',
1524
        'address',
1525
        'city',
1526
        'state',
1527
        'country',
1528
        'zip',
1529
    );
1530
    
1531
    foreach ( $address_fields as $field ) {
1532
        $user[$field]  = !empty( $_POST['wpinv_' . $field] ) ? sanitize_text_field( $_POST['wpinv_' . $field] ) : false;
1533
        
1534
        if ( !empty( $user['user_id'] ) && !empty( $valid_data['current_user']['user_id'] ) && $valid_data['current_user']['user_id'] == $valid_data['invoice_user']['user_id'] ) {
1535
            update_user_meta( $user['user_id'], '_wpinv_' . $field, $user[$field] );
1536
        }
1537
    }
1538
1539
    // Return valid user
1540
    return $user;
1541
}
1542
1543
function wpinv_set_checkout_session( $invoice_data = array() ) {
1544
    global $wpi_session;
1545
    
1546
    return $wpi_session->set( 'wpinv_checkout', $invoice_data );
1547
}
1548
1549
function wpinv_get_checkout_session() {
1550
	global $wpi_session;
1551
    
1552
    return $wpi_session->get( 'wpinv_checkout' );
1553
}
1554
1555
function wpinv_empty_cart() {
1556
    global $wpi_session;
1557
1558
    // Remove cart contents
1559
    $wpi_session->set( 'wpinv_checkout', NULL );
1560
1561
    // Remove all cart fees
1562
    $wpi_session->set( 'wpi_cart_fees', NULL );
1563
1564
    do_action( 'wpinv_empty_cart' );
1565
}
1566
1567
function wpinv_process_checkout() {
1568
    global $wpinv_euvat, $wpi_checkout_id, $wpi_cart;
1569
    
1570
    wpinv_clear_errors();
1571
    
1572
    $invoice = wpinv_get_invoice_cart();
1573
    if ( empty( $invoice ) ) {
1574
        return false;
1575
    }
1576
    
1577
    $wpi_cart = $invoice;
1578
    
1579
    $wpi_checkout_id = $invoice->ID;
1580
    
1581
    do_action( 'wpinv_pre_process_checkout' );
1582
    
1583
    if ( !wpinv_get_cart_contents() ) { // Make sure the cart isn't empty
1584
        $valid_data = false;
1585
        wpinv_set_error( 'empty_cart', __( 'Your cart is empty', 'invoicing' ) );
1586
    } else {
1587
        // Validate the form $_POST data
1588
        $valid_data = wpinv_validate_checkout_fields();
1589
        
1590
        // Allow themes and plugins to hook to errors
1591
        do_action( 'wpinv_checkout_error_checks', $valid_data, $_POST );
1592
    }
1593
    
1594
    $is_ajax    = defined( 'DOING_AJAX' ) && DOING_AJAX;
1595
    
1596
    // Validate the user
1597
    $user = wpinv_checkout_form_get_user( $valid_data );
1598
1599
    // Let extensions validate fields after user is logged in if user has used login/registration form
1600
    do_action( 'wpinv_checkout_user_error_checks', $user, $valid_data, $_POST );
1601
    
1602
    if ( false === $valid_data || wpinv_get_errors() || ! $user ) {
1603
        if ( $is_ajax ) {
1604
            do_action( 'wpinv_ajax_checkout_errors' );
1605
            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...
1606
        } else {
1607
            return false;
1608
        }
1609
    }
1610
1611
    if ( $is_ajax ) {
1612
        // Save address fields.
1613
        $address_fields = array( 'first_name', 'last_name', 'phone', 'address', 'city', 'country', 'state', 'zip', 'company' );
1614
        foreach ( $address_fields as $field ) {
1615
            if ( isset( $user[$field] ) ) {
1616
                $invoice->set( $field, $user[$field] );
1617
            }
1618
1619
            $invoice->save();
1620
        }
1621
1622
        $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...
1623
        $response['data']['subtotal']   = $invoice->get_subtotal();
1624
        $response['data']['subtotalf']  = $invoice->get_subtotal( true );
1625
        $response['data']['discount']   = $invoice->get_discount();
1626
        $response['data']['discountf']  = $invoice->get_discount( true );
1627
        $response['data']['tax']        = $invoice->get_tax();
1628
        $response['data']['taxf']       = $invoice->get_tax( true );
1629
        $response['data']['total']      = $invoice->get_total();
1630
        $response['data']['totalf']     = $invoice->get_total( true );
1631
	    $response['data']['free']       = $invoice->is_free() && ( ! ( (float) $response['data']['total'] > 0 ) || $invoice->is_free_trial() ) ? true : false;
1632
1633
        wp_send_json( $response );
1634
    }
1635
    
1636
    $user_info = array(
1637
        'user_id'        => $user['user_id'],
1638
        'first_name'     => $user['first_name'],
1639
        'last_name'      => $user['last_name'],
1640
        'email'          => $invoice->get_email(),
1641
        'company'        => $user['company'],
1642
        'phone'          => $user['phone'],
1643
        'address'        => $user['address'],
1644
        'city'           => $user['city'],
1645
        'country'        => $user['country'],
1646
        'state'          => $user['state'],
1647
        'zip'            => $user['zip'],
1648
    );
1649
    
1650
    $cart_items = wpinv_get_cart_contents();
1651
    $discounts  = wpinv_get_cart_discounts();
1652
    
1653
    // Setup invoice information
1654
    $invoice_data = array(
1655
        'invoice_id'        => !empty( $invoice ) ? $invoice->ID : 0,
1656
        'items'             => $cart_items,
1657
        'cart_discounts'    => $discounts,
1658
        'fees'              => wpinv_get_cart_fees(),        // Any arbitrary fees that have been added to the cart
1659
        'subtotal'          => wpinv_get_cart_subtotal( $cart_items ),    // Amount before taxes and discounts
1660
        'discount'          => wpinv_get_cart_items_discount_amount( $cart_items, $discounts ), // Discounted amount
1661
        'tax'               => wpinv_get_cart_tax( $cart_items ),               // Taxed amount
1662
        'price'             => wpinv_get_cart_total( $cart_items, $discounts ),    // Amount after taxes
1663
        'invoice_key'       => $invoice->get_key() ? $invoice->get_key() : $invoice->generate_key(),
1664
        'user_email'        => $invoice->get_email(),
1665
        'date'              => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ),
1666
        'user_info'         => stripslashes_deep( $user_info ),
1667
        'post_data'         => $_POST,
1668
        'cart_details'      => $cart_items,
1669
        'gateway'           => $valid_data['gateway'],
1670
        'card_info'         => $valid_data['cc_info']
1671
    );
1672
    
1673
    $vat_info   = $wpinv_euvat->current_vat_data();
1674
    if ( is_array( $vat_info ) ) {
1675
        $invoice_data['user_info']['vat_number']        = $vat_info['number'];
1676
        $invoice_data['user_info']['vat_rate']          = wpinv_get_tax_rate($invoice_data['user_info']['country'], $invoice_data['user_info']['state']);
1677
        $invoice_data['user_info']['adddress_confirmed']    = isset($vat_info['adddress_confirmed']) ? $vat_info['adddress_confirmed'] : false;
1678
1679
        // Add the VAT rate to each item in the cart
1680
        foreach( $invoice_data['cart_details'] as $key => $item_data) {
1681
            $rate = wpinv_get_tax_rate($invoice_data['user_info']['country'], $invoice_data['user_info']['state'], $item_data['id']);
1682
            $invoice_data['cart_details'][$key]['vat_rate'] = wpinv_round_amount( $rate, 4 );
1683
        }
1684
    }
1685
    
1686
    // Save vat fields.
1687
    $address_fields = array( 'vat_number', 'vat_rate', 'adddress_confirmed' );
1688
    foreach ( $address_fields as $field ) {
1689
        if ( isset( $invoice_data['user_info'][$field] ) ) {
1690
            $invoice->set( $field, $invoice_data['user_info'][$field] );
1691
        }
1692
1693
        $invoice->save();
1694
    }
1695
1696
    // Add the user data for hooks
1697
    $valid_data['user'] = $user;
1698
    
1699
    // Allow themes and plugins to hook before the gateway
1700
    do_action( 'wpinv_checkout_before_gateway', $_POST, $user_info, $valid_data );
1701
    
1702
    // If the total amount in the cart is 0, send to the manual gateway. This emulates a free invoice
1703
    if ( !$invoice_data['price'] ) {
1704
        // Revert to manual
1705
        $invoice_data['gateway'] = 'manual';
1706
        $_POST['wpi-gateway'] = 'manual';
1707
    }
1708
    
1709
    // Allow the invoice data to be modified before it is sent to the gateway
1710
    $invoice_data = apply_filters( 'wpinv_data_before_gateway', $invoice_data, $valid_data );
1711
    
1712
    if ( $invoice_data['price'] && $invoice_data['gateway'] == 'manual' ) {
1713
        $mode = 'test';
1714
    } else {
1715
        $mode = wpinv_is_test_mode( $invoice_data['gateway'] ) ? 'test' : 'live';
1716
    }
1717
    
1718
    // Setup the data we're storing in the purchase session
1719
    $session_data = $invoice_data;
1720
    // Make sure credit card numbers are never stored in sessions
1721
    if ( !empty( $session_data['card_info']['card_number'] ) ) {
1722
        unset( $session_data['card_info']['card_number'] );
1723
    }
1724
    
1725
    // Used for showing item links to non logged-in users after purchase, and for other plugins needing purchase data.
1726
    wpinv_set_checkout_session( $invoice_data );
1727
    
1728
    // Set gateway
1729
    $invoice->update_meta( '_wpinv_gateway', $invoice_data['gateway'] );
1730
    $invoice->update_meta( '_wpinv_mode', $mode );
1731
    $invoice->update_meta( '_wpinv_checkout', date_i18n( 'Y-m-d H:i:s', current_time( 'timestamp' ) ) );
1732
    
1733
    do_action( 'wpinv_checkout_before_send_to_gateway', $invoice, $invoice_data );
1734
1735
    // Send info to the gateway for payment processing
1736
    wpinv_send_to_gateway( $invoice_data['gateway'], $invoice_data );
1737
    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...
1738
}
1739
add_action( 'wpinv_payment', 'wpinv_process_checkout' );
1740
1741
function wpinv_get_invoices( $args ) {
1742
    $args = wp_parse_args( $args, array(
1743
        'status'   => array_keys( wpinv_get_invoice_statuses() ),
1744
        'type'     => 'wpi_invoice',
1745
        'parent'   => null,
1746
        'user'     => null,
1747
        'email'    => '',
1748
        'limit'    => get_option( 'posts_per_page' ),
1749
        'offset'   => null,
1750
        'page'     => 1,
1751
        'exclude'  => array(),
1752
        'orderby'  => 'date',
1753
        'order'    => 'DESC',
1754
        'return'   => 'objects',
1755
        'paginate' => false,
1756
    ) );
1757
    
1758
    // Handle some BW compatibility arg names where wp_query args differ in naming.
1759
    $map_legacy = array(
1760
        'numberposts'    => 'limit',
1761
        'post_type'      => 'type',
1762
        'post_status'    => 'status',
1763
        'post_parent'    => 'parent',
1764
        'author'         => 'user',
1765
        'posts_per_page' => 'limit',
1766
        'paged'          => 'page',
1767
    );
1768
1769
    foreach ( $map_legacy as $from => $to ) {
1770
        if ( isset( $args[ $from ] ) ) {
1771
            $args[ $to ] = $args[ $from ];
1772
        }
1773
    }
1774
1775
    if ( get_query_var( 'paged' ) )
1776
        $args['page'] = get_query_var('paged');
1777
    else if ( get_query_var( 'page' ) )
1778
        $args['page'] = get_query_var( 'page' );
1779
    else if ( !empty( $args[ 'page' ] ) )
1780
        $args['page'] = $args[ 'page' ];
1781
    else
1782
        $args['page'] = 1;
1783
1784
    /**
1785
     * Generate WP_Query args. This logic will change if orders are moved to
1786
     * custom tables in the future.
1787
     */
1788
    $wp_query_args = array(
1789
        'post_type'      => 'wpi_invoice',
1790
        'post_status'    => $args['status'],
1791
        'posts_per_page' => $args['limit'],
1792
        'meta_query'     => array(),
1793
        'date_query'     => !empty( $args['date_query'] ) ? $args['date_query'] : array(),
1794
        'fields'         => 'ids',
1795
        'orderby'        => $args['orderby'],
1796
        'order'          => $args['order'],
1797
    );
1798
    
1799
    if ( !empty( $args['user'] ) ) {
1800
        $wp_query_args['author'] = absint( $args['user'] );
1801
    }
1802
1803
    if ( ! is_null( $args['parent'] ) ) {
1804
        $wp_query_args['post_parent'] = absint( $args['parent'] );
1805
    }
1806
1807
    if ( ! is_null( $args['offset'] ) ) {
1808
        $wp_query_args['offset'] = absint( $args['offset'] );
1809
    } else {
1810
        $wp_query_args['paged'] = absint( $args['page'] );
1811
    }
1812
1813
    if ( ! empty( $args['exclude'] ) ) {
1814
        $wp_query_args['post__not_in'] = array_map( 'absint', $args['exclude'] );
1815
    }
1816
1817
    if ( ! $args['paginate' ] ) {
1818
        $wp_query_args['no_found_rows'] = true;
1819
    }
1820
1821
    $wp_query_args = apply_filters('wpinv_get_invoices_args', $wp_query_args, $args);
1822
1823
    // Get results.
1824
    $invoices = new WP_Query( $wp_query_args );
1825
1826
    if ( 'objects' === $args['return'] ) {
1827
        $return = array_map( 'wpinv_get_invoice', $invoices->posts );
1828
    } elseif ( 'self' === $args['return'] ) {
1829
        return $invoices;
1830
    } else {
1831
        $return = $invoices->posts;
1832
    }
1833
1834
    if ( $args['paginate' ] ) {
1835
        return (object) array(
1836
            'invoices'      => $return,
1837
            'total'         => $invoices->found_posts,
1838
            'max_num_pages' => $invoices->max_num_pages,
1839
        );
1840
    } else {
1841
        return $return;
1842
    }
1843
}
1844
1845
function wpinv_get_user_invoices_columns() {
1846
    $columns = array(
1847
            'invoice-number'  => array( 'title' => __( 'ID', 'invoicing' ), 'class' => 'text-left' ),
1848
            'created-date'    => array( 'title' => __( 'Created Date', 'invoicing' ), 'class' => 'text-left' ),
1849
            'payment-date'    => array( 'title' => __( 'Payment Date', 'invoicing' ), 'class' => 'text-left' ),
1850
            'invoice-status'  => array( 'title' => __( 'Status', 'invoicing' ), 'class' => 'text-center' ),
1851
            'invoice-total'   => array( 'title' => __( 'Total', 'invoicing' ), 'class' => 'text-right' ),
1852
            'invoice-actions' => array( 'title' => '&nbsp;', 'class' => 'text-center' ),
1853
        );
1854
1855
    return apply_filters( 'wpinv_user_invoices_columns', $columns );
1856
}
1857
1858
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

1858
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...
1859
    global $wpinv_receipt_args;
1860
1861
    $wpinv_receipt_args = shortcode_atts( array(
1862
        'error'           => __( 'Sorry, trouble retrieving payment receipt.', 'invoicing' ),
1863
        'price'           => true,
1864
        'discount'        => true,
1865
        'items'           => true,
1866
        'date'            => true,
1867
        'notes'           => true,
1868
        'invoice_key'     => false,
1869
        'payment_method'  => true,
1870
        'invoice_id'      => true
1871
    ), $atts, 'wpinv_receipt' );
1872
1873
    $session = wpinv_get_checkout_session();
1874
    if ( isset( $_GET['invoice_key'] ) ) {
1875
        $invoice_key = urldecode( $_GET['invoice_key'] );
1876
    } else if ( $session && isset( $session['invoice_key'] ) ) {
1877
        $invoice_key = $session['invoice_key'];
1878
    } elseif ( isset( $wpinv_receipt_args['invoice_key'] ) && $wpinv_receipt_args['invoice_key'] ) {
1879
        $invoice_key = $wpinv_receipt_args['invoice_key'];
1880
    } else if ( isset( $_GET['invoice-id'] ) ) {
1881
        $invoice_key = wpinv_get_payment_key( (int)$_GET['invoice-id'] );
1882
    }
1883
1884
    // No key found
1885
    if ( ! isset( $invoice_key ) ) {
1886
        return '<p class="alert alert-error">' . $wpinv_receipt_args['error'] . '</p>';
1887
    }
1888
1889
    $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...
1890
    $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...
1891
    if ( $user_can_view && isset( $_GET['invoice-id'] ) ) {
1892
        $invoice_id     = (int)$_GET['invoice-id'];
1893
        $user_can_view  = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? true : false;
1894
    }
1895
1896
    // Key was provided, but user is logged out. Offer them the ability to login and view the receipt
1897
    if ( ! $user_can_view && ! empty( $invoice_key ) && ! is_user_logged_in() ) {
1898
        // login redirect
1899
        return '<p class="alert alert-error">' . __( 'You are not allowed to access this section', 'invoicing' ) . '</p>';
1900
    }
1901
1902
    if ( ! apply_filters( 'wpinv_user_can_view_receipt', $user_can_view, $wpinv_receipt_args ) ) {
1903
        return '<p class="alert alert-error">' . $wpinv_receipt_args['error'] . '</p>';
1904
    }
1905
1906
    ob_start();
1907
1908
    wpinv_get_template_part( 'wpinv-invoice-receipt' );
1909
1910
    $display = ob_get_clean();
1911
1912
    return $display;
1913
}
1914
1915
function wpinv_get_invoice_id_by_key( $key ) {
1916
	global $wpdb;
1917
1918
	$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 ) );
1919
1920
	if ( $invoice_id != NULL )
1921
		return $invoice_id;
1922
1923
	return 0;
1924
}
1925
1926
function wpinv_can_view_receipt( $invoice_key = '' ) {
1927
	$return = false;
1928
1929
	if ( empty( $invoice_key ) ) {
1930
		return $return;
1931
	}
1932
1933
	global $wpinv_receipt_args;
1934
1935
	$wpinv_receipt_args['id'] = wpinv_get_invoice_id_by_key( $invoice_key );
1936
	if ( isset( $_GET['invoice-id'] ) ) {
1937
		$wpinv_receipt_args['id'] = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? (int)$_GET['invoice-id'] : 0;
1938
	}
1939
1940
	if ( empty( $wpinv_receipt_args['id'] ) ) {
1941
		return $return;
1942
	}
1943
1944
	$invoice = wpinv_get_invoice( $wpinv_receipt_args['id'] );
1945
	if ( !( !empty( $invoice->ID ) && $invoice->get_key() === $invoice_key ) ) {
1946
		return $return;
1947
	}
1948
1949
	if ( is_user_logged_in() ) {
1950
		if ( (int)$invoice->get_user_id() === (int) get_current_user_id() ) {
1951
			$return = true;
1952
		}
1953
	}
1954
1955
	$session = wpinv_get_checkout_session();
1956
	if ( isset( $_GET['invoice_key'] ) || ( $session && isset( $session['invoice_key'] ) ) ) {
1957
		$check_key = isset( $_GET['invoice_key'] ) ? $_GET['invoice_key'] : $session['invoice_key'];
1958
1959
		if ( wpinv_require_login_to_checkout() ) {
1960
			$return = $return && $check_key === $invoice_key;
1961
		} else {
1962
			$return = $check_key === $invoice_key;
1963
		}
1964
	}
1965
1966
	return (bool) apply_filters( 'wpinv_can_view_receipt', $return, $invoice_key );
1967
}
1968
1969
function wpinv_pay_for_invoice() {
1970
    global $wpinv_euvat;
1971
    
1972
    if ( isset( $_GET['invoice_key'] ) ) {
1973
        $checkout_uri   = wpinv_get_checkout_uri();
1974
        $invoice_key    = sanitize_text_field( $_GET['invoice_key'] );
1975
        
1976
        if ( empty( $invoice_key ) ) {
1977
            wpinv_set_error( 'invalid_invoice', __( 'Invoice not found', 'invoicing' ) );
1978
            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

1978
            wp_redirect( /** @scrutinizer ignore-type */ $checkout_uri );
Loading history...
1979
            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...
1980
        }
1981
        
1982
        do_action( 'wpinv_check_pay_for_invoice', $invoice_key );
1983
1984
        $invoice_id    = wpinv_get_invoice_id_by_key( $invoice_key );
1985
        $user_can_view = wpinv_can_view_receipt( $invoice_key );
1986
        if ( $user_can_view && isset( $_GET['invoice-id'] ) ) {
1987
            $invoice_id     = (int)$_GET['invoice-id'];
1988
            $user_can_view  = $invoice_key == wpinv_get_payment_key( (int)$_GET['invoice-id'] ) ? true : false;
1989
        }
1990
        
1991
        if ( $invoice_id && $user_can_view && ( $invoice = wpinv_get_invoice( $invoice_id ) ) ) {
1992
            if ( $invoice->needs_payment() ) {
1993
                $data                   = array();
1994
                $data['invoice_id']     = $invoice_id;
1995
                $data['cart_discounts'] = $invoice->get_discounts( true );
1996
                
1997
                wpinv_set_checkout_session( $data );
1998
                
1999
                if ( wpinv_get_option( 'vat_ip_country_default' ) ) {
2000
                    $_POST['country']   = $wpinv_euvat->get_country_by_ip();
2001
                    $_POST['state']     = $_POST['country'] == $invoice->country ? $invoice->state : '';
2002
                    
2003
                    wpinv_recalculate_tax( true );
2004
                }
2005
                
2006
            } else {
2007
                $checkout_uri = $invoice->get_view_url();
2008
            }
2009
        } else {
2010
            wpinv_set_error( 'invalid_invoice', __( 'You are not allowed to view this invoice', 'invoicing' ) );
2011
            
2012
            $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

2012
            $checkout_uri = is_user_logged_in() ? wpinv_get_history_page_uri() : wp_login_url( /** @scrutinizer ignore-type */ get_permalink() );
Loading history...
2013
        }
2014
        
2015
        if(wp_redirect( $checkout_uri )){
2016
            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...
2017
        };
2018
        wpinv_die();
2019
    }
2020
}
2021
add_action( 'wpinv_pay_for_invoice', 'wpinv_pay_for_invoice' );
2022
2023
function wpinv_handle_pay_via_invoice_link( $invoice_key ) {
2024
    if ( !empty( $invoice_key ) && !empty( $_REQUEST['_wpipay'] ) && !is_user_logged_in() && $invoice_id = wpinv_get_invoice_id_by_key( $invoice_key ) ) {
2025
        if ( $invoice = wpinv_get_invoice( $invoice_id ) ) {
2026
            $user_id = $invoice->get_user_id();
2027
            $secret = sanitize_text_field( $_GET['_wpipay'] );
2028
            
2029
            if ( $secret === md5( $user_id . '::' . $invoice->get_email() . '::' . $invoice_key ) ) { // valid invoice link
2030
                $redirect_to = remove_query_arg( '_wpipay', get_permalink() );
2031
                
2032
                wpinv_guest_redirect( $redirect_to, $user_id );
2033
                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...
2034
            }
2035
        }
2036
    }
2037
}
2038
add_action( 'wpinv_check_pay_for_invoice', 'wpinv_handle_pay_via_invoice_link' );
2039
2040
function wpinv_set_payment_transaction_id( $invoice_id = 0, $transaction_id = '' ) {
2041
    $invoice_id = is_object( $invoice_id ) && !empty( $invoice_id->ID ) ? $invoice_id : $invoice_id;
2042
    
2043
    if ( empty( $invoice_id ) && $invoice_id > 0 ) {
2044
        return false;
2045
    }
2046
    
2047
    if ( empty( $transaction_id ) ) {
2048
        $transaction_id = $invoice_id;
2049
    }
2050
2051
    $transaction_id = apply_filters( 'wpinv_set_payment_transaction_id', $transaction_id, $invoice_id );
2052
    
2053
    return wpinv_update_invoice_meta( $invoice_id, '_wpinv_transaction_id', $transaction_id );
2054
}
2055
2056
function wpinv_invoice_status_label( $status, $status_display = '' ) {
2057
    if ( empty( $status_display ) ) {
2058
        $status_display = wpinv_status_nicename( $status );
2059
    }
2060
    
2061
    switch ( $status ) {
2062
        case 'publish' :
2063
        case 'wpi-renewal' :
2064
            $class = 'label-success';
2065
        break;
2066
        case 'wpi-pending' :
2067
            $class = 'label-primary';
2068
        break;
2069
        case 'wpi-processing' :
2070
            $class = 'label-warning';
2071
        break;
2072
        case 'wpi-onhold' :
2073
            $class = 'label-info';
2074
        break;
2075
        case 'wpi-cancelled' :
2076
        case 'wpi-failed' :
2077
            $class = 'label-danger';
2078
        break;
2079
        default:
2080
            $class = 'label-default';
2081
        break;
2082
    }
2083
    
2084
    $label = '<span class="label label-inv-' . $status . ' ' . $class . '">' . $status_display . '</span>';
2085
    
2086
    return apply_filters( 'wpinv_invoice_status_label', $label, $status, $status_display );
2087
}
2088
2089
function wpinv_format_invoice_number( $number, $type = '' ) {
2090
    $check = apply_filters( 'wpinv_pre_format_invoice_number', null, $number, $type );
2091
    if ( null !== $check ) {
2092
        return $check;
2093
    }
2094
2095
    if ( !empty( $number ) && !is_numeric( $number ) ) {
2096
        return $number;
2097
    }
2098
2099
    $padd  = wpinv_get_option( 'invoice_number_padd' );
2100
    $prefix  = wpinv_get_option( 'invoice_number_prefix' );
2101
    $postfix = wpinv_get_option( 'invoice_number_postfix' );
2102
    
2103
    $padd = absint( $padd );
2104
    $formatted_number = absint( $number );
2105
    
2106
    if ( $padd > 0 ) {
2107
        $formatted_number = zeroise( $formatted_number, $padd );
2108
    }    
2109
2110
    $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

2110
    $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

2110
    $formatted_number = /** @scrutinizer ignore-type */ $prefix . $formatted_number . $postfix;
Loading history...
2111
2112
    return apply_filters( 'wpinv_format_invoice_number', $formatted_number, $number, $prefix, $postfix, $padd );
2113
}
2114
2115
function wpinv_get_next_invoice_number( $type = '' ) {
2116
    $check = apply_filters( 'wpinv_get_pre_next_invoice_number', null, $type );
2117
    if ( null !== $check ) {
2118
        return $check;
2119
    }
2120
    
2121
    if ( !wpinv_sequential_number_active() ) {
2122
        return false;
2123
    }
2124
2125
    $number = $last_number = get_option( 'wpinv_last_invoice_number', 0 );
2126
    $start  = wpinv_get_option( 'invoice_sequence_start', 1 );
2127
    if ( !absint( $start ) > 0 ) {
2128
        $start = 1;
2129
    }
2130
    $increment_number = true;
2131
    $save_number = false;
2132
2133
    if ( !empty( $number ) && !is_numeric( $number ) && $number == wpinv_format_invoice_number( $number ) ) {
2134
        $number = wpinv_clean_invoice_number( $number );
2135
    }
2136
2137
    if ( empty( $number ) ) {
2138
        if ( !( $last_number === 0 || $last_number === '0' ) ) {
2139
            $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 ) ) ) );
2140
2141
            if ( !empty( $last_invoice[0] ) && $invoice_number = wpinv_get_invoice_number( $last_invoice[0] ) ) {
2142
                if ( is_numeric( $invoice_number ) ) {
2143
                    $number = $invoice_number;
2144
                } else {
2145
                    $number = wpinv_clean_invoice_number( $invoice_number );
2146
                }
2147
            }
2148
2149
            if ( empty( $number ) ) {
2150
                $increment_number = false;
2151
                $number = $start;
2152
                $save_number = ( $number - 1 );
2153
            } else {
2154
                $save_number = $number;
2155
            }
2156
        }
2157
    }
2158
2159
    if ( $start > $number ) {
2160
        $increment_number = false;
2161
        $number = $start;
2162
        $save_number = ( $number - 1 );
2163
    }
2164
2165
    if ( $save_number !== false ) {
2166
        update_option( 'wpinv_last_invoice_number', $save_number );
2167
    }
2168
    
2169
    $increment_number = apply_filters( 'wpinv_increment_payment_number', $increment_number, $number );
2170
2171
    if ( $increment_number ) {
2172
        $number++;
2173
    }
2174
2175
    return apply_filters( 'wpinv_get_next_invoice_number', $number );
2176
}
2177
2178
function wpinv_clean_invoice_number( $number, $type = '' ) {
2179
    $check = apply_filters( 'wpinv_pre_clean_invoice_number', null, $number, $type );
2180
    if ( null !== $check ) {
2181
        return $check;
2182
    }
2183
    
2184
    $prefix  = wpinv_get_option( 'invoice_number_prefix' );
2185
    $postfix = wpinv_get_option( 'invoice_number_postfix' );
2186
2187
    $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

2187
    $number = preg_replace( '/' . /** @scrutinizer ignore-type */ $prefix . '/', '', $number, 1 );
Loading history...
2188
2189
    $length      = strlen( $number );
2190
    $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

2190
    $postfix_pos = strrpos( $number, /** @scrutinizer ignore-type */ $postfix );
Loading history...
2191
    
2192
    if ( false !== $postfix_pos ) {
2193
        $number      = substr_replace( $number, '', $postfix_pos, $length );
2194
    }
2195
2196
    $number = intval( $number );
2197
2198
    return apply_filters( 'wpinv_clean_invoice_number', $number, $prefix, $postfix );
2199
}
2200
2201
function wpinv_save_number_post_saved( $post_ID, $post, $update ) {
2202
    global $wpdb;
2203
2204
    if ( !$update && !get_post_meta( $post_ID, '_wpinv_number', true ) ) {
2205
        wpinv_update_invoice_number( $post_ID, $post->post_status != 'auto-draft', $post->post_type );
2206
    }
2207
2208
    if ( !$update ) {
2209
        $wpdb->update( $wpdb->posts, array( 'post_name' => wpinv_generate_post_name( $post_ID ) ), array( 'ID' => $post_ID ) );
2210
        clean_post_cache( $post_ID );
2211
    }
2212
}
2213
add_action( 'save_post_wpi_invoice', 'wpinv_save_number_post_saved', 1, 3 );
2214
2215
function wpinv_save_number_post_updated( $post_ID, $post_after, $post_before ) {
2216
    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 ) {
2217
        wpinv_update_invoice_number( $post_ID, true, $post_after->post_type );
2218
    }
2219
}
2220
add_action( 'post_updated', 'wpinv_save_number_post_updated', 1, 3 );
2221
2222
function wpinv_update_invoice_number( $post_ID, $save_sequential = false, $type = '' ) {
2223
    global $wpdb;
2224
    
2225
    $check = apply_filters( 'wpinv_pre_update_invoice_number', null, $post_ID, $save_sequential, $type );
2226
    if ( null !== $check ) {
2227
        return $check;
2228
    }
2229
2230
    if ( wpinv_sequential_number_active() ) {
2231
        $number = wpinv_get_next_invoice_number();
2232
2233
        if ( $save_sequential ) {
2234
            update_option( 'wpinv_last_invoice_number', $number );
2235
        }
2236
    } else {
2237
        $number = $post_ID;
2238
    }
2239
2240
    $number = wpinv_format_invoice_number( $number );
2241
2242
    update_post_meta( $post_ID, '_wpinv_number', $number );
2243
2244
    $wpdb->update( $wpdb->posts, array( 'post_title' => $number ), array( 'ID' => $post_ID ) );
2245
2246
    clean_post_cache( $post_ID );
2247
2248
    return $number;
2249
}
2250
2251
function wpinv_post_name_prefix( $post_type = 'wpi_invoice' ) {
2252
    return apply_filters( 'wpinv_post_name_prefix', 'inv-', $post_type );
2253
}
2254
2255
function wpinv_generate_post_name( $post_ID ) {
2256
    $prefix = wpinv_post_name_prefix( get_post_type( $post_ID ) );
2257
    $post_name = sanitize_title( $prefix . $post_ID );
2258
2259
    return apply_filters( 'wpinv_generate_post_name', $post_name, $post_ID, $prefix );
2260
}
2261
2262
function wpinv_is_invoice_viewed( $invoice_id ) {
2263
    if ( empty( $invoice_id ) ) {
2264
        return false;
2265
    }
2266
2267
    $viewed_meta = get_post_meta( $invoice_id, '_wpinv_is_viewed', true );
2268
2269
    return apply_filters( 'wpinv_is_invoice_viewed', 1 === (int)$viewed_meta, $invoice_id );
2270
}
2271
2272
function wpinv_mark_invoice_viewed() {
2273
2274
    if ( isset( $_GET['invoice_key'] ) || is_singular( 'wpi_invoice' ) || is_singular( 'wpi_quote' ) ) {
2275
        $invoice_key = isset( $_GET['invoice_key'] ) ? urldecode($_GET['invoice_key']) : '';
2276
	    global $post;
2277
2278
        if(!empty($invoice_key)){
2279
	        $invoice_id = wpinv_get_invoice_id_by_key($invoice_key);
2280
        } else if(!empty( $post ) && ($post->post_type == 'wpi_invoice' || $post->post_type == 'wpi_quote')) {
2281
			$invoice_id = $post->ID;
2282
        } else {
2283
        	return;
2284
        }
2285
2286
        $invoice = new WPInv_Invoice($invoice_id);
2287
2288
        if(!$invoice_id){
2289
            return;
2290
        }
2291
2292
	    if ( is_user_logged_in() ) {
2293
		    if ( (int)$invoice->get_user_id() === get_current_user_id() ) {
2294
			    update_post_meta($invoice_id,'_wpinv_is_viewed', 1);
2295
		    } else if ( !wpinv_require_login_to_checkout() && isset( $_GET['invoice_key'] ) && $_GET['invoice_key'] === $invoice->get_key() ) {
2296
			    update_post_meta($invoice_id,'_wpinv_is_viewed', 1);
2297
		    }
2298
	    } else {
2299
		    if ( !wpinv_require_login_to_checkout() && isset( $_GET['invoice_key'] ) && $_GET['invoice_key'] === $invoice->get_key() ) {
2300
			    update_post_meta($invoice_id,'_wpinv_is_viewed', 1);
2301
		    }
2302
	    }
2303
    }
2304
2305
}
2306
add_action( 'template_redirect', 'wpinv_mark_invoice_viewed' );
2307
2308
function wpinv_get_subscription( $invoice, $by_parent = false ) {
2309
    if ( empty( $invoice ) ) {
2310
        return false;
2311
    }
2312
    
2313
    if ( ! is_object( $invoice ) && is_scalar( $invoice ) ) {
2314
        $invoice = wpinv_get_invoice( $invoice );
2315
    }
2316
    
2317
    if ( !( is_object( $invoice ) && ! empty( $invoice->ID ) && $invoice->is_recurring() ) ) {
2318
        return false;
2319
    }
2320
    
2321
    $invoice_id = ! $by_parent && ! empty( $invoice->parent_invoice ) ? $invoice->parent_invoice : $invoice->ID;
2322
    
2323
    $subs_db    = new WPInv_Subscriptions_DB;
2324
    $subs       = $subs_db->get_subscriptions( array( 'parent_payment_id' => $invoice_id, 'number' => 1 ) );
2325
    
2326
    if ( ! empty( $subs ) ) {
2327
        return reset( $subs );
2328
    }
2329
    
2330
    return false;
2331
}
2332
2333
function wpinv_filter_posts_clauses( $clauses, $wp_query ) {
2334
    global $wpdb;
2335
2336
    if ( ! empty( $wp_query->query_vars['orderby'] ) && $wp_query->query_vars['orderby'] == 'invoice_date' ) {
2337
        if ( !empty( $clauses['join'] ) ) {
2338
            $clauses['join'] .= " ";
2339
        }
2340
2341
        if ( !empty( $clauses['fields'] ) ) {
2342
            $clauses['fields'] .= ", ";
2343
        }
2344
2345
        $clauses['join'] .= "LEFT JOIN {$wpdb->postmeta} ON ( {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_wpinv_completed_date' )";
2346
        $clauses['fields'] .= "IF( {$wpdb->postmeta}.meta_value, {$wpdb->postmeta}.meta_value, {$wpdb->posts}.post_date ) AS invoice_date";
2347
        $clauses['orderby'] = "invoice_date DESC, {$wpdb->posts}.post_date DESC, {$wpdb->posts}.ID DESC";
2348
    }
2349
2350
    return $clauses;
2351
}
2352
add_filter( 'posts_clauses', 'wpinv_filter_posts_clauses', 10, 2 );