Passed
Push — master ( 55ce22...91e0d8 )
by Brian
04:53
created

WPInv_Ajax   F

Complexity

Total Complexity 207

Size/Duplication

Total Lines 1153
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 647
dl 0
loc 1153
rs 1.953
c 2
b 0
f 0
wmc 207

23 Methods

Rating   Name   Duplication   Size   Complexity  
A wpinv_ajax_headers() 0 7 1
A init() 0 4 1
B define_ajax() 0 13 7
A do_wpinv_ajax() 0 11 3
F add_invoice_item() 0 108 21
A add_note() 0 22 6
A checkout() 0 7 2
A delete_note() 0 14 3
A get_states_field() 0 4 1
A add_ajax_events() 0 33 4
A apply_discount() 0 47 5
A run_tool() 0 12 3
B admin_apply_discount() 0 48 10
B check_email() 0 31 8
B admin_recalculate_totals() 0 62 8
F payment_form() 0 201 35
F buy_items() 0 140 30
B admin_remove_discount() 0 38 8
F create_invoice_item() 0 50 14
A get_billing_details() 0 24 4
A remove_discount() 0 24 3
C payment_form_get_taxes() 0 104 15
C remove_invoice_item() 0 83 15

How to fix   Complexity   

Complex Class

Complex classes like WPInv_Ajax often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WPInv_Ajax, and based on these observations, apply Extract Interface, too.

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
class WPInv_Ajax {
15
    public static function init() {
16
        add_action( 'init', array( __CLASS__, 'define_ajax' ), 0 );
17
        add_action( 'template_redirect', array( __CLASS__, 'do_wpinv_ajax' ), 0 );
18
        self::add_ajax_events();
19
    }
20
21
    public static function define_ajax() {
22
        if ( !empty( $_GET['wpinv-ajax'] ) ) {
23
            if ( ! defined( 'DOING_AJAX' ) ) {
24
                define( 'DOING_AJAX', true );
25
            }
26
            if ( ! defined( 'WC_DOING_AJAX' ) ) {
27
                define( 'WC_DOING_AJAX', true );
28
            }
29
            // Turn off display_errors during AJAX events to prevent malformed JSON
30
            if ( ! WP_DEBUG || ( WP_DEBUG && ! WP_DEBUG_DISPLAY ) ) {
31
                /** @scrutinizer ignore-unhandled */ @ini_set( 'display_errors', 0 );
32
            }
33
            $GLOBALS['wpdb']->hide_errors();
34
        }
35
    }
36
    
37
    public static function do_wpinv_ajax() {
38
        global $wp_query;
39
40
        if ( !empty( $_GET['wpinv-ajax'] ) ) {
41
            $wp_query->set( 'wpinv-ajax', sanitize_text_field( $_GET['wpinv-ajax'] ) );
42
        }
43
44
        if ( $action = $wp_query->get( 'wpinv-ajax' ) ) {
45
            self::wpinv_ajax_headers();
46
            do_action( 'wpinv_ajax_' . sanitize_text_field( $action ) );
47
            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...
48
        }
49
    }
50
    
51
    private static function wpinv_ajax_headers() {
52
        send_origin_headers();
53
        /** @scrutinizer ignore-unhandled */ @header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
54
        /** @scrutinizer ignore-unhandled */ @header( 'X-Robots-Tag: noindex' );
55
        send_nosniff_header();
56
        nocache_headers();
57
        status_header( 200 );
58
    }
59
    
60
    public static function add_ajax_events() {
61
        $ajax_events = array(
62
            'add_note' => false,
63
            'delete_note' => false,
64
            'get_states_field' => true,
65
            'checkout' => false,
66
            'payment_form_get_taxes' => true,
67
            'payment_form'     => true,
68
            'add_invoice_item' => false,
69
            'remove_invoice_item' => false,
70
            'create_invoice_item' => false,
71
            'get_billing_details' => false,
72
            'admin_recalculate_totals' => false,
73
            'admin_apply_discount' => false,
74
            'admin_remove_discount' => false,
75
            'check_email' => false,
76
            'run_tool' => false,
77
            'apply_discount' => true,
78
            'remove_discount' => true,
79
            'buy_items' => true,
80
        );
81
82
        foreach ( $ajax_events as $ajax_event => $nopriv ) {
83
            add_action( 'wp_ajax_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
84
            
85
            if ( !defined( 'WPI_AJAX_' . strtoupper( $nopriv ) ) ) {
86
                define( 'WPI_AJAX_' . strtoupper( $nopriv ), 1 );
87
            }
88
89
            if ( $nopriv ) {
90
                add_action( 'wp_ajax_nopriv_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
91
92
                add_action( 'wpinv_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) );
93
            }
94
        }
95
    }
96
    
97
    public static function add_note() {
98
        check_ajax_referer( 'add-invoice-note', '_nonce' );
99
100
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
101
            die(-1);
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...
102
        }
103
104
        $post_id   = absint( $_POST['post_id'] );
105
        $note      = wp_kses_post( trim( stripslashes( $_POST['note'] ) ) );
106
        $note_type = sanitize_text_field( $_POST['note_type'] );
107
108
        $is_customer_note = $note_type == 'customer' ? 1 : 0;
109
110
        if ( $post_id > 0 ) {
111
            $note_id = wpinv_insert_payment_note( $post_id, $note, $is_customer_note );
112
113
            if ( $note_id > 0 && !is_wp_error( $note_id ) ) {
114
                wpinv_get_invoice_note_line_item( $note_id );
115
            }
116
        }
117
118
        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...
119
    }
120
121
    public static function delete_note() {
122
        check_ajax_referer( 'delete-invoice-note', '_nonce' );
123
124
        if ( !wpinv_current_user_can_manage_invoicing() ) {
125
            die(-1);
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...
126
        }
127
128
        $note_id = (int)$_POST['note_id'];
129
130
        if ( $note_id > 0 ) {
131
            wp_delete_comment( $note_id, true );
132
        }
133
134
        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...
135
    }
136
    
137
    public static function get_states_field() {
138
        echo wpinv_get_states_field();
139
        
140
        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...
141
    }
142
    
143
    public static function checkout() {
144
        if ( ! defined( 'WPINV_CHECKOUT' ) ) {
145
            define( 'WPINV_CHECKOUT', true );
146
        }
147
148
        wpinv_process_checkout();
149
        die(0);
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...
150
    }
151
    
152
    public static function add_invoice_item() {
153
        global $wpi_userID, $wpinv_ip_address_country;
154
        check_ajax_referer( 'invoice-item', '_nonce' );
155
        if ( !wpinv_current_user_can_manage_invoicing() ) {
156
            die(-1);
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...
157
        }
158
        
159
        $item_id    = sanitize_text_field( $_POST['item_id'] );
160
        $invoice_id = absint( $_POST['invoice_id'] );
161
        
162
        if ( !is_numeric( $invoice_id ) || !is_numeric( $item_id ) ) {
163
            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...
164
        }
165
        
166
        $invoice    = wpinv_get_invoice( $invoice_id );
167
        if ( empty( $invoice ) ) {
168
            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...
169
        }
170
        
171
        if ( $invoice->is_paid() || $invoice->is_refunded() ) {
172
            die(); // Don't allow modify items for paid invoice.
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...
173
        }
174
        
175
        if ( !empty( $_POST['user_id'] ) ) {
176
            $wpi_userID = absint( $_POST['user_id'] ); 
177
        }
178
179
        $item = new WPInv_Item( $item_id );
180
        if ( !( !empty( $item ) && $item->post_type == 'wpi_item' ) ) {
181
            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...
182
        }
183
        
184
        // Validate item before adding to invoice because recurring item must be paid individually.
185
        if ( !empty( $invoice->cart_details ) ) {
186
            $valid = true;
187
            
188
            if ( $recurring_item = $invoice->get_recurring() ) {
189
                if ( $recurring_item != $item_id ) {
190
                    $valid = false;
191
                }
192
            } else if ( wpinv_is_recurring_item( $item_id ) ) {
193
                $valid = false;
194
            }
195
            
196
            if ( !$valid ) {
197
                $response               = array();
198
                $response['success']    = false;
199
                $response['msg']        = __( 'You can not add item because recurring item must be paid individually!', 'invoicing' );
200
                wp_send_json( $response );
201
            }
202
        }
203
        
204
        $checkout_session = wpinv_get_checkout_session();
205
        
206
        $data                   = array();
207
        $data['invoice_id']     = $invoice_id;
208
        $data['cart_discounts'] = $invoice->get_discounts( true );
209
        
210
        wpinv_set_checkout_session( $data );
211
        
212
        $quantity = wpinv_item_quantities_enabled() && !empty($_POST['qty']) && (int)$_POST['qty'] > 0 ? (int)$_POST['qty'] : 1;
213
214
        $args = array(
215
            'id'            => $item_id,
216
            'quantity'      => $quantity,
217
            'item_price'    => $item->get_price(),
218
            'custom_price'  => '',
219
            'tax'           => 0.00,
220
            'discount'      => 0,
221
            'meta'          => array(),
222
            'fees'          => array()
223
        );
224
225
        $invoice->add_item( $item_id, $args );
226
        $invoice->save();
227
        
228
        if ( empty( $_POST['country'] ) ) {
229
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
230
        }
231
        if ( empty( $_POST['state'] ) ) {
232
            $_POST['state'] = $invoice->state;
233
        }
234
         
235
        $invoice->country   = sanitize_text_field( $_POST['country'] );
236
        $invoice->state     = sanitize_text_field( $_POST['state'] );
237
        
238
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
239
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
240
        
241
        $wpinv_ip_address_country = $invoice->country;
242
243
        $invoice->recalculate_totals(true);
244
        
245
        $response                       = array();
246
        $response['success']            = true;
247
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
248
        $response['data']['subtotal']   = $invoice->get_subtotal();
249
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
250
        $response['data']['tax']        = $invoice->get_tax();
251
        $response['data']['taxf']       = $invoice->get_tax(true);
252
        $response['data']['discount']   = $invoice->get_discount();
253
        $response['data']['discountf']  = $invoice->get_discount(true);
254
        $response['data']['total']      = $invoice->get_total();
255
        $response['data']['totalf']     = $invoice->get_total(true);
256
        
257
        wpinv_set_checkout_session($checkout_session);
258
        
259
        wp_send_json( $response );
260
    }
261
262
263
    public static function remove_invoice_item() {
264
        global $wpi_userID, $wpinv_ip_address_country;
265
        
266
        check_ajax_referer( 'invoice-item', '_nonce' );
267
        if ( !wpinv_current_user_can_manage_invoicing() ) {
268
            die(-1);
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...
269
        }
270
        
271
        $item_id    = sanitize_text_field( $_POST['item_id'] );
272
        $invoice_id = absint( $_POST['invoice_id'] );
273
        $cart_index = isset( $_POST['index'] ) && $_POST['index'] >= 0 ? $_POST['index'] : false;
274
        
275
        if ( !is_numeric( $invoice_id ) || !is_numeric( $item_id ) ) {
276
            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...
277
        }
278
279
        $invoice    = wpinv_get_invoice( $invoice_id );
280
        if ( empty( $invoice ) ) {
281
            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...
282
        }
283
        
284
        if ( $invoice->is_paid() || $invoice->is_refunded() ) {
285
            die(); // Don't allow modify items for paid invoice.
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...
286
        }
287
        
288
        if ( !empty( $_POST['user_id'] ) ) {
289
            $wpi_userID = absint( $_POST['user_id'] ); 
290
        }
291
292
        $item       = new WPInv_Item( $item_id );
293
        if ( !( !empty( $item ) && $item->post_type == 'wpi_item' ) ) {
294
            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...
295
        }
296
        
297
        $checkout_session = wpinv_get_checkout_session();
298
        
299
        $data                   = array();
300
        $data['invoice_id']     = $invoice_id;
301
        $data['cart_discounts'] = $invoice->get_discounts( true );
302
        
303
        wpinv_set_checkout_session( $data );
304
305
        $args = array(
306
            'id'         => $item_id,
307
            'quantity'   => 1,
308
            'cart_index' => $cart_index
309
        );
310
311
        $invoice->remove_item( $item_id, $args );
312
        $invoice->save();
313
        
314
        if ( empty( $_POST['country'] ) ) {
315
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
316
        }
317
        if ( empty( $_POST['state'] ) ) {
318
            $_POST['state'] = $invoice->state;
319
        }
320
         
321
        $invoice->country   = sanitize_text_field( $_POST['country'] );
322
        $invoice->state     = sanitize_text_field( $_POST['state'] );
323
        
324
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
325
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
326
        
327
        $wpinv_ip_address_country = $invoice->country;
328
        
329
        $invoice->recalculate_totals(true);
330
        
331
        $response                       = array();
332
        $response['success']            = true;
333
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
334
        $response['data']['subtotal']   = $invoice->get_subtotal();
335
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
336
        $response['data']['tax']        = $invoice->get_tax();
337
        $response['data']['taxf']       = $invoice->get_tax(true);
338
        $response['data']['discount']   = $invoice->get_discount();
339
        $response['data']['discountf']  = $invoice->get_discount(true);
340
        $response['data']['total']      = $invoice->get_total();
341
        $response['data']['totalf']     = $invoice->get_total(true);
342
        
343
        wpinv_set_checkout_session($checkout_session);
344
        
345
        wp_send_json( $response );
346
    }
347
    
348
    public static function create_invoice_item() {
349
        check_ajax_referer( 'invoice-item', '_nonce' );
350
        if ( !wpinv_current_user_can_manage_invoicing() ) {
351
            die(-1);
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...
352
        }
353
        
354
        $invoice_id = absint( $_POST['invoice_id'] );
355
356
        // Find the item
357
        if ( !is_numeric( $invoice_id ) ) {
358
            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...
359
        }        
360
        
361
        $invoice     = wpinv_get_invoice( $invoice_id );
362
        if ( empty( $invoice ) ) {
363
            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...
364
        }
365
        
366
        // Validate item before adding to invoice because recurring item must be paid individually.
367
        if ( !empty( $invoice->cart_details ) && $invoice->get_recurring() ) {
368
            $response               = array();
369
            $response['success']    = false;
370
            $response['msg']        = __( 'You can not add item because recurring item must be paid individually!', 'invoicing' );
371
            wp_send_json( $response );
372
        }        
373
        
374
        $save_item = wp_unslash( $_POST['_wpinv_quick'] );
375
        
376
        $meta               = array();
377
        $meta['type']       = !empty($save_item['type']) ? sanitize_text_field($save_item['type']) : 'custom';
378
        $meta['price']      = !empty($save_item['price']) ? wpinv_sanitize_amount( $save_item['price'] ) : 0;
379
        $meta['vat_rule']   = !empty($save_item['vat_rule']) ? sanitize_text_field($save_item['vat_rule']) : 'digital';
380
        $meta['vat_class']  = !empty($save_item['vat_class']) ? sanitize_text_field($save_item['vat_class']) : '_standard';
381
        
382
        $data                   = array();
383
        $data['post_title']     = sanitize_text_field($save_item['name']);
384
        $data['post_status']    = 'publish';
385
        $data['post_excerpt']   = ! empty( $save_item['excerpt'] ) ? wp_kses_post( $save_item['excerpt'] ) : '';
386
        $data['meta']           = $meta;
387
        
388
        $item = new WPInv_Item();
389
        $item->create( $data );
390
        
391
        if ( !empty( $item ) ) {
392
            $_POST['item_id']   = $item->ID;
393
            $_POST['qty']       = !empty($save_item['qty']) && $save_item['qty'] > 0 ? (int)$save_item['qty'] : 1;
394
            
395
            self::add_invoice_item();
396
        }
397
        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...
398
    }
399
    
400
    public static function get_billing_details() {
401
        check_ajax_referer( 'get-billing-details', '_nonce' );
402
        
403
        if ( !wpinv_current_user_can_manage_invoicing() ) {
404
            die(-1);
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...
405
        }
406
407
        $user_id            = (int)$_POST['user_id'];
408
        $billing_details    = wpinv_get_user_address($user_id);
409
        $billing_details    = apply_filters( 'wpinv_fill_billing_details', $billing_details, $user_id );
410
        
411
        if (isset($billing_details['user_id'])) {
412
            unset($billing_details['user_id']);
413
        }
414
        
415
        if (isset($billing_details['email'])) {
416
            unset($billing_details['email']);
417
        }
418
419
        $response                               = array();
420
        $response['success']                    = true;
421
        $response['data']['billing_details']    = $billing_details;
422
        
423
        wp_send_json( $response );
424
    }
425
    
426
    public static function admin_recalculate_totals() {
427
        global $wpi_userID, $wpinv_ip_address_country;
428
        
429
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
430
        if ( !wpinv_current_user_can_manage_invoicing() ) {
431
            die(-1);
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...
432
        }
433
        
434
        $invoice_id = absint( $_POST['invoice_id'] );        
435
        $invoice    = wpinv_get_invoice( $invoice_id );
436
        if ( empty( $invoice ) ) {
437
            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...
438
        }
439
440
        $checkout_session = wpinv_get_checkout_session();
441
442
        $data                   = array();
443
        $data['invoice_id']     = $invoice_id;
444
        $data['cart_discounts'] = $invoice->get_discounts( true );
445
446
        wpinv_set_checkout_session( $data );
447
        
448
        if ( !empty( $_POST['user_id'] ) ) {
449
            $wpi_userID = absint( $_POST['user_id'] ); 
450
        }
451
        
452
        if ( empty( $_POST['country'] ) ) {
453
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
454
        }
455
456
        $disable_taxes = 0;
457
        if ( ! empty( $_POST['disable_taxes'] ) ) {
458
            $disable_taxes = 1;
459
        }
460
        $invoice->set( 'disable_taxes', $disable_taxes );
461
462
        $invoice->country = sanitize_text_field( $_POST['country'] );
463
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
464
        if ( isset( $_POST['state'] ) ) {
465
            $invoice->state = sanitize_text_field( $_POST['state'] );
466
            $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
467
        }
468
        
469
        $wpinv_ip_address_country = $invoice->country;
470
        
471
        $invoice = $invoice->recalculate_totals(true);
472
        
473
        $response                       = array();
474
        $response['success']            = true;
475
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
476
        $response['data']['subtotal']   = $invoice->get_subtotal();
477
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
478
        $response['data']['tax']        = $invoice->get_tax();
479
        $response['data']['taxf']       = $invoice->get_tax(true);
480
        $response['data']['discount']   = $invoice->get_discount();
481
        $response['data']['discountf']  = $invoice->get_discount(true);
482
        $response['data']['total']      = $invoice->get_total();
483
        $response['data']['totalf']     = $invoice->get_total(true);
484
        
485
        wpinv_set_checkout_session($checkout_session);
486
487
        wp_send_json( $response );
488
    }
489
    
490
    public static function admin_apply_discount() {
491
        global $wpi_userID;
492
        
493
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
494
        if ( !wpinv_current_user_can_manage_invoicing() ) {
495
            die(-1);
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...
496
        }
497
        
498
        $invoice_id = absint( $_POST['invoice_id'] );
499
        $discount_code = sanitize_text_field( $_POST['code'] );
500
        if ( empty( $invoice_id ) || empty( $discount_code ) ) {
501
            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...
502
        }
503
        
504
        $invoice = wpinv_get_invoice( $invoice_id );
505
        if ( empty( $invoice ) || ( !empty( $invoice ) && ( $invoice->is_paid() || $invoice->is_refunded() ) ) ) {
506
            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...
507
        }
508
        
509
        $checkout_session = wpinv_get_checkout_session();
510
        
511
        $data                   = array();
512
        $data['invoice_id']     = $invoice_id;
513
        $data['cart_discounts'] = $invoice->get_discounts( true );
514
        
515
        wpinv_set_checkout_session( $data );
516
        
517
        $response               = array();
518
        $response['success']    = false;
519
        $response['msg']        = __( 'This discount is invalid.', 'invoicing' );
520
        $response['data']['code'] = $discount_code;
521
        
522
        if ( wpinv_is_discount_valid( $discount_code, $invoice->get_user_id() ) ) {
523
            $discounts = wpinv_set_cart_discount( $discount_code );
0 ignored issues
show
Unused Code introduced by
The assignment to $discounts is dead and can be removed.
Loading history...
524
            
525
            $response['success'] = true;
526
            $response['msg'] = __( 'Discount has been applied successfully.', 'invoicing' );
527
        }  else {
528
            $errors = wpinv_get_errors();
529
            if ( !empty( $errors['wpinv-discount-error'] ) ) {
530
                $response['msg'] = $errors['wpinv-discount-error'];
531
            }
532
            wpinv_unset_error( 'wpinv-discount-error' );
533
        }
534
        
535
        wpinv_set_checkout_session($checkout_session);
536
        
537
        wp_send_json( $response );
538
    }
539
    
540
    public static function admin_remove_discount() {
541
        global $wpi_userID;
542
        
543
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
544
        if ( !wpinv_current_user_can_manage_invoicing() ) {
545
            die(-1);
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...
546
        }
547
        
548
        $invoice_id = absint( $_POST['invoice_id'] );
549
        $discount_code = sanitize_text_field( $_POST['code'] );
550
        if ( empty( $invoice_id ) || empty( $discount_code ) ) {
551
            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...
552
        }
553
        
554
        $invoice = wpinv_get_invoice( $invoice_id );
555
        if ( empty( $invoice ) || ( !empty( $invoice ) && ( $invoice->is_paid() || $invoice->is_refunded() ) ) ) {
556
            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...
557
        }
558
        
559
        $checkout_session = wpinv_get_checkout_session();
560
        
561
        $data                   = array();
562
        $data['invoice_id']     = $invoice_id;
563
        $data['cart_discounts'] = $invoice->get_discounts( true );
564
        
565
        wpinv_set_checkout_session( $data );
566
        
567
        $response               = array();
568
        $response['success']    = false;
569
        $response['msg']        = NULL;
570
        
571
        $discounts  = wpinv_unset_cart_discount( $discount_code );
0 ignored issues
show
Unused Code introduced by
The assignment to $discounts is dead and can be removed.
Loading history...
572
        $response['success'] = true;
573
        $response['msg'] = __( 'Discount has been removed successfully.', 'invoicing' );
574
        
575
        wpinv_set_checkout_session($checkout_session);
576
        
577
        wp_send_json( $response );
578
    }
579
    
580
    public static function check_email() {
581
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
582
        if ( !wpinv_current_user_can_manage_invoicing() ) {
583
            die(-1);
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...
584
        }
585
        
586
        $email = sanitize_text_field( $_POST['email'] );
587
        
588
        $response = array();
589
        if ( is_email( $email ) && email_exists( $email ) && $user_data = get_user_by( 'email', $email ) ) {
590
            $user_id            = $user_data->ID;
591
            $user_login         = $user_data->user_login;
592
            $display_name       = $user_data->display_name ? $user_data->display_name : $user_login;
0 ignored issues
show
Unused Code introduced by
The assignment to $display_name is dead and can be removed.
Loading history...
593
            $billing_details    = wpinv_get_user_address($user_id);
594
            $billing_details    = apply_filters( 'wpinv_fill_billing_details', $billing_details, $user_id );
595
            
596
            if (isset($billing_details['user_id'])) {
597
                unset($billing_details['user_id']);
598
            }
599
            
600
            if (isset($billing_details['email'])) {
601
                unset($billing_details['email']);
602
            }
603
            
604
            $response['success']                    = true;
605
            $response['data']['id']                 = $user_data->ID;
606
            $response['data']['name']               = $user_data->user_email;
607
            $response['data']['billing_details']    = $billing_details;
608
        }
609
        
610
        wp_send_json( $response );
611
    }
612
    
613
    public static function run_tool() {
614
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
615
        if ( !wpinv_current_user_can_manage_invoicing() ) {
616
            die(-1);
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...
617
        }
618
        
619
        $tool = sanitize_text_field( $_POST['tool'] );
620
        
621
        do_action( 'wpinv_run_tool' );
622
        
623
        if ( !empty( $tool ) ) {
624
            do_action( 'wpinv_tool_' . $tool );
625
        }
626
    }
627
    
628
    public static function apply_discount() {
629
        global $wpi_userID;
630
        
631
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
632
        
633
        $response = array();
634
        
635
        if ( isset( $_POST['code'] ) ) {
636
            $discount_code = sanitize_text_field( $_POST['code'] );
637
638
            $response['success']        = false;
639
            $response['msg']            = '';
640
            $response['data']['code']   = $discount_code;
641
            
642
            $invoice = wpinv_get_invoice_cart();
643
            if ( empty( $invoice->ID ) ) {
644
                $response['msg'] = __( 'Invalid checkout request.', 'invoicing' );
645
                wp_send_json( $response );
646
            }
647
648
            $wpi_userID = $invoice->get_user_id();
649
650
            if ( wpinv_is_discount_valid( $discount_code, $wpi_userID ) ) {
651
                $discount       = wpinv_get_discount_by_code( $discount_code );
652
                $discounts      = wpinv_set_cart_discount( $discount_code );
653
                $amount         = wpinv_format_discount_rate( wpinv_get_discount_type( $discount->ID ), wpinv_get_discount_amount( $discount->ID ) );
0 ignored issues
show
Unused Code introduced by
The assignment to $amount is dead and can be removed.
Loading history...
654
                $total          = wpinv_get_cart_total( null, $discounts );
0 ignored issues
show
Unused Code introduced by
The assignment to $total is dead and can be removed.
Loading history...
655
                $cart_totals    = wpinv_recalculate_tax( true );
656
            
657
                if ( !empty( $cart_totals ) ) {
658
                    $response['success']        = true;
659
                    $response['data']           = $cart_totals;
660
                    $response['data']['code']   = $discount_code;
661
                } else {
662
                    $response['success']        = false;
663
                }
664
            } else {
665
                $errors = wpinv_get_errors();
666
                $response['msg']  = $errors['wpinv-discount-error'];
667
                wpinv_unset_error( 'wpinv-discount-error' );
668
            }
669
670
            // Allow for custom discount code handling
671
            $response = apply_filters( 'wpinv_ajax_discount_response', $response );
672
        }
673
        
674
        wp_send_json( $response );
675
    }
676
    
677
    public static function remove_discount() {
678
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
679
        
680
        $response = array();
681
        
682
        if ( isset( $_POST['code'] ) ) {
683
            $discount_code  = sanitize_text_field( $_POST['code'] );
684
            $discounts      = wpinv_unset_cart_discount( $discount_code );
685
            $total          = wpinv_get_cart_total( null, $discounts );
0 ignored issues
show
Unused Code introduced by
The assignment to $total is dead and can be removed.
Loading history...
686
            $cart_totals    = wpinv_recalculate_tax( true );
687
            
688
            if ( !empty( $cart_totals ) ) {
689
                $response['success']        = true;
690
                $response['data']           = $cart_totals;
691
                $response['data']['code']   = $discount_code;
692
            } else {
693
                $response['success']        = false;
694
            }
695
            
696
            // Allow for custom discount code handling
697
            $response = apply_filters( 'wpinv_ajax_discount_response', $response );
698
        }
699
        
700
        wp_send_json( $response );
701
    }
702
703
    /**
704
     * Payment forms.
705
     *
706
     * @since 1.0.18
707
     */
708
    public static function payment_form() {
709
        global $invoicing, $wpi_checkout_id, $cart_total;
710
711
        // Check nonce.
712
        if ( ! isset( $_POST['wpinv_payment_form'] ) || ! wp_verify_nonce( $_POST['wpinv_payment_form'], 'wpinv_payment_form' ) ) {
713
            wp_send_json_error( __( 'Security checks failed.', 'invoicing' ) );
714
        }
715
716
        // Prepare submitted data...
717
        $data = wp_unslash( $_POST );
718
719
        // ... form fields...
720
        if ( empty( $data['form_id'] ) || 'publish' != get_post_status( $data['form_id'] ) ) {
721
            wp_send_json_error( __( 'This payment form is no longer active.', 'invoicing' ) );
722
        }
723
724
        if ( empty( $data['billing_email'] ) || ! is_email( $data['billing_email'] ) ) {
725
            wp_send_json_error( __( 'Provide a valid billing email.', 'invoicing' ) );
726
        }
727
728
        $prepared = array(
729
            'billing_email'                    => sanitize_email( $data['billing_email'] ),
730
            __( 'Billing Email', 'invoicing' ) => sanitize_email( $data['billing_email'] ),
731
            __( 'Form Id', 'invoicing' )       => absint( $data['form_id'] ),
732
        );
733
734
        $prepared['billing_email'] = sanitize_email( $data['billing_email'] );
735
736
        $fields = $invoicing->form_elements->get_form_elements( $data['form_id'] );
737
738
        // ... and form items.
739
        $items          = $invoicing->form_elements->get_form_items( $data['form_id'] );
740
        $prepared_items = array();
741
        $address_fields = array();
742
743
        if ( ! empty( $data['wpinv-items'] ) ) {
744
745
            $selected_items = wpinv_clean( $data['wpinv-items'] );
746
747
            foreach ( $items as $item ) {
748
749
                if ( ! empty( $item['required'] ) && ! isset( $selected_items[ $item['id'] ] ) ) {
750
                    wp_send_json_error( __( 'A required item is missing.', 'invoicing' ) );
751
                }
752
753
                if ( ! isset( $selected_items[ $item['id'] ] ) ) {
754
                    continue;
755
                }
756
757
                $quantity = 1;
758
759
                if ( ! empty( $item['allow_quantities'] ) && ! empty( $data["wpinv-item-{$item['id']}-quantity"] ) ) {
760
761
                    $quantity = intval( $data["wpinv-item-{$item['id']}-quantity"] );
762
763
                    if ( empty( $quantity ) ) {
764
                        $quantity = 1;
765
                    }
766
                }
767
768
                // Custom pricing.
769
                if ( ! empty( $item['custom_price'] ) ) {
770
771
                    $minimum_price = wpinv_sanitize_amount( $item['minimum_price'] );
772
                    $set_price     = wpinv_sanitize_amount( $selected_items[ $item['id'] ] );
773
774
                    if ( $set_price < $minimum_price ) {
775
                        wp_send_json_error( __( 'The provided amount is less than the minimum allowed value.', 'invoicing' ) );
776
                    }
777
778
                    $prepared_items[] = array(
779
                        'id'           =>$item['id'],
780
                        'item_price'   => wpinv_sanitize_amount( $item['price'] ),
781
                        'custom_price' => $set_price,
782
                        'name'         => $item['title'],
783
                        'quantity'     => $quantity,
784
                    );
785
786
                } else {
787
788
                    $prepared_items[] = array(
789
                        'id'           => $item['id'],
790
                        'item_price'   => wpinv_sanitize_amount( $item['price'] ),
791
                        'custom_price' => wpinv_sanitize_amount( $item['price'] ),
792
                        'name'         => $item['title'],
793
                        'quantity'     => $quantity,
794
                    );
795
796
                }
797
798
            }
799
800
        } else {
801
802
            wp_send_json_error( __( 'You have not selected any items.', 'invoicing' ) );
803
804
        }
805
806
        // Are all required fields provided?
807
        foreach ( $fields as $field ) {
808
809
            if ( ! empty( $field['premade'] ) ) {
810
                continue;
811
            }
812
813
            if ( ! empty( $field['required'] ) && empty( $data[ $field['id'] ] ) ) {
814
                wp_send_json_error( __( 'Some required fields have not been filled.', 'invoicing' ) );
815
            }
816
817
            if ( $field['type'] == 'address' ) {
818
819
                foreach ( $field['fields'] as $address_field ) {
820
821
                    if ( empty( $address_field['visible'] ) ) {
822
                        continue;
823
                    }
824
825
                    if ( ! empty( $address_field['required'] ) && empty( $data[ $address_field['name'] ] ) ) {
826
                        wp_send_json_error( __( 'Some required fields have not been filled.', 'invoicing' ) );
827
                    }
828
829
                    if ( isset( $data[ $address_field['name'] ] ) ) {
830
                        $label = str_replace( 'wpinv_', '', $address_field['name'] );
831
                        $address_fields[ $label ] = wpinv_clean( $data[ $address_field['name'] ] );
832
                    }
833
834
                }
835
836
            } else if ( isset( $data[ $field['id'] ] ) ) {
837
                $label = $field['id'];
838
839
                if ( isset( $field['label'] ) ) {
840
                    $label = $field['label'];
841
                }
842
843
                $prepared[ wpinv_clean( $label ) ] = wpinv_clean( $data[ $field['id'] ] );
844
            }
845
846
        }
847
848
        $user = get_user_by( 'email', $prepared['billing_email'] );
849
850
        if ( empty( $user ) ) {
851
            $user = wpinv_create_user( $prepared['billing_email'] );
852
        }
853
854
        if ( is_wp_error( $user ) ) {
855
            wp_send_json_error( $user->get_error_message() );
856
        }
857
858
        if ( is_numeric( $user ) ) {
859
            $user = get_user_by( 'id', $user );
860
        }
861
862
        // Create the invoice.
863
        $created = wpinv_insert_invoice(
864
            array(
865
                'status'        => 'wpi-pending',
866
                'created_via'   => 'wpi',
867
                'user_id'       => $user->ID,
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist on WP_Error.
Loading history...
868
                'cart_details'  => $prepared_items,
869
                'user_info'     => $address_fields,
870
            ),
871
            true
872
        );
873
874
        if ( is_wp_error( $created ) ) {
875
            wp_send_json_error( $created->get_error_message() );
0 ignored issues
show
Bug introduced by
The method get_error_message() does not exist on WPInv_Invoice. ( Ignorable by Annotation )

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

875
            wp_send_json_error( $created->/** @scrutinizer ignore-call */ get_error_message() );

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...
876
        }
877
878
        if ( empty( $created ) ) {
879
            wp_send_json_error( __( 'Could not create your invoice.', 'invoicing' ) );
880
        }
881
882
        unset( $prepared['billing_email'] );
883
        update_post_meta( $created->ID, 'payment_form_data', $prepared );
884
885
        $wpi_checkout_id = $created->ID;
886
        $cart_total = wpinv_price(
887
            wpinv_format_amount(
888
                wpinv_get_cart_total( $created->get_cart_details(), NULL, $created ) ),
0 ignored issues
show
Bug introduced by
The method get_cart_details() 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

888
                wpinv_get_cart_total( $created->/** @scrutinizer ignore-call */ get_cart_details(), NULL, $created ) ),

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...
889
                $created->get_currency()
0 ignored issues
show
Bug introduced by
The method get_currency() 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

889
                $created->/** @scrutinizer ignore-call */ 
890
                          get_currency()

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...
890
        );
891
892
        $data                   = array();
893
        $data['invoice_id']     = $created->ID;
894
        $data['cart_discounts'] = $created->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

894
        /** @scrutinizer ignore-call */ 
895
        $data['cart_discounts'] = $created->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...
895
896
        wpinv_set_checkout_session( $data );
897
        add_filter( 'wp_redirect', array( $invoicing->form_elements, 'send_redirect_response' ) );
898
        add_action( 'wpinv_pre_send_back_to_checkout', array( $invoicing->form_elements, 'checkout_error' ) );
899
        
900
        if ( ! defined( 'WPINV_CHECKOUT' ) ) {
901
            define( 'WPINV_CHECKOUT', true );
902
        }
903
904
        wpinv_process_checkout();
905
906
        $invoicing->form_elements->checkout_error();
907
908
        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...
909
    }
910
911
    /**
912
     * Payment forms.
913
     *
914
     * @since 1.0.18
915
     */
916
    public static function payment_form_get_taxes() {
917
        global $invoicing;
918
919
        // Check nonce.
920
        check_ajax_referer( 'wpinv_payment_form', 'wpinv_payment_form' );
921
922
        // Prepare submitted data...
923
        $data = wp_unslash( $_POST );
924
925
        // ... form fields...
926
        if ( empty( $data['form_id'] ) || 'publish' != get_post_status( $data['form_id'] ) ) {
927
            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...
928
        }
929
930
        // ... and form items.
931
        $items = $invoicing->form_elements->get_form_items( $data['form_id'] );
932
        $total = 0;
933
        $tax   = 0;
934
        $sub_total = 0;
935
936
        if ( ! empty( $data['wpinv-items'] ) ) {
937
938
            $selected_items = wpinv_clean( $data['wpinv-items'] );
939
940
            foreach ( $items as $item ) {
941
942
                if ( ! isset( $selected_items[ $item['id'] ] ) ) {
943
                    continue;
944
                }
945
946
                $quantity = 1;
947
948
                if ( ! empty( $item['allow_quantities'] ) && ! empty( $data["wpinv-item-{$item['id']}-quantity"] ) ) {
949
950
                    $quantity = intval( $data["wpinv-item-{$item['id']}-quantity"] );
951
952
                    if ( 1 > $quantity ) {
953
                        $quantity = 1;
954
                    }
955
956
                }
957
958
                // Custom pricing.
959
                $price = wpinv_sanitize_amount( $item['price'] );
960
                if ( ! empty( $item['custom_price'] ) ) {
961
962
                    $minimum_price = wpinv_sanitize_amount( $item['minimum_price'] );
963
                    $set_price     = wpinv_sanitize_amount( $selected_items[ $item['id'] ] );
964
965
                    if ( $set_price < $minimum_price ) {
966
                        $set_price = $minimum_price;
967
                    }
968
969
                    $price = wpinv_sanitize_amount( $set_price );
970
971
                }
972
973
                $price  = $quantity * floatval( $price );
974
975
                if ( wpinv_use_taxes() ) {
976
977
                    $rate = wpinv_get_tax_rate( false, false, (int) $item['id'] );
978
979
                    if ( ! wpinv_prices_include_tax() ) {
980
                        $pre_tax  = ( $price - $price * $rate * 0.01 );
981
                        $item_tax = $price - $pre_tax;
982
                    } else {
983
                        $item_tax = $price * $rate * 0.01;
984
                    }
985
986
                    $tax = $tax + $item_tax;
987
988
                }
989
990
                if ( wpinv_use_taxes() ) {
991
992
                    $rate = wpinv_get_tax_rate( false, false, (int) $item['id'] );
993
994
                    if ( wpinv_prices_include_tax() ) {
995
                        $pre_tax  = ( $price - $price * $rate * 0.01 );
996
                        $item_tax = $price - $pre_tax;
997
                    } else {
998
                        $pre_tax  = $price;
999
                        $item_tax = $price * $rate * 0.01;
1000
                    }
1001
1002
                    $tax       = $tax + $item_tax;
1003
                    $sub_total = $sub_total + $pre_tax;
1004
                    $total     = $sub_total + $tax;
1005
1006
                } else {
1007
                    $total  = $total + $price;
1008
                }
1009
1010
            }
1011
1012
        }
1013
1014
        wp_send_json_success( array(
1015
            'total'     => wpinv_price( wpinv_format_amount( $total ) ),
1016
            'tax'       => wpinv_price( wpinv_format_amount( $tax ) ),
1017
            'sub_total' => wpinv_price( wpinv_format_amount( $sub_total ) ),
1018
        ));
1019
        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...
1020
    }
1021
1022
    /**
1023
     * Lets users buy items via ajax.
1024
     *
1025
     * @since 1.0.0
1026
     */
1027
    public static function buy_items() {
1028
        $user_id = get_current_user_id();
1029
1030
        if ( empty( $user_id ) ) { // If not logged in then lets redirect to the login page
1031
            wp_send_json( array(
1032
                'success' => wp_login_url( wp_get_referer() )
0 ignored issues
show
Bug introduced by
It seems like wp_get_referer() 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

1032
                'success' => wp_login_url( /** @scrutinizer ignore-type */ wp_get_referer() )
Loading history...
1033
            ) );
1034
        } else {
1035
            // Only check nonce if logged in as it could be cached when logged out.
1036
            if ( ! isset( $_POST['wpinv_buy_nonce'] ) || ! wp_verify_nonce( $_POST['wpinv_buy_nonce'], 'wpinv_buy_items' ) ) {
1037
                wp_send_json( array(
1038
                    'error' => __( 'Security checks failed.', 'invoicing' )
1039
                ) );
1040
                wp_die();
1041
            }
1042
1043
            // allow to set a custom price through post_id
1044
            $items = $_POST['items'];
1045
            $related_post_id = isset( $_POST['post_id'] ) ? (int)$_POST['post_id'] : 0;
1046
            $custom_item_price = $related_post_id ? abs( get_post_meta( $related_post_id, '_wpi_custom_price', true ) ) : 0;
1047
1048
            $cart_items = array();
1049
            if ( $items ) {
1050
                $items = explode( ',', $items );
1051
1052
                foreach( $items as $item ) {
1053
                    $item_id = $item;
1054
                    $quantity = 1;
1055
1056
                    if ( strpos( $item, '|' ) !== false ) {
1057
                        $item_parts = explode( '|', $item );
1058
                        $item_id = $item_parts[0];
1059
                        $quantity = $item_parts[1];
1060
                    }
1061
1062
                    if ( $item_id && $quantity ) {
1063
                        $cart_items_arr = array(
1064
                            'id'            => (int)$item_id,
1065
                            'quantity'      => (int)$quantity
1066
                        );
1067
1068
                        // If there is a related post id then add it to meta
1069
                        if ( $related_post_id ) {
1070
                            $cart_items_arr['meta'] = array(
1071
                                'post_id'   => $related_post_id
1072
                            );
1073
                        }
1074
1075
                        // If there is a custom price then set it.
1076
                        if ( $custom_item_price ) {
1077
                            $cart_items_arr['custom_price'] = $custom_item_price;
1078
                        }
1079
1080
                        $cart_items[] = $cart_items_arr;
1081
                    }
1082
                }
1083
            }
1084
1085
            /**
1086
             * Filter the wpinv_buy shortcode cart items on the fly.
1087
             *
1088
             * @param array $cart_items The cart items array.
1089
             * @param int $related_post_id The related post id if any.
1090
             * @since 1.0.0
1091
             */
1092
            $cart_items = apply_filters( 'wpinv_buy_cart_items', $cart_items, $related_post_id );
1093
1094
            // Make sure its not in the cart already, if it is then redirect to checkout.
1095
            $cart_invoice = wpinv_get_invoice_cart();
1096
1097
            if ( isset( $cart_invoice->items ) && !empty( $cart_invoice->items ) && !empty( $cart_items ) && serialize( $cart_invoice->items ) == serialize( $cart_items ) ) {
1098
                wp_send_json( array(
1099
                    'success' =>  $cart_invoice->get_checkout_payment_url()
1100
                ) );
1101
                wp_die();
1102
            }
1103
1104
            // Check if user has invoice with same items waiting to be paid.
1105
            $user_invoices = wpinv_get_users_invoices( $user_id , 10 , false , 'wpi-pending' );
1106
            if ( !empty( $user_invoices ) ) {
1107
                foreach( $user_invoices as $user_invoice ) {
1108
                    $user_cart_details = array();
1109
                    $invoice  = wpinv_get_invoice( $user_invoice->ID );
1110
                    $cart_details = $invoice->get_cart_details();
1111
1112
                    if ( !empty( $cart_details ) ) {
1113
                        foreach ( $cart_details as $invoice_item ) {
1114
                            $ii_arr = array();
1115
                            $ii_arr['id'] = (int)$invoice_item['id'];
1116
                            $ii_arr['quantity'] = (int)$invoice_item['quantity'];
1117
1118
                            if (isset( $invoice_item['meta'] ) && !empty( $invoice_item['meta'] ) ) {
1119
                                $ii_arr['meta'] = $invoice_item['meta'];
1120
                            }
1121
1122
                            if ( isset( $invoice_item['custom_price'] ) && !empty( $invoice_item['custom_price'] ) ) {
1123
                                $ii_arr['custom_price'] = $invoice_item['custom_price'];
1124
                            }
1125
1126
                            $user_cart_details[] = $ii_arr;
1127
                        }
1128
                    }
1129
1130
                    if ( !empty( $user_cart_details ) && serialize( $cart_items ) == serialize( $user_cart_details ) ) {
1131
                        wp_send_json( array(
1132
                            'success' =>  $invoice->get_checkout_payment_url()
1133
                        ) );
1134
                        wp_die();
1135
                    }
1136
                }
1137
            }
1138
1139
            // Create invoice and send user to checkout
1140
            if ( !empty( $cart_items ) ) {
1141
                $invoice_data = array(
1142
                    'status'        =>  'wpi-pending',
1143
                    'created_via'   =>  'wpi',
1144
                    'user_id'       =>  $user_id,
1145
                    'cart_details'  =>  $cart_items,
1146
                );
1147
1148
                $invoice = wpinv_insert_invoice( $invoice_data, true );
1149
1150
                if ( !empty( $invoice ) && isset( $invoice->ID ) ) {
1151
                    wp_send_json( array(
1152
                        'success' =>  $invoice->get_checkout_payment_url()
0 ignored issues
show
Bug introduced by
The method get_checkout_payment_url() 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

1152
                        'success' =>  $invoice->/** @scrutinizer ignore-call */ get_checkout_payment_url()

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...
1153
                    ) );
1154
                } else {
1155
                    wp_send_json( array(
1156
                        'error' => __( 'Invoice failed to create', 'invoicing' )
1157
                    ) );
1158
                }
1159
            } else {
1160
                wp_send_json( array(
1161
                    'error' => __( 'Items not valid.', 'invoicing' )
1162
                ) );
1163
            }
1164
        }
1165
1166
        wp_die();
1167
    }
1168
}
1169
1170
WPInv_Ajax::init();