Passed
Push — master ( ff3d3b...35d84f )
by Brian
05:12
created

WPInv_Ajax::run_tool()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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

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

889
                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...
890
                $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

890
                $created->/** @scrutinizer ignore-call */ 
891
                          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...
891
        );
892
893
        $data                   = array();
894
        $data['invoice_id']     = $created->ID;
895
        $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

895
        /** @scrutinizer ignore-call */ 
896
        $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...
896
897
        wpinv_set_checkout_session( $data );
898
        add_filter( 'wp_redirect', array( $invoicing->form_elements, 'send_redirect_response' ) );
899
        add_action( 'wpinv_pre_send_back_to_checkout', array( $invoicing->form_elements, 'checkout_error' ) );
900
        
901
        if ( ! defined( 'WPINV_CHECKOUT' ) ) {
902
            define( 'WPINV_CHECKOUT', true );
903
        }
904
905
        wpinv_process_checkout();
906
907
        $invoicing->form_elements->checkout_error();
908
909
        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...
910
    }
911
912
    /**
913
     * Payment forms.
914
     *
915
     * @since 1.0.18
916
     */
917
    public static function get_payment_form_states_field() {
918
        global $invoicing;
919
920
        if ( empty( $_GET['country'] ) || empty( $_GET['form'] ) ) {
921
            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...
922
        }
923
924
        $elements = $invoicing->form_elements->get_form_elements( $_GET['form'] );
925
926
        if ( empty( $elements ) ) {
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
        $address_fields = array();
931
        foreach ( $elements as $element ) {
932
            if ( 'address' === $element['type'] ) {
933
                $address_fields = $element;
934
                break;
935
            }
936
        }
937
938
        if ( empty( $address_fields ) ) {
939
            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...
940
        }
941
942
        foreach( $address_fields['fields'] as $address_field ) {
943
944
            if ( 'wpinv_state' == $address_field['name'] ) {
945
946
                $label = $address_field['label'];
947
948
                if ( ! empty( $address_field['required'] ) ) {
949
                    $label .= "<span class='text-danger'> *</span>";
950
                }
951
952
                $states = wpinv_get_country_states( $_GET['country'] );
953
954
                if ( ! empty( $states ) ) {
955
956
                    $html = aui()->select(
0 ignored issues
show
Bug introduced by
The function aui was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

956
                    $html = /** @scrutinizer ignore-call */ aui()->select(
Loading history...
957
                            array(
958
                                'options'          => $states,
959
                                'name'             => esc_attr( $address_field['name'] ),
960
                                'id'               => esc_attr( $address_field['name'] ),
961
                                'placeholder'      => esc_attr( $address_field['placeholder'] ),
962
                                'required'         => (bool) $address_field['required'],
963
                                'no_wrap'          => true,
964
                                'label'            => wp_kses_post( $label ),
965
                                'select2'          => false,
966
                            )
967
                        );
968
969
                } else {
970
971
                    $html = aui()->input(
972
                            array(
973
                                'name'       => esc_attr( $address_field['name'] ),
974
                                'id'         => esc_attr( $address_field['name'] ),
975
                                'required'   => (bool) $address_field['required'],
976
                                'label'      => wp_kses_post( $label ),
977
                                'no_wrap'    => true,
978
                                'type'       => 'text',
979
                            )
980
                        );
981
982
                }
983
984
                wp_send_json_success( str_replace( 'sr-only', '', $html ) );
985
                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...
986
987
            }
988
989
        }
990
    
991
        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...
992
    }
993
994
    /**
995
     * Payment forms.
996
     *
997
     * @since 1.0.18
998
     */
999
    public static function payment_form_get_taxes() {
1000
        global $invoicing;
1001
1002
        // Check nonce.
1003
        check_ajax_referer( 'wpinv_payment_form', 'wpinv_payment_form' );
1004
1005
        // Prepare submitted data...
1006
        $data = wp_unslash( $_POST );
1007
1008
        // ... form fields...
1009
        if ( empty( $data['form_id'] ) || 'publish' != get_post_status( $data['form_id'] ) ) {
1010
            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...
1011
        }
1012
1013
        // ... and form items.
1014
        $items     = $invoicing->form_elements->get_form_items( $data['form_id'] );
1015
        $total     = 0;
1016
        $tax       = 0;
1017
        $sub_total = 0;
1018
        $country   = wpinv_default_billing_country();
1019
        $state     = false;
1020
1021
        if ( ! empty( $_POST['wpinv_country'] ) ) {
1022
            $country = $_POST['wpinv_country'];
1023
        }
1024
1025
        if ( ! empty( $_POST['wpinv_state'] ) ) {
1026
            $state = $_POST['wpinv_state'];
1027
        }
1028
1029
        if ( ! empty( $data['wpinv-items'] ) ) {
1030
1031
            $selected_items = wpinv_clean( $data['wpinv-items'] );
1032
1033
            foreach ( $items as $item ) {
1034
1035
                if ( ! isset( $selected_items[ $item['id'] ] ) ) {
1036
                    continue;
1037
                }
1038
1039
                $quantity = 1;
1040
1041
                if ( ! empty( $item['allow_quantities'] ) && ! empty( $data["wpinv-item-{$item['id']}-quantity"] ) ) {
1042
1043
                    $quantity = intval( $data["wpinv-item-{$item['id']}-quantity"] );
1044
1045
                    if ( 1 > $quantity ) {
1046
                        $quantity = 1;
1047
                    }
1048
1049
                }
1050
1051
                // Custom pricing.
1052
                $price = wpinv_sanitize_amount( $item['price'] );
1053
                if ( ! empty( $item['custom_price'] ) ) {
1054
1055
                    $minimum_price = wpinv_sanitize_amount( $item['minimum_price'] );
1056
                    $set_price     = wpinv_sanitize_amount( $selected_items[ $item['id'] ] );
1057
1058
                    if ( $set_price < $minimum_price ) {
1059
                        $set_price = $minimum_price;
1060
                    }
1061
1062
                    $price = wpinv_sanitize_amount( $set_price );
1063
1064
                }
1065
1066
                $price  = $quantity * floatval( $price );
1067
1068
                if ( wpinv_use_taxes() ) {
1069
1070
                    $rate = wpinv_get_tax_rate( $country, $state, (int) $item['id'] );
1071
1072
                    if ( wpinv_prices_include_tax() ) {
1073
                        $pre_tax  = ( $price - $price * $rate * 0.01 );
1074
                        $item_tax = $price - $pre_tax;
1075
                    } else {
1076
                        $pre_tax  = $price;
1077
                        $item_tax = $price * $rate * 0.01;
1078
                    }
1079
1080
                    $tax       = $tax + $item_tax;
1081
                    $sub_total = $sub_total + $pre_tax;
1082
                    $total     = $sub_total + $tax;
1083
1084
                } else {
1085
                    $total  = $total + $price;
1086
                }
1087
1088
            }
1089
1090
        }
1091
1092
        wp_send_json_success( array(
1093
            'total'     => wpinv_price( wpinv_format_amount( $total ) ),
1094
            'tax'       => wpinv_price( wpinv_format_amount( $tax ) ),
1095
            'sub_total' => wpinv_price( wpinv_format_amount( $sub_total ) ),
1096
        ));
1097
        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...
1098
    }
1099
1100
    /**
1101
     * Lets users buy items via ajax.
1102
     *
1103
     * @since 1.0.0
1104
     */
1105
    public static function buy_items() {
1106
        $user_id = get_current_user_id();
1107
1108
        if ( empty( $user_id ) ) { // If not logged in then lets redirect to the login page
1109
            wp_send_json( array(
1110
                '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

1110
                'success' => wp_login_url( /** @scrutinizer ignore-type */ wp_get_referer() )
Loading history...
1111
            ) );
1112
        } else {
1113
            // Only check nonce if logged in as it could be cached when logged out.
1114
            if ( ! isset( $_POST['wpinv_buy_nonce'] ) || ! wp_verify_nonce( $_POST['wpinv_buy_nonce'], 'wpinv_buy_items' ) ) {
1115
                wp_send_json( array(
1116
                    'error' => __( 'Security checks failed.', 'invoicing' )
1117
                ) );
1118
                wp_die();
1119
            }
1120
1121
            // allow to set a custom price through post_id
1122
            $items = $_POST['items'];
1123
            $related_post_id = isset( $_POST['post_id'] ) ? (int)$_POST['post_id'] : 0;
1124
            $custom_item_price = $related_post_id ? abs( get_post_meta( $related_post_id, '_wpi_custom_price', true ) ) : 0;
1125
1126
            $cart_items = array();
1127
            if ( $items ) {
1128
                $items = explode( ',', $items );
1129
1130
                foreach( $items as $item ) {
1131
                    $item_id = $item;
1132
                    $quantity = 1;
1133
1134
                    if ( strpos( $item, '|' ) !== false ) {
1135
                        $item_parts = explode( '|', $item );
1136
                        $item_id = $item_parts[0];
1137
                        $quantity = $item_parts[1];
1138
                    }
1139
1140
                    if ( $item_id && $quantity ) {
1141
                        $cart_items_arr = array(
1142
                            'id'            => (int)$item_id,
1143
                            'quantity'      => (int)$quantity
1144
                        );
1145
1146
                        // If there is a related post id then add it to meta
1147
                        if ( $related_post_id ) {
1148
                            $cart_items_arr['meta'] = array(
1149
                                'post_id'   => $related_post_id
1150
                            );
1151
                        }
1152
1153
                        // If there is a custom price then set it.
1154
                        if ( $custom_item_price ) {
1155
                            $cart_items_arr['custom_price'] = $custom_item_price;
1156
                        }
1157
1158
                        $cart_items[] = $cart_items_arr;
1159
                    }
1160
                }
1161
            }
1162
1163
            /**
1164
             * Filter the wpinv_buy shortcode cart items on the fly.
1165
             *
1166
             * @param array $cart_items The cart items array.
1167
             * @param int $related_post_id The related post id if any.
1168
             * @since 1.0.0
1169
             */
1170
            $cart_items = apply_filters( 'wpinv_buy_cart_items', $cart_items, $related_post_id );
1171
1172
            // Make sure its not in the cart already, if it is then redirect to checkout.
1173
            $cart_invoice = wpinv_get_invoice_cart();
1174
1175
            if ( isset( $cart_invoice->items ) && !empty( $cart_invoice->items ) && !empty( $cart_items ) && serialize( $cart_invoice->items ) == serialize( $cart_items ) ) {
1176
                wp_send_json( array(
1177
                    'success' =>  $cart_invoice->get_checkout_payment_url()
1178
                ) );
1179
                wp_die();
1180
            }
1181
1182
            // Check if user has invoice with same items waiting to be paid.
1183
            $user_invoices = wpinv_get_users_invoices( $user_id , 10 , false , 'wpi-pending' );
1184
            if ( !empty( $user_invoices ) ) {
1185
                foreach( $user_invoices as $user_invoice ) {
1186
                    $user_cart_details = array();
1187
                    $invoice  = wpinv_get_invoice( $user_invoice->ID );
1188
                    $cart_details = $invoice->get_cart_details();
1189
1190
                    if ( !empty( $cart_details ) ) {
1191
                        foreach ( $cart_details as $invoice_item ) {
1192
                            $ii_arr = array();
1193
                            $ii_arr['id'] = (int)$invoice_item['id'];
1194
                            $ii_arr['quantity'] = (int)$invoice_item['quantity'];
1195
1196
                            if (isset( $invoice_item['meta'] ) && !empty( $invoice_item['meta'] ) ) {
1197
                                $ii_arr['meta'] = $invoice_item['meta'];
1198
                            }
1199
1200
                            if ( isset( $invoice_item['custom_price'] ) && !empty( $invoice_item['custom_price'] ) ) {
1201
                                $ii_arr['custom_price'] = $invoice_item['custom_price'];
1202
                            }
1203
1204
                            $user_cart_details[] = $ii_arr;
1205
                        }
1206
                    }
1207
1208
                    if ( !empty( $user_cart_details ) && serialize( $cart_items ) == serialize( $user_cart_details ) ) {
1209
                        wp_send_json( array(
1210
                            'success' =>  $invoice->get_checkout_payment_url()
1211
                        ) );
1212
                        wp_die();
1213
                    }
1214
                }
1215
            }
1216
1217
            // Create invoice and send user to checkout
1218
            if ( !empty( $cart_items ) ) {
1219
                $invoice_data = array(
1220
                    'status'        =>  'wpi-pending',
1221
                    'created_via'   =>  'wpi',
1222
                    'user_id'       =>  $user_id,
1223
                    'cart_details'  =>  $cart_items,
1224
                );
1225
1226
                $invoice = wpinv_insert_invoice( $invoice_data, true );
1227
1228
                if ( !empty( $invoice ) && isset( $invoice->ID ) ) {
1229
                    wp_send_json( array(
1230
                        '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

1230
                        '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...
1231
                    ) );
1232
                } else {
1233
                    wp_send_json( array(
1234
                        'error' => __( 'Invoice failed to create', 'invoicing' )
1235
                    ) );
1236
                }
1237
            } else {
1238
                wp_send_json( array(
1239
                    'error' => __( 'Items not valid.', 'invoicing' )
1240
                ) );
1241
            }
1242
        }
1243
1244
        wp_die();
1245
    }
1246
}
1247
1248
WPInv_Ajax::init();