Passed
Pull Request — master (#377)
by Brian
05:03
created

WPInv_Ajax::get_invoice_items()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 30
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 30
rs 9.5555
cc 5
nc 5
nop 0
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
            'get_aui_states_field' => true,
66
            'checkout' => false,
67
            'payment_form'     => true,
68
            'get_payment_form' => true,
69
            'get_payment_form_states_field' => true,
70
            'get_invoicing_items' => false,
71
            'get_invoice_items' => false,
72
            'add_invoice_items' => false,
73
            'edit_invoice_item' => false,
74
            'add_invoice_item' => false,
75
            'remove_invoice_item' => false,
76
            'create_invoice_item' => false,
77
            'get_billing_details' => false,
78
            'admin_recalculate_totals' => false,
79
            'admin_apply_discount' => false,
80
            'admin_remove_discount' => false,
81
            'check_new_user_email' => false,
82
            'run_tool' => false,
83
            'apply_discount' => true,
84
            'remove_discount' => true,
85
            'buy_items' => true,
86
            'payment_form_refresh_prices' => true,
87
        );
88
89
        foreach ( $ajax_events as $ajax_event => $nopriv ) {
90
            add_action( 'wp_ajax_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
91
            add_action( 'wp_ajax_getpaid_' . $ajax_event, array( __CLASS__, $ajax_event ) );
92
            
93
            if ( !defined( 'WPI_AJAX_' . strtoupper( $nopriv ) ) ) {
94
                define( 'WPI_AJAX_' . strtoupper( $nopriv ), 1 );
95
            }
96
97
            if ( $nopriv ) {
98
                add_action( 'wp_ajax_nopriv_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
99
                add_action( 'wp_ajax_nopriv_getpaid_' . $ajax_event, array( __CLASS__, $ajax_event ) );
100
101
                add_action( 'wpinv_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) );
102
            }
103
        }
104
    }
105
    
106
    public static function add_note() {
107
        check_ajax_referer( 'add-invoice-note', '_nonce' );
108
109
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
110
            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...
111
        }
112
113
        $post_id   = absint( $_POST['post_id'] );
114
        $note      = wp_kses_post( trim( stripslashes( $_POST['note'] ) ) );
115
        $note_type = sanitize_text_field( $_POST['note_type'] );
116
117
        $is_customer_note = $note_type == 'customer' ? 1 : 0;
118
119
        if ( $post_id > 0 ) {
120
            $note_id = wpinv_insert_payment_note( $post_id, $note, $is_customer_note );
121
122
            if ( $note_id > 0 && !is_wp_error( $note_id ) ) {
123
                wpinv_get_invoice_note_line_item( $note_id );
124
            }
125
        }
126
127
        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...
128
    }
129
130
    public static function delete_note() {
131
        check_ajax_referer( 'delete-invoice-note', '_nonce' );
132
133
        if ( !wpinv_current_user_can_manage_invoicing() ) {
134
            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...
135
        }
136
137
        $note_id = (int)$_POST['note_id'];
138
139
        if ( $note_id > 0 ) {
140
            wp_delete_comment( $note_id, true );
141
        }
142
143
        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...
144
    }
145
    
146
    public static function get_states_field() {
147
        echo wpinv_get_states_field();
148
        
149
        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...
150
    }
151
    
152
    public static function checkout() {
153
        if ( ! defined( 'WPINV_CHECKOUT' ) ) {
154
            define( 'WPINV_CHECKOUT', true );
155
        }
156
157
        wpinv_process_checkout();
158
        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...
159
    }
160
    
161
    public static function add_invoice_item() {
162
        global $wpi_userID, $wpinv_ip_address_country;
163
        check_ajax_referer( 'invoice-item', '_nonce' );
164
        if ( !wpinv_current_user_can_manage_invoicing() ) {
165
            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...
166
        }
167
        
168
        $item_id    = sanitize_text_field( $_POST['item_id'] );
169
        $invoice_id = absint( $_POST['invoice_id'] );
170
        
171
        if ( !is_numeric( $invoice_id ) || !is_numeric( $item_id ) ) {
172
            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...
173
        }
174
        
175
        $invoice    = wpinv_get_invoice( $invoice_id );
176
        if ( empty( $invoice ) ) {
177
            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...
178
        }
179
        
180
        if ( $invoice->is_paid() || $invoice->is_refunded() ) {
181
            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...
182
        }
183
        
184
        if ( !empty( $_POST['user_id'] ) ) {
185
            $wpi_userID = absint( $_POST['user_id'] ); 
186
        }
187
188
        $item = new WPInv_Item( $item_id );
189
        if ( !( !empty( $item ) && $item->post_type == 'wpi_item' ) ) {
190
            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...
191
        }
192
        
193
        // Validate item before adding to invoice because recurring item must be paid individually.
194
        if ( !empty( $invoice->cart_details ) ) {
195
            $valid = true;
196
            
197
            if ( $recurring_item = $invoice->get_recurring() ) {
198
                if ( $recurring_item != $item_id ) {
199
                    $valid = false;
200
                }
201
            } else if ( wpinv_is_recurring_item( $item_id ) ) {
202
                $valid = false;
203
            }
204
            
205
            if ( !$valid ) {
206
                $response               = array();
207
                $response['success']    = false;
208
                $response['msg']        = __( 'You can not add item because recurring item must be paid individually!', 'invoicing' );
209
                wp_send_json( $response );
210
            }
211
        }
212
        
213
        $checkout_session = wpinv_get_checkout_session();
214
        
215
        $data                   = array();
216
        $data['invoice_id']     = $invoice_id;
217
        $data['cart_discounts'] = $invoice->get_discounts( true );
218
        
219
        wpinv_set_checkout_session( $data );
220
        
221
        $quantity = wpinv_item_quantities_enabled() && !empty($_POST['qty']) && (int)$_POST['qty'] > 0 ? (int)$_POST['qty'] : 1;
222
223
        $args = array(
224
            'id'            => $item_id,
225
            'quantity'      => $quantity,
226
            'item_price'    => $item->get_price(),
227
            'custom_price'  => '',
228
            'tax'           => 0.00,
229
            'discount'      => 0,
230
            'meta'          => array(),
231
            'fees'          => array()
232
        );
233
234
        $invoice->add_item( $item_id, $args );
235
        $invoice->save();
236
        
237
        if ( empty( $_POST['country'] ) ) {
238
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
239
        }
240
        if ( empty( $_POST['state'] ) ) {
241
            $_POST['state'] = $invoice->state;
242
        }
243
         
244
        $invoice->country   = sanitize_text_field( $_POST['country'] );
245
        $invoice->state     = sanitize_text_field( $_POST['state'] );
246
        
247
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
248
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
249
        
250
        $wpinv_ip_address_country = $invoice->country;
251
252
        $invoice->recalculate_totals(true);
253
        
254
        $response                       = array();
255
        $response['success']            = true;
256
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
257
        $response['data']['subtotal']   = $invoice->get_subtotal();
258
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
259
        $response['data']['tax']        = $invoice->get_tax();
260
        $response['data']['taxf']       = $invoice->get_tax(true);
261
        $response['data']['discount']   = $invoice->get_discount();
262
        $response['data']['discountf']  = $invoice->get_discount(true);
263
        $response['data']['total']      = $invoice->get_total();
264
        $response['data']['totalf']     = $invoice->get_total(true);
265
        
266
        wpinv_set_checkout_session($checkout_session);
267
        
268
        wp_send_json( $response );
269
    }
270
271
272
    public static function remove_invoice_item() {
273
        global $wpi_userID, $wpinv_ip_address_country;
274
        
275
        check_ajax_referer( 'invoice-item', '_nonce' );
276
        if ( !wpinv_current_user_can_manage_invoicing() ) {
277
            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...
278
        }
279
        
280
        $item_id    = sanitize_text_field( $_POST['item_id'] );
281
        $invoice_id = absint( $_POST['invoice_id'] );
282
        $cart_index = isset( $_POST['index'] ) && $_POST['index'] >= 0 ? $_POST['index'] : false;
283
        
284
        if ( !is_numeric( $invoice_id ) || !is_numeric( $item_id ) ) {
285
            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...
286
        }
287
288
        $invoice    = wpinv_get_invoice( $invoice_id );
289
        if ( empty( $invoice ) ) {
290
            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...
291
        }
292
        
293
        if ( $invoice->is_paid() || $invoice->is_refunded() ) {
294
            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...
295
        }
296
        
297
        if ( !empty( $_POST['user_id'] ) ) {
298
            $wpi_userID = absint( $_POST['user_id'] ); 
299
        }
300
301
        $item       = new WPInv_Item( $item_id );
302
        if ( !( !empty( $item ) && $item->post_type == 'wpi_item' ) ) {
303
            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...
304
        }
305
        
306
        $checkout_session = wpinv_get_checkout_session();
307
        
308
        $data                   = array();
309
        $data['invoice_id']     = $invoice_id;
310
        $data['cart_discounts'] = $invoice->get_discounts( true );
311
        
312
        wpinv_set_checkout_session( $data );
313
314
        $args = array(
315
            'id'         => $item_id,
316
            'quantity'   => 1,
317
            'cart_index' => $cart_index
318
        );
319
320
        $invoice->remove_item( $item_id, $args );
321
        $invoice->save();
322
        
323
        if ( empty( $_POST['country'] ) ) {
324
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
325
        }
326
        if ( empty( $_POST['state'] ) ) {
327
            $_POST['state'] = $invoice->state;
328
        }
329
         
330
        $invoice->country   = sanitize_text_field( $_POST['country'] );
331
        $invoice->state     = sanitize_text_field( $_POST['state'] );
332
        
333
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
334
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
335
        
336
        $wpinv_ip_address_country = $invoice->country;
337
        
338
        $invoice->recalculate_totals(true);
339
        
340
        $response                       = array();
341
        $response['success']            = true;
342
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
343
        $response['data']['subtotal']   = $invoice->get_subtotal();
344
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
345
        $response['data']['tax']        = $invoice->get_tax();
346
        $response['data']['taxf']       = $invoice->get_tax(true);
347
        $response['data']['discount']   = $invoice->get_discount();
348
        $response['data']['discountf']  = $invoice->get_discount(true);
349
        $response['data']['total']      = $invoice->get_total();
350
        $response['data']['totalf']     = $invoice->get_total(true);
351
        
352
        wpinv_set_checkout_session($checkout_session);
353
        
354
        wp_send_json( $response );
355
    }
356
    
357
    public static function create_invoice_item() {
358
        check_ajax_referer( 'invoice-item', '_nonce' );
359
        if ( !wpinv_current_user_can_manage_invoicing() ) {
360
            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...
361
        }
362
        
363
        $invoice_id = absint( $_POST['invoice_id'] );
364
365
        // Find the item
366
        if ( !is_numeric( $invoice_id ) ) {
367
            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...
368
        }        
369
        
370
        $invoice     = wpinv_get_invoice( $invoice_id );
371
        if ( empty( $invoice ) ) {
372
            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...
373
        }
374
        
375
        // Validate item before adding to invoice because recurring item must be paid individually.
376
        if ( !empty( $invoice->cart_details ) && $invoice->get_recurring() ) {
377
            $response               = array();
378
            $response['success']    = false;
379
            $response['msg']        = __( 'You can not add item because recurring item must be paid individually!', 'invoicing' );
380
            wp_send_json( $response );
381
        }        
382
        
383
        $save_item = wp_unslash( $_POST['_wpinv_quick'] );
384
        
385
        $meta               = array();
386
        $meta['type']       = !empty($save_item['type']) ? sanitize_text_field($save_item['type']) : 'custom';
387
        $meta['price']      = !empty($save_item['price']) ? wpinv_sanitize_amount( $save_item['price'] ) : 0;
388
        $meta['vat_rule']   = !empty($save_item['vat_rule']) ? sanitize_text_field($save_item['vat_rule']) : 'digital';
389
        $meta['vat_class']  = !empty($save_item['vat_class']) ? sanitize_text_field($save_item['vat_class']) : '_standard';
390
        
391
        $data                   = array();
392
        $data['post_title']     = sanitize_text_field($save_item['name']);
393
        $data['post_status']    = 'publish';
394
        $data['post_excerpt']   = ! empty( $save_item['excerpt'] ) ? wp_kses_post( $save_item['excerpt'] ) : '';
395
        $data['meta']           = $meta;
396
        
397
        $item = new WPInv_Item();
398
        $item->create( $data );
399
        
400
        if ( !empty( $item ) ) {
401
            $_POST['item_id']   = $item->ID;
402
            $_POST['qty']       = !empty($save_item['qty']) && $save_item['qty'] > 0 ? (int)$save_item['qty'] : 1;
403
            
404
            self::add_invoice_item();
405
        }
406
        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...
407
    }
408
409
    /**
410
     * Retrieves a given user's billing address.
411
     */
412
    public static function get_billing_details() {
413
414
        // Verify nonce.
415
        check_ajax_referer( 'wpinv-nonce' );
416
417
        // Can the user manage the plugin?
418
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
419
            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...
420
        }
421
422
        // Do we have a user id?
423
        $user_id = $_GET['user_id'];
424
425
        if ( empty( $user_id ) || ! is_numeric( $user_id ) ) {
426
            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...
427
        }
428
429
        // Fetch the billing details.
430
        $billing_details    = wpinv_get_user_address( $user_id );
431
        $billing_details    = apply_filters( 'wpinv_ajax_billing_details', $billing_details, $user_id );
432
433
        // unset the user id and email.
434
        $to_ignore = array( 'user_id', 'email' );
435
436
        foreach ( $to_ignore as $key ) {
437
            if ( isset( $billing_details[ $key ] ) ) {
438
                unset( $billing_details[ $key ] );
439
            }
440
        }
441
442
        wp_send_json_success( $billing_details );
443
444
    }
