Passed
Pull Request — master (#343)
by Brian
100:00
created

WPInv_Ajax::payment_form_discount()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 39
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 20
nc 5
nop 0
dl 0
loc 39
rs 8.9777
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
        );
84
85
        foreach ( $ajax_events as $ajax_event => $nopriv ) {
86
            add_action( 'wp_ajax_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
87
            add_action( 'wp_ajax_getpaid_' . $ajax_event, array( __CLASS__, $ajax_event ) );
88
            
89
            if ( !defined( 'WPI_AJAX_' . strtoupper( $nopriv ) ) ) {
90
                define( 'WPI_AJAX_' . strtoupper( $nopriv ), 1 );
91
            }
92
93
            if ( $nopriv ) {
94
                add_action( 'wp_ajax_nopriv_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
95
                add_action( 'wp_ajax_nopriv_getpaid_' . $ajax_event, array( __CLASS__, $ajax_event ) );
96
97
                add_action( 'wpinv_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) );
98
            }
99
        }
100
    }
101
    
102
    public static function add_note() {
103
        check_ajax_referer( 'add-invoice-note', '_nonce' );
104
105
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
106
            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...
107
        }
108
109
        $post_id   = absint( $_POST['post_id'] );
110
        $note      = wp_kses_post( trim( stripslashes( $_POST['note'] ) ) );
111
        $note_type = sanitize_text_field( $_POST['note_type'] );
112
113
        $is_customer_note = $note_type == 'customer' ? 1 : 0;
114
115
        if ( $post_id > 0 ) {
116
            $note_id = wpinv_insert_payment_note( $post_id, $note, $is_customer_note );
117
118
            if ( $note_id > 0 && !is_wp_error( $note_id ) ) {
119
                wpinv_get_invoice_note_line_item( $note_id );
120
            }
121
        }
122
123
        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...
124
    }
125
126
    public static function delete_note() {
127
        check_ajax_referer( 'delete-invoice-note', '_nonce' );
128
129
        if ( !wpinv_current_user_can_manage_invoicing() ) {
130
            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...
131
        }
132
133
        $note_id = (int)$_POST['note_id'];
134
135
        if ( $note_id > 0 ) {
136
            wp_delete_comment( $note_id, true );
137
        }
138
139
        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...
140
    }
141
    
142
    public static function get_states_field() {
143
        echo wpinv_get_states_field();
144
        
145
        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...
146
    }
147
    
148
    public static function checkout() {
149
        if ( ! defined( 'WPINV_CHECKOUT' ) ) {
150
            define( 'WPINV_CHECKOUT', true );
151
        }
152
153
        wpinv_process_checkout();
154
        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...
155
    }
156
    
157
    public static function add_invoice_item() {
158
        global $wpi_userID, $wpinv_ip_address_country;
159
        check_ajax_referer( 'invoice-item', '_nonce' );
160
        if ( !wpinv_current_user_can_manage_invoicing() ) {
161
            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...
162
        }
163
        
164
        $item_id    = sanitize_text_field( $_POST['item_id'] );
165
        $invoice_id = absint( $_POST['invoice_id'] );
166
        
167
        if ( !is_numeric( $invoice_id ) || !is_numeric( $item_id ) ) {
168
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
169
        }
170
        
171
        $invoice    = wpinv_get_invoice( $invoice_id );
172
        if ( empty( $invoice ) ) {
173
            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...
174
        }
175
        
176
        if ( $invoice->is_paid() || $invoice->is_refunded() ) {
177
            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...
178
        }
179
        
180
        if ( !empty( $_POST['user_id'] ) ) {
181
            $wpi_userID = absint( $_POST['user_id'] ); 
182
        }
183
184
        $item = new WPInv_Item( $item_id );
185
        if ( !( !empty( $item ) && $item->post_type == 'wpi_item' ) ) {
186
            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...
187
        }
188
        
189
        // Validate item before adding to invoice because recurring item must be paid individually.
190
        if ( !empty( $invoice->cart_details ) ) {
191
            $valid = true;
192
            
193
            if ( $recurring_item = $invoice->get_recurring() ) {
194
                if ( $recurring_item != $item_id ) {
195
                    $valid = false;
196
                }
197
            } else if ( wpinv_is_recurring_item( $item_id ) ) {
198
                $valid = false;
199
            }
200
            
201
            if ( !$valid ) {
202
                $response               = array();
203
                $response['success']    = false;
204
                $response['msg']        = __( 'You can not add item because recurring item must be paid individually!', 'invoicing' );
205
                wp_send_json( $response );
206
            }
207
        }
208
        
209
        $checkout_session = wpinv_get_checkout_session();
210
        
211
        $data                   = array();
212
        $data['invoice_id']     = $invoice_id;
213
        $data['cart_discounts'] = $invoice->get_discounts( true );
214
        
215
        wpinv_set_checkout_session( $data );
216
        
217
        $quantity = wpinv_item_quantities_enabled() && !empty($_POST['qty']) && (int)$_POST['qty'] > 0 ? (int)$_POST['qty'] : 1;
218
219
        $args = array(
220
            'id'            => $item_id,
221
            'quantity'      => $quantity,
222
            'item_price'    => $item->get_price(),
223
            'custom_price'  => '',
224
            'tax'           => 0.00,
225
            'discount'      => 0,
226
            'meta'          => array(),
227
            'fees'          => array()
228
        );
229
230
        $invoice->add_item( $item_id, $args );
231
        $invoice->save();
232
        
233
        if ( empty( $_POST['country'] ) ) {
234
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
235
        }
236
        if ( empty( $_POST['state'] ) ) {
237
            $_POST['state'] = $invoice->state;
238
        }
239
         
240
        $invoice->country   = sanitize_text_field( $_POST['country'] );
241
        $invoice->state     = sanitize_text_field( $_POST['state'] );
242
        
243
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
244
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
245
        
246
        $wpinv_ip_address_country = $invoice->country;
247
248
        $invoice->recalculate_totals(true);
249
        
250
        $response                       = array();
251
        $response['success']            = true;
252
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
253
        $response['data']['subtotal']   = $invoice->get_subtotal();
254
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
255
        $response['data']['tax']        = $invoice->get_tax();
256
        $response['data']['taxf']       = $invoice->get_tax(true);
257
        $response['data']['discount']   = $invoice->get_discount();
258
        $response['data']['discountf']  = $invoice->get_discount(true);
259
        $response['data']['total']      = $invoice->get_total();
260
        $response['data']['totalf']     = $invoice->get_total(true);
261
        
262
        wpinv_set_checkout_session($checkout_session);
263
        
264
        wp_send_json( $response );
265
    }
266
267
268
    public static function remove_invoice_item() {
269
        global $wpi_userID, $wpinv_ip_address_country;
270
        
271
        check_ajax_referer( 'invoice-item', '_nonce' );
272
        if ( !wpinv_current_user_can_manage_invoicing() ) {
273
            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...
274
        }
275
        
276
        $item_id    = sanitize_text_field( $_POST['item_id'] );
277
        $invoice_id = absint( $_POST['invoice_id'] );
278
        $cart_index = isset( $_POST['index'] ) && $_POST['index'] >= 0 ? $_POST['index'] : false;
279
        
280
        if ( !is_numeric( $invoice_id ) || !is_numeric( $item_id ) ) {
281
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
282
        }
283
284
        $invoice    = wpinv_get_invoice( $invoice_id );
285
        if ( empty( $invoice ) ) {
286
            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...
287
        }
288
        
289
        if ( $invoice->is_paid() || $invoice->is_refunded() ) {
290
            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...
291
        }
292
        
293
        if ( !empty( $_POST['user_id'] ) ) {
294
            $wpi_userID = absint( $_POST['user_id'] ); 
295
        }
296
297
        $item       = new WPInv_Item( $item_id );
298
        if ( !( !empty( $item ) && $item->post_type == 'wpi_item' ) ) {
299
            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...
300
        }
301
        
302
        $checkout_session = wpinv_get_checkout_session();
303
        
304
        $data                   = array();
305
        $data['invoice_id']     = $invoice_id;
306
        $data['cart_discounts'] = $invoice->get_discounts( true );
307
        
308
        wpinv_set_checkout_session( $data );
309
310
        $args = array(
311
            'id'         => $item_id,
312
            'quantity'   => 1,
313
            'cart_index' => $cart_index
314
        );
315
316
        $invoice->remove_item( $item_id, $args );
317
        $invoice->save();
318
        
319
        if ( empty( $_POST['country'] ) ) {
320
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
321
        }
322
        if ( empty( $_POST['state'] ) ) {
323
            $_POST['state'] = $invoice->state;
324
        }
325
         
326
        $invoice->country   = sanitize_text_field( $_POST['country'] );
327
        $invoice->state     = sanitize_text_field( $_POST['state'] );
328
        
329
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
330
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
331
        
332
        $wpinv_ip_address_country = $invoice->country;
333
        
334
        $invoice->recalculate_totals(true);
335
        
336
        $response                       = array();
337
        $response['success']            = true;
338
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
339
        $response['data']['subtotal']   = $invoice->get_subtotal();
340
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
341
        $response['data']['tax']        = $invoice->get_tax();
342
        $response['data']['taxf']       = $invoice->get_tax(true);
343
        $response['data']['discount']   = $invoice->get_discount();
344
        $response['data']['discountf']  = $invoice->get_discount(true);
345
        $response['data']['total']      = $invoice->get_total();
346
        $response['data']['totalf']     = $invoice->get_total(true);
347
        
348
        wpinv_set_checkout_session($checkout_session);
349
        
350
        wp_send_json( $response );
351
    }
352
    
353
    public static function create_invoice_item() {
354
        check_ajax_referer( 'invoice-item', '_nonce' );
355
        if ( !wpinv_current_user_can_manage_invoicing() ) {
356
            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...
357
        }
358
        
359
        $invoice_id = absint( $_POST['invoice_id'] );
360
361
        // Find the item
362
        if ( !is_numeric( $invoice_id ) ) {
363
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
364
        }        
365
        
366
        $invoice     = wpinv_get_invoice( $invoice_id );
367
        if ( empty( $invoice ) ) {
368
            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...
369
        }
370
        
371
        // Validate item before adding to invoice because recurring item must be paid individually.
372
        if ( !empty( $invoice->cart_details ) && $invoice->get_recurring() ) {
373
            $response               = array();
374
            $response['success']    = false;
375
            $response['msg']        = __( 'You can not add item because recurring item must be paid individually!', 'invoicing' );
376
            wp_send_json( $response );
377
        }        
378
        
379
        $save_item = wp_unslash( $_POST['_wpinv_quick'] );
380
        
381
        $meta               = array();
382
        $meta['type']       = !empty($save_item['type']) ? sanitize_text_field($save_item['type']) : 'custom';
383
        $meta['price']      = !empty($save_item['price']) ? wpinv_sanitize_amount( $save_item['price'] ) : 0;
384
        $meta['vat_rule']   = !empty($save_item['vat_rule']) ? sanitize_text_field($save_item['vat_rule']) : 'digital';
385
        $meta['vat_class']  = !empty($save_item['vat_class']) ? sanitize_text_field($save_item['vat_class']) : '_standard';
386
        
387
        $data                   = array();
388
        $data['post_title']     = sanitize_text_field($save_item['name']);
389
        $data['post_status']    = 'publish';
390
        $data['post_excerpt']   = ! empty( $save_item['excerpt'] ) ? wp_kses_post( $save_item['excerpt'] ) : '';
391
        $data['meta']           = $meta;
392
        
393
        $item = new WPInv_Item();
394
        $item->create( $data );
395
        
396
        if ( !empty( $item ) ) {
397
            $_POST['item_id']   = $item->ID;
398
            $_POST['qty']       = !empty($save_item['qty']) && $save_item['qty'] > 0 ? (int)$save_item['qty'] : 1;
399
            
400
            self::add_invoice_item();
401
        }
402
        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...
403
    }
404
    
405
    public static function get_billing_details() {
406
        check_ajax_referer( 'get-billing-details', '_nonce' );
407
        
408
        if ( !wpinv_current_user_can_manage_invoicing() ) {
409
            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...
410
        }
411
412
        $user_id            = (int)$_POST['user_id'];
413
        $billing_details    = wpinv_get_user_address($user_id);
414
        $billing_details    = apply_filters( 'wpinv_fill_billing_details', $billing_details, $user_id );
415
        
416
        if (isset($billing_details['user_id'])) {
417
            unset($billing_details['user_id']);
418
        }
419
        
420
        if (isset($billing_details['email'])) {
421
            unset($billing_details['email']);
422
        }
423
424
        $response                               = array();
425
        $response['success']                    = true;
426
        $response['data']['billing_details']    = $billing_details;
427
        
428
        wp_send_json( $response );
429
    }
430
    
431
    public static function admin_recalculate_totals() {
432
        global $wpi_userID, $wpinv_ip_address_country;
433
        
434
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
435
        if ( !wpinv_current_user_can_manage_invoicing() ) {
436
            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...
437
        }
438
        
439
        $invoice_id = absint( $_POST['invoice_id'] );        
440
        $invoice    = wpinv_get_invoice( $invoice_id );
441
        if ( empty( $invoice ) ) {
442
            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...
443
        }
444
445
        $checkout_session = wpinv_get_checkout_session();
446
447
        $data                   = array();
448
        $data['invoice_id']     = $invoice_id;
449
        $data['cart_discounts'] = $invoice->get_discounts( true );
450
451
        wpinv_set_checkout_session( $data );
452
        
453
        if ( !empty( $_POST['user_id'] ) ) {
454
            $wpi_userID = absint( $_POST['user_id'] ); 
455
        }
456
        
457
        if ( empty( $_POST['country'] ) ) {
458
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
459
        }
460
461
        $disable_taxes = 0;
462
        if ( ! empty( $_POST['disable_taxes'] ) ) {
463
            $disable_taxes = 1;
464
        }
465
        $invoice->set( 'disable_taxes', $disable_taxes );
466
467
        $invoice->country = sanitize_text_field( $_POST['country'] );
468
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
469
        if ( isset( $_POST['state'] ) ) {
470
            $invoice->state = sanitize_text_field( $_POST['state'] );
471
            $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
472
        }
473
        
474
        $wpinv_ip_address_country = $invoice->country;
475
        
476
        $invoice = $invoice->recalculate_totals(true);
477
        
478
        $response                       = array();
479
        $response['success']            = true;
480
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
481
        $response['data']['subtotal']   = $invoice->get_subtotal();
482
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
483
        $response['data']['tax']        = $invoice->get_tax();
484
        $response['data']['taxf']       = $invoice->get_tax(true);
485
        $response['data']['discount']   = $invoice->get_discount();
486
        $response['data']['discountf']  = $invoice->get_discount(true);
487
        $response['data']['total']      = $invoice->get_total();
488
        $response['data']['totalf']     = $invoice->get_total(true);
489
        
490
        wpinv_set_checkout_session($checkout_session);
491
492
        wp_send_json( $response );
493
    }
494
    
495
    public static function admin_apply_discount() {
496
        global $wpi_userID;
497
        
498
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
499
        if ( !wpinv_current_user_can_manage_invoicing() ) {
500
            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...
501
        }
502
        
503
        $invoice_id = absint( $_POST['invoice_id'] );
504
        $discount_code = sanitize_text_field( $_POST['code'] );
505
        if ( empty( $invoice_id ) || empty( $discount_code ) ) {
506
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
507
        }
508
        
509
        $invoice = wpinv_get_invoice( $invoice_id );
510
        if ( empty( $invoice ) || ( !empty( $invoice ) && ( $invoice->is_paid() || $invoice->is_refunded() ) ) ) {
511
            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...
512
        }
513
        
514
        $checkout_session = wpinv_get_checkout_session();
515
        
516
        $data                   = array();
517
        $data['invoice_id']     = $invoice_id;
518
        $data['cart_discounts'] = $invoice->get_discounts( true );
519
        
520
        wpinv_set_checkout_session( $data );
521
        
522
        $response               = array();
523
        $response['success']    = false;
524
        $response['msg']        = __( 'This discount is invalid.', 'invoicing' );
525
        $response['data']['code'] = $discount_code;
526
        
527
        if ( wpinv_is_discount_valid( $discount_code, $invoice->get_user_id() ) ) {
528
            $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...
529
            
530
            $response['success'] = true;
531
            $response['msg'] = __( 'Discount has been applied successfully.', 'invoicing' );
532
        }  else {
533
            $errors = wpinv_get_errors();
534
            if ( !empty( $errors['wpinv-discount-error'] ) ) {
535
                $response['msg'] = $errors['wpinv-discount-error'];
536
            }
537
            wpinv_unset_error( 'wpinv-discount-error' );
538
        }
539
        
540
        wpinv_set_checkout_session($checkout_session);
541
        
542
        wp_send_json( $response );
543
    }
544
    
545
    public static function admin_remove_discount() {
546
        global $wpi_userID;
547
        
548
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
549
        if ( !wpinv_current_user_can_manage_invoicing() ) {
550
            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...
551
        }
552
        
553
        $invoice_id = absint( $_POST['invoice_id'] );
554
        $discount_code = sanitize_text_field( $_POST['code'] );
555
        if ( empty( $invoice_id ) || empty( $discount_code ) ) {
556
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
557
        }
558
        
559
        $invoice = wpinv_get_invoice( $invoice_id );
560
        if ( empty( $invoice ) || ( !empty( $invoice ) && ( $invoice->is_paid() || $invoice->is_refunded() ) ) ) {
561
            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...
562
        }
563
        
564
        $checkout_session = wpinv_get_checkout_session();
565
        
566
        $data                   = array();
567
        $data['invoice_id']     = $invoice_id;
568
        $data['cart_discounts'] = $invoice->get_discounts( true );
569
        
570
        wpinv_set_checkout_session( $data );
571
        
572
        $response               = array();
573
        $response['success']    = false;
574
        $response['msg']        = NULL;
575
        
576
        $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...
577
        $response['success'] = true;
578
        $response['msg'] = __( 'Discount has been removed successfully.', 'invoicing' );
579
        
580
        wpinv_set_checkout_session($checkout_session);
581
        
582
        wp_send_json( $response );
583
    }
584
    
585
    public static function check_email() {
586
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
587
        if ( !wpinv_current_user_can_manage_invoicing() ) {
588
            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...
589
        }
590
        
591
        $email = sanitize_text_field( $_POST['email'] );
592
        
593
        $response = array();
594
        if ( is_email( $email ) && email_exists( $email ) && $user_data = get_user_by( 'email', $email ) ) {
595
            $user_id            = $user_data->ID;
596
            $user_login         = $user_data->user_login;
597
            $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...
598
            $billing_details    = wpinv_get_user_address($user_id);
599
            $billing_details    = apply_filters( 'wpinv_fill_billing_details', $billing_details, $user_id );
600
            
601
            if (isset($billing_details['user_id'])) {
602
                unset($billing_details['user_id']);
603
            }
604
            
605
            if (isset($billing_details['email'])) {
606
                unset($billing_details['email']);
607
            }
608
            
609
            $response['success']                    = true;
610
            $response['data']['id']                 = $user_data->ID;
611
            $response['data']['name']               = $user_data->user_email;
612
            $response['data']['billing_details']    = $billing_details;
613
        }
614
        
615
        wp_send_json( $response );
616
    }
617
    
618
    public static function run_tool() {
619
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
620
        if ( !wpinv_current_user_can_manage_invoicing() ) {
621
            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...
622
        }
623
        
624
        $tool = sanitize_text_field( $_POST['tool'] );
625
        
626
        do_action( 'wpinv_run_tool' );
627
        
628
        if ( !empty( $tool ) ) {
629
            do_action( 'wpinv_tool_' . $tool );
630
        }
631
    }
632
    
633
    public static function apply_discount() {
634
        global $wpi_userID;
635
        
636
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
637
        
638
        $response = array();
639
        
640
        if ( isset( $_POST['code'] ) ) {
641
            $discount_code = sanitize_text_field( $_POST['code'] );
642
643
            $response['success']        = false;
644
            $response['msg']            = '';
645
            $response['data']['code']   = $discount_code;
646
            
647
            $invoice = wpinv_get_invoice_cart();
648
            if ( empty( $invoice->ID ) ) {
649
                $response['msg'] = __( 'Invalid checkout request.', 'invoicing' );
650
                wp_send_json( $response );
651
            }
652
653
            $wpi_userID = $invoice->get_user_id();
654
655
            if ( wpinv_is_discount_valid( $discount_code, $wpi_userID ) ) {
656
                $discount       = wpinv_get_discount_by_code( $discount_code );
657
                $discounts      = wpinv_set_cart_discount( $discount_code );
658
                $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...
659
                $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...
660
                $cart_totals    = wpinv_recalculate_tax( true );
661
            
662
                if ( !empty( $cart_totals ) ) {
663
                    $response['success']        = true;
664
                    $response['data']           = $cart_totals;
665
                    $response['data']['code']   = $discount_code;
666
                } else {
667
                    $response['success']        = false;
668
                }
669
            } else {
670
                $errors = wpinv_get_errors();
671
                $response['msg']  = $errors['wpinv-discount-error'];
672
                wpinv_unset_error( 'wpinv-discount-error' );
673
            }
674
675
            // Allow for custom discount code handling
676
            $response = apply_filters( 'wpinv_ajax_discount_response', $response );
677
        }
678
        
679
        wp_send_json( $response );
680
    }
681
    
682
    public static function remove_discount() {
683
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
684
        
685
        $response = array();
686
        
687
        if ( isset( $_POST['code'] ) ) {
688
            $discount_code  = sanitize_text_field( $_POST['code'] );
689
            $discounts      = wpinv_unset_cart_discount( $discount_code );
690
            $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...
691
            $cart_totals    = wpinv_recalculate_tax( true );
692
            
693
            if ( !empty( $cart_totals ) ) {
694
                $response['success']        = true;
695
                $response['data']           = $cart_totals;
696
                $response['data']['code']   = $discount_code;
697
            } else {
698
                $response['success']        = false;
699
            }
700
            
701
            // Allow for custom discount code handling
702
            $response = apply_filters( 'wpinv_ajax_discount_response', $response );
703
        }
704
        
705
        wp_send_json( $response );
706
    }
707
708
    /**
709
     * Retrieves the markup for a payment form.
710
     */
711
    public static function get_payment_form() {
712
713
        // Check nonce.
714
        if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'], 'getpaid_ajax_form' ) ) {
715
            _e( 'Error: Reload the page and try again.', 'invoicing' );
716
            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...
717
        }
718
719
        // Is the request set up correctly?
720
		if ( empty( $_GET['form'] ) && empty( $_GET['item'] ) ) {
721
			echo aui()->alert(
0 ignored issues
show
Bug introduced by
The function aui was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

721
			echo /** @scrutinizer ignore-call */ aui()->alert(
Loading history...
722
				array(
723
					'type'    => 'warning',
724
					'content' => __( 'No payment form or item provided', 'invoicing' ),
725
				)
726
            );
727
            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...
728
        }
729
730
        // Payment form or button?
731
		if ( ! empty( $_GET['form'] ) ) {
732
            echo getpaid_display_payment_form( $_GET['form'] );
733
		} else if( $_GET['invoice'] ) {
734
		    echo getpaid_display_invoice_payment_form( $_GET['invoice'] );
735
        } else {
736
			$items = getpaid_convert_items_to_array( $_GET['item'] );
737
		    echo getpaid_display_item_payment_form( $items );
738
        }
739
        
740
        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...
741
742
    }
743
744
    /**
745
     * Payment forms.
746
     *
747
     * @since 1.0.18
748
     */
749
    public static function payment_form() {
750
        global $invoicing, $wpi_checkout_id, $cart_total;
751
752
        // Check nonce.
753
        if ( ! isset( $_POST['wpinv_payment_form'] ) || ! wp_verify_nonce( $_POST['wpinv_payment_form'], 'wpinv_payment_form' ) ) {
754
            wp_send_json_error( __( 'Security checks failed.', 'invoicing' ) );
755
        }
756
757
        // Prepare submitted data...
758
        $data = wp_unslash( $_POST );
759
760
        // ... form fields...
761
        if ( empty( $data['form_id'] ) || 'publish' != get_post_status( $data['form_id'] ) ) {
762
            wp_send_json_error( __( 'This payment form is no longer active.', 'invoicing' ) );
763
        }
764
765
        if ( empty( $data['billing_email'] ) || ! is_email( $data['billing_email'] ) ) {
766
            wp_send_json_error( __( 'Provide a valid billing email.', 'invoicing' ) );
767
        }
768
769
        $prepared = array(
770
            'billing_email'                    => sanitize_email( $data['billing_email'] ),
771
            __( 'Billing Email', 'invoicing' ) => sanitize_email( $data['billing_email'] ),
772
            __( 'Form Id', 'invoicing' )       => absint( $data['form_id'] ),
773
        );
774
775
        // Do we have a discount?
776
        $discount = 0;
777
        if ( ! empty( $data['discount'] ) ) {
778
779
            // Validate discount.
780
            $discount = self::payment_form_validate_discount( $data );
781
782
            if ( is_string( $discount ) ){
783
                wp_send_json_error( $discount );
784
            }
785
786
            if ( is_array( $discount ) ){
787
                $discount = $discount[ 'discount' ];
788
            }
789
790
            if ( ! $discount ) {
791
                $discount = 0;
792
            }
793
794
        }
795
796
        $fields = $invoicing->form_elements->get_form_elements( $data['form_id'] );
797
798
        // ... and form items.
799
        if ( ! empty( $data['invoice_id'] ) ) {
800
            $invoice = wpinv_get_invoice( $data['invoice_id'] );
801
802
            if ( empty( $invoice ) ) {
803
                wp_send_json_error( __( 'Invalid invoice.', 'invoicing' ) );
804
            }
805
806
            if ( $invoice->is_paid() ) {
807
                wp_send_json_error( __( 'This invoice has already been paid.', 'invoicing' ) );
808
            }
809
810
            $items   = $invoicing->form_elements->convert_checkout_items( $invoice->cart_details, $invoice );
811
812
        } else {
813
814
            if ( isset( $data['form_items'] ) ) {
815
                $items = getpaid_convert_items_to_array( $data['form_items'] );
816
                $items = $invoicing->form_elements->convert_normal_items( $items );
817
            } else {
818
                $items = $invoicing->form_elements->get_form_items( $data['form_id'] );
819
            }
820
821
            $invoice = 0;
822
        }
823
824
        $prepared_items = array();
825
        $address_fields = array();
826
        $has_recurring  = false;
827
828
        if ( ! empty( $data['wpinv-items'] ) ) {
829
830
            $selected_items = wpinv_clean( $data['wpinv-items'] );
831
832
            foreach ( $items as $item ) {
833
834
                if ( ! empty( $item['required'] ) && ! isset( $selected_items[ $item['id'] ] ) ) {
835
                    wp_send_json_error( __( 'A required item is missing.', 'invoicing' ) );
836
                }
837
838
                if ( ! isset( $selected_items[ $item['id'] ] ) ) {
839
                    continue;
840
                }
841
842
                if ( ! empty( $item['recurring'] ) ) {
843
                    $has_recurring  = true;
844
                }
845
846
                $quantity = empty( $item['quantity'] ) ? 1 : absint( $item['quantity'] );
847
848
                if ( ! empty( $item['allow_quantities'] ) && ! empty( $data["wpinv-item-{$item['id']}-quantity"] ) ) {
849
850
                    $_quantity = intval( $data["wpinv-item-{$item['id']}-quantity"] );
851
852
                    if ( ! empty( $_quantity ) ) {
853
                        $quantity = $_quantity;
854
                    }
855
                }
856
857
                // Custom pricing.
858
                if ( ! empty( $item['custom_price'] ) ) {
859
860
                    $minimum_price = wpinv_sanitize_amount( $item['minimum_price'] );
861
                    $set_price     = wpinv_sanitize_amount( $selected_items[ $item['id'] ] );
862
863
                    if ( $set_price < $minimum_price ) {
864
                        wp_send_json_error( __( 'The provided amount is less than the minimum allowed value.', 'invoicing' ) );
865
                    }
866
867
                    $prepared_items[] = array(
868
                        'id'           =>$item['id'],
869
                        'item_price'   => wpinv_sanitize_amount( $item['price'] ),
870
                        'custom_price' => $set_price,
871
                        'name'         => $item['title'],
872
                        'quantity'     => $quantity,
873
                    );
874
875
                } else {
876
877
                    $prepared_items[] = array(
878
                        'id'           => $item['id'],
879
                        'item_price'   => wpinv_sanitize_amount( $item['price'] ),
880
                        'custom_price' => wpinv_sanitize_amount( $item['price'] ),
881
                        'name'         => $item['title'],
882
                        'quantity'     => $quantity,
883
                    );
884
885
                }
886
887
            }
888
889
        } else {
890
891
            wp_send_json_error( __( 'You have not selected any items.', 'invoicing' ) );
892
893
        }
894
895
        if ( $has_recurring && 1 != count( $prepared_items ) ) {
896
            wp_send_json_error( __( 'Recurring items should be bought individually.', 'invoicing' ) );
897
        }
898
899
        // Are all required fields provided?
900
        foreach ( $fields as $field ) {
901
902
            if ( ! empty( $field['premade'] ) ) {
903
                continue;
904
            }
905
906
            if ( ! empty( $field['required'] ) && empty( $data[ $field['id'] ] ) ) {
907
                wp_send_json_error( __( 'Some required fields have not been filled.', 'invoicing' ) );
908
            }
909
910
            if ( $field['type'] == 'address' ) {
911
912
                foreach ( $field['fields'] as $address_field ) {
913
914
                    if ( empty( $address_field['visible'] ) ) {
915
                        continue;
916
                    }
917
918
                    if ( ! empty( $address_field['required'] ) && empty( $data[ $address_field['name'] ] ) ) {
919
                        wp_send_json_error( __( 'Some required fields have not been filled.', 'invoicing' ) );
920
                    }
921
922
                    if ( isset( $data[ $address_field['name'] ] ) ) {
923
                        $label = str_replace( 'wpinv_', '', $address_field['name'] );
924
                        $address_fields[ $label ] = wpinv_clean( $data[ $address_field['name'] ] );
925
                    }
926
927
                }
928
929
            } else if ( isset( $data[ $field['id'] ] ) ) {
930
                $label = $field['id'];
931
932
                if ( isset( $field['label'] ) ) {
933
                    $label = $field['label'];
934
                }
935
936
                $prepared[ wpinv_clean( $label ) ] = wpinv_clean( $data[ $field['id'] ] );
937
            }
938
939
        }
940
941
        $user = get_user_by( 'email', $prepared['billing_email'] );
942
943
        if ( empty( $user ) ) {
944
            $user = wpinv_create_user( $prepared['billing_email'] );
945
        }
946
947
        if ( is_wp_error( $user ) ) {
948
            wp_send_json_error( $user->get_error_message() );
949
        }
950
951
        if ( is_numeric( $user ) ) {
952
            $user = get_user_by( 'id', $user );
953
        }
954
955
        if ( $discount ) {
956
            $address_fields['discount'] = array( $data['discount'] );
957
        }
958
959
        // Create the invoice.
960
        if ( empty( $invoice ) ) {
961
962
            $invoice = wpinv_insert_invoice(
963
                array(
964
                    'status'        => 'wpi-pending',
965
                    'created_via'   => 'payment_form',
966
                    'user_id'       => $user->ID,
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist on WP_Error.
Loading history...
967
                    'cart_details'  => $prepared_items,
968
                    'user_info'     => $address_fields,
969
                ),
970
                true
971
            );
972
973
        } else {
974
975
            $invoice = wpinv_update_invoice(
976
                array(
977
                    'ID'            => $invoice->ID,
978
                    'status'        => 'wpi-pending',
979
                    'cart_details'  => $prepared_items,
980
                    'user_info'     => $address_fields,
981
                ),
982
                true
983
            );
984
985
        }
986
987
        if ( is_wp_error( $invoice ) ) {
988
            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

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

1001
                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...
1002
                $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

1002
                $invoice->/** @scrutinizer ignore-call */ 
1003
                          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...
1003
        );
1004
1005
        $data                   = array();
1006
        $data['invoice_id']     = $invoice->ID;
1007
        $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

1007
        /** @scrutinizer ignore-call */ 
1008
        $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...
1008
1009
        wpinv_set_checkout_session( $data );
1010
        add_filter( 'wp_redirect', array( $invoicing->form_elements, 'send_redirect_response' ) );
1011
        add_action( 'wpinv_pre_send_back_to_checkout', array( $invoicing->form_elements, 'checkout_error' ) );
1012
        
1013
        if ( ! defined( 'WPINV_CHECKOUT' ) ) {
1014
            define( 'WPINV_CHECKOUT', true );
1015
        }
1016
1017
        wpinv_process_checkout();
1018
1019
        $invoicing->form_elements->checkout_error();
1020
1021
        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...
1022
    }
1023
1024
    /**
1025
     * Payment forms.
1026
     *
1027
     * @since 1.0.18
1028
     */
1029
    public static function get_payment_form_states_field() {
1030
        global $invoicing;
1031
1032
        if ( empty( $_GET['country'] ) || empty( $_GET['form'] ) ) {
1033
            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...
1034
        }
1035
1036
        $elements = $invoicing->form_elements->get_form_elements( $_GET['form'] );
1037
1038
        if ( empty( $elements ) ) {
1039
            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...
1040
        }
1041
1042
        $address_fields = array();
1043
        foreach ( $elements as $element ) {
1044
            if ( 'address' === $element['type'] ) {
1045
                $address_fields = $element;
1046
                break;
1047
            }
1048
        }
1049
1050
        if ( empty( $address_fields ) ) {
1051
            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...
1052
        }
1053
1054
        foreach( $address_fields['fields'] as $address_field ) {
1055
1056
            if ( 'wpinv_state' == $address_field['name'] ) {
1057
1058
                $label = $address_field['label'];
1059
1060
                if ( ! empty( $address_field['required'] ) ) {
1061
                    $label .= "<span class='text-danger'> *</span>";
1062
                }
1063
1064
                $states = wpinv_get_country_states( $_GET['country'] );
1065
1066
                if ( ! empty( $states ) ) {
1067
1068
                    $html = aui()->select(
0 ignored issues
show
Bug introduced by
The function aui was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1068
                    $html = /** @scrutinizer ignore-call */ aui()->select(
Loading history...
1069
                            array(
1070
                                'options'          => $states,
1071
                                'name'             => esc_attr( $address_field['name'] ),
1072
                                'id'               => esc_attr( $address_field['name'] ),
1073
                                'placeholder'      => esc_attr( $address_field['placeholder'] ),
1074
                                'required'         => (bool) $address_field['required'],
1075
                                'no_wrap'          => true,
1076
                                'label'            => wp_kses_post( $label ),
1077
                                'select2'          => false,
1078
                            )
1079
                        );
1080
1081
                } else {
1082
1083
                    $html = aui()->input(
1084
                            array(
1085
                                'name'       => esc_attr( $address_field['name'] ),
1086
                                'id'         => esc_attr( $address_field['name'] ),
1087
                                'required'   => (bool) $address_field['required'],
1088
                                'label'      => wp_kses_post( $label ),
1089
                                'no_wrap'    => true,
1090
                                'type'       => 'text',
1091
                            )
1092
                        );
1093
1094
                }
1095
1096
                wp_send_json_success( str_replace( 'sr-only', '', $html ) );
1097
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
1098
1099
            }
1100
1101
        }
1102
    
1103
        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...
1104
    }
1105
1106
    /**
1107
     * Apply taxes.
1108
     *
1109
     * @since 1.0.18
1110
     */
1111
    public static function payment_form_get_taxes() {
1112
        global $invoicing;
1113
1114
        // Check nonce.
1115
        check_ajax_referer( 'wpinv_payment_form', 'wpinv_payment_form' );
1116
1117
        // Prepare submitted data...
1118
        $data = wp_unslash( $_POST );
1119
1120
        // ... form fields...
1121
        if ( empty( $data['form_id'] ) || 'publish' != get_post_status( $data['form_id'] ) ) {
1122
            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...
1123
        }
1124
1125
        // Do we have a discount?
1126
        if ( ! empty( $data['discount'] ) ) {
1127
1128
            // Validate discount.
1129
            $discount = self::payment_form_validate_discount( $data );
1130
1131
            if ( is_array( $discount ) ){
1132
                $data['total']     = wpinv_price( wpinv_format_amount( $discount['total'] ) );
1133
                $data['sub_total'] = wpinv_price( wpinv_format_amount( $discount['sub_total'] ) );
1134
                $data['discount']  = wpinv_price( wpinv_format_amount( $discount['discount'] ) );
1135
                $data['tax']       = wpinv_price( wpinv_format_amount( $discount['tax'] ) );
1136
                wp_send_json_success( $discount );
1137
            }
1138
1139
        }        
1140
1141
        // For existing invoices.
1142
        if ( ! empty( $data['invoice_id'] ) ) {
1143
            $invoice = wpinv_get_invoice( $data['invoice_id'] );
1144
1145
            if ( empty( $invoice ) ) {
1146
                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...
1147
            }
1148
1149
            $items   = $invoicing->form_elements->convert_checkout_items( $invoice->cart_details, $invoice );
1150
            $country = $invoice->country;
1151
            $state   = $invoice->state;
1152
1153
        } else {
1154
1155
            if ( isset( $data['form_items'] ) ) {
1156
                $items = getpaid_convert_items_to_array( $data['form_items'] );
1157
                $items = $invoicing->form_elements->convert_normal_items( $items );
1158
            } else {
1159
                $items = $invoicing->form_elements->get_form_items( $data['form_id'] );
1160
            }
1161
1162
            $country   = wpinv_default_billing_country();
1163
            $state     = false;
1164
        }
1165
1166
        // What we will calculate.
1167
        $total     = 0;
1168
        $tax       = 0;
1169
        $sub_total = 0;
1170
1171
        if ( ! empty( $data['wpinv_country'] ) ) {
1172
            $country = $data['wpinv_country'];
1173
        }
1174
1175
        if ( ! empty( $data['wpinv_state'] ) ) {
1176
            $state = $data['wpinv_state'];
1177
        }
1178
1179
        $has_recurring  = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $has_recurring is dead and can be removed.
Loading history...
1180
        if ( ! empty( $data['wpinv-items'] ) ) {
1181
1182
            $selected_items = wpinv_clean( $data['wpinv-items'] );
1183
1184
            foreach ( $items as $item ) {
1185
1186
                if ( ! isset( $selected_items[ $item['id'] ] ) ) {
1187
                    continue;
1188
                }
1189
1190
                if ( ! empty( $item['recurring'] ) ) {
1191
                    $has_recurring  = true;
1192
                }
1193
1194
                $quantity = empty( $item['quantity'] ) ? 1 : absint( $item['quantity'] );
1195
1196
                if ( ! empty( $item['allow_quantities'] ) && ! empty( $data["wpinv-item-{$item['id']}-quantity"] ) ) {
1197
1198
                    $quantity = intval( $data["wpinv-item-{$item['id']}-quantity"] );
1199
1200
                    if ( 1 > $quantity ) {
1201
                        $quantity = 1;
1202
                    }
1203
1204
                }
1205
1206
                // Custom pricing.
1207
                $price = wpinv_sanitize_amount( $item['price'] );
1208
                if ( ! empty( $item['custom_price'] ) ) {
1209
1210
                    $minimum_price = wpinv_sanitize_amount( $item['minimum_price'] );
1211
                    $set_price     = wpinv_sanitize_amount( $selected_items[ $item['id'] ] );
1212
1213
                    if ( $set_price < $minimum_price ) {
1214
                        $set_price = $minimum_price;
1215
                    }
1216
1217
                    $price = wpinv_sanitize_amount( $set_price );
1218
1219
                }
1220
1221
                $price  = $quantity * floatval( $price );
1222
1223
                if ( wpinv_use_taxes() ) {
1224
1225
                    $rate = wpinv_get_tax_rate( $country, $state, (int) $item['id'] );
1226
1227
                    if ( wpinv_prices_include_tax() ) {
1228
                        $pre_tax  = ( $price - $price * $rate * 0.01 );
1229
                        $item_tax = $price - $pre_tax;
1230
                    } else {
1231
                        $pre_tax  = $price;
1232
                        $item_tax = $price * $rate * 0.01;
1233
                    }
1234
1235
                    $tax       = $tax + $item_tax;
1236
                    $sub_total = $sub_total + $pre_tax;
1237
                    $total     = $sub_total + $tax;
1238
1239
                } else {
1240
                    $total  = $total + $price;
1241
                }
1242
1243
            }
1244
1245
        }
1246
1247
        wp_send_json_success( array(
1248
            'free'          => $total == 0,
1249
            'total'         => wpinv_price( wpinv_format_amount( $total ) ),
1250
            'tax'           => wpinv_price( wpinv_format_amount( $tax ) ),
1251
            'sub_total'     => wpinv_price( wpinv_format_amount( $sub_total ) ),
1252
            'has_recurring' => false,
1253
            'discount'      => false,
1254
        ));
1255
        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...
1256
    }
1257
1258
    /**
1259
     * Apply discounts.
1260
     *
1261
     * @since 1.0.19
1262
     */
1263
    public static function payment_form_discount() {
1264
1265
        // Check nonce.
1266
        check_ajax_referer( 'wpinv_payment_form', 'wpinv_payment_form' );
1267
1268
        // Prepare submitted data...
1269
        $data = wp_unslash( $_POST );
1270
1271
        // ... form fields...
1272
        if ( empty( $data['form_id'] ) || 'publish' != get_post_status( $data['form_id'] ) ) {
1273
            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...
1274
        }
1275
1276
        // Do we have a discount?
1277
        if ( empty( $data['discount'] ) ) {
1278
            _e( 'Please enter your discount code', 'invoicing' );
1279
            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...
1280
        }
1281
1282
        // Validate discount.
1283
        $data = self::payment_form_validate_discount( $data );
1284
1285
        if ( false === $data ) {
1286
            _e( 'There was an error applying your discount code', 'invoicing' );
1287
            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...
1288
        }
1289
1290
        if ( is_string( $data ) ) {
1291
            echo $data;
1292
            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...
1293
        }
1294
1295
        $data['total']     = wpinv_price( wpinv_format_amount( $data['total'] ) );
1296
        $data['sub_total'] = wpinv_price( wpinv_format_amount( $data['sub_total'] ) );
1297
        $data['discount']  = wpinv_price( wpinv_format_amount( $data['discount'] ) );
1298
        $data['tax']       = wpinv_price( wpinv_format_amount( $data['tax'] ) );
1299
1300
        wp_send_json_success( $data );
1301
        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...
1302
1303
    }
1304
1305
    /**
1306
     * Validates discounts.
1307
     *
1308
     * @since 1.0.19
1309
     */
1310
    public static function payment_form_validate_discount( $data ) {
1311
        global $invoicing;
1312
1313
        // Do we have a discount?
1314
        if ( empty( $data['discount'] ) ) {
1315
            return false;
1316
        }
1317
1318
        // If yes, ensure that it exists.
1319
        $discount = wpinv_get_discount_obj( $data['discount'] );
1320
1321
        // Ensure it is active.
1322
        if ( ! $discount->exists() || ! $discount->is_active() || ! $discount->has_started() || $discount->is_expired() ) {
1323
            return __( 'This discount code is not valid', 'invoicing' );
1324
        }
1325
1326
        // If it can only be used once per user...
1327
        if ( $discount->is_single_use ) {
1328
1329
            if ( empty( $data['billing_email'] ) ) {
1330
                return __( 'Please enter your billing email before applying this discount', 'invoicing' );
1331
            }
1332
1333
            if ( ! $discount->is_valid_for_user( $data['billing_email'] ) ) {
1334
                return __( 'You have already used this discount', 'invoicing' );
1335
            }
1336
1337
        }
1338
1339
        // Prepare items.
1340
        if ( ! empty( $data['invoice_id'] ) ) {
1341
            $invoice = wpinv_get_invoice( $data['invoice_id'] );
1342
1343
            if ( empty( $invoice ) ) {
1344
                return false;
1345
            }
1346
1347
            $country = $invoice->country;
1348
            $state   = $invoice->state;
1349
            $items   = $invoicing->form_elements->convert_checkout_items( $invoice->cart_details, $invoice );
1350
1351
        } else {
1352
1353
            if ( isset( $data['form_items'] ) ) {
1354
                $items = getpaid_convert_items_to_array( $data['form_items'] );
1355
                $items = $invoicing->form_elements->convert_normal_items( $items );
1356
            } else {
1357
                $items = $invoicing->form_elements->get_form_items( $data['form_id'] );
1358
            }
1359
1360
            $country   = wpinv_default_billing_country();
1361
            $state     = false;
1362
1363
        }
1364
1365
        // What we will calculate.
1366
        $total     = 0;
1367
        $tax       = 0;
1368
        $sub_total = 0;
1369
1370
        if ( ! empty( $data['wpinv_country'] ) ) {
1371
            $country = $data['wpinv_country'];
1372
        }
1373
1374
        if ( ! empty( $data['wpinv_state'] ) ) {
1375
            $state = $data['wpinv_state'];
1376
        }
1377
1378
        if ( ! empty( $data['wpinv-items'] ) ) {
1379
1380
            $selected_items = wpinv_clean( $data['wpinv-items'] );
1381
1382
            // Check if it is valid for the selected items.
1383
            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

1383
            if ( ! $discount->is_valid_for_items( array_keys( /** @scrutinizer ignore-type */ $selected_items ) ) ) {
Loading history...
1384
                return __( 'This discount is not valid for the items in your cart', 'invoicing' );
1385
            }
1386
1387
            $has_recurring  = false;
1388
            foreach ( $items as $item ) {
1389
1390
                if ( ! isset( $selected_items[ $item['id'] ] ) ) {
1391
                    continue;
1392
                }
1393
1394
                $quantity = empty( $item['quantity'] ) ? 1 : absint( $item['quantity'] );
1395
1396
                if ( ! empty( $item['recurring'] ) ) {
1397
                    $has_recurring  = true;
1398
                }
1399
1400
                if ( ! empty( $item['allow_quantities'] ) && ! empty( $data["wpinv-item-{$item['id']}-quantity"] ) ) {
1401
1402
                    $quantity = intval( $data["wpinv-item-{$item['id']}-quantity"] );
1403
1404
                    if ( 1 > $quantity ) {
1405
                        $quantity = 1;
1406
                    }
1407
1408
                }
1409
1410
                // Custom pricing.
1411
                $price = wpinv_sanitize_amount( $item['price'] );
1412
                if ( ! empty( $item['custom_price'] ) ) {
1413
1414
                    $minimum_price = wpinv_sanitize_amount( $item['minimum_price'] );
1415
                    $set_price     = wpinv_sanitize_amount( $selected_items[ $item['id'] ] );
1416
1417
                    if ( $set_price < $minimum_price ) {
1418
                        $set_price = $minimum_price;
1419
                    }
1420
1421
                    $price = wpinv_sanitize_amount( $set_price );
1422
1423
                }
1424
1425
                $price  = $quantity * floatval( $price );
1426
1427
                if ( wpinv_use_taxes() ) {
1428
1429
                    $rate = wpinv_get_tax_rate( $country, $state, (int) $item['id'] );
1430
1431
                    if ( wpinv_prices_include_tax() ) {
1432
                        $pre_tax  = ( $price - $price * $rate * 0.01 );
1433
                        $item_tax = $price - $pre_tax;
1434
                    } else {
1435
                        $pre_tax  = $price;
1436
                        $item_tax = $price * $rate * 0.01;
1437
                    }
1438
1439
                    $tax       = $tax + $item_tax;
1440
                    $sub_total = $sub_total + $pre_tax;
1441
                    $total     = $sub_total + $tax;
1442
1443
                } else {
1444
                    $total  = $total + $price;
1445
                }
1446
1447
            }
1448
1449
        }
1450
1451
        if ( ! $discount->is_minimum_amount_met( $total ) ) {
1452
            $min = wpinv_price( wpinv_format_amount( $discount->min_total ) );
1453
            return sprintf( __( 'The minimum total for using this discount is %s', 'invoicing' ), $min );
1454
        }
1455
1456
        if ( ! $discount->is_maximum_amount_met( $total ) ) {
1457
            $max = wpinv_price( wpinv_format_amount( $discount->max_total ) );
1458
            return sprintf( __( 'The maximum total for using this discount is %s', 'invoicing' ), $max );
1459
        }
1460
1461
        $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...
1462
        $discount = $discount->get_discounted_amount( $total );
1463
        $total    = $total - $discount;
1464
        $free     = false;
1465
1466
        if ( $total == 0 ) {
1467
            $free = true;
1468
        }
1469
1470
        return compact( 'total', 'tax', 'sub_total', 'discount', 'recurring_discount', 'free', 'has_recurring' );
1471
1472
    }
1473
1474
    /**
1475
     * Lets users buy items via ajax.
1476
     *
1477
     * @since 1.0.0
1478
     */
1479
    public static function buy_items() {
1480
        $user_id = get_current_user_id();
1481
1482
        if ( empty( $user_id ) ) { // If not logged in then lets redirect to the login page
1483
            wp_send_json( array(
1484
                '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

1484
                'success' => wp_login_url( /** @scrutinizer ignore-type */ wp_get_referer() )
Loading history...
1485
            ) );
1486
        } else {
1487
            // Only check nonce if logged in as it could be cached when logged out.
1488
            if ( ! isset( $_POST['wpinv_buy_nonce'] ) || ! wp_verify_nonce( $_POST['wpinv_buy_nonce'], 'wpinv_buy_items' ) ) {
1489
                wp_send_json( array(
1490
                    'error' => __( 'Security checks failed.', 'invoicing' )
1491
                ) );
1492
                wp_die();
1493
            }
1494
1495
            // allow to set a custom price through post_id
1496
            $items = $_POST['items'];
1497
            $related_post_id = isset( $_POST['post_id'] ) ? (int)$_POST['post_id'] : 0;
1498
            $custom_item_price = $related_post_id ? abs( get_post_meta( $related_post_id, '_wpi_custom_price', true ) ) : 0;
1499
1500
            $cart_items = array();
1501
            if ( $items ) {
1502
                $items = explode( ',', $items );
1503
1504
                foreach( $items as $item ) {
1505
                    $item_id = $item;
1506
                    $quantity = 1;
1507
1508
                    if ( strpos( $item, '|' ) !== false ) {
1509
                        $item_parts = explode( '|', $item );
1510
                        $item_id = $item_parts[0];
1511
                        $quantity = $item_parts[1];
1512
                    }
1513
1514
                    if ( $item_id && $quantity ) {
1515
                        $cart_items_arr = array(
1516
                            'id'            => (int)$item_id,
1517
                            'quantity'      => (int)$quantity
1518
                        );
1519
1520
                        // If there is a related post id then add it to meta
1521
                        if ( $related_post_id ) {
1522
                            $cart_items_arr['meta'] = array(
1523
                                'post_id'   => $related_post_id
1524
                            );
1525
                        }
1526
1527
                        // If there is a custom price then set it.
1528
                        if ( $custom_item_price ) {
1529
                            $cart_items_arr['custom_price'] = $custom_item_price;
1530
                        }
1531
1532
                        $cart_items[] = $cart_items_arr;
1533
                    }
1534
                }
1535
            }
1536
1537
            /**
1538
             * Filter the wpinv_buy shortcode cart items on the fly.
1539
             *
1540
             * @param array $cart_items The cart items array.
1541
             * @param int $related_post_id The related post id if any.
1542
             * @since 1.0.0
1543
             */
1544
            $cart_items = apply_filters( 'wpinv_buy_cart_items', $cart_items, $related_post_id );
1545
1546
            // Make sure its not in the cart already, if it is then redirect to checkout.
1547
            $cart_invoice = wpinv_get_invoice_cart();
1548
1549
            if ( isset( $cart_invoice->items ) && !empty( $cart_invoice->items ) && !empty( $cart_items ) && serialize( $cart_invoice->items ) == serialize( $cart_items ) ) {
1550
                wp_send_json( array(
1551
                    'success' =>  $cart_invoice->get_checkout_payment_url()
1552
                ) );
1553
                wp_die();
1554
            }
1555
1556
            // Check if user has invoice with same items waiting to be paid.
1557
            $user_invoices = wpinv_get_users_invoices( $user_id , 10 , false , 'wpi-pending' );
1558
            if ( !empty( $user_invoices ) ) {
1559
                foreach( $user_invoices as $user_invoice ) {
1560
                    $user_cart_details = array();
1561
                    $invoice  = wpinv_get_invoice( $user_invoice->ID );
1562
                    $cart_details = $invoice->get_cart_details();
1563
1564
                    if ( !empty( $cart_details ) ) {
1565
                        foreach ( $cart_details as $invoice_item ) {
1566
                            $ii_arr = array();
1567
                            $ii_arr['id'] = (int)$invoice_item['id'];
1568
                            $ii_arr['quantity'] = (int)$invoice_item['quantity'];
1569
1570
                            if (isset( $invoice_item['meta'] ) && !empty( $invoice_item['meta'] ) ) {
1571
                                $ii_arr['meta'] = $invoice_item['meta'];
1572
                            }
1573
1574
                            if ( isset( $invoice_item['custom_price'] ) && !empty( $invoice_item['custom_price'] ) ) {
1575
                                $ii_arr['custom_price'] = $invoice_item['custom_price'];
1576
                            }
1577
1578
                            $user_cart_details[] = $ii_arr;
1579
                        }
1580
                    }
1581
1582
                    if ( !empty( $user_cart_details ) && serialize( $cart_items ) == serialize( $user_cart_details ) ) {
1583
                        wp_send_json( array(
1584
                            'success' =>  $invoice->get_checkout_payment_url()
1585
                        ) );
1586
                        wp_die();
1587
                    }
1588
                }
1589
            }
1590
1591
            // Create invoice and send user to checkout
1592
            if ( !empty( $cart_items ) ) {
1593
                $invoice_data = array(
1594
                    'status'        =>  'wpi-pending',
1595
                    'created_via'   =>  'wpi',
1596
                    'user_id'       =>  $user_id,
1597
                    'cart_details'  =>  $cart_items,
1598
                );
1599
1600
                $invoice = wpinv_insert_invoice( $invoice_data, true );
1601
1602
                if ( !empty( $invoice ) && isset( $invoice->ID ) ) {
1603
                    wp_send_json( array(
1604
                        '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

1604
                        '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...
1605
                    ) );
1606
                } else {
1607
                    wp_send_json( array(
1608
                        'error' => __( 'Invoice failed to create', 'invoicing' )
1609
                    ) );
1610
                }
1611
            } else {
1612
                wp_send_json( array(
1613
                    'error' => __( 'Items not valid.', 'invoicing' )
1614
                ) );
1615
            }
1616
        }
1617
1618
        wp_die();
1619
    }
1620
}
1621
1622
WPInv_Ajax::init();