Passed
Pull Request — master (#375)
by Brian
101:34
created

WPInv_Ajax::init()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 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
            'checkout' => false,
66
            'payment_form_get_taxes' => true,
67
            'payment_form'     => true,
68
            'payment_form_discount' => true,
69
            'get_payment_form' => true,
70
            'get_payment_form_states_field' => true,
71
            'add_invoice_item' => false,
72
            'remove_invoice_item' => false,
73
            'create_invoice_item' => false,
74
            'get_billing_details' => false,
75
            'admin_recalculate_totals' => false,
76
            'admin_apply_discount' => false,
77
            'admin_remove_discount' => false,
78
            'check_email' => false,
79
            'run_tool' => false,
80
            'apply_discount' => true,
81
            'remove_discount' => true,
82
            'buy_items' => true,
83
            'payment_form_refresh_prices' => true,
84
        );
85
86
        foreach ( $ajax_events as $ajax_event => $nopriv ) {
87
            add_action( 'wp_ajax_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
88
            add_action( 'wp_ajax_getpaid_' . $ajax_event, array( __CLASS__, $ajax_event ) );
89
            
90
            if ( !defined( 'WPI_AJAX_' . strtoupper( $nopriv ) ) ) {
91
                define( 'WPI_AJAX_' . strtoupper( $nopriv ), 1 );
92
            }
93
94
            if ( $nopriv ) {
95
                add_action( 'wp_ajax_nopriv_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
96
                add_action( 'wp_ajax_nopriv_getpaid_' . $ajax_event, array( __CLASS__, $ajax_event ) );
97
98
                add_action( 'wpinv_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) );
99
            }
100
        }
101
    }
102
    
103
    public static function add_note() {
104
        check_ajax_referer( 'add-invoice-note', '_nonce' );
105
106
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
107
            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...
108
        }
109
110
        $post_id   = absint( $_POST['post_id'] );
111
        $note      = wp_kses_post( trim( stripslashes( $_POST['note'] ) ) );
112
        $note_type = sanitize_text_field( $_POST['note_type'] );
113
114
        $is_customer_note = $note_type == 'customer' ? 1 : 0;
115
116
        if ( $post_id > 0 ) {
117
            $note_id = wpinv_insert_payment_note( $post_id, $note, $is_customer_note );
118
119
            if ( $note_id > 0 && !is_wp_error( $note_id ) ) {
120
                wpinv_get_invoice_note_line_item( $note_id );
121
            }
122
        }
123
124
        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...
125
    }
126
127
    public static function delete_note() {
128
        check_ajax_referer( 'delete-invoice-note', '_nonce' );
129
130
        if ( !wpinv_current_user_can_manage_invoicing() ) {
131
            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...
132
        }
133
134
        $note_id = (int)$_POST['note_id'];
135
136
        if ( $note_id > 0 ) {
137
            wp_delete_comment( $note_id, true );
138
        }
139
140
        die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
141
    }
142
    
143
    public static function get_states_field() {
144
        echo wpinv_get_states_field();
145
        
146
        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...
147
    }
148
    
149
    public static function checkout() {
150
        if ( ! defined( 'WPINV_CHECKOUT' ) ) {
151
            define( 'WPINV_CHECKOUT', true );
152
        }
153
154
        wpinv_process_checkout();
155
        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...
156
    }
157
    
158
    public static function add_invoice_item() {
159
        global $wpi_userID, $wpinv_ip_address_country;
160
        check_ajax_referer( 'invoice-item', '_nonce' );
161
        if ( !wpinv_current_user_can_manage_invoicing() ) {
162
            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...
163
        }
164
        
165
        $item_id    = sanitize_text_field( $_POST['item_id'] );
166
        $invoice_id = absint( $_POST['invoice_id'] );
167
        
168
        if ( !is_numeric( $invoice_id ) || !is_numeric( $item_id ) ) {
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
        $invoice    = wpinv_get_invoice( $invoice_id );
173
        if ( empty( $invoice ) ) {
174
            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...
175
        }
176
        
177
        if ( $invoice->is_paid() || $invoice->is_refunded() ) {
178
            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...
179
        }
180
        
181
        if ( !empty( $_POST['user_id'] ) ) {
182
            $wpi_userID = absint( $_POST['user_id'] ); 
183
        }
184
185
        $item = new WPInv_Item( $item_id );
186
        if ( !( !empty( $item ) && $item->post_type == 'wpi_item' ) ) {
187
            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...
188
        }
189
        
190
        // Validate item before adding to invoice because recurring item must be paid individually.
191
        if ( !empty( $invoice->cart_details ) ) {
192
            $valid = true;
193
            
194
            if ( $recurring_item = $invoice->get_recurring() ) {
195
                if ( $recurring_item != $item_id ) {
196
                    $valid = false;
197
                }
198
            } else if ( wpinv_is_recurring_item( $item_id ) ) {
199
                $valid = false;
200
            }
201
            
202
            if ( !$valid ) {
203
                $response               = array();
204
                $response['success']    = false;
205
                $response['msg']        = __( 'You can not add item because recurring item must be paid individually!', 'invoicing' );
206
                wp_send_json( $response );
207
            }
208
        }
209
        
210
        $checkout_session = wpinv_get_checkout_session();
211
        
212
        $data                   = array();
213
        $data['invoice_id']     = $invoice_id;
214
        $data['cart_discounts'] = $invoice->get_discounts( true );
215
        
216
        wpinv_set_checkout_session( $data );
217
        
218
        $quantity = wpinv_item_quantities_enabled() && !empty($_POST['qty']) && (int)$_POST['qty'] > 0 ? (int)$_POST['qty'] : 1;
219
220
        $args = array(
221
            'id'            => $item_id,
222
            'quantity'      => $quantity,
223
            'item_price'    => $item->get_price(),
224
            'custom_price'  => '',
225
            'tax'           => 0.00,
226
            'discount'      => 0,
227
            'meta'          => array(),
228
            'fees'          => array()
229
        );
230
231
        $invoice->add_item( $item_id, $args );
232
        $invoice->save();
233
        
234
        if ( empty( $_POST['country'] ) ) {
235
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
236
        }
237
        if ( empty( $_POST['state'] ) ) {
238
            $_POST['state'] = $invoice->state;
239
        }
240
         
241
        $invoice->country   = sanitize_text_field( $_POST['country'] );
242
        $invoice->state     = sanitize_text_field( $_POST['state'] );
243
        
244
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
245
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
246
        
247
        $wpinv_ip_address_country = $invoice->country;
248
249
        $invoice->recalculate_totals(true);
250
        
251
        $response                       = array();
252
        $response['success']            = true;
253
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
254
        $response['data']['subtotal']   = $invoice->get_subtotal();
255
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
256
        $response['data']['tax']        = $invoice->get_tax();
257
        $response['data']['taxf']       = $invoice->get_tax(true);
258
        $response['data']['discount']   = $invoice->get_discount();
259
        $response['data']['discountf']  = $invoice->get_discount(true);
260
        $response['data']['total']      = $invoice->get_total();
261
        $response['data']['totalf']     = $invoice->get_total(true);
262
        
263
        wpinv_set_checkout_session($checkout_session);
264
        
265
        wp_send_json( $response );
266
    }
267
268
269
    public static function remove_invoice_item() {
270
        global $wpi_userID, $wpinv_ip_address_country;
271
        
272
        check_ajax_referer( 'invoice-item', '_nonce' );
273
        if ( !wpinv_current_user_can_manage_invoicing() ) {
274
            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...
275
        }
276
        
277
        $item_id    = sanitize_text_field( $_POST['item_id'] );
278
        $invoice_id = absint( $_POST['invoice_id'] );
279
        $cart_index = isset( $_POST['index'] ) && $_POST['index'] >= 0 ? $_POST['index'] : false;
280
        
281
        if ( !is_numeric( $invoice_id ) || !is_numeric( $item_id ) ) {
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
        $invoice    = wpinv_get_invoice( $invoice_id );
286
        if ( empty( $invoice ) ) {
287
            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...
288
        }
289
        
290
        if ( $invoice->is_paid() || $invoice->is_refunded() ) {
291
            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...
292
        }
293
        
294
        if ( !empty( $_POST['user_id'] ) ) {
295
            $wpi_userID = absint( $_POST['user_id'] ); 
296
        }
297
298
        $item       = new WPInv_Item( $item_id );
299
        if ( !( !empty( $item ) && $item->post_type == 'wpi_item' ) ) {
300
            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...
301
        }
302
        
303
        $checkout_session = wpinv_get_checkout_session();
304
        
305
        $data                   = array();
306
        $data['invoice_id']     = $invoice_id;
307
        $data['cart_discounts'] = $invoice->get_discounts( true );
308
        
309
        wpinv_set_checkout_session( $data );
310
311
        $args = array(
312
            'id'         => $item_id,
313
            'quantity'   => 1,
314
            'cart_index' => $cart_index
315
        );
316
317
        $invoice->remove_item( $item_id, $args );
318
        $invoice->save();
319
        
320
        if ( empty( $_POST['country'] ) ) {
321
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
322
        }
323
        if ( empty( $_POST['state'] ) ) {
324
            $_POST['state'] = $invoice->state;
325
        }
326
         
327
        $invoice->country   = sanitize_text_field( $_POST['country'] );
328
        $invoice->state     = sanitize_text_field( $_POST['state'] );
329
        
330
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
331
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
332
        
333
        $wpinv_ip_address_country = $invoice->country;
334
        
335
        $invoice->recalculate_totals(true);
336
        
337
        $response                       = array();
338
        $response['success']            = true;
339
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
340
        $response['data']['subtotal']   = $invoice->get_subtotal();
341
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
342
        $response['data']['tax']        = $invoice->get_tax();
343
        $response['data']['taxf']       = $invoice->get_tax(true);
344
        $response['data']['discount']   = $invoice->get_discount();
345
        $response['data']['discountf']  = $invoice->get_discount(true);
346
        $response['data']['total']      = $invoice->get_total();
347
        $response['data']['totalf']     = $invoice->get_total(true);
348
        
349
        wpinv_set_checkout_session($checkout_session);
350
        
351
        wp_send_json( $response );
352
    }
353
    
354
    public static function create_invoice_item() {
355
        check_ajax_referer( 'invoice-item', '_nonce' );
356
        if ( !wpinv_current_user_can_manage_invoicing() ) {
357
            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...
358
        }
359
        
360
        $invoice_id = absint( $_POST['invoice_id'] );
361
362
        // Find the item
363
        if ( !is_numeric( $invoice_id ) ) {
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
        $invoice     = wpinv_get_invoice( $invoice_id );
368
        if ( empty( $invoice ) ) {
369
            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...
370
        }
371
        
372
        // Validate item before adding to invoice because recurring item must be paid individually.
373
        if ( !empty( $invoice->cart_details ) && $invoice->get_recurring() ) {
374
            $response               = array();
375
            $response['success']    = false;
376
            $response['msg']        = __( 'You can not add item because recurring item must be paid individually!', 'invoicing' );
377
            wp_send_json( $response );
378
        }        
379
        
380
        $save_item = wp_unslash( $_POST['_wpinv_quick'] );
381
        
382
        $meta               = array();
383
        $meta['type']       = !empty($save_item['type']) ? sanitize_text_field($save_item['type']) : 'custom';
384
        $meta['price']      = !empty($save_item['price']) ? wpinv_sanitize_amount( $save_item['price'] ) : 0;
385
        $meta['vat_rule']   = !empty($save_item['vat_rule']) ? sanitize_text_field($save_item['vat_rule']) : 'digital';
386
        $meta['vat_class']  = !empty($save_item['vat_class']) ? sanitize_text_field($save_item['vat_class']) : '_standard';
387
        
388
        $data                   = array();
389
        $data['post_title']     = sanitize_text_field($save_item['name']);
390
        $data['post_status']    = 'publish';
391
        $data['post_excerpt']   = ! empty( $save_item['excerpt'] ) ? wp_kses_post( $save_item['excerpt'] ) : '';
392
        $data['meta']           = $meta;
393
        
394
        $item = new WPInv_Item();
395
        $item->create( $data );
396
        
397
        if ( !empty( $item ) ) {
398
            $_POST['item_id']   = $item->ID;
399
            $_POST['qty']       = !empty($save_item['qty']) && $save_item['qty'] > 0 ? (int)$save_item['qty'] : 1;
400
            
401
            self::add_invoice_item();
402
        }
403
        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...
404
    }
405
    
406
    public static function get_billing_details() {
407
        check_ajax_referer( 'get-billing-details', '_nonce' );
408
        
409
        if ( !wpinv_current_user_can_manage_invoicing() ) {
410
            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...
411
        }
412
413
        $user_id            = (int)$_POST['user_id'];
414
        $billing_details    = wpinv_get_user_address($user_id);
415
        $billing_details    = apply_filters( 'wpinv_fill_billing_details', $billing_details, $user_id );
416
        
417
        if (isset($billing_details['user_id'])) {
418
            unset($billing_details['user_id']);
419
        }
420
        
421
        if (isset($billing_details['email'])) {
422
            unset($billing_details['email']);
423
        }
424
425
        $response                               = array();
426
        $response['success']                    = true;
427
        $response['data']['billing_details']    = $billing_details;
428
        
429
        wp_send_json( $response );
430
    }
431
    
432
    public static function admin_recalculate_totals() {
433
        global $wpi_userID, $wpinv_ip_address_country;
434
        
435
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
436
        if ( !wpinv_current_user_can_manage_invoicing() ) {
437
            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...
438
        }
439
        
440
        $invoice_id = absint( $_POST['invoice_id'] );        
441
        $invoice    = wpinv_get_invoice( $invoice_id );
442
        if ( empty( $invoice ) ) {
443
            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...
444
        }
445
446
        $checkout_session = wpinv_get_checkout_session();
447
448
        $data                   = array();
449
        $data['invoice_id']     = $invoice_id;
450
        $data['cart_discounts'] = $invoice->get_discounts( true );
451
452
        wpinv_set_checkout_session( $data );
453
        
454
        if ( !empty( $_POST['user_id'] ) ) {
455
            $wpi_userID = absint( $_POST['user_id'] ); 
456
        }
457
        
458
        if ( empty( $_POST['country'] ) ) {
459
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
460
        }
461
462
        $disable_taxes = 0;
463
        if ( ! empty( $_POST['disable_taxes'] ) ) {
464
            $disable_taxes = 1;
465
        }
466
        $invoice->set( 'disable_taxes', $disable_taxes );
467
468
        $invoice->country = sanitize_text_field( $_POST['country'] );
469
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
470
        if ( isset( $_POST['state'] ) ) {
471
            $invoice->state = sanitize_text_field( $_POST['state'] );
472
            $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
473
        }
474
        
475
        $wpinv_ip_address_country = $invoice->country;
476
        
477
        $invoice = $invoice->recalculate_totals(true);
478
        
479
        $response                       = array();
480
        $response['success']            = true;
481
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
482
        $response['data']['subtotal']   = $invoice->get_subtotal();
483
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
484
        $response['data']['tax']        = $invoice->get_tax();
485
        $response['data']['taxf']       = $invoice->get_tax(true);
486
        $response['data']['discount']   = $invoice->get_discount();
487
        $response['data']['discountf']  = $invoice->get_discount(true);
488
        $response['data']['total']      = $invoice->get_total();
489
        $response['data']['totalf']     = $invoice->get_total(true);
490
        
491
        wpinv_set_checkout_session($checkout_session);
492
493
        wp_send_json( $response );
494
    }
495
    
496
    public static function admin_apply_discount() {
497
        global $wpi_userID;
498
        
499
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
500
        if ( !wpinv_current_user_can_manage_invoicing() ) {
501
            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...
502
        }
503
        
504
        $invoice_id = absint( $_POST['invoice_id'] );
505
        $discount_code = sanitize_text_field( $_POST['code'] );
506
        if ( empty( $invoice_id ) || empty( $discount_code ) ) {
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
        $invoice = wpinv_get_invoice( $invoice_id );
511
        if ( empty( $invoice ) || ( !empty( $invoice ) && ( $invoice->is_paid() || $invoice->is_refunded() ) ) ) {
512
            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...
513
        }
514
        
515
        $checkout_session = wpinv_get_checkout_session();
516
        
517
        $data                   = array();
518
        $data['invoice_id']     = $invoice_id;
519
        $data['cart_discounts'] = $invoice->get_discounts( true );
520
        
521
        wpinv_set_checkout_session( $data );
522
        
523
        $response               = array();
524
        $response['success']    = false;
525
        $response['msg']        = __( 'This discount is invalid.', 'invoicing' );
526
        $response['data']['code'] = $discount_code;
527
        
528
        if ( wpinv_is_discount_valid( $discount_code, $invoice->get_user_id() ) ) {
529
            $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...
530
            
531
            $response['success'] = true;
532
            $response['msg'] = __( 'Discount has been applied successfully.', 'invoicing' );
533
        }  else {
534
            $errors = wpinv_get_errors();
535
            if ( !empty( $errors['wpinv-discount-error'] ) ) {
536
                $response['msg'] = $errors['wpinv-discount-error'];
537
            }
538
            wpinv_unset_error( 'wpinv-discount-error' );
539
        }
540
        
541
        wpinv_set_checkout_session($checkout_session);
542
        
543
        wp_send_json( $response );
544
    }
545
    
546
    public static function admin_remove_discount() {
547
        global $wpi_userID;
548
        
549
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
550
        if ( !wpinv_current_user_can_manage_invoicing() ) {
551
            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...
552
        }
553
        
554
        $invoice_id = absint( $_POST['invoice_id'] );
555
        $discount_code = sanitize_text_field( $_POST['code'] );
556
        if ( empty( $invoice_id ) || empty( $discount_code ) ) {
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
        $invoice = wpinv_get_invoice( $invoice_id );
561
        if ( empty( $invoice ) || ( !empty( $invoice ) && ( $invoice->is_paid() || $invoice->is_refunded() ) ) ) {
562
            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...
563
        }
564
        
565
        $checkout_session = wpinv_get_checkout_session();
566
        
567
        $data                   = array();
568
        $data['invoice_id']     = $invoice_id;
569
        $data['cart_discounts'] = $invoice->get_discounts( true );
570
        
571
        wpinv_set_checkout_session( $data );
572
        
573
        $response               = array();
574
        $response['success']    = false;
575
        $response['msg']        = NULL;
576
        
577
        $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...
578
        $response['success'] = true;
579
        $response['msg'] = __( 'Discount has been removed successfully.', 'invoicing' );
580
        
581
        wpinv_set_checkout_session($checkout_session);
582
        
583
        wp_send_json( $response );
584
    }
585
    
586
    public static function check_email() {
587
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
588
        if ( !wpinv_current_user_can_manage_invoicing() ) {
589
            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...
590
        }
591
        
592
        $email = sanitize_text_field( $_POST['email'] );
593
        
594
        $response = array();
595
        if ( is_email( $email ) && email_exists( $email ) && $user_data = get_user_by( 'email', $email ) ) {
596
            $user_id            = $user_data->ID;
597
            $user_login         = $user_data->user_login;
598
            $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...
599
            $billing_details    = wpinv_get_user_address($user_id);
600
            $billing_details    = apply_filters( 'wpinv_fill_billing_details', $billing_details, $user_id );
601
            
602
            if (isset($billing_details['user_id'])) {
603
                unset($billing_details['user_id']);
604
            }
605
            
606
            if (isset($billing_details['email'])) {
607
                unset($billing_details['email']);
608
            }
609
            
610
            $response['success']                    = true;
611
            $response['data']['id']                 = $user_data->ID;
612
            $response['data']['name']               = $user_data->user_email;
613
            $response['data']['billing_details']    = $billing_details;
614
        }
615
        
616
        wp_send_json( $response );
617
    }
618
    
619
    public static function run_tool() {
620
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
621
        if ( !wpinv_current_user_can_manage_invoicing() ) {
622
            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...
623
        }
624
        
625
        $tool = sanitize_text_field( $_POST['tool'] );
626
        
627
        do_action( 'wpinv_run_tool' );
628
        
629
        if ( !empty( $tool ) ) {
630
            do_action( 'wpinv_tool_' . $tool );
631
        }
632
    }
633
    
634
    public static function apply_discount() {
635
        global $wpi_userID;
636
        
637
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
638
        
639
        $response = array();
640
        
641
        if ( isset( $_POST['code'] ) ) {
642
            $discount_code = sanitize_text_field( $_POST['code'] );
643
644
            $response['success']        = false;
645
            $response['msg']            = '';
646
            $response['data']['code']   = $discount_code;
647
            
648
            $invoice = wpinv_get_invoice_cart();
649
            if ( empty( $invoice->ID ) ) {
650
                $response['msg'] = __( 'Invalid checkout request.', 'invoicing' );
651
                wp_send_json( $response );
652
            }
653
654
            $wpi_userID = $invoice->get_user_id();
655
656
            if ( wpinv_is_discount_valid( $discount_code, $wpi_userID ) ) {
657
                $discount       = wpinv_get_discount_by_code( $discount_code );
658
                $discounts      = wpinv_set_cart_discount( $discount_code );
659
                $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...
660
                $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...
661
                $cart_totals    = wpinv_recalculate_tax( true );
662
            
663
                if ( !empty( $cart_totals ) ) {
664
                    $response['success']        = true;
665
                    $response['data']           = $cart_totals;
666
                    $response['data']['code']   = $discount_code;
667
                } else {
668
                    $response['success']        = false;
669
                }
670
            } else {
671
                $errors = wpinv_get_errors();
672
                $response['msg']  = $errors['wpinv-discount-error'];
673
                wpinv_unset_error( 'wpinv-discount-error' );
674
            }
675
676
            // Allow for custom discount code handling
677
            $response = apply_filters( 'wpinv_ajax_discount_response', $response );
678
        }
679
        
680
        wp_send_json( $response );
681
    }
682
    
683
    public static function remove_discount() {
684
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
685
        
686
        $response = array();
687
        
688
        if ( isset( $_POST['code'] ) ) {
689
            $discount_code  = sanitize_text_field( $_POST['code'] );
690
            $discounts      = wpinv_unset_cart_discount( $discount_code );
691
            $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...
692
            $cart_totals    = wpinv_recalculate_tax( true );
693
            
694
            if ( !empty( $cart_totals ) ) {
695
                $response['success']        = true;
696
                $response['data']           = $cart_totals;
697
                $response['data']['code']   = $discount_code;
698
            } else {
699
                $response['success']        = false;
700
            }
701
            
702
            // Allow for custom discount code handling
703
            $response = apply_filters( 'wpinv_ajax_discount_response', $response );
704
        }
705
        
706
        wp_send_json( $response );
707
    }
708
709
    /**
710
     * Retrieves the markup for a payment form.
711
     */
712
    public static function get_payment_form() {
713
714
        // Check nonce.
715
        if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'], 'getpaid_ajax_form' ) ) {
716
            _e( 'Error: Reload the page and try again.', 'invoicing' );
717
            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...
718
        }
719
720
        // Is the request set up correctly?
721
		if ( empty( $_GET['form'] ) && empty( $_GET['item'] ) ) {
722
			echo aui()->alert(
723
				array(
724
					'type'    => 'warning',
725
					'content' => __( 'No payment form or item provided', 'invoicing' ),
726
				)
727
            );
728
            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...
729
        }
730
731
        // Payment form or button?
732
		if ( ! empty( $_GET['form'] ) ) {
733
            echo getpaid_display_payment_form( $_GET['form'] );
734
		} else if( $_GET['invoice'] ) {
735
		    echo getpaid_display_invoice_payment_form( $_GET['invoice'] );
736
        } else {
737
			$items = getpaid_convert_items_to_array( $_GET['item'] );
738
		    echo getpaid_display_item_payment_form( $items );
739
        }
740
        
741
        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...
742
743
    }
744
745
    /**
746
     * Payment forms.
747
     *
748
     * @since 1.0.18
749
     */
750
    public static function payment_form() {
751
        global $invoicing, $wpi_checkout_id, $cart_total;
752
753
        // Check nonce.
754
        if ( ! isset( $_POST['wpinv_payment_form'] ) || ! wp_verify_nonce( $_POST['wpinv_payment_form'], 'wpinv_payment_form' ) ) {
755
            wp_send_json_error( __( 'Security checks failed.', 'invoicing' ) );
756
        }
757
758
        // Prepare submitted data...
759
        $data = wp_unslash( $_POST );
760
761
        // ... form fields...
762
        if ( empty( $data['form_id'] ) || 'publish' != get_post_status( $data['form_id'] ) ) {
763
            wp_send_json_error( __( 'This payment form is no longer active.', 'invoicing' ) );
764
        }
765
766
        if ( empty( $data['billing_email'] ) || ! is_email( $data['billing_email'] ) ) {
767
            wp_send_json_error( __( 'Provide a valid billing email.', 'invoicing' ) );
768
        }
769
770
        $prepared = array(
771
            'billing_email'                    => sanitize_email( $data['billing_email'] ),
772
            __( 'Billing Email', 'invoicing' ) => sanitize_email( $data['billing_email'] ),
773
            __( 'Form Id', 'invoicing' )       => absint( $data['form_id'] ),
774
        );
775
776
        // Do we have a discount?
777
        $discount = 0;
778
        if ( ! empty( $data['discount'] ) ) {
779
780
            // Validate discount.
781
            $discount = self::payment_form_validate_discount( $data );
782
783
            if ( is_string( $discount ) ){
784
                wp_send_json_error( $discount );
785
            }
786
787
            if ( is_array( $discount ) ){
788
                $discount = $discount[ 'discount' ];
789
            }
790
791
            if ( ! $discount ) {
792
                $discount = 0;
793
            }
794
795
        }
796
797
        $fields = $invoicing->form_elements->get_form_elements( $data['form_id'] );
798
799
        // ... and form items.
800
        if ( ! empty( $data['invoice_id'] ) ) {
801
            $invoice = wpinv_get_invoice( $data['invoice_id'] );
802
803
            if ( empty( $invoice ) ) {
804
                wp_send_json_error( __( 'Invalid invoice.', 'invoicing' ) );
805
            }
806
807
            if ( $invoice->is_paid() ) {
808
                wp_send_json_error( __( 'This invoice has already been paid.', 'invoicing' ) );
809
            }
810
811
            $items   = $invoicing->form_elements->convert_checkout_items( $invoice->cart_details, $invoice );
812
813
        } else {
814
815
            if ( isset( $data['form_items'] ) ) {
816
                $items = getpaid_convert_items_to_array( $data['form_items'] );
817
                $items = $invoicing->form_elements->convert_normal_items( $items );
818
            } else {
819
                $items = $invoicing->form_elements->get_form_items( $data['form_id'] );
820
            }
821
822
            $invoice = 0;
823
        }
824
825
        $prepared_items = array();
826
        $address_fields = array();
827
        $has_recurring  = false;
828
829
        if ( ! empty( $data['wpinv-items'] ) ) {
830
831
            $selected_items = wpinv_clean( $data['wpinv-items'] );
832
833
            foreach ( $items as $item ) {
834
835
                if ( ! empty( $item['required'] ) && ! isset( $selected_items[ $item['id'] ] ) ) {
836
                    wp_send_json_error( __( 'A required item is missing.', 'invoicing' ) );
837
                }
838
839
                if ( ! isset( $selected_items[ $item['id'] ] ) ) {
840
                    continue;
841
                }
842
843
                if ( ! empty( $item['recurring'] ) ) {
844
                    $has_recurring  = true;
845
                }
846
847
                $quantity = empty( $item['quantity'] ) ? 1 : absint( $item['quantity'] );
848
849
                if ( ! empty( $item['allow_quantities'] ) && ! empty( $data["wpinv-item-{$item['id']}-quantity"] ) ) {
850
851
                    $_quantity = intval( $data["wpinv-item-{$item['id']}-quantity"] );
852
853
                    if ( ! empty( $_quantity ) ) {
854
                        $quantity = $_quantity;
855
                    }
856
                }
857
858
                // Custom pricing.
859
                if ( ! empty( $item['custom_price'] ) ) {
860
861
                    $minimum_price = wpinv_sanitize_amount( $item['minimum_price'] );
862
                    $set_price     = wpinv_sanitize_amount( $selected_items[ $item['id'] ] );
863
864
                    if ( $set_price < $minimum_price ) {
865
                        wp_send_json_error( __( 'The provided amount is less than the minimum allowed value.', 'invoicing' ) );
866
                    }
867
868
                    $prepared_items[] = array(
869
                        'id'           =>$item['id'],
870
                        'item_price'   => wpinv_sanitize_amount( $item['price'] ),
871
                        'custom_price' => $set_price,
872
                        'name'         => $item['title'],
873
                        'quantity'     => $quantity,
874
                    );
875
876
                } else {
877
878
                    $prepared_items[] = array(
879
                        'id'           => $item['id'],
880
                        'item_price'   => wpinv_sanitize_amount( $item['price'] ),
881
                        'custom_price' => wpinv_sanitize_amount( $item['price'] ),
882
                        'name'         => $item['title'],
883
                        'quantity'     => $quantity,
884
                    );
885
886
                }
887
888
            }
889
890
        } else {
891
892
            wp_send_json_error( __( 'You have not selected any items.', 'invoicing' ) );
893
894
        }
895
896
        if ( $has_recurring && 1 != count( $prepared_items ) ) {
897
            wp_send_json_error( __( 'Recurring items should be bought individually.', 'invoicing' ) );
898
        }
899
900
        // Are all required fields provided?
901
        foreach ( $fields as $field ) {
902
903
            if ( ! empty( $field['premade'] ) ) {
904
                continue;
905
            }
906
907
            if ( ! empty( $field['required'] ) && empty( $data[ $field['id'] ] ) ) {
908
                wp_send_json_error( __( 'Some required fields have not been filled.', 'invoicing' ) );
909
            }
910
911
            if ( $field['type'] == 'address' ) {
912
913
                foreach ( $field['fields'] as $address_field ) {
914
915
                    if ( empty( $address_field['visible'] ) ) {
916
                        continue;
917
                    }
918
919
                    if ( ! empty( $address_field['required'] ) && empty( $data[ $address_field['name'] ] ) ) {
920
                        wp_send_json_error( __( 'Some required fields have not been filled.', 'invoicing' ) );
921
                    }
922
923
                    if ( isset( $data[ $address_field['name'] ] ) ) {
924
                        $label = str_replace( 'wpinv_', '', $address_field['name'] );
925
                        $address_fields[ $label ] = wpinv_clean( $data[ $address_field['name'] ] );
926
                    }
927
928
                }
929
930
            } else if ( isset( $data[ $field['id'] ] ) ) {
931
                $label = $field['id'];
932
933
                if ( isset( $field['label'] ) ) {
934
                    $label = $field['label'];
935
                }
936
937
                $prepared[ wpinv_clean( $label ) ] = wpinv_clean( $data[ $field['id'] ] );
938
            }
939
940
        }
941
942
        $user = get_user_by( 'email', $prepared['billing_email'] );
943
944
        if ( empty( $user ) ) {
945
            $user = wpinv_create_user( $prepared['billing_email'] );
946
        }
947
948
        if ( is_wp_error( $user ) ) {
949
            wp_send_json_error( $user->get_error_message() );
950
        }
951
952
        if ( is_numeric( $user ) ) {
953
            $user = get_user_by( 'id', $user );
954
        }
955
956
        if ( ! empty( $discount ) ) {
957
            $address_fields['discount'] = array( $data['discount'] );
958
        }
959
960
        // Create the invoice.
961
        if ( empty( $invoice ) ) {
962
963
            $invoice = wpinv_insert_invoice(
964
                array(
965
                    'status'        => 'wpi-pending',
966
                    'created_via'   => 'payment_form',
967
                    'user_id'       => $user->ID,
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist on WP_Error.
Loading history...
968
                    'cart_details'  => $prepared_items,
969
                    'user_info'     => $address_fields,
970
                ),
971
                true
972
            );
973
974
        } else {
975
976
            $invoice = wpinv_update_invoice(
977
                array(
978
                    'ID'            => $invoice->ID,
979
                    'status'        => 'wpi-pending',
980
                    'cart_details'  => $prepared_items,
981
                    'user_info'     => $address_fields,
982
                ),
983
                true
984
            );
985
986
        }
987
988
        if ( is_wp_error( $invoice ) ) {
989
            wp_send_json_error( $invoice->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

989
            wp_send_json_error( $invoice->/** @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...
990
        }
991
992
        if ( empty( $invoice ) ) {
993
            wp_send_json_error( __( 'Could not create your invoice.', 'invoicing' ) );
994
        }
995
996
        unset( $prepared['billing_email'] );
997
        update_post_meta( $invoice->ID, 'payment_form_data', $prepared );
998
999
        $wpi_checkout_id = $invoice->ID;
1000
        $cart_total = wpinv_price(
1001
            wpinv_format_amount(
1002
                wpinv_get_cart_total( $invoice->get_cart_details(), NULL, $invoice ) ),
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

1002
                wpinv_get_cart_total( $invoice->/** @scrutinizer ignore-call */ get_cart_details(), NULL, $invoice ) ),

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...
1003
                $invoice->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

1003
                $invoice->/** @scrutinizer ignore-call */ 
1004
                          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...
1004
        );
1005
1006
        $data                   = array();
1007
        $data['invoice_id']     = $invoice->ID;
1008
        $data['cart_discounts'] = $invoice->get_discounts( true );
0 ignored issues
show
Bug introduced by
The method get_discounts() does not exist on WP_Error. ( Ignorable by Annotation )

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

1008
        /** @scrutinizer ignore-call */ 
1009
        $data['cart_discounts'] = $invoice->get_discounts( true );

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

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

Loading history...
1009
1010
        wpinv_set_checkout_session( $data );
1011
        add_filter( 'wp_redirect', array( $invoicing->form_elements, 'send_redirect_response' ) );
1012
        add_action( 'wpinv_pre_send_back_to_checkout', array( $invoicing->form_elements, 'checkout_error' ) );
1013
        
1014
        if ( ! defined( 'WPINV_CHECKOUT' ) ) {
1015
            define( 'WPINV_CHECKOUT', true );
1016
        }
1017
1018
        wpinv_process_checkout();
1019
1020
        $invoicing->form_elements->checkout_error();
1021
1022
        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...
1023
    }
1024
1025
    /**
1026
     * Payment forms.
1027
     *
1028
     * @since 1.0.18
1029
     */
1030
    public static function get_payment_form_states_field() {
1031
        global $invoicing;
1032
1033
        if ( empty( $_GET['country'] ) || empty( $_GET['form'] ) ) {
1034
            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...
1035
        }
1036
1037
        $elements = $invoicing->form_elements->get_form_elements( $_GET['form'] );
1038
1039
        if ( empty( $elements ) ) {
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
        $address_fields = array();
1044
        foreach ( $elements as $element ) {
1045
            if ( 'address' === $element['type'] ) {
1046
                $address_fields = $element;
1047
                break;
1048
            }
1049
        }
1050
1051
        if ( empty( $address_fields ) ) {
1052
            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...
1053
        }
1054
1055
        foreach( $address_fields['fields'] as $address_field ) {
1056
1057
            if ( 'wpinv_state' == $address_field['name'] ) {
1058
1059
                $label = $address_field['label'];
1060
1061
                if ( ! empty( $address_field['required'] ) ) {
1062
                    $label .= "<span class='text-danger'> *</span>";
1063
                }
1064
1065
                $states = wpinv_get_country_states( $_GET['country'] );
1066
1067
                if ( ! empty( $states ) ) {
1068
1069
                    $html = aui()->select(
1070
                            array(
1071
                                'options'          => $states,
1072
                                'name'             => esc_attr( $address_field['name'] ),
1073
                                'id'               => esc_attr( $address_field['name'] ),
1074
                                'placeholder'      => esc_attr( $address_field['placeholder'] ),
1075
                                'required'         => (bool) $address_field['required'],
1076
                                'no_wrap'          => true,
1077
                                'label'            => wp_kses_post( $label ),
1078
                                'select2'          => false,
1079
                            )
1080
                        );
1081
1082
                } else {
1083
1084
                    $html = aui()->input(
1085
                            array(
1086
                                'name'       => esc_attr( $address_field['name'] ),
1087
                                'id'         => esc_attr( $address_field['name'] ),
1088
                                'required'   => (bool) $address_field['required'],
1089
                                'label'      => wp_kses_post( $label ),
1090
                                'no_wrap'    => true,
1091
                                'type'       => 'text',
1092
                            )
1093
                        );
1094
1095
                }
1096
1097
                wp_send_json_success( str_replace( 'sr-only', '', $html ) );
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
1102
        }
1103
    
1104
        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...
1105
    }
1106
1107
    /**
1108
     * Apply taxes.
1109
     *
1110
     * @since 1.0.18
1111
     */
1112
    public static function payment_form_get_taxes() {
1113
        global $invoicing;
1114
1115
        // Check nonce.
1116
        check_ajax_referer( 'wpinv_payment_form', 'wpinv_payment_form' );
1117
1118
        // Prepare submitted data...
1119
        $data = wp_unslash( $_POST );
1120
1121
        // ... form fields...
1122
        if ( empty( $data['form_id'] ) || 'publish' != get_post_status( $data['form_id'] ) ) {
1123
            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...
1124
        }
1125
1126
        // Do we have a discount?
1127
        if ( ! empty( $data['discount'] ) ) {
1128
1129
            // Validate discount.
1130
            $discount = self::payment_form_validate_discount( $data );
1131
1132
            if ( is_array( $discount ) ){
1133
                $data['total']     = wpinv_price( wpinv_format_amount( $discount['total'] ) );
1134
                $data['sub_total'] = wpinv_price( wpinv_format_amount( $discount['sub_total'] ) );
1135
                $data['discount']  = wpinv_price( wpinv_format_amount( $discount['discount'] ) );
1136
                $data['tax']       = wpinv_price( wpinv_format_amount( $discount['tax'] ) );
1137
                wp_send_json_success( $discount );
1138
            }
1139
1140
        }        
1141
1142
        // For existing invoices.
1143
        if ( ! empty( $data['invoice_id'] ) ) {
1144
            $invoice = wpinv_get_invoice( $data['invoice_id'] );
1145
1146
            if ( empty( $invoice ) ) {
1147
                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...
1148
            }
1149
1150
            $items   = $invoicing->form_elements->convert_checkout_items( $invoice->cart_details, $invoice );
1151
            $country = $invoice->country;
1152
            $state   = $invoice->state;
1153
1154
        } else {
1155
1156
            if ( isset( $data['form_items'] ) ) {
1157
                $items = getpaid_convert_items_to_array( $data['form_items'] );
1158
                $items = $invoicing->form_elements->convert_normal_items( $items );
1159
            } else {
1160
                $items = $invoicing->form_elements->get_form_items( $data['form_id'] );
1161
            }
1162
1163
            $country   = wpinv_default_billing_country();
1164
            $state     = false;
1165
        }
1166
1167
        // What we will calculate.
1168
        $total     = 0;
1169
        $tax       = 0;
1170
        $sub_total = 0;
1171
1172
        if ( ! empty( $data['wpinv_country'] ) ) {
1173
            $country = $data['wpinv_country'];
1174
        }
1175
1176
        if ( ! empty( $data['wpinv_state'] ) ) {
1177
            $state = $data['wpinv_state'];
1178
        }
1179
1180
        $has_recurring  = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $has_recurring is dead and can be removed.
Loading history...
1181
        if ( ! empty( $data['wpinv-items'] ) ) {
1182
1183
            $selected_items = wpinv_clean( $data['wpinv-items'] );
1184
1185
            foreach ( $items as $item ) {
1186
1187
                if ( ! isset( $selected_items[ $item['id'] ] ) ) {
1188
                    continue;
1189
                }
1190
1191
                if ( ! empty( $item['recurring'] ) ) {
1192
                    $has_recurring  = true;
1193
                }
1194
1195
                $quantity = empty( $item['quantity'] ) ? 1 : absint( $item['quantity'] );
1196
1197
                if ( ! empty( $item['allow_quantities'] ) && ! empty( $data["wpinv-item-{$item['id']}-quantity"] ) ) {
1198
1199
                    $quantity = intval( $data["wpinv-item-{$item['id']}-quantity"] );
1200
1201
                    if ( 1 > $quantity ) {
1202
                        $quantity = 1;
1203
                    }
1204
1205
                }
1206
1207
                // Custom pricing.
1208
                $price = wpinv_sanitize_amount( $item['price'] );
1209
                if ( ! empty( $item['custom_price'] ) ) {
1210
1211
                    $minimum_price = wpinv_sanitize_amount( $item['minimum_price'] );
1212
                    $set_price     = wpinv_sanitize_amount( $selected_items[ $item['id'] ] );
1213
1214
                    if ( $set_price < $minimum_price ) {
1215
                        $set_price = $minimum_price;
1216
                    }
1217
1218
                    $price = wpinv_sanitize_amount( $set_price );
1219
1220
                }
1221
1222
                $price  = $quantity * floatval( $price );
1223
1224
                if ( wpinv_use_taxes() ) {
1225
1226
                    $rate = wpinv_get_tax_rate( $country, $state, (int) $item['id'] );
1227
1228
                    if ( wpinv_prices_include_tax() ) {
1229
                        $pre_tax  = ( $price - $price * $rate * 0.01 );
1230
                        $item_tax = $price - $pre_tax;
1231
                    } else {
1232
                        $pre_tax  = $price;
1233
                        $item_tax = $price * $rate * 0.01;
1234
                    }
1235
1236
                    $tax       = $tax + $item_tax;
1237
                    $sub_total = $sub_total + $pre_tax;
1238
                    $total     = $sub_total + $tax;
1239
1240
                } else {
1241
                    $total  = $total + $price;
1242
                }
1243
1244
            }
1245
1246
        }
1247
1248
        wp_send_json_success( array(
1249
            'free'          => $total == 0,
1250
            'total'         => wpinv_price( wpinv_format_amount( $total ) ),
1251
            'tax'           => wpinv_price( wpinv_format_amount( $tax ) ),
1252
            'sub_total'     => wpinv_price( wpinv_format_amount( $sub_total ) ),
1253
            'has_recurring' => false,
1254
            'discount'      => false,
1255
        ));
1256
        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...
1257
    }
1258
1259
    /**
1260
     * Refresh prices.
1261
     *
1262
     * @since 1.0.19
1263
     */
1264
    public static function payment_form_refresh_prices() {
1265
1266
        // Check nonce.
1267
        check_ajax_referer( 'getpaid_form_nonce' );
1268
1269
        // ... form fields...
1270
        if ( empty( $_POST['getpaid_payment_form_submission'] ) ) {
1271
            _e( 'Error: Reload the page and try again.', 'invoicing' );
1272
            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...
1273
        }
1274
1275
        // Load the submission.
1276
        $submission = new GetPaid_Payment_Form_Submission();
1277
1278
        // Do we have an error?
1279
        if ( ! empty( $submission->last_error ) ) {
1280
            echo $submission->last_error;
1281
            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...
1282
        }
1283
1284
        // Prepare the result.
1285
        $result = array(
1286
            'submission_id' => $submission->id,
1287
            'has_recurring' => $submission->has_recurring,
1288
            'is_free'       => $submission->get_payment_details(),
1289
            'totals'        => array(
1290
                'subtotal'  => wpinv_price( wpinv_format_amount( $submission->subtotal_amount ), $submission->get_currency() ),
1291
                'discount'  => wpinv_price( wpinv_format_amount( $submission->get_total_discount() ), $submission->get_currency() ),
1292
                'fees'      => wpinv_price( wpinv_format_amount( $submission->get_total_fees() ), $submission->get_currency() ),
1293
                'tax'       => wpinv_price( wpinv_format_amount( $submission->get_total_tax() ), $submission->get_currency() ),
1294
                'total'     => wpinv_price( wpinv_format_amount( $submission->get_total() ), $submission->get_currency() ),
1295
            ),
1296
        );
1297
1298
        // Add items.
1299
        $items = $submission->get_items();
1300
        if ( ! empty( $items ) ) {
1301
            $result['items'] = array();
1302
1303
            foreach( $items as $item_id => $item ) {
1304
                $result['items']["$item_id"] = wpinv_price( wpinv_format_amount( $item->get_price() * $item->get_qantity() ) );
1305
            }
1306
        }
1307
1308
        // Add invoice.
1309
        if ( $submission->has_invoice() ) {
1310
            $result['invoice'] = $submission->get_invoice()->ID;
1311
        }
1312
1313
        // Add discount code.
1314
        if ( $submission->has_discount_code() ) {
1315
            $result['discount_code'] = $submission->get_discount_code();
1316
        }
1317
1318
        // Filter the result.
1319
        $result = apply_filters( 'getpaid_payment_form_ajax_refresh_prices', $result, $submission );
1320
1321
        wp_send_json_success( $result );
1322
    }
1323
1324
    /**
1325
     * Apply discounts.
1326
     *
1327
     * @since 1.0.19
1328
     */
1329
    public static function payment_form_discount() {
1330
1331
        // Check nonce.
1332
        check_ajax_referer( 'getpaid_form_nonce' );
1333
1334
        // Prepare submitted data...
1335
        $data = wp_unslash( $_POST );
1336
1337
        // ... form fields...
1338
        if ( empty( $data['form_id'] ) || 'publish' != get_post_status( $data['form_id'] ) ) {
1339
            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...
1340
        }
1341
1342
        // Do we have a discount?
1343
        if ( empty( $data['discount'] ) ) {
1344
            _e( 'Please enter your discount code', 'invoicing' );
1345
            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...
1346
        }
1347
1348
        // Validate discount.
1349
        $data = self::payment_form_validate_discount( $data );
1350
1351
        if ( false === $data ) {
1352
            _e( 'There was an error applying your discount code', 'invoicing' );
1353
            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...
1354
        }
1355
1356
        if ( is_string( $data ) ) {
1357
            echo $data;
1358
            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...
1359
        }
1360
1361
        $data['total']     = wpinv_price( wpinv_format_amount( $data['total'] ) );
1362
        $data['sub_total'] = wpinv_price( wpinv_format_amount( $data['sub_total'] ) );
1363
        $data['discount']  = wpinv_price( wpinv_format_amount( $data['discount'] ) );
1364
        $data['tax']       = wpinv_price( wpinv_format_amount( $data['tax'] ) );
1365
1366
        wp_send_json_success( $data );
1367
        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...
1368
1369
    }
1370
1371
    /**
1372
     * Validates discounts.
1373
     *
1374
     * @since 1.0.19
1375
     */
1376
    public static function payment_form_validate_discount( $data ) {
1377
        global $invoicing;
1378
1379
        // Do we have a discount?
1380
        if ( empty( $data['discount'] ) ) {
1381
            return false;
1382
        }
1383
1384
        // If yes, ensure that it exists.
1385
        $discount = wpinv_get_discount_obj( $data['discount'] );
1386
1387
1388
        // Prepare items.
1389
        if ( ! empty( $data['invoice_id'] ) ) {
1390
            $invoice = wpinv_get_invoice( $data['invoice_id'] );
1391
1392
            if ( empty( $invoice ) ) {
1393
                return false;
1394
            }
1395
1396
            $country = $invoice->country;
1397
            $state   = $invoice->state;
1398
            $items   = $invoicing->form_elements->convert_checkout_items( $invoice->cart_details, $invoice );
1399
1400
        } else {
1401
1402
            if ( isset( $data['form_items'] ) ) {
1403
                $items = getpaid_convert_items_to_array( $data['form_items'] );
1404
                $items = $invoicing->form_elements->convert_normal_items( $items );
1405
            } else {
1406
                $items = $invoicing->form_elements->get_form_items( $data['form_id'] );
1407
            }
1408
1409
            $country   = wpinv_default_billing_country();
1410
            $state     = false;
1411
1412
        }
1413
1414
        // What we will calculate.
1415
        $total     = 0;
1416
        $tax       = 0;
1417
        $sub_total = 0;
1418
1419
        if ( ! empty( $data['wpinv_country'] ) ) {
1420
            $country = $data['wpinv_country'];
1421
        }
1422
1423
        if ( ! empty( $data['wpinv_state'] ) ) {
1424
            $state = $data['wpinv_state'];
1425
        }
1426
1427
        if ( ! empty( $data['wpinv-items'] ) ) {
1428
1429
            $selected_items = wpinv_clean( $data['wpinv-items'] );
1430
1431
            // Check if it is valid for the selected items.
1432
            if ( ! $discount->is_valid_for_items( array_keys( $selected_items ) ) ) {
0 ignored issues
show
Bug introduced by
It seems like $selected_items can also be of type string; however, parameter $input of array_keys() does only seem to accept array, 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

1432
            if ( ! $discount->is_valid_for_items( array_keys( /** @scrutinizer ignore-type */ $selected_items ) ) ) {
Loading history...
1433
                return __( 'This discount is not valid for the items in your cart', 'invoicing' );
1434
            }
1435
1436
            $has_recurring  = false;
1437
            foreach ( $items as $item ) {
1438
1439
                if ( ! isset( $selected_items[ $item['id'] ] ) ) {
1440
                    continue;
1441
                }
1442
1443
                $quantity = empty( $item['quantity'] ) ? 1 : absint( $item['quantity'] );
1444
1445
                if ( ! empty( $item['recurring'] ) ) {
1446
                    $has_recurring  = true;
1447
                }
1448
1449
                if ( ! empty( $item['allow_quantities'] ) && ! empty( $data["wpinv-item-{$item['id']}-quantity"] ) ) {
1450
1451
                    $quantity = intval( $data["wpinv-item-{$item['id']}-quantity"] );
1452
1453
                    if ( 1 > $quantity ) {
1454
                        $quantity = 1;
1455
                    }
1456
1457
                }
1458
1459
                // Custom pricing.
1460
                $price = wpinv_sanitize_amount( $item['price'] );
1461
                if ( ! empty( $item['custom_price'] ) ) {
1462
1463
                    $minimum_price = wpinv_sanitize_amount( $item['minimum_price'] );
1464
                    $set_price     = wpinv_sanitize_amount( $selected_items[ $item['id'] ] );
1465
1466
                    if ( $set_price < $minimum_price ) {
1467
                        $set_price = $minimum_price;
1468
                    }
1469
1470
                    $price = wpinv_sanitize_amount( $set_price );
1471
1472
                }
1473
1474
                $price  = $quantity * floatval( $price );
1475
1476
                if ( wpinv_use_taxes() ) {
1477
1478
                    $rate = wpinv_get_tax_rate( $country, $state, (int) $item['id'] );
1479
1480
                    if ( wpinv_prices_include_tax() ) {
1481
                        $pre_tax  = ( $price - $price * $rate * 0.01 );
1482
                        $item_tax = $price - $pre_tax;
1483
                    } else {
1484
                        $pre_tax  = $price;
1485
                        $item_tax = $price * $rate * 0.01;
1486
                    }
1487
1488
                    $tax       = $tax + $item_tax;
1489
                    $sub_total = $sub_total + $pre_tax;
1490
                    $total     = $sub_total + $tax;
1491
1492
                } else {
1493
                    $total  = $total + $price;
1494
                }
1495
1496
            }
1497
1498
        }
1499
1500
        if ( ! $discount->is_minimum_amount_met( $total ) ) {
1501
            $min = wpinv_price( wpinv_format_amount( $discount->min_total ) );
1502
            return sprintf( __( 'The minimum total for using this discount is %s', 'invoicing' ), $min );
1503
        }
1504
1505
        if ( ! $discount->is_maximum_amount_met( $total ) ) {
1506
            $max = wpinv_price( wpinv_format_amount( $discount->max_total ) );
1507
            return sprintf( __( 'The maximum total for using this discount is %s', 'invoicing' ), $max );
1508
        }
1509
1510
        $recurring_discount = $discount->get_is_recurring() && $has_recurring;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $has_recurring does not seem to be defined for all execution paths leading up to this point.
Loading history...
1511
        $discount = $discount->get_discounted_amount( $total );
1512
        $total    = $total - $discount;
1513
        $free     = false;
1514
1515
        if ( $total == 0 ) {
1516
            $free = true;
1517
        }
1518
1519
        return compact( 'total', 'tax', 'sub_total', 'discount', 'recurring_discount', 'free', 'has_recurring' );
1520
1521
    }
1522
1523
    /**
1524
     * Lets users buy items via ajax.
1525
     *
1526
     * @since 1.0.0
1527
     */
1528
    public static function buy_items() {
1529
        $user_id = get_current_user_id();
1530
1531
        if ( empty( $user_id ) ) { // If not logged in then lets redirect to the login page
1532
            wp_send_json( array(
1533
                '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

1533
                'success' => wp_login_url( /** @scrutinizer ignore-type */ wp_get_referer() )
Loading history...
1534
            ) );
1535
        } else {
1536
            // Only check nonce if logged in as it could be cached when logged out.
1537
            if ( ! isset( $_POST['wpinv_buy_nonce'] ) || ! wp_verify_nonce( $_POST['wpinv_buy_nonce'], 'wpinv_buy_items' ) ) {
1538
                wp_send_json( array(
1539
                    'error' => __( 'Security checks failed.', 'invoicing' )
1540
                ) );
1541
                wp_die();
1542
            }
1543
1544
            // allow to set a custom price through post_id
1545
            $items = $_POST['items'];
1546
            $related_post_id = isset( $_POST['post_id'] ) ? (int)$_POST['post_id'] : 0;
1547
            $custom_item_price = $related_post_id ? abs( get_post_meta( $related_post_id, '_wpi_custom_price', true ) ) : 0;
1548
1549
            $cart_items = array();
1550
            if ( $items ) {
1551
                $items = explode( ',', $items );
1552
1553
                foreach( $items as $item ) {
1554
                    $item_id = $item;
1555
                    $quantity = 1;
1556
1557
                    if ( strpos( $item, '|' ) !== false ) {
1558
                        $item_parts = explode( '|', $item );
1559
                        $item_id = $item_parts[0];
1560
                        $quantity = $item_parts[1];
1561
                    }
1562
1563
                    if ( $item_id && $quantity ) {
1564
                        $cart_items_arr = array(
1565
                            'id'            => (int)$item_id,
1566
                            'quantity'      => (int)$quantity
1567
                        );
1568
1569
                        // If there is a related post id then add it to meta
1570
                        if ( $related_post_id ) {
1571
                            $cart_items_arr['meta'] = array(
1572
                                'post_id'   => $related_post_id
1573
                            );
1574
                        }
1575
1576
                        // If there is a custom price then set it.
1577
                        if ( $custom_item_price ) {
1578
                            $cart_items_arr['custom_price'] = $custom_item_price;
1579
                        }
1580
1581
                        $cart_items[] = $cart_items_arr;
1582
                    }
1583
                }
1584
            }
1585
1586
            /**
1587
             * Filter the wpinv_buy shortcode cart items on the fly.
1588
             *
1589
             * @param array $cart_items The cart items array.
1590
             * @param int $related_post_id The related post id if any.
1591
             * @since 1.0.0
1592
             */
1593
            $cart_items = apply_filters( 'wpinv_buy_cart_items', $cart_items, $related_post_id );
1594
1595
            // Make sure its not in the cart already, if it is then redirect to checkout.
1596
            $cart_invoice = wpinv_get_invoice_cart();
1597
1598
            if ( isset( $cart_invoice->items ) && !empty( $cart_invoice->items ) && !empty( $cart_items ) && serialize( $cart_invoice->items ) == serialize( $cart_items ) ) {
1599
                wp_send_json( array(
1600
                    'success' =>  $cart_invoice->get_checkout_payment_url()
1601
                ) );
1602
                wp_die();
1603
            }
1604
1605
            // Check if user has invoice with same items waiting to be paid.
1606
            $user_invoices = wpinv_get_users_invoices( $user_id , 10 , false , 'wpi-pending' );
1607
            if ( !empty( $user_invoices ) ) {
1608
                foreach( $user_invoices as $user_invoice ) {
1609
                    $user_cart_details = array();
1610
                    $invoice  = wpinv_get_invoice( $user_invoice->ID );
1611
                    $cart_details = $invoice->get_cart_details();
1612
1613
                    if ( !empty( $cart_details ) ) {
1614
                        foreach ( $cart_details as $invoice_item ) {
1615
                            $ii_arr = array();
1616
                            $ii_arr['id'] = (int)$invoice_item['id'];
1617
                            $ii_arr['quantity'] = (int)$invoice_item['quantity'];
1618
1619
                            if (isset( $invoice_item['meta'] ) && !empty( $invoice_item['meta'] ) ) {
1620
                                $ii_arr['meta'] = $invoice_item['meta'];
1621
                            }
1622
1623
                            if ( isset( $invoice_item['custom_price'] ) && !empty( $invoice_item['custom_price'] ) ) {
1624
                                $ii_arr['custom_price'] = $invoice_item['custom_price'];
1625
                            }
1626
1627
                            $user_cart_details[] = $ii_arr;
1628
                        }
1629
                    }
1630
1631
                    if ( !empty( $user_cart_details ) && serialize( $cart_items ) == serialize( $user_cart_details ) ) {
1632
                        wp_send_json( array(
1633
                            'success' =>  $invoice->get_checkout_payment_url()
1634
                        ) );
1635
                        wp_die();
1636
                    }
1637
                }
1638
            }
1639
1640
            // Create invoice and send user to checkout
1641
            if ( !empty( $cart_items ) ) {
1642
                $invoice_data = array(
1643
                    'status'        =>  'wpi-pending',
1644
                    'created_via'   =>  'wpi',
1645
                    'user_id'       =>  $user_id,
1646
                    'cart_details'  =>  $cart_items,
1647
                );
1648
1649
                $invoice = wpinv_insert_invoice( $invoice_data, true );
1650
1651
                if ( !empty( $invoice ) && isset( $invoice->ID ) ) {
1652
                    wp_send_json( array(
1653
                        '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

1653
                        '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...
1654
                    ) );
1655
                } else {
1656
                    wp_send_json( array(
1657
                        'error' => __( 'Invoice failed to create', 'invoicing' )
1658
                    ) );
1659
                }
1660
            } else {
1661
                wp_send_json( array(
1662
                    'error' => __( 'Items not valid.', 'invoicing' )
1663
                ) );
1664
            }
1665
        }
1666
1667
        wp_die();
1668
    }
1669
}
1670
1671
WPInv_Ajax::init();