445
    
446
    public static function admin_recalculate_totals() {
447
        global $wpi_userID, $wpinv_ip_address_country;
448
        
449
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
450
        if ( !wpinv_current_user_can_manage_invoicing() ) {
451
            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...
452
        }
453
        
454
        $invoice_id = absint( $_POST['invoice_id'] );        
455
        $invoice    = wpinv_get_invoice( $invoice_id );
456
        if ( empty( $invoice ) ) {
457
            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...
458
        }
459
460
        $checkout_session = wpinv_get_checkout_session();
461
462
        $data                   = array();
463
        $data['invoice_id']     = $invoice_id;
464
        $data['cart_discounts'] = $invoice->get_discounts( true );
465
466
        wpinv_set_checkout_session( $data );
467
        
468
        if ( !empty( $_POST['user_id'] ) ) {
469
            $wpi_userID = absint( $_POST['user_id'] ); 
470
        }
471
        
472
        if ( empty( $_POST['country'] ) ) {
473
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
474
        }
475
476
        $disable_taxes = 0;
477
        if ( ! empty( $_POST['disable_taxes'] ) ) {
478
            $disable_taxes = 1;
479
        }
480
        $invoice->set( 'disable_taxes', $disable_taxes );
481
482
        $invoice->country = sanitize_text_field( $_POST['country'] );
483
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
484
        if ( isset( $_POST['state'] ) ) {
485
            $invoice->state = sanitize_text_field( $_POST['state'] );
486
            $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
487
        }
488
        
489
        $wpinv_ip_address_country = $invoice->country;
490
        
491
        $invoice = $invoice->recalculate_totals(true);
492
        
493
        $response                       = array();
494
        $response['success']            = true;
495
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
496
        $response['data']['subtotal']   = $invoice->get_subtotal();
497
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
498
        $response['data']['tax']        = $invoice->get_tax();
499
        $response['data']['taxf']       = $invoice->get_tax(true);
500
        $response['data']['discount']   = $invoice->get_discount();
501
        $response['data']['discountf']  = $invoice->get_discount(true);
502
        $response['data']['total']      = $invoice->get_total();
503
        $response['data']['totalf']     = $invoice->get_total(true);
504
        
505
        wpinv_set_checkout_session($checkout_session);
506
507
        wp_send_json( $response );
508
    }
