Passed
Pull Request — master (#377)
by Brian
04:53
created

WPInv_Ajax::apply_discount()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 47
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 31
c 0
b 0
f 0
nc 7
nop 0
dl 0
loc 47
rs 9.1128
1
<?php
2
/**
3
 * Contains the ajax handlers.
4
 *
5
 * @since 1.0.0
6
 * @package Invoicing
7
 */
8
 
9
defined( 'ABSPATH' ) || exit;
10
11
/**
12
 * WPInv_Ajax class.
13
 */
14
class WPInv_Ajax {
15
16
    /**
17
	 * Hook in ajax handlers.
18
	 */
19
	public static function init() {
20
		add_action( 'init', array( __CLASS__, 'define_ajax' ), 0 );
21
		add_action( 'template_redirect', array( __CLASS__, 'do_wpinv_ajax' ), 0 );
22
		self::add_ajax_events();
23
    }
24
25
    /**
26
	 * Set GetPaid AJAX constant and headers.
27
	 */
28
	public static function define_ajax() {
29
30
		if ( ! empty( $_GET['wpinv-ajax'] ) ) {
31
			getpaid_maybe_define_constant( 'DOING_AJAX', true );
32
			getpaid_maybe_define_constant( 'WPInv_DOING_AJAX', true );
33
			if ( ! WP_DEBUG || ( WP_DEBUG && ! WP_DEBUG_DISPLAY ) ) {
34
				/** @scrutinizer ignore-unhandled */ @ini_set( 'display_errors', 0 );
35
			}
36
			$GLOBALS['wpdb']->hide_errors();
37
		}
38
39
    }
40
    
41
    /**
42
	 * Send headers for GetPaid Ajax Requests.
43
	 *
44
	 * @since 1.0.18
45
	 */
46
	private static function wpinv_ajax_headers() {
47
		if ( ! headers_sent() ) {
48
			send_origin_headers();
49
			send_nosniff_header();
50
			nocache_headers();
51
			header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
52
			header( 'X-Robots-Tag: noindex' );
53
			status_header( 200 );
54
		}
55
    }
56
    
57
    /**
58
	 * Check for GetPaid Ajax request and fire action.
59
	 */
60
	public static function do_wpinv_ajax() {
61
		global $wp_query;
62
63
		if ( ! empty( $_GET['wpinv-ajax'] ) ) {
64
			$wp_query->set( 'wpinv-ajax', sanitize_text_field( wp_unslash( $_GET['wpinv-ajax'] ) ) );
0 ignored issues
show
Bug introduced by
It seems like wp_unslash($_GET['wpinv-ajax']) can also be of type string[]; however, parameter $str of sanitize_text_field() 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

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

1282
                'success' => wp_login_url( /** @scrutinizer ignore-type */ wp_get_referer() )
Loading history...
1283
            ) );
1284
        } else {
1285
            // Only check nonce if logged in as it could be cached when logged out.
1286
            if ( ! isset( $_POST['wpinv_buy_nonce'] ) || ! wp_verify_nonce( $_POST['wpinv_buy_nonce'], 'wpinv_buy_items' ) ) {
1287
                wp_send_json( array(
1288
                    'error' => __( 'Security checks failed.', 'invoicing' )
1289
                ) );
1290
                wp_die();
1291
            }
1292
1293
            // allow to set a custom price through post_id
1294
            $items = $_POST['items'];
1295
            $related_post_id = isset( $_POST['post_id'] ) ? (int)$_POST['post_id'] : 0;
1296
            $custom_item_price = $related_post_id ? abs( get_post_meta( $related_post_id, '_wpi_custom_price', true ) ) : 0;
1297
1298
            $cart_items = array();
1299
            if ( $items ) {
1300
                $items = explode( ',', $items );
1301
1302
                foreach( $items as $item ) {
1303
                    $item_id = $item;
1304
                    $quantity = 1;
1305
1306
                    if ( strpos( $item, '|' ) !== false ) {
1307
                        $item_parts = explode( '|', $item );
1308
                        $item_id = $item_parts[0];
1309
                        $quantity = $item_parts[1];
1310
                    }
1311
1312
                    if ( $item_id && $quantity ) {
1313
                        $cart_items_arr = array(
1314
                            'id'            => (int)$item_id,
1315
                            'quantity'      => (int)$quantity
1316
                        );
1317
1318
                        // If there is a related post id then add it to meta
1319
                        if ( $related_post_id ) {
1320
                            $cart_items_arr['meta'] = array(
1321
                                'post_id'   => $related_post_id
1322
                            );
1323
                        }
1324
1325
                        // If there is a custom price then set it.
1326
                        if ( $custom_item_price ) {
1327
                            $cart_items_arr['custom_price'] = $custom_item_price;
1328
                        }
1329
1330
                        $cart_items[] = $cart_items_arr;
1331
                    }
1332
                }
1333
            }
1334
1335
            /**
1336
             * Filter the wpinv_buy shortcode cart items on the fly.
1337
             *
1338
             * @param array $cart_items The cart items array.
1339
             * @param int $related_post_id The related post id if any.
1340
             * @since 1.0.0
1341
             */
1342
            $cart_items = apply_filters( 'wpinv_buy_cart_items', $cart_items, $related_post_id );
1343
1344
            // Make sure its not in the cart already, if it is then redirect to checkout.
1345
            $cart_invoice = wpinv_get_invoice_cart();
1346
1347
            if ( isset( $cart_invoice->items ) && !empty( $cart_invoice->items ) && !empty( $cart_items ) && serialize( $cart_invoice->items ) == serialize( $cart_items ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The property items does not exist on WPInv_Invoice. Since you implemented __get, consider adding a @property annotation.
Loading history...
1348
                wp_send_json( array(
1349
                    'success' =>  $cart_invoice->get_checkout_payment_url()
1350
                ) );
1351
                wp_die();
1352
            }
1353
1354
            // Check if user has invoice with same items waiting to be paid.
1355
            $user_invoices = wpinv_get_users_invoices( $user_id , 10 , false , 'wpi-pending' );
1356
            if ( !empty( $user_invoices ) ) {
1357
                foreach( $user_invoices as $user_invoice ) {
1358
                    $user_cart_details = array();
1359
                    $invoice  = wpinv_get_invoice( $user_invoice->ID );
1360
                    $cart_details = $invoice->get_cart_details();
1361
1362
                    if ( !empty( $cart_details ) ) {
1363
                        foreach ( $cart_details as $invoice_item ) {
1364
                            $ii_arr = array();
1365
                            $ii_arr['id'] = (int)$invoice_item['id'];
1366
                            $ii_arr['quantity'] = (int)$invoice_item['quantity'];
1367
1368
                            if (isset( $invoice_item['meta'] ) && !empty( $invoice_item['meta'] ) ) {
1369
                                $ii_arr['meta'] = $invoice_item['meta'];
1370
                            }
1371
1372
                            if ( isset( $invoice_item['custom_price'] ) && !empty( $invoice_item['custom_price'] ) ) {
1373
                                $ii_arr['custom_price'] = $invoice_item['custom_price'];
1374
                            }
1375
1376
                            $user_cart_details[] = $ii_arr;
1377
                        }
1378
                    }
1379
1380
                    if ( !empty( $user_cart_details ) && serialize( $cart_items ) == serialize( $user_cart_details ) ) {
1381
                        wp_send_json( array(
1382
                            'success' =>  $invoice->get_checkout_payment_url()
1383
                        ) );
1384
                        wp_die();
1385
                    }
1386
                }
1387
            }
1388
1389
            // Create invoice and send user to checkout
1390
            if ( !empty( $cart_items ) ) {
1391
                $invoice_data = array(
1392
                    'status'        =>  'wpi-pending',
1393
                    'created_via'   =>  'wpi',
1394
                    'user_id'       =>  $user_id,
1395
                    'cart_details'  =>  $cart_items,
1396
                );
1397
1398
                $invoice = wpinv_insert_invoice( $invoice_data, true );
1399
1400
                if ( !empty( $invoice ) && isset( $invoice->ID ) ) {
1401
                    wp_send_json( array(
1402
                        '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

1402
                        '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...
1403
                    ) );
1404
                } else {
1405
                    wp_send_json( array(
1406
                        'error' => __( 'Invoice failed to create', 'invoicing' )
1407
                    ) );
1408
                }
1409
            } else {
1410
                wp_send_json( array(
1411
                    'error' => __( 'Items not valid.', 'invoicing' )
1412
                ) );
1413
            }
1414
        }
1415
1416
        wp_die();
1417
    }
1418
}
1419
1420
WPInv_Ajax::init();