509
    
510
    public static function admin_apply_discount() {
511
        global $wpi_userID;
512
        
513
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
514
        if ( !wpinv_current_user_can_manage_invoicing() ) {
515
            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...
516
        }
517
        
518
        $invoice_id = absint( $_POST['invoice_id'] );
519
        $discount_code = sanitize_text_field( $_POST['code'] );
520
        if ( empty( $invoice_id ) || empty( $discount_code ) ) {
521
            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...
522
        }
523
        
524
        $invoice = wpinv_get_invoice( $invoice_id );
525
        if ( empty( $invoice ) || ( !empty( $invoice ) && ( $invoice->is_paid() || $invoice->is_refunded() ) ) ) {
526
            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...
527
        }
528
        
529
        $checkout_session = wpinv_get_checkout_session();
530
        
531
        $data                   = array();
532
        $data['invoice_id']     = $invoice_id;
533
        $data['cart_discounts'] = $invoice->get_discounts( true );
534
        
535
        wpinv_set_checkout_session( $data );
536
        
537
        $response               = array();
538
        $response['success']    = false;
539
        $response['msg']        = __( 'This discount is invalid.', 'invoicing' );
540
        $response['data']['code'] = $discount_code;
541
        
542
        if ( wpinv_is_discount_valid( $discount_code, $invoice->get_user_id() ) ) {
543
            $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...
544
            
545
            $response['success'] = true;
546
            $response['msg'] = __( 'Discount has been applied successfully.', 'invoicing' );
547
        }  else {
548
            $errors = wpinv_get_errors();
549
            if ( !empty( $errors['wpinv-discount-error'] ) ) {
550
                $response['msg'] = $errors['wpinv-discount-error'];
551
            }
552
            wpinv_unset_error( 'wpinv-discount-error' );
553
        }
554
        
555
        wpinv_set_checkout_session($checkout_session);
556
        
557
        wp_send_json( $response );
558
    }
559
    
560
    public static function admin_remove_discount() {
561
        global $wpi_userID;
562
        
563
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
564
        if ( !wpinv_current_user_can_manage_invoicing() ) {
565
            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...
566
        }
567
        
568
        $invoice_id = absint( $_POST['invoice_id'] );
569
        $discount_code = sanitize_text_field( $_POST['code'] );
570
        if ( empty( $invoice_id ) || empty( $discount_code ) ) {
571
            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...
572
        }
573
        
574
        $invoice = wpinv_get_invoice( $invoice_id );
575
        if ( empty( $invoice ) || ( !empty( $invoice ) && ( $invoice->is_paid() || $invoice->is_refunded() ) ) ) {
576
            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...
577
        }
578
        
579
        $checkout_session = wpinv_get_checkout_session();
580
        
581
        $data                   = array();
582
        $data['invoice_id']     = $invoice_id;
583
        $data['cart_discounts'] = $invoice->get_discounts( true );
584
        
585
        wpinv_set_checkout_session( $data );
586
        
587
        $response               = array();
588
        $response['success']    = false;
589
        $response['msg']        = NULL;
590
        
591
        $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...
592
        $response['success'] = true;
593
        $response['msg'] = __( 'Discount has been removed successfully.', 'invoicing' );
594
        
595
        wpinv_set_checkout_session($checkout_session);
596
        
597
        wp_send_json( $response );
598
    }
599
600
    /**
601
     * Checks if a new users email is valid.
602
     */
603
    public static function check_new_user_email() {
604
605
        // Verify nonce.
606
        check_ajax_referer( 'wpinv-nonce' );
607
608
        // Can the user manage the plugin?
609
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
610
            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...
611
        }
612
613
        // We need an email address.
614
        if ( empty( $_GET['email'] ) ) {
615
            _e( "Provide the new user's email address", 'invoicing' );
616
            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...
617
        }
618
619
        // Ensure the email is valid.
620
        $email = sanitize_text_field( $_GET['email'] );
621
        if ( ! is_email( $email ) ) {
622
            _e( 'Invalid email address', 'invoicing' );
623
            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...
624
        }
625
626
        // And it does not exist.
627
        if ( email_exists( $email ) ) {
628
            _e( 'A user with this email address already exists', 'invoicing' );
629
            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...
630
        }
631
632
        wp_send_json_success( true );
633
    }
634
    
635
    public static function run_tool() {
636
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
637
        if ( !wpinv_current_user_can_manage_invoicing() ) {
638
            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...
639
        }
640
        
641
        $tool = sanitize_text_field( $_POST['tool'] );
642
        
643
        do_action( 'wpinv_run_tool' );
644
        
645
        if ( !empty( $tool ) ) {
646
            do_action( 'wpinv_tool_' . $tool );
647
        }
648
    }
649
    
650
    public static function apply_discount() {
651
        global $wpi_userID;
652
        
653
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
654
        
655
        $response = array();
656
        
657
        if ( isset( $_POST['code'] ) ) {
658
            $discount_code = sanitize_text_field( $_POST['code'] );
659
660
            $response['success']        = false;
661
            $response['msg']            = '';
662
            $response['data']['code']   = $discount_code;
663
            
664
            $invoice = wpinv_get_invoice_cart();
665
            if ( empty( $invoice->ID ) ) {
666
                $response['msg'] = __( 'Invalid checkout request.', 'invoicing' );
667
                wp_send_json( $response );
668
            }
669
670
            $wpi_userID = $invoice->get_user_id();
671
672
            if ( wpinv_is_discount_valid( $discount_code, $wpi_userID ) ) {
673
                $discount       = wpinv_get_discount_by_code( $discount_code );
674
                $discounts      = wpinv_set_cart_discount( $discount_code );
675
                $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...
676
                $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...
677
                $cart_totals    = wpinv_recalculate_tax( true );
678
            
679
                if ( !empty( $cart_totals ) ) {
680
                    $response['success']        = true;
681
                    $response['data']           = $cart_totals;
682
                    $response['data']['code']   = $discount_code;
683
                } else {
684
                    $response['success']        = false;
685
                }
686
            } else {
687
                $errors = wpinv_get_errors();
688
                $response['msg']  = $errors['wpinv-discount-error'];
689
                wpinv_unset_error( 'wpinv-discount-error' );
690
            }
691
692
            // Allow for custom discount code handling
693
            $response = apply_filters( 'wpinv_ajax_discount_response', $response );
694
        }
695
        
696
        wp_send_json( $response );
697
    }
698
    
699
    public static function remove_discount() {
700
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
701
        
702
        $response = array();
703
        
704
        if ( isset( $_POST['code'] ) ) {
705
            $discount_code  = sanitize_text_field( $_POST['code'] );
706
            $discounts      = wpinv_unset_cart_discount( $discount_code );
707
            $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...
708
            $cart_totals    = wpinv_recalculate_tax( true );
709
            
710
            if ( !empty( $cart_totals ) ) {
711
                $response['success']        = true;
712
                $response['data']           = $cart_totals;
713
                $response['data']['code']   = $discount_code;
714
            } else {
715
                $response['success']        = false;
716
            }
717
            
718
            // Allow for custom discount code handling
719
            $response = apply_filters( 'wpinv_ajax_discount_response', $response );
720
        }
721
        
722
        wp_send_json( $response );
723
    }
724
725
    /**
726
     * Retrieves the markup for a payment form.
727
     */
728
    public static function get_payment_form() {
729
730
        // Check nonce.
731
        if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'], 'getpaid_ajax_form' ) ) {
732
            _e( 'Error: Reload the page and try again.', 'invoicing' );
733
            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...
734
        }
735
736
        // Is the request set up correctly?
737
		if ( empty( $_GET['form'] ) && empty( $_GET['item'] ) ) {
738
			echo aui()->alert(
739
				array(
740
					'type'    => 'warning',
741
					'content' => __( 'No payment form or item provided', 'invoicing' ),
742
				)
743
            );
744
            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...
745
        }
746
747
        // Payment form or button?
748
		if ( ! empty( $_GET['form'] ) ) {
749
            echo getpaid_display_payment_form( $_GET['form'] );
750
		} else if( $_GET['invoice'] ) {
751
		    echo getpaid_display_invoice_payment_form( $_GET['invoice'] );
752
        } else {
753
			$items = getpaid_convert_items_to_array( $_GET['item'] );
754
		    echo getpaid_display_item_payment_form( $items );
755
        }
756
        
757
        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...
758
759
    }
760
761
    /**
762
     * Payment forms.
763
     *
764
     * @since 1.0.18
765
     */
766
    public static function payment_form() {
767
        global $invoicing, $wpi_checkout_id, $cart_total;
768
769
        // Check nonce.
770
        check_ajax_referer( 'getpaid_form_nonce' );
771
772
        // ... form fields...
773
        if ( empty( $_POST['getpaid_payment_form_submission'] ) ) {
774
            _e( 'Error: Reload the page and try again.', 'invoicing' );
775
            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...
776
        }
777
778
        // Load the submission.
779
        $submission = new GetPaid_Payment_Form_Submission();
780
781
        // Do we have an error?
782
        if ( ! empty( $submission->last_error ) ) {
783
            echo $submission->last_error;
784
            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...
785
        }
786
787
        // We need a billing email.
788
        if ( ! $submission->has_billing_email() || ! is_email( $submission->get_billing_email() ) ) {
789
            wp_send_json_error( __( 'Provide a valid billing email.', 'invoicing' ) );
790
        }
791
792
        // Prepare items.
793
        $items            = $submission->get_items();
794
        $prepared_items   = array();
795
796
        if ( ! empty( $items ) ) {
797
798
            foreach( $items as $item_id => $item ) {
799
800
                if ( $item->can_purchase() ) {
801
                    $prepared_items[] = array(
802
                        'id'           => $item_id,
803
                        'item_price'   => $item->get_price(),
804
                        'custom_price' => $item->get_price(),
805
                        'name'         => $item->get_name(),
806
                        'quantity'     => $item->get_quantity(),
807
                    );
808
                }
809
810
            }
811
812
        }
813
814
        if ( empty( $prepared_items ) ) {
815
            wp_send_json_error( __( 'You have not selected any items.', 'invoicing' ) );
816
        }
817
818
        if ( $submission->has_recurring && 1 != count( $prepared_items ) ) {
819
            wp_send_json_error( __( 'Recurring items should be bought individually.', 'invoicing' ) );
820
        }
821
822
        // Prepare the submission details.
823
        $prepared = array(
824
            'billing_email'                    => sanitize_email( $submission->get_billing_email() ),
825
            __( 'Billing Email', 'invoicing' ) => sanitize_email( $submission->get_billing_email() ),
826
            __( 'Form Id', 'invoicing' )       => absint( $submission->payment_form->get_id() ),
827
        );
828
829
        // Address fields.
830
        $address_fields = array();
831
832
        // Add discount code.
833
        if ( $submission->has_discount_code() ) {
834
            $address_fields['discount'] = array( $submission->get_discount_code() );
835
        }
836
837
        // Are all required fields provided?
838
        $data = $submission->get_data();
839
840
        foreach ( $submission->payment_form->get_elements() as $field ) {
841
842
            if ( ! empty( $field['premade'] ) ) {
843
                continue;
844
            }
845
846
            if ( ! $submission->is_required_field_set( $field ) ) {
847
                wp_send_json_error( __( 'Fill all required fields.', 'invoicing' ) );
848
            }
849
850
            if ( $field['type'] == 'address' ) {
851
852
                foreach ( $field['fields'] as $address_field ) {
853
854
                    if ( empty( $address_field['visible'] ) ) {
855
                        continue;
856
                    }
857
858
                    if ( ! empty( $address_field['required'] ) && empty( $data[ $address_field['name'] ] ) ) {
859
                        wp_send_json_error( __( 'Some required fields have not been filled.', 'invoicing' ) );
860
                    }
861
862
                    if ( isset( $data[ $address_field['name'] ] ) ) {
863
                        $label = str_replace( 'wpinv_', '', $address_field['name'] );
864
                        $address_fields[ $label ] = wpinv_clean( $data[ $address_field['name'] ] );
865
                    }
866
867
                }
868
869
            } else if ( isset( $data[ $field['id'] ] ) ) {
870
                $label = $field['id'];
871
872
                if ( isset( $field['label'] ) ) {
873
                    $label = $field['label'];
874
                }
875
876
                $prepared[ wpinv_clean( $label ) ] = wpinv_clean( $data[ $field['id'] ] );
877
            }
878
879
        }
880
881
        // (Maybe) create the user.
882
        $user = get_user_by( 'email', $prepared['billing_email'] );
883
884
        if ( empty( $user ) ) {
885
            $user = wpinv_create_user( $prepared['billing_email'] );
886
        }
887
888
        if ( is_wp_error( $user ) ) {
889
            wp_send_json_error( $user->get_error_message() );
890
        }
891
892
        if ( is_numeric( $user ) ) {
893
            $user = get_user_by( 'id', $user );
894
        }
895
896
        // Create the invoice.
897
        if ( ! $submission->has_invoice() ) {
898
899
            $invoice = wpinv_insert_invoice(
900
                array(
901
                    'status'        => 'wpi-pending',
902
                    'created_via'   => 'payment_form',
903
                    'user_id'       => $user->ID,
904
                    'cart_details'  => $prepared_items,
905
                    'user_info'     => $address_fields,
906
                ),
907
                true
908
            );
909
910
        } else {
911
912
            $invoice = $submission->get_invoice();
913
914
            if ( $invoice->is_paid() ) {
915
                wp_send_json_error( __( 'This invoice has already been paid for.', 'invoicing' ) );
916
            }
917
918
            $invoice = wpinv_update_invoice(
919
                array(
920
                    'ID'            => $submission->get_invoice()->ID,
921
                    'status'        => 'wpi-pending',
922
                    'cart_details'  => $prepared_items,
923
                    'user_info'     => $address_fields,
924
                ),
925
                true
926
            );
927
928
        }
929
930
        if ( is_wp_error( $invoice ) ) {
931
            wp_send_json_error( $invoice->get_error_message() );
932
        }
933
934
        if ( empty( $invoice ) ) {
935
            wp_send_json_error( __( 'Could not create your invoice.', 'invoicing' ) );
936
        }
937
938
        unset( $prepared['billing_email'] );
939
        update_post_meta( $invoice->ID, 'payment_form_data', $prepared );
940
941
        $wpi_checkout_id = $invoice->ID;
942
        $cart_total = wpinv_price(
943
            wpinv_format_amount(
944
                wpinv_get_cart_total( $invoice->get_cart_details(), NULL, $invoice ) ),
945
                $invoice->get_currency()
946
        );
947
948
        $data                   = array();
949
        $data['invoice_id']     = $invoice->ID;
950
        $data['cart_discounts'] = $invoice->get_discounts( true );
951
952
        wpinv_set_checkout_session( $data );
953
        add_filter( 'wp_redirect', array( $invoicing->form_elements, 'send_redirect_response' ) );
954
        add_action( 'wpinv_pre_send_back_to_checkout', array( $invoicing->form_elements, 'checkout_error' ) );
955
        
956
        if ( ! defined( 'WPINV_CHECKOUT' ) ) {
957
            define( 'WPINV_CHECKOUT', true );
958
        }
959
960
        wpinv_process_checkout();
961
962
        $invoicing->form_elements->checkout_error();
963
964
        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...
965
    }
966
967
    /**
968
     * Payment forms.
969
     *
970
     * @since 1.0.18
971
     */
972
    public static function get_payment_form_states_field() {
973
        global $invoicing;
974
975
        if ( empty( $_GET['country'] ) || empty( $_GET['form'] ) ) {
976
            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...
977
        }
978
979
        $elements = $invoicing->form_elements->get_form_elements( $_GET['form'] );
980
981
        if ( empty( $elements ) ) {
982
            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...
983
        }
984
985
        $address_fields = array();
986
        foreach ( $elements as $element ) {
987
            if ( 'address' === $element['type'] ) {
988
                $address_fields = $element;
989
                break;
990
            }
991
        }
992
993
        if ( empty( $address_fields ) ) {
994
            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...
995
        }
996
997
        foreach( $address_fields['fields'] as $address_field ) {
998
999
            if ( 'wpinv_state' == $address_field['name'] ) {
1000
1001
                $label = $address_field['label'];
1002
1003
                if ( ! empty( $address_field['required'] ) ) {
1004
                    $label .= "<span class='text-danger'> *</span>";
1005
                }
1006
1007
                $states = wpinv_get_country_states( $_GET['country'] );
1008
1009
                if ( ! empty( $states ) ) {
1010
1011
                    $html = aui()->select(
1012
                            array(
1013
                                'options'          => $states,
1014
                                'name'             => esc_attr( $address_field['name'] ),
1015
                                'id'               => esc_attr( $address_field['name'] ),
1016
                                'placeholder'      => esc_attr( $address_field['placeholder'] ),
1017
                                'required'         => (bool) $address_field['required'],
1018
                                'no_wrap'          => true,
1019
                                'label'            => wp_kses_post( $label ),
1020
                                'select2'          => false,
1021
                            )
1022
                        );
1023
1024
                } else {
1025
1026
                    $html = aui()->input(
1027
                            array(
1028
                                'name'       => esc_attr( $address_field['name'] ),
1029
                                'id'         => esc_attr( $address_field['name'] ),
1030
                                'required'   => (bool) $address_field['required'],
1031
                                'label'      => wp_kses_post( $label ),
1032
                                'no_wrap'    => true,
1033
                                'type'       => 'text',
1034
                            )
1035
                        );
1036
1037
                }
1038
1039
                wp_send_json_success( str_replace( 'sr-only', '', $html ) );
1040
                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...
1041
1042
            }
1043
1044
        }
1045
    
1046
        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...
1047
    }
1048
1049
    /**
1050
     * Get items belonging to a given invoice.
1051
     */
1052
    public static function get_invoice_items() {
1053
1054
        // Verify nonce.
1055
        check_ajax_referer( 'wpinv-nonce' );
1056
1057
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
1058
            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...
1059
        }
1060
1061
        // We need an invoice and items.
1062
        if ( empty( $_POST['post_id'] ) ) {
1063
            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...
1064
        }
1065
1066
        // Fetch the invoice.
1067
        $invoice = new WPInv_Invoice( trim( $_POST['post_id'] ) );
1068
1069
        // Ensure it exists.
1070
        if ( ! $invoice->get_id() ) {
1071
            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...
1072
        }
1073
1074
        // Return an array of invoice items.
1075
        $items = array();
1076
1077
        foreach ( $invoice->get_items() as $item_id => $item ) {
1078
            $items[ $item_id ] = $item->prepare_data_for_invoice_edit_ajax();
1079
        }
1080
1081
        wp_send_json_success( compact( 'items' ) );
1082
    }
1083
1084
    /**
1085
     * Edits an invoice item.
1086
     */
1087
    public static function edit_invoice_item() {
1088
1089
        // Verify nonce.
1090
        check_ajax_referer( 'wpinv-nonce' );
1091
1092
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
1093
            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...
1094
        }
1095
1096
        // We need an invoice and item details.
1097
        if ( empty( $_POST['post_id'] ) || empty( $_POST['data'] ) ) {
1098
            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...
1099
        }
1100
1101
        // Fetch the invoice.
1102
        $invoice = new WPInv_Invoice( trim( $_POST['post_id'] ) );
1103
1104
        // Ensure it exists and its not been paid for.
1105
        if ( ! $invoice->get_id() || $invoice->is_paid() || $invoice->is_refunded() ) {
1106
            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...
1107
        }
1108
1109
        // Format the data.
1110
        $data = wp_list_pluck( $_POST['data'], 'value', 'field' );
1111
1112
        // Ensure that we have an item id.
1113
        if ( empty( $data['id'] ) ) {
1114
            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...
1115
        }
1116
1117
        // Abort if the invoice does not have the specified item.
1118
        $item = $invoice->get_item( (int) $data['id'] );
1119
1120
        if ( empty( $item ) ) {
1121
            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...
1122
        }
1123
1124
        // Update the item.
1125
        $item->set_price( $data['price'] );
1126
        $item->set_name( $data['name'] );
1127
        $item->set_description( $data['description'] );
1128
        $item->set_quantity( $data['quantity'] );
1129
1130
        // Add it to the invoice.
1131
        $invoice->add_item( $item );
1132
1133
        // Update totals.
1134
        $invoice->recalculate_total();
1135
1136
        // Save the invoice.
1137
        $invoice->save();
1138
1139
        // Return an array of invoice items.
1140
        $items = array();
1141
1142
        foreach ( $invoice->get_items() as $item_id => $item ) {
1143
            $items[ $item_id ] = $item->prepare_data_for_invoice_edit_ajax();
1144
        }
1145
1146
        wp_send_json_success( compact( 'items' ) );
1147
    }
1148
    /**
1149
     * Adds a items to an invoice.
1150
     */
1151
    public static function add_invoice_items() {
1152
1153
        // Verify nonce.
1154
        check_ajax_referer( 'wpinv-nonce' );
1155
1156
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
1157
            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...
1158
        }
1159
1160
        // We need an invoice and items.
1161
        if ( empty( $_POST['post_id'] ) || empty( $_POST['items'] ) ) {
1162
            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...
1163
        }
1164
1165
        // Fetch the invoice.
1166
        $invoice = new WPInv_Invoice( trim( $_POST['post_id'] ) );
1167
        $alert   = false;
1168
1169
        // Ensure it exists and its not been paid for.
1170
        if ( ! $invoice->get_id() || $invoice->is_paid() || $invoice->is_refunded() ) {
1171
            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...
1172
        }
1173
1174
        // Add the items.
1175
        foreach ( $_POST['items'] as $data ) {
1176
1177
            $item = new GetPaid_Form_Item( $data[ 'id' ] );
1178
1179
            if ( is_numeric( $data[ 'qty' ] ) && (int) $data[ 'qty' ] > 0 ) {
1180
                $item->set_quantity( $data[ 'qty' ] );
1181
            }
1182
1183
            if ( $item->get_id() > 0 ) {
1184
                if ( ! $invoice->add_item( $item ) ) {
1185
                    $alert = __( 'An invoice can only contain one recurring item', 'invoicing' );
1186
                }
1187
            }
1188
1189
        }
1190
1191
        // Save the invoice.
1192
        $invoice->recalculate_total();
1193
        $invoice->save();
1194
1195
        // Return an array of invoice items.
1196
        $items = array();
1197
1198
        foreach ( $invoice->get_items() as $item_id => $item ) {
1199
            $items[ $item_id ] = $item->prepare_data_for_invoice_edit_ajax();
1200
        }
1201
1202
        wp_send_json_success( compact( 'items', 'alert' ) );
1203
    }
1204
1205
    /**
1206
     * Retrieves items that should be added to an invoice.
1207
     */
1208
    public static function get_invoicing_items() {
1209
1210
        // Verify nonce.
1211
        check_ajax_referer( 'wpinv-nonce' );
1212
1213
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
1214
            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...
1215
        }
1216
1217
        // We need a search term.
1218
        if ( empty( $_GET['search'] ) ) {
1219
            wp_send_json_success( array() );
1220
        }
1221
1222
        // Retrieve items.
1223
        $item_args = array(
1224
            'post_type'      => 'wpi_item',
1225
            'orderby'        => 'title',
1226
            'order'          => 'ASC',
1227
            'posts_per_page' => -1,
1228
            'post_status'    => array( 'publish' ),
1229
            's'              => trim( $_GET['search'] ),
1230
            'meta_query'     => array(
1231
                array(
1232
                    'key'       => '_wpinv_type',
1233
                    'compare'   => '!=',
1234
                    'value'     => 'package'
1235
                )
1236
            )
1237
        );
1238
1239
        $items = get_posts( apply_filters( 'getpaid_ajax_invoice_items_query_args', $item_args ) );
1240
        $data  = array();
1241
1242
        foreach ( $items as $item ) {
1243
            $item      = new GetPaid_Form_Item( $item );
1244
            $data[] = array(
1245
                'id'   => $item->get_id(),
1246
                'text' => $item->get_name()
1247
            );
1248
        }
1249
1250
        wp_send_json_success( $data );
1251
1252
    }
1253
1254
    /**
1255
     * Retrieves the states field for AUI forms.
1256
     */
1257
    public static function get_aui_states_field() {
1258
1259
        // Verify nonce.
1260
        check_ajax_referer( 'wpinv-nonce' );
1261
1262
        // We need a country.
1263
        if ( empty( $_GET['country'] ) ) {
1264
            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...
1265
        }
1266
1267
        $states = wpinv_get_country_states( trim( $_GET['country'] ) );
1268
        $state  = isset( $_GET['state'] ) ? trim( $_GET['state'] ) : wpinv_get_default_state();
1269
1270
        if ( empty( $states ) ) {
1271
1272
            $html = aui()->input(
1273
                array(
1274
                    'type'        => 'text',
1275
                    'id'          => 'wpinv_state',
1276
                    'name'        => 'wpinv_state',
1277
                    'label'       => __( 'State', 'invoicing' ),
1278
                    'label_type'  => 'vertical',
1279
                    'placeholder' => 'Liège',
1280
                    'class'       => 'form-control-sm',
1281
                    'value'       => $state,
1282
                )
1283
            );
1284
1285
        } else {
1286
1287
            $html = aui()->select(
1288
                array(
1289
                    'id'          => 'wpinv_state',
1290
                    'name'        => 'wpinv_state',
1291
                    'label'       => __( 'State', 'invoicing' ),
1292
                    'label_type'  => 'vertical',
1293
                    'placeholder' => __( 'Select a state', 'invoicing' ),
1294
                    'class'       => 'form-control-sm',
1295
                    'value'       => $state,
1296
                    'options'     => $states,
1297
                    'data-allow-clear' => 'false',
1298
                    'select2'          => true,
1299
                )
1300
            );
1301
1302
        }
1303
1304
        wp_send_json_success(
1305
            array(
1306
                'html'   => $html,
1307
                'select' => ! empty ( $states )
1308
            )
1309
        );
1310
1311
    }
1312
1313
    /**
1314
     * Refresh prices.
1315
     *
1316
     * @since 1.0.19
1317
     */
1318
    public static function payment_form_refresh_prices() {
1319
1320
        // Check nonce.
1321
        check_ajax_referer( 'getpaid_form_nonce' );
1322
1323
        // ... form fields...
1324
        if ( empty( $_POST['getpaid_payment_form_submission'] ) ) {
1325
            _e( 'Error: Reload the page and try again.', 'invoicing' );
1326
            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...
1327
        }
1328
1329
        // Load the submission.
1330
        $submission = new GetPaid_Payment_Form_Submission();
1331
1332
        // Do we have an error?
1333
        if ( ! empty( $submission->last_error ) ) {
1334
            echo $submission->last_error;
1335
            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...
1336
        }
1337
1338
        // Prepare the result.
1339
        $result = array(
1340
            'submission_id' => $submission->id,
1341
            'has_recurring' => $submission->has_recurring,
1342
            'is_free'       => $submission->get_payment_details(),
1343
            'totals'        => array(
1344
                'subtotal'  => wpinv_price( wpinv_format_amount( $submission->subtotal_amount ), $submission->get_currency() ),
1345
                'discount'  => wpinv_price( wpinv_format_amount( $submission->get_total_discount() ), $submission->get_currency() ),
1346
                'fees'      => wpinv_price( wpinv_format_amount( $submission->get_total_fees() ), $submission->get_currency() ),
1347
                'tax'       => wpinv_price( wpinv_format_amount( $submission->get_total_tax() ), $submission->get_currency() ),
1348
                'total'     => wpinv_price( wpinv_format_amount( $submission->get_total() ), $submission->get_currency() ),
1349
            ),
1350
        );
1351
1352
        // Add items.
1353
        $items = $submission->get_items();
1354
        if ( ! empty( $items ) ) {
1355
            $result['items'] = array();
1356
1357
            foreach( $items as $item_id => $item ) {
1358
                $result['items']["$item_id"] = wpinv_price( wpinv_format_amount( $item->get_price() * $item->get_qantity() ) );
1359
            }
1360
        }
1361
1362
        // Add invoice.
1363
        if ( $submission->has_invoice() ) {
1364
            $result['invoice'] = $submission->get_invoice()->ID;
1365
        }
1366
1367
        // Add discount code.
1368
        if ( $submission->has_discount_code() ) {
1369
            $result['discount_code'] = $submission->get_discount_code();
1370
        }
1371
1372
        // Filter the result.
1373
        $result = apply_filters( 'getpaid_payment_form_ajax_refresh_prices', $result, $submission );
1374
1375
        wp_send_json_success( $result );
1376
    }
1377
1378
    /**
1379
     * Lets users buy items via ajax.
1380
     *
1381
     * @since 1.0.0
1382
     */
1383
    public static function buy_items() {
1384
        $user_id = get_current_user_id();
1385
1386
        if ( empty( $user_id ) ) { // If not logged in then lets redirect to the login page
1387
            wp_send_json( array(
1388
                '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

1388
                'success' => wp_login_url( /** @scrutinizer ignore-type */ wp_get_referer() )
Loading history...
1389
            ) );
1390
        } else {
1391
            // Only check nonce if logged in as it could be cached when logged out.
1392
            if ( ! isset( $_POST['wpinv_buy_nonce'] ) || ! wp_verify_nonce( $_POST['wpinv_buy_nonce'], 'wpinv_buy_items' ) ) {
1393
                wp_send_json( array(
1394
                    'error' => __( 'Security checks failed.', 'invoicing' )
1395
                ) );
1396
                wp_die();
1397
            }
1398
1399
            // allow to set a custom price through post_id
1400
            $items = $_POST['items'];
1401
            $related_post_id = isset( $_POST['post_id'] ) ? (int)$_POST['post_id'] : 0;
1402
            $custom_item_price = $related_post_id ? abs( get_post_meta( $related_post_id, '_wpi_custom_price', true ) ) : 0;
1403
1404
            $cart_items = array();
1405
            if ( $items ) {
1406
                $items = explode( ',', $items );
1407
1408
                foreach( $items as $item ) {
1409
                    $item_id = $item;
1410
                    $quantity = 1;
1411
1412
                    if ( strpos( $item, '|' ) !== false ) {
1413
                        $item_parts = explode( '|', $item );
1414
                        $item_id = $item_parts[0];
1415
                        $quantity = $item_parts[1];
1416
                    }
1417
1418
                    if ( $item_id && $quantity ) {
1419
                        $cart_items_arr = array(
1420
                            'id'            => (int)$item_id,
1421
                            'quantity'      => (int)$quantity
1422
                        );
1423
1424
                        // If there is a related post id then add it to meta
1425
                        if ( $related_post_id ) {
1426
                            $cart_items_arr['meta'] = array(
1427
                                'post_id'   => $related_post_id
1428
                            );
1429
                        }
1430
1431
                        // If there is a custom price then set it.
1432
                        if ( $custom_item_price ) {
1433
                            $cart_items_arr['custom_price'] = $custom_item_price;
1434
                        }
1435
1436
                        $cart_items[] = $cart_items_arr;
1437
                    }
1438
                }
1439
            }
1440
1441
            /**
1442
             * Filter the wpinv_buy shortcode cart items on the fly.
1443
             *
1444
             * @param array $cart_items The cart items array.
1445
             * @param int $related_post_id The related post id if any.
1446
             * @since 1.0.0
1447
             */
1448
            $cart_items = apply_filters( 'wpinv_buy_cart_items', $cart_items, $related_post_id );
1449
1450
            // Make sure its not in the cart already, if it is then redirect to checkout.
1451
            $cart_invoice = wpinv_get_invoice_cart();
1452
1453
            if ( isset( $cart_invoice->items ) && !empty( $cart_invoice->items ) && !empty( $cart_items ) && serialize( $cart_invoice->items ) == serialize( $cart_items ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The property items does not exist on WPInv_Invoice. Since you implemented __get, consider adding a @property annotation.
Loading history...
1454
                wp_send_json( array(
1455
                    'success' =>  $cart_invoice->get_checkout_payment_url()
1456
                ) );
1457
                wp_die();
1458
            }
1459
1460
            // Check if user has invoice with same items waiting to be paid.
1461
            $user_invoices = wpinv_get_users_invoices( $user_id , 10 , false , 'wpi-pending' );
1462
            if ( !empty( $user_invoices ) ) {
1463
                foreach( $user_invoices as $user_invoice ) {
1464
                    $user_cart_details = array();
1465
                    $invoice  = wpinv_get_invoice( $user_invoice->ID );
1466
                    $cart_details = $invoice->get_cart_details();
1467
1468
                    if ( !empty( $cart_details ) ) {
1469
                        foreach ( $cart_details as $invoice_item ) {
1470
                            $ii_arr = array();
1471
                            $ii_arr['id'] = (int)$invoice_item['id'];
1472
                            $ii_arr['quantity'] = (int)$invoice_item['quantity'];
1473
1474
                            if (isset( $invoice_item['meta'] ) && !empty( $invoice_item['meta'] ) ) {
1475
                                $ii_arr['meta'] = $invoice_item['meta'];
1476
                            }
1477
1478
                            if ( isset( $invoice_item['custom_price'] ) && !empty( $invoice_item['custom_price'] ) ) {
1479
                                $ii_arr['custom_price'] = $invoice_item['custom_price'];
1480
                            }
1481
1482
                            $user_cart_details[] = $ii_arr;
1483
                        }
1484
                    }
1485
1486
                    if ( !empty( $user_cart_details ) && serialize( $cart_items ) == serialize( $user_cart_details ) ) {
1487
                        wp_send_json( array(
1488
                            'success' =>  $invoice->get_checkout_payment_url()
1489
                        ) );
1490
                        wp_die();
1491
                    }
1492
                }
1493
            }
1494
1495
            // Create invoice and send user to checkout
1496
            if ( !empty( $cart_items ) ) {
1497
                $invoice_data = array(
1498
                    'status'        =>  'wpi-pending',
1499
                    'created_via'   =>  'wpi',
1500
                    'user_id'       =>  $user_id,
1501
                    'cart_details'  =>  $cart_items,
1502
                );
1503
1504
                $invoice = wpinv_insert_invoice( $invoice_data, true );
1505
1506
                if ( !empty( $invoice ) && isset( $invoice->ID ) ) {
1507
                    wp_send_json( array(
1508
                        '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

1508
                        '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...
1509
                    ) );
1510
                } else {
1511
                    wp_send_json( array(
1512
                        'error' => __( 'Invoice failed to create', 'invoicing' )
1513
                    ) );
1514
                }
1515
            } else {
1516
                wp_send_json( array(
1517
                    'error' => __( 'Items not valid.', 'invoicing' )
1518
                ) );
1519
            }
1520
        }
1521
1522
        wp_die();
1523
    }
1524
}
1525
1526
WPInv_Ajax::init();