Passed
Push — master ( 99a69d...f79e9b )
by Brian
123:35 queued 12s
created

WPInv_EUVat::geoip2_download_database()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 43
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 22
nc 7
nop 0
dl 0
loc 43
rs 9.2568
c 1
b 0
f 0
1
<?php
2
// Exit if accessed directly.
3
if (!defined( 'ABSPATH' ) ) exit;
4
5
class WPInv_EUVat {
6
    private static $is_ajax = false;
7
    private static $default_country;
8
    private static $instance = false;
9
    
10
    public static function get_instance() {
11
        if ( !self::$instance ) {
12
            self::$instance = new self();
13
            self::$instance->actions();
14
        }
15
16
        return self::$instance;
17
    }
18
    
19
    public function __construct() {
20
        self::$is_ajax          = defined( 'DOING_AJAX' ) && DOING_AJAX;
21
        self::$default_country  = wpinv_get_default_country();
22
    }
23
    
24
    public static function actions() {
25
        if ( is_admin() ) {
26
            add_action( 'admin_enqueue_scripts', array( self::$instance, 'enqueue_admin_scripts' ) );
27
            add_action( 'wpinv_settings_sections_taxes', array( self::$instance, 'section_vat_settings' ) ); 
28
            add_action( 'wpinv_settings_taxes', array( self::$instance, 'vat_settings' ) );
29
            add_filter( 'wpinv_settings_taxes-vat_sanitize', array( self::$instance, 'sanitize_vat_settings' ) );
30
            add_filter( 'wpinv_settings_taxes-vat_rates_sanitize', array( self::$instance, 'sanitize_vat_rates' ) );
31
            add_action( 'wp_ajax_wpinv_add_vat_class', array( self::$instance, 'add_class' ) );
32
            add_action( 'wp_ajax_nopriv_wpinv_add_vat_class', array( self::$instance, 'add_class' ) );
33
            add_action( 'wp_ajax_wpinv_delete_vat_class', array( self::$instance, 'delete_class' ) );
34
            add_action( 'wp_ajax_nopriv_wpinv_delete_vat_class', array( self::$instance, 'delete_class' ) );
35
            add_action( 'wp_ajax_wpinv_update_vat_rates', array( self::$instance, 'update_eu_rates' ) );
36
            add_action( 'wp_ajax_nopriv_wpinv_update_vat_rates', array( self::$instance, 'update_eu_rates' ) );
37
            add_action( 'wp_ajax_wpinv_geoip2', array( self::$instance, 'geoip2_download_database' ) );
38
            add_action( 'wp_ajax_nopriv_wpinv_geoip2', array( self::$instance, 'geoip2_download_database' ) );
39
        }
40
        
41
        add_action( 'wp_enqueue_scripts', array( self::$instance, 'enqueue_vat_scripts' ) );
42
        add_filter( 'wpinv_default_billing_country', array( self::$instance, 'get_user_country' ), 10 );
43
        add_filter( 'wpinv_get_user_country', array( self::$instance, 'set_user_country' ), 10 );
44
        add_action( 'wp_ajax_wpinv_vat_validate', array( self::$instance, 'ajax_vat_validate' ) );
45
        add_action( 'wp_ajax_nopriv_wpinv_vat_validate', array( self::$instance, 'ajax_vat_validate' ) );
46
        add_action( 'wp_ajax_wpinv_vat_reset', array( self::$instance, 'ajax_vat_reset' ) );
47
        add_action( 'wp_ajax_nopriv_wpinv_vat_reset', array( self::$instance, 'ajax_vat_reset' ) );
48
        add_action( 'wpinv_invoice_print_after_line_items', array( self::$instance, 'show_vat_notice' ), 999, 1 );
49
        if ( wpinv_use_taxes() ) {
50
            add_action( 'wpinv_after_billing_fields', array( self::$instance, 'checkout_vat_fields' ) );
51
            if ( self::allow_vat_rules() ) {
52
                add_action( 'wpinv_checkout_error_checks', array( self::$instance, 'checkout_vat_validate' ), 10, 2 );
53
                add_filter( 'wpinv_tax_rate', array( self::$instance, 'get_rate' ), 10, 4 );
54
            }
55
        }
56
    }        
57
    
58
    public static function get_eu_states( $sort = true ) {
59
        $eu_states = array( 'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GB', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE' );
60
        if ( $sort ) {
61
            $sort = sort( $eu_states );
62
        }
63
        
64
        return apply_filters( 'wpinv_get_eu_states', $eu_states, $sort );
65
    }
66
    
67
    public static function get_gst_countries( $sort = true ) {
68
        $gst_countries  = array( 'AU', 'NZ', 'CA', 'CN' );
69
        
70
        if ( $sort ) {
71
            $sort = sort( $gst_countries );
72
        }
73
        
74
        return apply_filters( 'wpinv_get_gst_countries', $gst_countries, $sort );
75
    }
76
    
77
    public static function is_eu_state( $country_code ) {
78
        $return = !empty( $country_code ) && in_array( strtoupper( $country_code ), self::get_eu_states() ) ? true : false;
79
                
80
        return apply_filters( 'wpinv_is_eu_state', $return, $country_code );
81
    }
82
    
83
    public static function is_gst_country( $country_code ) {
84
        $return = !empty( $country_code ) && in_array( strtoupper( $country_code ), self::get_gst_countries() ) ? true : false;
85
                
86
        return apply_filters( 'wpinv_is_gst_country', $return, $country_code );
87
    }
88
    
89
    public static function enqueue_vat_scripts() {
90
        if( wpinv_use_taxes() && wpinv_get_option( 'apply_vat_rules' ) ) {
91
            self::load_vat_scripts();
92
        }
93
    }
94
95
    public static function load_vat_scripts(){
96
        $suffix     = '';//defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
97
98
        wp_register_script( 'wpinv-vat-validation-script', WPINV_PLUGIN_URL . 'assets/js/jsvat' . $suffix . '.js', array( 'jquery' ),  WPINV_VERSION );
99
        wp_register_script( 'wpinv-vat-script', WPINV_PLUGIN_URL . 'assets/js/euvat' . $suffix . '.js', array( 'jquery' ),  WPINV_VERSION );
100
101
        $vat_name   = self::get_vat_name();
102
103
        $vars = array();
104
        $vars['UseTaxes'] = wpinv_use_taxes();
105
        $vars['EUStates'] = self::get_eu_states();
106
        $vars['NoRateSet'] = __( 'You have not set a rate. Do you want to continue?', 'invoicing' );
107
        $vars['EmptyCompany'] = __( 'Please enter your registered company name!', 'invoicing' );
108
        $vars['EmptyVAT'] = wp_sprintf( __( 'Please enter your %s number!', 'invoicing' ), $vat_name );
109
        $vars['TotalsRefreshed'] = wp_sprintf( __( 'The invoice totals will be refreshed to update the %s.', 'invoicing' ), $vat_name );
110
        $vars['ErrValidateVAT'] = wp_sprintf( __( 'Fail to validate the %s number!', 'invoicing' ), $vat_name );
111
        $vars['ErrResetVAT'] = wp_sprintf( __( 'Fail to reset the %s number!', 'invoicing' ), $vat_name );
112
        $vars['ErrInvalidVat'] = wp_sprintf( __( 'The %s number supplied does not have a valid format!', 'invoicing' ), $vat_name );
113
        $vars['ErrInvalidResponse'] = __( 'An invalid response has been received from the server!', 'invoicing' );
114
        $vars['ApplyVATRules'] = $vars['UseTaxes'] ? self::allow_vat_rules() : false;
115
        $vars['HideVatFields'] = $vars['ApplyVATRules'] ? self::hide_vat_fields() : true;
116
        $vars['ErrResponse'] = __( 'The request response is invalid!', 'invoicing' );
117
        $vars['ErrRateResponse'] = __( 'The get rate request response is invalid', 'invoicing' );
118
        $vars['PageRefresh'] = __( 'The page will be refreshed in 10 seconds to show the new options.', 'invoicing' );
119
        $vars['RequestResponseNotValidJSON'] = __( 'The get rate request response is not valid JSON', 'invoicing' );
120
        $vars['GetRateRequestFailed'] = __( 'The get rate request failed: ', 'invoicing' );
121
        $vars['NoRateInformationInResponse'] = __( 'The get rate request response does not contain any rate information', 'invoicing' );
122
        $vars['RatesUpdated'] = __( 'The rates have been updated. Press the save button to record these new rates.', 'invoicing' );
123
        $vars['IPAddressInformation'] = __( 'IP Address Information', 'invoicing' );
124
        $vars['VatValidating'] = wp_sprintf( __( 'Validating %s number...', 'invoicing' ), $vat_name );
125
        $vars['VatReseting'] = __( 'Reseting...', 'invoicing' );
126
        $vars['VatValidated'] = wp_sprintf( __( '%s number validated', 'invoicing' ), $vat_name );
127
        $vars['VatNotValidated'] = wp_sprintf( __( '%s number not validated', 'invoicing' ), $vat_name );
128
        $vars['ConfirmDeleteClass'] = __( 'Are you sure you wish to delete this rates class?', 'invoicing' );
129
        $vars['isFront'] = is_admin() ? false : true;
130
        $vars['baseCountry'] = wpinv_get_default_country();
131
        $vars['disableVATSameCountry'] = ( self::same_country_rule() == 'no' ? true : false );
132
        $vars['disableVATSimpleCheck'] = wpinv_get_option( 'vat_offline_check' ) ? true : false;
133
134
        wp_enqueue_script( 'wpinv-vat-validation-script' );
135
        wp_enqueue_script( 'wpinv-vat-script' );
136
        wp_localize_script( 'wpinv-vat-script', 'WPInv_VAT_Vars', $vars );
137
    }
138
139
    public static function enqueue_admin_scripts() {
140
        if( isset( $_GET['page'] ) && 'wpinv-settings' == $_GET['page'] ) {
141
            self::load_vat_scripts();
142
        }
143
    }
144
    
145
    public static function section_vat_settings( $sections ) {
146
        if ( !empty( $sections ) ) {
147
            $sections['vat'] = __( 'EU VAT Settings', 'invoicing' );
148
            
149
            if ( self::allow_vat_classes() ) {
150
                $sections['vat_rates'] = __( 'EU VAT Rates', 'invoicing' );
151
            }
152
        }
153
        return $sections;
154
    }
155
    
156
    public static function vat_rates_settings() {
157
        $vat_classes = self::get_rate_classes();
158
        $vat_rates = array();
159
        $vat_class = isset( $_REQUEST['wpi_sub'] ) && $_REQUEST['wpi_sub'] !== '' && isset( $vat_classes[$_REQUEST['wpi_sub']] )? sanitize_text_field( $_REQUEST['wpi_sub'] ) : '_new';
160
        $current_url = remove_query_arg( 'wpi_sub' );
161
        
162
        $vat_rates['vat_rates_header'] = array(
163
            'id' => 'vat_rates_header',
164
            'name' => '<h3>' . __( 'Manage VAT Rates', 'invoicing' ) . '</h3>',
165
            'desc' => '',
166
            'type' => 'header',
167
            'size' => 'regular'
168
        );
169
        $vat_rates['vat_rates_class'] = array(
170
            'id'          => 'vat_rates_class',
171
            'name'        => __( 'Edit VAT Rates', 'invoicing' ),
172
            'desc'        => __( 'The standard rate will apply where no explicit rate is provided.', 'invoicing' ),
173
            'type'        => 'select',
174
            'options'     => array_merge( $vat_classes, array( '_new' => __( 'Add New Rate Class', 'invoicing' ) ) ),
175
            'placeholder' => __( 'Select a VAT Rate', 'invoicing' ),
176
            'selected'    => $vat_class,
177
            'class'       => 'wpi_select2',
178
            'onchange'    => 'document.location.href="' . $current_url . '&wpi_sub=" + this.value;',
179
        );
180
        
181
        if ( $vat_class != '_standard' && $vat_class != '_new' ) {
182
            $vat_rates['vat_rate_delete'] = array(
183
                'id'   => 'vat_rate_delete',
184
                'type' => 'vat_rate_delete',
185
            );
186
        }
187
                    
188
        if ( $vat_class == '_new' ) {
189
            $vat_rates['vat_rates_settings'] = array(
190
                'id' => 'vat_rates_settings',
191
                'name' => '<h3>' . __( 'Add New Rate Class', 'invoicing' ) . '</h3>',
192
                'type' => 'header',
193
            );
194
            $vat_rates['vat_rate_name'] = array(
195
                'id'   => 'vat_rate_name',
196
                'name' => __( 'Name', 'invoicing' ),
197
                'desc' => __( 'A short name for the new VAT Rate class', 'invoicing' ),
198
                'type' => 'text',
199
                'size' => 'regular',
200
            );
201
            $vat_rates['vat_rate_desc'] = array(
202
                'id'   => 'vat_rate_desc',
203
                'name' => __( 'Description', 'invoicing' ),
204
                'desc' => __( 'Manage VAT Rate class', 'invoicing' ),
205
                'type' => 'text',
206
                'size' => 'regular',
207
            );
208
            $vat_rates['vat_rate_add'] = array(
209
                'id'   => 'vat_rate_add',
210
                'type' => 'vat_rate_add',
211
            );
212
        } else {
213
            $vat_rates['vat_rates'] = array(
214
                'id'   => 'vat_rates',
215
                'name' => '<h3>' . $vat_classes[$vat_class] . '</h3>',
216
                'desc' => self::get_class_desc( $vat_class ),
217
                'type' => 'vat_rates',
218
            );
219
        }
220
        
221
        return $vat_rates;
222
    }
223
    
224
    public static function vat_settings( $settings ) {
225
        if ( !empty( $settings ) ) {    
226
            $vat_settings = array();
227
            $vat_settings['vat_company_title'] = array(
228
                'id' => 'vat_company_title',
229
                'name' => '<h3>' . __( 'Your Company Details', 'invoicing' ) . '</h3>',
230
                'desc' => '',
231
                'type' => 'header',
232
                'size' => 'regular'
233
            );
234
            
235
            $vat_settings['vat_company_name'] = array(
236
                'id' => 'vat_company_name',
237
                'name' => __( 'Your Company Name', 'invoicing' ),
238
                'desc' => wp_sprintf(__( 'Your company name as it appears on your VAT return, you can verify it via your VAT ID on the %sEU VIES System.%s', 'invoicing' ), '<a href="http://ec.europa.eu/taxation_customs/vies/" target="_blank">', '</a>' ),
239
                'type' => 'text',
240
                'size' => 'regular',
241
            );
242
            
243
            $vat_settings['vat_number'] = array(
244
                'id'   => 'vat_number',
245
                'name' => __( 'Your VAT Number', 'invoicing' ),
246
                'type' => 'vat_number',
247
                'size' => 'regular',
248
            );
249
250
            $vat_settings['vat_settings_title'] = array(
251
                'id' => 'vat_settings_title',
252
                'name' => '<h3>' . __( 'Apply VAT Settings', 'invoicing' ) . '</h3>',
253
                'desc' => '',
254
                'type' => 'header',
255
                'size' => 'regular'
256
            );
257
258
            $vat_settings['apply_vat_rules'] = array(
259
                'id' => 'apply_vat_rules',
260
                'name' => __( 'Enable VAT Rules', 'invoicing' ),
261
                'desc' => __( 'Apply VAT to consumer sales from IP addresses within the EU, even if the billing address is outside the EU.', 'invoicing' ) . '<br><font style="color:red">' . __( 'Do not disable unless you know what you are doing.', 'invoicing' ) . '</font>',
262
                'type' => 'checkbox',
263
                'std' => '1'
264
            );
265
266
            /*
267
            $vat_settings['vat_allow_classes'] = array(
268
                'id' => 'vat_allow_classes',
269
                'name' => __( 'Allow the use of VAT rate classes', 'invoicing' ),
270
                'desc' =>  __( 'When enabled this option makes it possible to define alternative rate classes so rates for items that do not use the standard VAT rate in all member states can be defined.<br>A menu option will appear under the "Invoicing -> Settings -> Taxes -> EU VAT Rates" menu heading that will take you to a page on which new classes can be defined and rates entered. A meta-box will appear in the invoice page in which you are able to select one of the alternative classes you create so the rates associated with the class will be applied to invoice.<br>By default the standard rates class will be used just as they are when this option is not enabled.', 'invoicing' ),
271
                'type' => 'checkbox'
272
            );
273
            */
274
275
            $vat_settings['vat_prevent_b2c_purchase'] = array(
276
                'id' => 'vat_prevent_b2c_purchase',
277
                'name' => __( 'Prevent EU B2C Sales', 'invoicing' ),
278
                'desc' => __( 'Enable this option if you are not registered for VAT in the EU.', 'invoicing' ),
279
                'type' => 'checkbox'
280
            );
281
282
283
284
            $vat_settings['vat_same_country_rule'] = array(
285
                'id'          => 'vat_same_country_rule',
286
                'name'        => __( 'Same Country Rule', 'invoicing' ),
287
                'desc'        => __( 'Select how you want to handle VAT charge if sales are in the same country as the base country.', 'invoicing' ),
288
                'type'        => 'select',
289
                'options'     => array(
290
                    ''          => __( 'Normal', 'invoicing' ),
291
                    'no'        => __( 'No VAT', 'invoicing' ),
292
                    'always'    => __( 'Always apply VAT', 'invoicing' ),
293
                ),
294
                'placeholder' => __( 'Select an option', 'invoicing' ),
295
                'std'         => '',
296
                'class'   => 'wpi_select2',
297
            );
298
299
            $vat_settings['vat_checkout_title'] = array(
300
                'id' => 'vat_checkout_title',
301
                'name' => '<h3>' . __( 'Checkout Fields', 'invoicing' ) . '</h3>',
302
                'desc' => '',
303
                'type' => 'header',
304
                'size' => 'regular'
305
            );
306
307
            $vat_settings['vat_disable_fields'] = array(
308
                'id' => 'vat_disable_fields',
309
                'name' => __( 'Disable VAT Fields', 'invoicing' ),
310
                'desc' => __( 'Disable VAT fields if Invoicing is being used for GST.', 'invoicing' ) . '<br><font style="color:red">' . __( 'Do not disable if you have enabled Prevent EU B2C sales, otherwise Prevent EU B2C sales setting will not work.', 'invoicing' ) . '</font>',
311
                'type' => 'checkbox'
312
            );
313
314
            $vat_settings['maxmind_license_key'] = array(
315
                'id'   => 'maxmind_license_key',
316
                'name' => __( 'MaxMind License Key', 'invoicing' ),
317
                'type' => 'text',
318
                'size' => 'regular',
319
                'desc' => '<a href="https://support.maxmind.com/account-faq/license-keys/how-do-i-generate-a-license-key/">' . __( 'The key that will be used when dealing with MaxMind Geolocation services.', 'invoicing' ) . '</a>',
320
            );
321
322
            $vat_settings['vat_ip_lookup'] = array(
323
                'id'   => 'vat_ip_lookup',
324
                'name' => __( 'IP Country Look-up', 'invoicing' ),
325
                'type' => 'vat_ip_lookup',
326
                'size' => 'regular',
327
                'std' => 'default',
328
                'class'   => 'wpi_select2',
329
            );
330
331
            $vat_settings['hide_ip_address'] = array(
332
                'id' => 'hide_ip_address',
333
                'name' => __( 'Hide IP Info at Checkout', 'invoicing' ),
334
                'desc' => __( 'Hide the user IP info at checkout.', 'invoicing' ),
335
                'type' => 'checkbox'
336
            );
337
338
            $vat_settings['vat_ip_country_default'] = array(
339
                'id' => 'vat_ip_country_default',
340
                'name' => __( 'Enable IP Country as Default', 'invoicing' ),
341
                'desc' => __( 'Show the country of the users IP as the default country, otherwise the site default country will be used.', 'invoicing' ),
342
                'type' => 'checkbox'
343
            );
344
345
            $vat_settings['vies_validation_title'] = array(
346
                'id' => 'vies_validation_title',
347
                'name' => '<h3>' . __( 'VIES Validation', 'invoicing' ) . '</h3>',
348
                'desc' => '',
349
                'type' => 'header',
350
                'size' => 'regular'
351
            );
352
353
            $vat_settings['vat_vies_check'] = array(
354
                'id' => 'vat_vies_check',
355
                'name' => __( 'Disable VIES VAT ID Check', 'invoicing' ),
356
                'desc' => wp_sprintf( __( 'Prevent VAT numbers from being validated by the %sEU VIES System.%s', 'invoicing' ), '<a href="http://ec.europa.eu/taxation_customs/vies/" target="_blank">', '</a>' ),
357
                'type' => 'checkbox'
358
            );
359
360
            $vat_settings['vat_disable_company_name_check'] = array(
361
                'id' => 'vat_disable_company_name_check',
362
                'name' => __( 'Disable VIES Name Check', 'invoicing' ),
363
                'desc' => wp_sprintf( __( 'Prevent company name from being validated by the %sEU VIES System.%s', 'invoicing' ), '<a href="http://ec.europa.eu/taxation_customs/vies/" target="_blank">', '</a>' ),
364
                'type' => 'checkbox'
365
            );
366
367
            $vat_settings['vat_offline_check'] = array(
368
                'id' => 'vat_offline_check',
369
                'name' => __( 'Disable Basic Checks', 'invoicing' ),
370
                'desc' => __( 'Disable basic JS checks for correct format of VAT number. (Not Recommended)', 'invoicing' ),
371
                'type' => 'checkbox'
372
            );
373
            
374
375
            $settings['vat'] = $vat_settings;
376
            
377
            if ( self::allow_vat_classes() ) {
378
                $settings['vat_rates'] = self::vat_rates_settings();
379
            }
380
            
381
            $eu_fallback_rate = array(
382
                'id'   => 'eu_fallback_rate',
383
                'name' => '<h3>' . __( 'VAT rate for EU member states', 'invoicing' ) . '</h3>',
384
                'type' => 'eu_fallback_rate',
385
                'desc' => __( 'Enter the VAT rate to be charged for EU member states. You can edit the rates for each member state when a country rate has been set up by pressing this button.', 'invoicing' ),
386
                'std'  => '20',
387
                'size' => 'small'
388
            );
389
            $settings['rates']['eu_fallback_rate'] = $eu_fallback_rate;
390
        }
391
392
        return $settings;
393
    }
394
395
    /**
396
     * The folder for our maxmind databases.
397
     */
398
    public static function maxmind_folder() {
399
400
        $upload_dir      = wp_upload_dir();
401
        return $upload_dir['basedir'] . '/invoicing';
402
403
    }
404
405
    /**
406
	 * Fetches the database from the MaxMind service.
407
	 *
408
	 * @param string $license_key The license key to be used when downloading the database.
409
	 */
410
    public static function geoip2_download_database() {
411
412
        // Allow us to easily interact with the filesystem.
413
        require_once ABSPATH . 'wp-admin/includes/file.php';
414
		WP_Filesystem();
415
        global $wp_filesystem;
416
417
        $license_key = wpinv_get_option( 'maxmind_license_key' );
418
419
        if ( empty( $license_key ) ) {
420
            echo __( 'Please enter your MaxMind license key then save the settings first before downloading the databases.', 'invoicing' );
421
            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...
422
        }
423
424
        // The database files that we will download.
425
        $database_files     = array( 'GeoLite2-Country', 'GeoLite2-City' );
426
427
        // The destination dir of all databases.
428
        $destination_dir = self::maxmind_folder();
429
430
        if ( ! $wp_filesystem->is_dir( $destination_dir ) ) { 
431
            $wp_filesystem->mkdir( $destination_dir );
432
        }
433
434
        foreach( $database_files as $database ) {
435
436
            $database_path = self::geoip2_download_file( $license_key, $database );
437
            $target_path   = trailingslashit( $destination_dir ) .  $database . '.mmdb';
438
439
            if ( is_wp_error( $database_path ) ) {
440
                echo $database_path->get_error_message();
441
                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...
442
            }
443
444
            // Move the new database into position.
445
		    $wp_filesystem->move( $database_path, $target_path, true );
446
            $wp_filesystem->delete( dirname( $database_path ) );
0 ignored issues
show
Bug introduced by
$database_path of type WP_Error is incompatible with the type string expected by parameter $path of dirname(). ( Ignorable by Annotation )

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

446
            $wp_filesystem->delete( dirname( /** @scrutinizer ignore-type */ $database_path ) );
Loading history...
447
448
            wpinv_update_option( 'wpinv_geoip2_date_updated', current_time( 'timestamp' ) );
449
            echo sprintf( __( 'GeoIP2 %s database updated successfully.', 'invoicing' ), $database ) . ' ';
450
        }
451
452
        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...
453
    }
454
455
    /**
456
     * Actually downloads and unzips a database.
457
     * 
458
     * @return string|WP_Error
459
     */
460
    public static function geoip2_download_file( $license_key, $database ) {
461
462
        // The download URI of the database.
463
        $source_url = add_query_arg(
464
			array(
465
                'license_key' => urlencode( sanitize_text_field( $license_key ) ),
466
                'edition_id'  => $database,
467
				'suffix'      => 'tar.gz',
468
			),
469
			'https://download.maxmind.com/app/geoip_download'
470
        );
471
472
        // Needed for the download_url call right below.
473
		require_once ABSPATH . 'wp-admin/includes/file.php';
474
475
        // Download the file.
476
        $tmp_archive_path = download_url( esc_url_raw( $source_url ) );
477
478
        // Did we encounter an error?
479
        if ( is_wp_error( $tmp_archive_path ) ) {
480
481
            // Transform the error into something more informative.
482
			$error_data = $tmp_archive_path->get_error_data();
483
			if ( isset( $error_data['code'] ) ) {
484
				switch ( $error_data['code'] ) {
485
					case 401:
486
						return new WP_Error(
487
							'invoicing_maxmind_geolocation_database_license_key',
488
							__( 'The MaxMind license key is invalid. If you have recently created this key, you may need to wait for it to become active.', 'invoicing' )
489
						);
490
				}
491
			}
492
493
            return new WP_Error( 'invoicing_maxmind_geolocation_database_download', __( 'Failed to download the MaxMind database.', 'invoicing' ) );
494
495
        }
496
497
        // Extract the database from the archive.
498
        try {
499
			$file      = new PharData( $tmp_archive_path );
0 ignored issues
show
Bug introduced by
$tmp_archive_path of type WP_Error is incompatible with the type string expected by parameter $fname of PharData::__construct(). ( Ignorable by Annotation )

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

499
			$file      = new PharData( /** @scrutinizer ignore-type */ $tmp_archive_path );
Loading history...
500
            $file_path = trailingslashit( dirname( $tmp_archive_path ) ) . trailingslashit( $file->current()->getFilename() ) . $database . '.mmdb';
0 ignored issues
show
Bug introduced by
$tmp_archive_path of type WP_Error is incompatible with the type string expected by parameter $path of dirname(). ( Ignorable by Annotation )

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

500
            $file_path = trailingslashit( dirname( /** @scrutinizer ignore-type */ $tmp_archive_path ) ) . trailingslashit( $file->current()->getFilename() ) . $database . '.mmdb';
Loading history...
501
502
			$file->extractTo(
503
				dirname( $tmp_archive_path ),
504
				trailingslashit( $file->current()->getFilename() ) . $database . '.mmdb',
505
				true
506
            );
507
508
		} catch ( Exception $exception ) {
509
			return new WP_Error( 'invoicing_maxmind_geolocation_database_archive', $exception->getMessage() );
510
		} finally {
511
			// Remove the archive since we only care about a single file in it.
512
            unlink( $tmp_archive_path );
0 ignored issues
show
Bug introduced by
$tmp_archive_path of type WP_Error is incompatible with the type string expected by parameter $filename of unlink(). ( Ignorable by Annotation )

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

512
            unlink( /** @scrutinizer ignore-type */ $tmp_archive_path );
Loading history...
513
        }
514
515
        return $file_path;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $file_path does not seem to be defined for all execution paths leading up to this point.
Loading history...
516
    }
517
518
    public static function load_geoip2() {
519
        if ( defined( 'WPINV_GEOIP2_LODDED' ) ) {
520
            return;
521
        }
522
        
523
        if ( !class_exists( '\MaxMind\Db\Reader' ) ) {
524
            $maxmind_db_files = array(
525
                'Reader/Decoder.php',
526
                'Reader/InvalidDatabaseException.php',
527
                'Reader/Metadata.php',
528
                'Reader/Util.php',
529
                'Reader.php',
530
            );
531
            
532
            foreach ( $maxmind_db_files as $key => $file ) {
533
                require_once( WPINV_PLUGIN_DIR . 'includes/libraries/MaxMind/Db/' . $file );
534
            }
535
        }
536
        
537
        if ( !class_exists( '\GeoIp2\Database\Reader' ) ) {        
538
            $geoip2_files = array(
539
                'ProviderInterface.php',
540
                'Compat/JsonSerializable.php',
541
                'Database/Reader.php',
542
                'Exception/GeoIp2Exception.php',
543
                'Exception/AddressNotFoundException.php',
544
                'Exception/AuthenticationException.php',
545
                'Exception/HttpException.php',
546
                'Exception/InvalidRequestException.php',
547
                'Exception/OutOfQueriesException.php',
548
                'Model/AbstractModel.php',
549
                'Model/AnonymousIp.php',
550
                'Model/Country.php',
551
                'Model/City.php',
552
                'Model/ConnectionType.php',
553
                'Model/Domain.php',
554
                'Model/Enterprise.php',
555
                'Model/Insights.php',
556
                'Model/Isp.php',
557
                'Record/AbstractRecord.php',
558
                'Record/AbstractPlaceRecord.php',
559
                'Record/Country.php',
560
                'Record/City.php',
561
                'Record/Continent.php',
562
                'Record/Location.php',
563
                'Record/MaxMind.php',
564
                'Record/Postal.php',
565
                'Record/RepresentedCountry.php',
566
                'Record/Subdivision.php',
567
                'Record/Traits.php',
568
                'WebService/Client.php',
569
            );
570
            
571
            foreach ( $geoip2_files as $key => $file ) {
572
                require_once( WPINV_PLUGIN_DIR . 'includes/libraries/GeoIp2/' . $file );
573
            }
574
        }
575
576
        define( 'WPINV_GEOIP2_LODDED', true );
577
    }
578
579
    public static function geoip2_country_dbfile() {
580
        $upload_dir = wp_upload_dir();
581
582
        if ( !isset( $upload_dir['basedir'] ) ) {
583
            return false;
584
        }
585
586
        $filename = $upload_dir['basedir'] . '/invoicing/GeoLite2-Country.mmdb';
587
        if ( !file_exists( $filename ) ) {
588
            return false;
589
        }
590
        
591
        return $filename;
592
    }
593
594
    public static function geoip2_city_dbfile() {
595
        $upload_dir = wp_upload_dir();
596
597
        if ( !isset( $upload_dir['basedir'] ) ) {
598
            return false;
599
        }
600
601
        $filename = $upload_dir['basedir'] . '/invoicing/GeoLite2-City.mmdb';
602
        if ( !file_exists( $filename ) ) {
603
            return false;
604
        }
605
        
606
        return $filename;
607
    }
608
609
    public static function geoip2_country_reader() {
610
        try {
611
            self::load_geoip2();
612
613
            if ( $filename = self::geoip2_country_dbfile() ) {
614
                return new \GeoIp2\Database\Reader( $filename );
615
            }
616
        } catch( Exception $e ) {
617
            return false;
618
        }
619
        
620
        return false;
621
    }
622
623
    public static function geoip2_city_reader() {
624
        try {
625
            self::load_geoip2();
626
627
            if ( $filename = self::geoip2_city_dbfile() ) {
628
                return new \GeoIp2\Database\Reader( $filename );
629
            }
630
        } catch( Exception $e ) {
631
            return false;
632
        }
633
        
634
        return false;
635
    }
636
637
    public static function geoip2_country_record( $ip_address ) {
638
        try {
639
            $reader = self::geoip2_country_reader();
640
641
            if ( $reader ) {
642
                $record = $reader->country( $ip_address );
643
                
644
                if ( !empty( $record->country->isoCode ) ) {
645
                    return $record;
646
                }
647
            }
648
        } catch(\InvalidArgumentException $e) {
649
            wpinv_error_log( $e->getMessage(), 'GeoIp2 Lookup( ' . $ip_address . ' )' );
650
            
651
            return false;
652
        } catch(\GeoIp2\Exception\AddressNotFoundException $e) {
653
            wpinv_error_log( $e->getMessage(), 'GeoIp2 Lookup( ' . $ip_address . ' )' );
654
            
655
            return false;
656
        } catch( Exception $e ) {
657
            return false;
658
        }
659
        
660
        return false;
661
    }
662
663
    public static function geoip2_city_record( $ip_address ) {
664
        try {
665
            $reader = self::geoip2_city_reader();
666
667
            if ( $reader ) {
668
                $record = $reader->city( $ip_address );
669
                
670
                if ( !empty( $record->country->isoCode ) ) {
671
                    return $record;
672
                }
673
            }
674
        } catch(\InvalidArgumentException $e) {
675
            wpinv_error_log( $e->getMessage(), 'GeoIp2 Lookup( ' . $ip_address . ' )' );
676
            
677
            return false;
678
        } catch(\GeoIp2\Exception\AddressNotFoundException $e) {
679
            wpinv_error_log( $e->getMessage(), 'GeoIp2 Lookup( ' . $ip_address . ' )' );
680
            
681
            return false;
682
        } catch( Exception $e ) {
683
            return false;
684
        }
685
        
686
        return false;
687
    }
688
689
    public static function geoip2_country_code( $ip_address ) {
690
        $record = self::geoip2_country_record( $ip_address );
691
        return !empty( $record->country->isoCode ) ? $record->country->isoCode : wpinv_get_default_country();
692
    }
693
694
    // Find country by IP address.
695
    public static function get_country_by_ip( $ip = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $ip is not used and could be removed. ( Ignorable by Annotation )

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

695
    public static function get_country_by_ip( /** @scrutinizer ignore-unused */ $ip = '' ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
696
        global $wpinv_ip_address_country;
697
        return '';
698
        if ( !empty( $wpinv_ip_address_country ) ) {
0 ignored issues
show
Unused Code introduced by
IfNode is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
699
            return $wpinv_ip_address_country;
700
        }
701
        
702
        if ( empty( $ip ) ) {
703
            $ip = wpinv_get_ip();
704
        }
705
706
        $ip_country_service = wpinv_get_option( 'vat_ip_lookup' );
707
        $is_default         = empty( $ip_country_service ) || $ip_country_service === 'default' ? true : false;
708
709
        if ( !empty( $ip ) && $ip !== '127.0.0.1' ) { // For 127.0.0.1(localhost) use default country.
710
            if ( function_exists( 'geoip_country_code_by_name') && ( $ip_country_service === 'geoip' || $is_default ) ) {
711
                try {
712
                    $wpinv_ip_address_country = geoip_country_code_by_name( $ip );
713
                } catch( Exception $e ) {
714
                    wpinv_error_log( $e->getMessage(), 'GeoIP Lookup( ' . $ip . ' )' );
715
                }
716
            } else if ( self::geoip2_country_dbfile() && ( $ip_country_service === 'geoip2' || $is_default ) ) {
717
                $wpinv_ip_address_country = self::geoip2_country_code( $ip );
718
            } else if ( function_exists( 'simplexml_load_file' ) && ini_get('allow_url_fopen') && ( $ip_country_service === 'geoplugin' || $is_default ) ) {
719
                $load_xml = simplexml_load_file( 'http://www.geoplugin.net/xml.gp?ip=' . $ip );
720
                
721
                if ( !empty( $load_xml ) && !empty( $load_xml->geoplugin_countryCode ) ) {
722
                    $wpinv_ip_address_country = (string)$load_xml->geoplugin_countryCode;
723
                }
724
            }elseif(!empty( $ip )){
725
                $url = 'http://ip-api.com/json/' . $ip;
726
                $response = wp_remote_get($url);
727
728
                if ( is_array( $response ) && wp_remote_retrieve_response_code( $response ) == '200' ) {
729
                    $data = json_decode(wp_remote_retrieve_body( $response ),true);
730
                    if(!empty($data['countryCode'])){
731
                        $wpinv_ip_address_country = (string)$data['countryCode'];
732
                    }
733
                }
734
            }
735
        }
736
737
        if ( empty( $wpinv_ip_address_country ) ) {
738
            $wpinv_ip_address_country = wpinv_get_default_country();
739
        }
740
741
        return $wpinv_ip_address_country;
742
    }
743
    
744
    public static function sanitize_vat_settings( $input ) {
745
        global $wpinv_options;
746
        
747
        $valid      = false;
748
        $message    = '';
749
        
750
        if ( !empty( $wpinv_options['vat_vies_check'] ) ) {
751
            if ( empty( $wpinv_options['vat_offline_check'] ) ) {
752
                $valid = self::offline_check( $input['vat_number'] );
753
            } else {
754
                $valid = true;
755
            }
756
            
757
            $message = $valid ? '' : __( 'VAT number not validated', 'invoicing' );
758
        } else {
759
            $result = self::check_vat( $input['vat_number'] );
760
            
761
            if ( empty( $result['valid'] ) ) {
762
                $valid      = false;
763
                $message    = $result['message'];
764
            } else {
765
                $valid      = ( isset( $result['company'] ) && ( $result['company'] == '---' || ( strcasecmp( trim( $result['company'] ), trim( $input['vat_company_name'] ) ) == 0 ) ) ) || !empty( $wpinv_options['vat_disable_company_name_check'] );
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: $valid = (IssetNode && $..._company_name_check'])), Probably Intended Meaning: $valid = IssetNode && ($..._company_name_check']))
Loading history...
766
                $message    = $valid ? '' : __( 'The company name associated with the VAT number provided is not the same as the company name provided.', 'invoicing' );
767
            }
768
        }
769
770
        if ( $message && self::is_vat_validated() != $valid ) {
771
            add_settings_error( 'wpinv-notices', '', $message, ( $valid ? 'updated' : 'error' ) );
772
        }
773
774
        $input['vat_valid'] = $valid;
775
        return $input;
776
    }
777
    
778
    public static function sanitize_vat_rates( $input ) {
779
        if( !wpinv_current_user_can_manage_invoicing() ) {
780
            add_settings_error( 'wpinv-notices', '', __( 'Your account does not have permission to add rate classes.', 'invoicing' ), 'error' );
781
            return $input;
782
        }
783
        
784
        $vat_classes = self::get_rate_classes();
785
        $vat_class = !empty( $_REQUEST['wpi_vat_class'] ) && isset( $vat_classes[$_REQUEST['wpi_vat_class']] )? sanitize_text_field( $_REQUEST['wpi_vat_class'] ) : '';
786
        
787
        if ( empty( $vat_class ) ) {
788
            add_settings_error( 'wpinv-notices', '', __( 'No valid VAT rates class contained in the request to save rates.', 'invoicing' ), 'error' );
789
            
790
            return $input;
791
        }
792
793
        $new_rates = ! empty( $_POST['vat_rates'] ) ? array_values( $_POST['vat_rates'] ) : array();
794
795
        if ( $vat_class === '_standard' ) {
796
            // Save the active rates in the invoice settings
797
            update_option( 'wpinv_tax_rates', $new_rates );
798
        } else {
799
            // Get the existing set of rates
800
            $rates = self::get_non_standard_rates();
801
            $rates[$vat_class] = $new_rates;
802
803
            update_option( 'wpinv_vat_rates', $rates );
804
        }
805
        
806
        return $input;
807
    }
808
    
809
    public static function add_class() {        
810
        $response = array();
811
        $response['success'] = false;
812
        
813
        if ( !wpinv_current_user_can_manage_invoicing() ) {
814
            $response['error'] = __( 'Invalid access!', 'invoicing' );
815
            wp_send_json( $response );
816
        }
817
        
818
        $vat_class_name = !empty( $_POST['name'] ) ? sanitize_text_field( $_POST['name'] ) : false;
819
        $vat_class_desc = !empty( $_POST['desc'] ) ? sanitize_text_field( $_POST['desc'] ) : false;
820
        
821
        if ( empty( $vat_class_name ) ) {
822
            $response['error'] = __( 'Select the VAT rate name', 'invoicing' );
823
            wp_send_json( $response );
824
        }
825
        
826
        $vat_classes = (array)self::get_rate_classes();
827
828
        if ( !empty( $vat_classes ) && in_array( strtolower( $vat_class_name ), array_map( 'strtolower', array_values( $vat_classes ) ) ) ) {
829
            $response['error'] = wp_sprintf( __( 'A VAT Rate name "%s" already exists', 'invoicing' ), $vat_class_name );
830
            wp_send_json( $response );
831
        }
832
        
833
        $rate_class_key = normalize_whitespace( 'wpi-' . $vat_class_name );
834
        $rate_class_key = sanitize_key( str_replace( " ", "-", $rate_class_key ) );
835
        
836
        $vat_classes = (array)self::get_rate_classes( true );
837
        $vat_classes[$rate_class_key] = array( 'name' => $vat_class_name, 'desc' => $vat_class_desc );
838
        
839
        update_option( '_wpinv_vat_rate_classes', $vat_classes );
840
        
841
        $response['success'] = true;
842
        $response['redirect'] = admin_url( 'admin.php?page=wpinv-settings&tab=taxes&section=vat_rates&wpi_sub=' . $rate_class_key );
843
        
844
        wp_send_json( $response );
845
    }
846
    
847
    public static function delete_class() {
848
        $response = array();
849
        $response['success'] = false;
850
        
851
        if ( !wpinv_current_user_can_manage_invoicing() || !isset( $_POST['class'] ) ) {
852
            $response['error'] = __( 'Invalid access!', 'invoicing' );
853
            wp_send_json( $response );
854
        }
855
        
856
        $vat_class = isset( $_POST['class'] ) && $_POST['class'] !== '' ? sanitize_text_field( $_POST['class'] ) : false;
857
        $vat_classes = (array)self::get_rate_classes();
858
859
        if ( !isset( $vat_classes[$vat_class] ) ) {
860
            $response['error'] = __( 'Requested class does not exists', 'invoicing' );
861
            wp_send_json( $response );
862
        }
863
        
864
        if ( $vat_class == '_new' || $vat_class == '_standard' ) {
865
            $response['error'] = __( 'You can not delete standard rates class', 'invoicing' );
866
            wp_send_json( $response );
867
        }
868
            
869
        $vat_classes = (array)self::get_rate_classes( true );
870
        unset( $vat_classes[$vat_class] );
871
        
872
        update_option( '_wpinv_vat_rate_classes', $vat_classes );
873
        
874
        $response['success'] = true;
875
        $response['redirect'] = admin_url( 'admin.php?page=wpinv-settings&tab=taxes&section=vat_rates&wpi_sub=_new' );
876
        
877
        wp_send_json( $response );
878
    }
879
    
880
    public static function update_eu_rates() {        
881
        $response               = array();
882
        $response['success']    = false;
883
        $response['error']      = null;
884
        $response['data']       = null;
885
        
886
        if ( !wpinv_current_user_can_manage_invoicing() ) {
887
            $response['error'] = __( 'Invalid access!', 'invoicing' );
888
            wp_send_json( $response );
889
        }
890
        
891
        $group      = !empty( $_POST['group'] ) ? sanitize_text_field( $_POST['group'] ) : '';
892
        $euvatrates = self::request_euvatrates( $group );
893
        
894
        if ( !empty( $euvatrates ) ) {
895
            if ( !empty( $euvatrates['success'] ) && !empty( $euvatrates['rates'] ) ) {
896
                $response['success']        = true;
897
                $response['data']['rates']  = $euvatrates['rates'];
898
            } else if ( !empty( $euvatrates['error'] ) ) {
899
                $response['error']          = $euvatrates['error'];
900
            }
901
        }
902
            
903
        wp_send_json( $response );
904
    }
905
    
906
    public static function hide_vat_fields() {
907
        $hide = wpinv_get_option( 'vat_disable_fields' );
908
        
909
        return apply_filters( 'wpinv_hide_vat_fields', $hide );
910
    }
911
    
912
    public static function same_country_rule() {
913
        $same_country_rule = wpinv_get_option( 'vat_same_country_rule' );
914
        
915
        return apply_filters( 'wpinv_vat_same_country_rule', $same_country_rule );
916
    }
917
    
918
    public static function get_vat_name() {
919
        $vat_name   = wpinv_get_option( 'vat_name' );
920
        $vat_name   = !empty( $vat_name ) ? $vat_name : 'VAT';
921
        
922
        return apply_filters( 'wpinv_get_owner_vat_name', $vat_name );
923
    }
924
    
925
    public static function get_company_name() {
926
        $company_name = wpinv_get_option( 'vat_company_name' );
927
        
928
        return apply_filters( 'wpinv_get_owner_company_name', $company_name );
929
    }
930
    
931
    public static function get_vat_number() {
932
        $vat_number = wpinv_get_option( 'vat_number' );
933
        
934
        return apply_filters( 'wpinv_get_owner_vat_number', $vat_number );
935
    }
936
    
937
    public static function is_vat_validated() {
938
        $validated = self::get_vat_number() && wpinv_get_option( 'vat_valid' );
939
        
940
        return apply_filters( 'wpinv_is_owner_vat_validated', $validated );
941
    }
942
    
943
    public static function sanitize_vat( $vat_number, $country_code = '' ) {        
944
        $vat_number = str_replace( array(' ', '.', '-', '_', ',' ), '', strtoupper( trim( $vat_number ) ) );
945
        
946
        if ( empty( $country_code ) ) {
947
            $country_code = substr( $vat_number, 0, 2 );
948
        }
949
        
950
        if ( strpos( $vat_number , $country_code ) === 0 ) {
951
            $vat = str_replace( $country_code, '', $vat_number );
952
        } else {
953
            $vat = $country_code . $vat_number;
954
        }
955
        
956
        $return                 = array();
957
        $return['vat']          = $vat;
958
        $return['iso']          = $country_code;
959
        $return['vat_number']   = $country_code . $vat;
960
        
961
        return $return;
962
    }
963
    
964
    public static function offline_check( $vat_number, $country_code = '', $formatted = false ) {
965
        $vat            = self::sanitize_vat( $vat_number, $country_code );
966
        $vat_number     = $vat['vat_number'];
967
        $country_code   = $vat['iso'];
968
        $regex          = array();
969
        
970
        switch ( $country_code ) {
971
            case 'AT':
972
                $regex[] = '/^(AT)U(\d{8})$/';                           // Austria
973
                break;
974
            case 'BE':
975
                $regex[] = '/^(BE)(0?\d{9})$/';                          // Belgium
976
                break;
977
            case 'BG':
978
                $regex[] = '/^(BG)(\d{9,10})$/';                         // Bulgaria
979
                break;
980
            case 'CH':
981
            case 'CHE':
982
                $regex[] = '/^(CHE)(\d{9})MWST$/';                       // Switzerland (Not EU)
983
                break;
984
            case 'CY':
985
                $regex[] = '/^(CY)([0-5|9]\d{7}[A-Z])$/';                // Cyprus
986
                break;
987
            case 'CZ':
988
                $regex[] = '/^(CZ)(\d{8,13})$/';                         // Czech Republic
989
                break;
990
            case 'DE':
991
                $regex[] = '/^(DE)([1-9]\d{8})$/';                       // Germany
992
                break;
993
            case 'DK':
994
                $regex[] = '/^(DK)(\d{8})$/';                            // Denmark
995
                break;
996
            case 'EE':
997
                $regex[] = '/^(EE)(10\d{7})$/';                          // Estonia
998
                break;
999
            case 'EL':
1000
                $regex[] = '/^(EL)(\d{9})$/';                            // Greece
1001
                break;
1002
            case 'ES':
1003
                $regex[] = '/^(ES)([A-Z]\d{8})$/';                       // Spain (National juridical entities)
1004
                $regex[] = '/^(ES)([A-H|N-S|W]\d{7}[A-J])$/';            // Spain (Other juridical entities)
1005
                $regex[] = '/^(ES)([0-9|Y|Z]\d{7}[A-Z])$/';              // Spain (Personal entities type 1)
1006
                $regex[] = '/^(ES)([K|L|M|X]\d{7}[A-Z])$/';              // Spain (Personal entities type 2)
1007
                break;
1008
            case 'EU':
1009
                $regex[] = '/^(EU)(\d{9})$/';                            // EU-type
1010
                break;
1011
            case 'FI':
1012
                $regex[] = '/^(FI)(\d{8})$/';                            // Finland
1013
                break;
1014
            case 'FR':
1015
                $regex[] = '/^(FR)(\d{11})$/';                           // France (1)
1016
                $regex[] = '/^(FR)[(A-H)|(J-N)|(P-Z)](\d{10})$/';        // France (2)
1017
                $regex[] = '/^(FR)\d[(A-H)|(J-N)|(P-Z)](\d{9})$/';       // France (3)
1018
                $regex[] = '/^(FR)[(A-H)|(J-N)|(P-Z)]{2}(\d{9})$/';      // France (4)
1019
                break;
1020
            case 'GB':
1021
                $regex[] = '/^(GB)?(\d{9})$/';                           // UK (Standard)
1022
                $regex[] = '/^(GB)?(\d{12})$/';                          // UK (Branches)
1023
                $regex[] = '/^(GB)?(GD\d{3})$/';                         // UK (Government)
1024
                $regex[] = '/^(GB)?(HA\d{3})$/';                         // UK (Health authority)
1025
                break;
1026
            case 'GR':
1027
                $regex[] = '/^(GR)(\d{8,9})$/';                          // Greece
1028
                break;
1029
            case 'HR':
1030
                $regex[] = '/^(HR)(\d{11})$/';                           // Croatia
1031
                break;
1032
            case 'HU':
1033
                $regex[] = '/^(HU)(\d{8})$/';                            // Hungary
1034
                break;
1035
            case 'IE':
1036
                $regex[] = '/^(IE)(\d{7}[A-W])$/';                       // Ireland (1)
1037
                $regex[] = '/^(IE)([7-9][A-Z\*\+)]\d{5}[A-W])$/';        // Ireland (2)
1038
                $regex[] = '/^(IE)(\d{7}[A-Z][AH])$/';                   // Ireland (3) (new format from 1 Jan 2013)
1039
                break;
1040
            case 'IT':
1041
                $regex[] = '/^(IT)(\d{11})$/';                           // Italy
1042
                break;
1043
            case 'LV':
1044
                $regex[] = '/^(LV)(\d{11})$/';                           // Latvia
1045
                break;
1046
            case 'LT':
1047
                $regex[] = '/^(LT)(\d{9}|\d{12})$/';                     // Lithuania
1048
                break;
1049
            case 'LU':
1050
                $regex[] = '/^(LU)(\d{8})$/';                            // Luxembourg
1051
                break;
1052
            case 'MT':
1053
                $regex[] = '/^(MT)([1-9]\d{7})$/';                       // Malta
1054
                break;
1055
            case 'NL':
1056
                $regex[] = '/^(NL)(\d{9})B\d{2}$/';                      // Netherlands
1057
                break;
1058
            case 'NO':
1059
                $regex[] = '/^(NO)(\d{9})$/';                            // Norway (Not EU)
1060
                break;
1061
            case 'PL':
1062
                $regex[] = '/^(PL)(\d{10})$/';                           // Poland
1063
                break;
1064
            case 'PT':
1065
                $regex[] = '/^(PT)(\d{9})$/';                            // Portugal
1066
                break;
1067
            case 'RO':
1068
                $regex[] = '/^(RO)([1-9]\d{1,9})$/';                     // Romania
1069
                break;
1070
            case 'RS':
1071
                $regex[] = '/^(RS)(\d{9})$/';                            // Serbia (Not EU)
1072
                break;
1073
            case 'SI':
1074
                $regex[] = '/^(SI)([1-9]\d{7})$/';                       // Slovenia
1075
                break;
1076
            case 'SK':
1077
                $regex[] = '/^(SK)([1-9]\d[(2-4)|(6-9)]\d{7})$/';        // Slovakia Republic
1078
                break;
1079
            case 'SE':
1080
                $regex[] = '/^(SE)(\d{10}01)$/';                         // Sweden
1081
                break;
1082
            default:
1083
                $regex = array();
1084
            break;
1085
        }
1086
        
1087
        if ( empty( $regex ) ) {
1088
            return false;
1089
        }
1090
        
1091
        foreach ( $regex as $pattern ) {
1092
            $matches = null;
1093
            preg_match_all( $pattern, $vat_number, $matches );
1094
            
1095
            if ( !empty( $matches[1][0] ) && !empty( $matches[2][0] ) ) {
1096
                if ( $formatted ) {
1097
                    return array( 'code' => $matches[1][0], 'number' => $matches[2][0] );
1098
                } else {
1099
                    return true;
1100
                }
1101
            }
1102
        }
1103
        
1104
        return false;
1105
    }
1106
    
1107
    public static function vies_check( $vat_number, $country_code = '', $result = false ) {
1108
        $vat            = self::sanitize_vat( $vat_number, $country_code );
1109
        $vat_number     = $vat['vat'];
1110
        $iso            = $vat['iso'];
1111
        
1112
        $url = 'http://ec.europa.eu/taxation_customs/vies/viesquer.do?ms=' . urlencode( $iso ) . '&iso=' . urlencode( $iso ) . '&vat=' . urlencode( $vat_number );
1113
        
1114
        if ( ini_get( 'allow_url_fopen' ) ) {
1115
            $response = file_get_contents( $url );
1116
        } else if ( function_exists( 'curl_init' ) ) {
1117
            $ch = curl_init();
1118
            
1119
            curl_setopt( $ch, CURLOPT_URL, $url );
0 ignored issues
show
Security Header Injection introduced by
$url can contain request data and is used in request header context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST, and ! empty($_POST['wpinv_vat_number']) ? $_POST['wpinv_vat_number'] : null is assigned to $vat_number
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1871
  2. WPInv_EUVat::check_vat() is called
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1918
  3. Enters via parameter $vat_number
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1187
  4. WPInv_EUVat::vies_check() is called
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1198
  5. Enters via parameter $vat_number
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1107
  6. Data is passed through sanitize_vat(), and self::sanitize_vat($vat_number, $country_code) is assigned to $vat
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1108
  7. $vat['iso'] is assigned to $iso
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1110
  8. Data is passed through urlencode(), and 'http://ec.europa.eu/taxation_customs/vies/viesquer.do?ms=' . urlencode($iso) . '&iso=' . urlencode($iso) . '&vat=' . urlencode($vat_number) is assigned to $url
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1112

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, 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

1119
            curl_setopt( /** @scrutinizer ignore-type */ $ch, CURLOPT_URL, $url );
Loading history...
1120
            curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 30 );
1121
            curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
1122
            curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
1123
            curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
1124
            
1125
            $response = curl_exec( $ch );
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, 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

1125
            $response = curl_exec( /** @scrutinizer ignore-type */ $ch );
Loading history...
1126
            
1127
            if ( curl_errno( $ch ) ) {
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_errno() does only seem to accept resource, 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

1127
            if ( curl_errno( /** @scrutinizer ignore-type */ $ch ) ) {
Loading history...
1128
                wpinv_error_log( curl_error( $ch ), 'VIES CHECK ERROR' );
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_error() does only seem to accept resource, 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

1128
                wpinv_error_log( curl_error( /** @scrutinizer ignore-type */ $ch ), 'VIES CHECK ERROR' );
Loading history...
1129
                $response = '';
1130
            }
1131
            
1132
            curl_close( $ch );
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_close() does only seem to accept resource, 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

1132
            curl_close( /** @scrutinizer ignore-type */ $ch );
Loading history...
1133
        } else {
1134
            wpinv_error_log( 'To use VIES CHECK you must have allow_url_fopen is ON or cURL installed & active on your server.', 'VIES CHECK ERROR' );
1135
        }
1136
        
1137
        if ( empty( $response ) ) {
1138
            return $result;
1139
        }
1140
1141
        if ( preg_match( '/invalid VAT number/i', $response ) ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $response does not seem to be defined for all execution paths leading up to this point.
Loading history...
1142
            return false;
1143
        } else if ( preg_match( '/valid VAT number/i', $response, $matches ) ) {
1144
            $content = explode( "valid VAT number", htmlentities( $response ) );
1145
            
1146
            if ( !empty( $content[1] ) ) {
1147
                preg_match_all( '/<tr>(.*?)<td.*?>(.*?)<\/td>(.*?)<\/tr>/si', html_entity_decode( $content[1] ), $matches );
1148
                
1149
                if ( !empty( $matches[2] ) && $matches[3] ) {
1150
                    $return = array();
1151
                    
1152
                    foreach ( $matches[2] as $key => $label ) {
1153
                        $label = trim( $label );
1154
                        
1155
                        switch ( strtolower( $label ) ) {
1156
                            case 'member state':
1157
                                $return['state'] = trim( strip_tags( $matches[3][$key] ) );
1158
                            break;
1159
                            case 'vat number':
1160
                                $return['number'] = trim( strip_tags( $matches[3][$key] ) );
1161
                            break;
1162
                            case 'name':
1163
                                $return['company'] = trim( strip_tags( $matches[3][$key] ) );
1164
                            break;
1165
                            case 'address':
1166
                                $address           = str_replace( array( "<br><br>", "<br /><br />", "<br/><br/>" ), "<br>", html_entity_decode( trim( $matches[3][$key] ) ) );
1167
                                $return['address'] = trim( strip_tags( $address, '<br>' ) );
1168
                            break;
1169
                            case 'consultation number':
1170
                                $return['consultation'] = trim( strip_tags( $matches[3][$key] ) );
1171
                            break;
1172
                        }
1173
                    }
1174
                    
1175
                    if ( !empty( $return ) ) {
1176
                        return $return;
1177
                    }
1178
                }
1179
            }
1180
            
1181
            return true;
1182
        } else {
1183
            return $result;
1184
        }
1185
    }
1186
    
1187
    public static function check_vat( $vat_number, $country_code = '' ) {        
1188
        $vat_name           = self::get_vat_name();
1189
        
1190
        $return             = array();
1191
        $return['valid']    = false;
1192
        $return['message']  = wp_sprintf( __( '%s number not validated', 'invoicing' ), $vat_name );
1193
                
1194
        if ( !wpinv_get_option( 'vat_offline_check' ) && !self::offline_check( $vat_number, $country_code ) ) {
1195
            return $return;
1196
        }
1197
            
1198
        $response = self::vies_check( $vat_number, $country_code );
1199
        
1200
        if ( $response ) {
1201
            $return['valid']    = true;
1202
            
1203
            if ( is_array( $response ) ) {
1204
                $return['company'] = isset( $response['company'] ) ? $response['company'] : '';
1205
                $return['address'] = isset( $response['address'] ) ? $response['address'] : '';
1206
                $return['message'] = $return['company'] . '<br/>' . $return['address'];
1207
            }
1208
        } else {
1209
            $return['valid']    = false;
1210
            $return['message']  = wp_sprintf( __( 'Fail to validate the %s number: EU Commission VAT server (VIES) check fails.', 'invoicing' ), $vat_name );
1211
        }
1212
        
1213
        return $return;
1214
    }
1215
1216
    public static function request_euvatrates( $group ) {
1217
        $response               = array();
1218
        $response['success']    = false;
1219
        $response['error']      = null;
1220
        $response['eurates']    = null;
1221
        
1222
        $euvatrates_url = 'https://euvatrates.com/rates.json';
1223
        $euvatrates_url = apply_filters( 'wpinv_euvatrates_url', $euvatrates_url );
1224
        $api_response   = wp_remote_get( $euvatrates_url );
1225
1226
        try {
1227
            if ( is_wp_error( $api_response ) ) {
1228
                $response['error']      = __( $api_response->get_error_message(), 'invoicing' );
1229
            } else {
1230
                $body = json_decode( $api_response['body'] );
1231
                if ( isset( $body->rates ) ) {
1232
                    $rates = array();
1233
                    
1234
                    foreach ( $body->rates as $country_code => $rate ) {
1235
                        $vat_rate = array();
1236
                        $vat_rate['country']        = $rate->country;
1237
                        $vat_rate['standard']       = (float)$rate->standard_rate;
1238
                        $vat_rate['reduced']        = (float)$rate->reduced_rate;
1239
                        $vat_rate['superreduced']   = (float)$rate->super_reduced_rate;
1240
                        $vat_rate['parking']        = (float)$rate->parking_rate;
1241
                        
1242
                        if ( $group !== '' && in_array( $group, array( 'standard', 'reduced', 'superreduced', 'parking' ) ) ) {
1243
                            $vat_rate_group = array();
1244
                            $vat_rate_group['country'] = $rate->country;
1245
                            $vat_rate_group[$group]    = $vat_rate[$group];
1246
                            
1247
                            $vat_rate = $vat_rate_group;
1248
                        }
1249
                        
1250
                        $rates[$country_code] = $vat_rate;
1251
                    }
1252
                    
1253
                    $response['success']    = true;                                
1254
                    $response['rates']      = apply_filters( 'wpinv_process_euvatrates', $rates, $api_response, $group );
1255
                } else {
1256
                    $response['error']      = __( 'No EU rates found!', 'invoicing' );
1257
                }
1258
            }
1259
        } catch ( Exception $e ) {
1260
            $response['error'] = __( $e->getMessage(), 'invoicing' );
1261
        }
1262
        
1263
        return apply_filters( 'wpinv_response_euvatrates', $response, $group );
1264
    }    
1265
    
1266
    public static function requires_vat( $requires_vat = false, $user_id = 0, $is_digital = null ) {
1267
        global $wpi_item_id, $wpi_country;
1268
        
1269
        if ( !empty( $_POST['wpinv_country'] ) ) {
1270
            $country_code = trim( $_POST['wpinv_country'] );
1271
        } else if ( !empty( $_POST['country'] ) ) {
1272
            $country_code = trim( $_POST['country'] );
1273
        } else if ( !empty( $wpi_country ) ) {
1274
            $country_code = $wpi_country;
1275
        } else {
1276
            $country_code = self::get_user_country( '', $user_id );
1277
        }
1278
        
1279
        if ( $is_digital === null && $wpi_item_id ) {
1280
            $is_digital = $wpi_item_id ? self::item_has_digital_rule( $wpi_item_id ) : self::allow_vat_rules();
1281
        }
1282
        
1283
        if ( !empty( $country_code ) ) {
1284
            $requires_vat = ( self::is_eu_state( $country_code ) && ( self::is_eu_state( self::$default_country ) || $is_digital ) ) || ( self::is_gst_country( $country_code ) && self::is_gst_country( self::$default_country ) );
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: $requires_vat = (self::i...self::default_country)), Probably Intended Meaning: $requires_vat = self::is...self::default_country))
Loading history...
1285
        }
1286
        
1287
        return apply_filters( 'wpinv_requires_vat', $requires_vat, $user_id );
1288
    }
1289
    
1290
    public static function tax_label( $label = '' ) {
1291
        global $wpi_requires_vat;
1292
        
1293
        if ( !( $wpi_requires_vat !== 0 && $wpi_requires_vat ) ) {
1294
            $wpi_requires_vat = self::requires_vat( 0, false );
1295
        }
1296
        
1297
        return $wpi_requires_vat ? __( self::get_vat_name(), 'invoicing' ) : ( $label ? $label : __( 'Tax', 'invoicing' ) );
1298
    }
1299
    
1300
    public static function standard_rates_label() {
1301
        return __( 'Standard Rates', 'invoicing' );
1302
    }
1303
    
1304
    public static function get_rate_classes( $with_desc = false ) {        
1305
        $rate_classes_option = get_option( '_wpinv_vat_rate_classes', true );
1306
        $classes = maybe_unserialize( $rate_classes_option );
1307
1308
        if ( empty( $classes ) || !is_array( $classes ) ) {
1309
            $classes = array();
1310
        }
1311
1312
        $rate_classes = array();
1313
        if ( !array_key_exists( '_standard', $classes ) ) {
1314
            if ( $with_desc ) {
1315
                $rate_classes['_standard'] = array( 'name' => self::standard_rates_label(), 'desc' => __( 'EU member states standard VAT rates', 'invoicing' ) );
1316
            } else {
1317
                $rate_classes['_standard'] = self::standard_rates_label();
1318
            }
1319
        }
1320
        
1321
        foreach ( $classes as $key => $class ) {
1322
            $name = !empty( $class['name'] ) ? __( $class['name'], 'invoicing' ) : $key;
1323
            $desc = !empty( $class['desc'] ) ? __( $class['desc'], 'invoicing' ) : '';
1324
            
1325
            if ( $with_desc ) {
1326
                $rate_classes[$key] = array( 'name' => $name, 'desc' => $desc );
1327
            } else {
1328
                $rate_classes[$key] = $name;
1329
            }
1330
        }
1331
1332
        return $rate_classes;
1333
    }
1334
    
1335
    public static function get_all_classes() {        
1336
        $classes            = self::get_rate_classes();
1337
        $classes['_exempt'] = __( 'Exempt (0%)', 'invoicing' );
1338
        
1339
        return apply_filters( 'wpinv_vat_get_all_classes', $classes );
1340
    }
1341
    
1342
    public static function get_class_desc( $rate_class ) {        
1343
        $rate_classes = self::get_rate_classes( true );
1344
1345
        if ( !empty( $rate_classes ) && isset( $rate_classes[$rate_class] ) && isset( $rate_classes[$rate_class]['desc'] ) ) {
1346
            return $rate_classes[$rate_class]['desc'];
1347
        }
1348
        
1349
        return '';
1350
    }
1351
    
1352
    public static function get_vat_groups() {
1353
        $vat_groups = array(
1354
            'standard'      => 'Standard',
1355
            'reduced'       => 'Reduced',
1356
            'superreduced'  => 'Super Reduced',
1357
            'parking'       => 'Parking',
1358
            'increased'     => 'Increased'
1359
        );
1360
        
1361
        return apply_filters( 'wpinv_get_vat_groups', $vat_groups );
1362
    }
1363
1364
    public static function get_rules() {
1365
        $vat_rules = array(
1366
            'digital' => __( 'Digital Product', 'invoicing' ),
1367
            'physical' => __( 'Physical Product', 'invoicing' ),    
1368
            '_exempt' => __( 'Tax-Free Product', 'invoicing' ),
1369
        );
1370
        return apply_filters( 'wpinv_get_vat_rules', $vat_rules );
1371
    }
1372
1373
    public static function get_vat_rates( $class ) {
1374
        if ( $class === '_standard' ) {
1375
            return wpinv_get_tax_rates();
1376
        }
1377
1378
        $rates = self::get_non_standard_rates();
1379
1380
        return array_key_exists( $class, $rates ) ? $rates[$class] : array();
1381
    }
1382
1383
    public static function get_non_standard_rates() {
1384
        $option = get_option( 'wpinv_vat_rates', array());
1385
        return is_array( $option ) ? $option : array();
1386
    }
1387
    
1388
    public static function allow_vat_rules() {
1389
        return ( wpinv_use_taxes() && wpinv_get_option( 'apply_vat_rules' ) ? true : false );
1390
    }
1391
    
1392
    public static function allow_vat_classes() {
1393
        return false; // TODO
1394
        return ( wpinv_get_option( 'vat_allow_classes' ) ? true : false );
0 ignored issues
show
Unused Code introduced by
return wpinv_get_option(...lasses') ? true : false is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1395
    }
1396
    
1397
    public static function get_item_class( $postID ) {
1398
        $class = get_post_meta( $postID, '_wpinv_vat_class', true );
1399
1400
        if ( empty( $class ) ) {
1401
            $class = '_standard';
1402
        }
1403
        
1404
        return apply_filters( 'wpinv_get_item_vat_class', $class, $postID );
1405
    }
1406
    
1407
    public static function item_class_label( $postID ) {        
1408
        $vat_classes = self::get_all_classes();
1409
        
1410
        $class = self::get_item_class( $postID );
1411
        $class = isset( $vat_classes[$class] ) ? $vat_classes[$class] : __( $class, 'invoicing' );
1412
        
1413
        return apply_filters( 'wpinv_item_class_label', $class, $postID );
1414
    }
1415
    
1416
    public static function get_item_rule( $postID ) {        
1417
        $rule_type = get_post_meta( $postID, '_wpinv_vat_rule', true );
1418
1419
        if ( empty( $rule_type ) ) {        
1420
            $rule_type = self::allow_vat_rules() ? 'digital' : 'physical';
1421
        }
1422
        
1423
        return apply_filters( 'wpinv_item_get_vat_rule', $rule_type, $postID );
1424
    }
1425
    
1426
    public static function item_rule_label( $postID ) {
1427
        $vat_rules  = self::get_rules();
1428
        $vat_rule   = self::get_item_rule( $postID );
1429
        $vat_rule   = isset( $vat_rules[$vat_rule] ) ? $vat_rules[$vat_rule] : $vat_rule;
1430
        
1431
        return apply_filters( 'wpinv_item_rule_label', $vat_rule, $postID );
1432
    }
1433
    
1434
    public static function item_has_digital_rule( $item_id = 0 ) {        
1435
        return self::get_item_rule( $item_id ) == 'digital' ? true : false;
1436
    }
1437
1438
    public static function invoice_has_digital_rule( $invoice = 0 ) {        
1439
        if ( !self::allow_vat_rules() ) {
1440
            return false;
1441
        }
1442
        
1443
        if ( empty( $invoice ) ) {
1444
            return true;
1445
        }
1446
        
1447
        if ( is_int( $invoice ) ) {
1448
            $invoice = new WPInv_Invoice( $invoice );
1449
        }
1450
        
1451
        if ( !( is_object( $invoice ) && is_a( $invoice, 'WPInv_Invoice' ) ) ) {
1452
            return true;
1453
        }
1454
        
1455
        $cart_items  = $invoice->get_cart_details();
1456
        
1457
        if ( !empty( $cart_items ) ) {
1458
            $has_digital_rule = false;
1459
            
1460
            foreach ( $cart_items as $key => $item ) {
1461
                if ( self::item_has_digital_rule( $item['id'] ) ) {
1462
                    $has_digital_rule = true;
1463
                    break;
1464
                }
1465
            }
1466
        } else {
1467
            $has_digital_rule = true;
1468
        }
1469
        
1470
        return $has_digital_rule;
1471
    }
1472
    
1473
    public static function item_is_taxable( $item_id = 0, $country = false, $state = false ) {        
1474
        if ( !wpinv_use_taxes() ) {
1475
            return false;
1476
        }
1477
1478
        $is_taxable = true;
1479
1480
        if ( !empty( $item_id ) && self::get_item_class( $item_id ) == '_exempt' ) {
1481
            $is_taxable = false;
1482
        }
1483
1484
        if ( !empty( $item_id ) && self::get_item_rule( $item_id ) == '_exempt' ) {
1485
            $is_taxable = false;
1486
        }
1487
1488
        return apply_filters( 'wpinv_item_is_taxable', $is_taxable, $item_id, $country , $state );
1489
    }
1490
    
1491
    public static function find_rate( $country, $state, $rate, $class ) {
1492
        global $wpi_zero_tax;
1493
1494
        if ( $class === '_exempt' || $wpi_zero_tax ) {
1495
            return 0;
1496
        }
1497
1498
        $tax_rates   = wpinv_get_tax_rates();
1499
        
1500
        if ( $class !== '_standard' ) {
1501
            $class_rates = self::get_vat_rates( $class );
1502
            
1503
            if ( is_array( $class_rates ) ) {
1504
                $indexed_class_rates = array();
1505
                
1506
                foreach ( $class_rates as $key => $cr ) {
1507
                    $indexed_class_rates[$cr['country']] = $cr;
1508
                }
1509
1510
                $tax_rates = array_map( function( $tr ) use( $indexed_class_rates ) {
1511
                    $tr_country = $tr['country'];
1512
                    if ( !isset( $indexed_class_rates[$tr_country] ) ) {
1513
                        return $tr;
1514
                    }
1515
                    $icr = $indexed_class_rates[$tr_country];
1516
                    return ( empty( $icr['rate'] ) && $icr['rate'] !== '0' ) ? $tr : $icr;
1517
1518
                }, $tax_rates, $class_rates );
0 ignored issues
show
Bug introduced by
It seems like $tax_rates can also be of type false; however, parameter $arr1 of array_map() 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

1518
                }, /** @scrutinizer ignore-type */ $tax_rates, $class_rates );
Loading history...
1519
            }
1520
        }
1521
1522
        if ( !empty( $tax_rates ) ) {
1523
            foreach ( $tax_rates as $key => $tax_rate ) {
1524
                if ( $country != $tax_rate['country'] )
1525
                    continue;
1526
1527
                if ( !empty( $tax_rate['global'] ) ) {
1528
                    if ( 0 !== $tax_rate['rate'] || !empty( $tax_rate['rate'] ) ) {
1529
                        $rate = number_format( $tax_rate['rate'], 4 );
1530
                    }
1531
                } else {
1532
                    if ( empty( $tax_rate['state'] ) || strtolower( $state ) != strtolower( $tax_rate['state'] ) )
1533
                        continue;
1534
1535
                    $state_rate = $tax_rate['rate'];
1536
                    if ( 0 !== $state_rate || !empty( $state_rate ) ) {
1537
                        $rate = number_format( $state_rate, 4 );
1538
                    }
1539
                }
1540
            }
1541
        }
1542
        
1543
        return $rate;
1544
    }
1545
    
1546
    public static function get_rate( $rate = 1, $country = '', $state = '', $item_id = 0 ) {
1547
        global $wpinv_options, $wpi_session, $wpi_item_id, $wpi_zero_tax;
1548
        
1549
        $item_id = $item_id > 0 ? $item_id : $wpi_item_id;
1550
        $allow_vat_classes = self::allow_vat_classes();
1551
        $class = $item_id ? self::get_item_class( $item_id ) : '_standard';
1552
1553
        if ( $class === '_exempt' || $wpi_zero_tax ) {
1554
            return 0;
1555
        } else if ( !$allow_vat_classes ) {
0 ignored issues
show
introduced by
The condition $allow_vat_classes is always false.
Loading history...
1556
            $class = '_standard';
1557
        }
1558
1559
        if( !empty( $_POST['wpinv_country'] ) ) {
1560
            $post_country = $_POST['wpinv_country'];
1561
        } elseif( !empty( $_POST['wpinv_country'] ) ) {
1562
            $post_country = $_POST['wpinv_country'];
1563
        } elseif( !empty( $_POST['country'] ) ) {
1564
            $post_country = $_POST['country'];
1565
        } else {
1566
            $post_country = '';
1567
        }
1568
1569
        $country        = !empty( $post_country ) ? $post_country : wpinv_default_billing_country( $country );
1570
        $base_country   = wpinv_is_base_country( $country );
1571
        
1572
        $requires_vat   = self::requires_vat( 0, false );
1573
        $is_digital     = self::get_item_rule( $item_id ) == 'digital' ;
1574
        $rate           = $requires_vat && isset( $wpinv_options['eu_fallback_rate'] ) ? $wpinv_options['eu_fallback_rate'] : $rate;
1575
          
1576
        if ( self::same_country_rule() == 'no' && $base_country ) { // Disable VAT to same country
1577
            $rate = 0;
1578
        } else if ( $requires_vat ) {
1579
            $vat_number = self::get_user_vat_number( '', 0, true );
1580
            $vat_info   = self::current_vat_data();
1581
            
1582
            if ( is_array( $vat_info ) ) {
1583
                $vat_number = isset( $vat_info['number'] ) && !empty( $vat_info['valid'] ) ? $vat_info['number'] : "";
1584
            }
1585
            
1586
            if ( $country == 'UK' ) {
1587
                $country = 'GB';
1588
            }
1589
1590
            if ( !empty( $vat_number ) ) {
1591
                $rate = 0;
1592
            } else {
1593
                $rate = self::find_rate( $country, $state, $rate, $class ); // Fix if there are no tax rated and you try to pay an invoice it does not add the fallback tax rate
1594
            }
1595
1596
            if ( empty( $vat_number ) && !$is_digital ) {
1597
                if ( $base_country ) {
1598
                    $rate = self::find_rate( $country, null, $rate, $class );
1599
                } else {
1600
                    if ( empty( $country ) && isset( $wpinv_options['eu_fallback_rate'] ) ) {
1601
                        $rate = $wpinv_options['eu_fallback_rate'];
1602
                    } else if( !empty( $country ) ) {
1603
                        $rate = self::find_rate( $country, $state, $rate, $class );
1604
                    }
1605
                }
1606
            } else if ( empty( $vat_number ) || ( self::same_country_rule() == 'always' && $base_country ) ) {
1607
                if ( empty( $country ) && isset( $wpinv_options['eu_fallback_rate'] ) ) {
1608
                    $rate = $wpinv_options['eu_fallback_rate'];
1609
                } else if( !empty( $country ) ) {
1610
                    $rate = self::find_rate( $country, $state, $rate, $class );
1611
                }
1612
            }
1613
        } else {
1614
            if ( $is_digital ) {
1615
                $ip_country_code = self::get_country_by_ip();
1616
                
1617
                if ( $ip_country_code && self::is_eu_state( $ip_country_code ) ) {
1618
                    $rate = self::find_rate( $ip_country_code, '', 0, $class );
1619
                } else {
1620
                    $rate = self::find_rate( $country, $state, $rate, $class );
1621
                }
1622
            } else {
1623
                $rate = self::find_rate( $country, $state, $rate, $class );
1624
            }
1625
        }
1626
1627
        return $rate;
1628
    }
1629
    
1630
    public static function current_vat_data() {
1631
        global $wpi_session;
1632
        
1633
        return $wpi_session->get( 'user_vat_data' );
1634
    }
1635
    
1636
    public static function get_user_country( $country = '', $user_id = 0 ) {
1637
        $user_address = wpinv_get_user_address( $user_id, false );
1638
        
1639
        if ( wpinv_get_option( 'vat_ip_country_default' ) ) {
1640
            $country = '';
1641
        }
1642
        
1643
        $country    = empty( $user_address ) || !isset( $user_address['country'] ) || empty( $user_address['country'] ) ? $country : $user_address['country'];
1644
        $result     = apply_filters( 'wpinv_get_user_country', $country, $user_id );
1645
1646
        if ( empty( $result ) ) {
1647
            $result = self::get_country_by_ip();
1648
        }
1649
1650
        return $result;
1651
    }
1652
    
1653
    public static function set_user_country( $country = '', $user_id = 0 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $user_id is not used and could be removed. ( Ignorable by Annotation )

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

1653
    public static function set_user_country( $country = '', /** @scrutinizer ignore-unused */ $user_id = 0 ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1654
        global $wpi_userID;
1655
        
1656
        if ( empty($country) && !empty($wpi_userID) && get_current_user_id() != $wpi_userID ) {
1657
            $country = wpinv_get_default_country();
1658
        }
1659
        
1660
        return $country;
1661
    }
1662
    
1663
    public static function get_user_vat_number( $vat_number = '', $user_id = 0, $is_valid = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $vat_number is not used and could be removed. ( Ignorable by Annotation )

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

1663
    public static function get_user_vat_number( /** @scrutinizer ignore-unused */ $vat_number = '', $user_id = 0, $is_valid = false ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1664
        global $wpi_current_id, $wpi_userID;
1665
        
1666
        if ( !empty( $_POST['new_user'] ) ) {
1667
            return '';
1668
        }
1669
        
1670
        if ( empty( $user_id ) ) {
1671
            $user_id = !empty( $wpi_userID ) ? $wpi_userID : ( $wpi_current_id ? wpinv_get_user_id( $wpi_current_id ) : get_current_user_id() );
1672
        }
1673
1674
        $vat_number = empty( $user_id ) ? '' : get_user_meta( $user_id, '_wpinv_vat_number', true );
1675
        
1676
        /* TODO
1677
        if ( $is_valid && $vat_number ) {
1678
            $adddress_confirmed = empty( $user_id ) ? false : get_user_meta( $user_id, '_wpinv_adddress_confirmed', true );
1679
            if ( !$adddress_confirmed ) {
1680
                $vat_number = '';
1681
            }
1682
        }
1683
        */
1684
1685
        return apply_filters('wpinv_get_user_vat_number', $vat_number, $user_id, $is_valid );
1686
    }
1687
    
1688
    public static function get_user_company( $company = '', $user_id = 0 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $company is not used and could be removed. ( Ignorable by Annotation )

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

1688
    public static function get_user_company( /** @scrutinizer ignore-unused */ $company = '', $user_id = 0 ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1689
        global $wpi_current_id, $wpi_userID;
1690
        
1691
        if ( empty( $user_id ) ) {
1692
            $user_id = !empty( $wpi_userID ) ? $wpi_userID : ( $wpi_current_id ? wpinv_get_user_id( $wpi_current_id ) : get_current_user_id() );
1693
        }
1694
1695
        $company = empty( $user_id ) ? "" : get_user_meta( $user_id, '_wpinv_company', true );
1696
1697
        return apply_filters( 'wpinv_user_company', $company, $user_id );
1698
    }
1699
    
1700
    public static function save_user_vat_details( $company = '', $vat_number = '' ) {
1701
        $save = apply_filters( 'wpinv_allow_save_user_vat_details', true );
1702
1703
        if ( is_user_logged_in() && $save ) {
1704
            $user_id = get_current_user_id();
1705
1706
            if ( !empty( $vat_number ) ) {
1707
                update_user_meta( $user_id, '_wpinv_vat_number', $vat_number );
1708
            } else {
1709
                delete_user_meta( $user_id, '_wpinv_vat_number');
1710
            }
1711
1712
            if ( !empty( $company ) ) {
1713
                update_user_meta( $user_id, '_wpinv_company', $company );
1714
            } else {
1715
                delete_user_meta( $user_id, '_wpinv_company');
1716
                delete_user_meta( $user_id, '_wpinv_vat_number');
1717
            }
1718
        }
1719
1720
        do_action('wpinv_save_user_vat_details', $company, $vat_number);
1721
    }
1722
    
1723
    public static function ajax_vat_validate() {
1724
        global $wpinv_options, $wpi_session;
1725
        
1726
        $is_checkout            = ( !empty( $_POST['source'] ) && $_POST['source'] == 'checkout' ) ? true : false;
1727
        $response               = array();
1728
        $response['success']    = false;
1729
        
1730
        if ( empty( $_REQUEST['_wpi_nonce'] ) || ( !empty( $_REQUEST['_wpi_nonce'] ) && !wp_verify_nonce( $_REQUEST['_wpi_nonce'], 'vat_validation' ) ) ) {
1731
            $response['error'] = __( 'Invalid security nonce', 'invoicing' );
1732
            wp_send_json( $response );
1733
        }
1734
        
1735
        $vat_name   = self::get_vat_name();
1736
        
1737
        if ( $is_checkout ) {
1738
            $invoice = wpinv_get_invoice_cart();
1739
            
1740
            if ( !self::requires_vat( false, 0, self::invoice_has_digital_rule( $invoice ) ) ) {
1741
                $vat_info = array();
1742
                $wpi_session->set( 'user_vat_data', $vat_info );
1743
1744
                self::save_user_vat_details();
1745
                
1746
                $response['success'] = true;
1747
                $response['message'] = wp_sprintf( __( 'Ignore %s', 'invoicing' ), $vat_name );
1748
                wp_send_json( $response );
1749
            }
1750
        }
1751
        
1752
        $company    = !empty( $_POST['company'] ) ? sanitize_text_field( $_POST['company'] ) : '';
1753
        $vat_number = !empty( $_POST['number'] ) ? sanitize_text_field( $_POST['number'] ) : '';
1754
        
1755
        $vat_info = $wpi_session->get( 'user_vat_data' );
1756
        if ( !is_array( $vat_info ) || empty( $vat_info ) ) {
1757
            $vat_info = array( 'company'=> $company, 'number' => '', 'valid' => true );
1758
        }
1759
        
1760
        if ( empty( $vat_number ) ) {
1761
            if ( $is_checkout ) {
1762
                $response['success'] = true;
1763
                $response['message'] = wp_sprintf( __( 'No %s number has been applied. %s will be added to invoice totals', 'invoicing' ), $vat_name, $vat_name );
1764
                
1765
                $vat_info = $wpi_session->get( 'user_vat_data' );
1766
                $vat_info['number'] = "";
1767
                $vat_info['valid'] = true;
1768
                
1769
                self::save_user_vat_details( $company );
1770
            } else {
1771
                $response['error'] = wp_sprintf( __( 'Please enter your %s number!', 'invoicing' ), $vat_name );
1772
                
1773
                $vat_info['valid'] = false;
1774
            }
1775
1776
            $wpi_session->set( 'user_vat_data', $vat_info );
1777
            wp_send_json( $response );
1778
        }
1779
        
1780
        if ( empty( $company ) ) {
1781
            $vat_info['valid'] = false;
1782
            $wpi_session->set( 'user_vat_data', $vat_info );
1783
            
1784
            $response['error'] = __( 'Please enter your registered company name!', 'invoicing' );
1785
            wp_send_json( $response );
1786
        }
1787
        
1788
        if ( !empty( $wpinv_options['vat_vies_check'] ) ) {
1789
            if ( empty( $wpinv_options['vat_offline_check'] ) && !self::offline_check( $vat_number ) ) {
1790
                $vat_info['valid'] = false;
1791
                $wpi_session->set( 'user_vat_data', $vat_info );
1792
                
1793
                $response['error'] = wp_sprintf( __( '%s number not validated', 'invoicing' ), $vat_name );
1794
                wp_send_json( $response );
1795
            }
1796
            
1797
            $response['success'] = true;
1798
            $response['message'] = wp_sprintf( __( '%s number validated', 'invoicing' ), $vat_name );
1799
        } else {
1800
            $result = self::check_vat( $vat_number );
1801
            
1802
            if ( empty( $result['valid'] ) ) {
1803
                $response['error'] = $result['message'];
1804
                wp_send_json( $response );
1805
            }
1806
            
1807
            $vies_company = !empty( $result['company'] ) ? $result['company'] : '';
1808
            $vies_company = apply_filters( 'wpinv_vies_company_name', $vies_company );
1809
            
1810
            $valid_company = $vies_company && $company && ( $vies_company == '---' || strcasecmp( trim( $vies_company ), trim( $company ) ) == 0 ) ? true : false;
1811
1812
            if ( !empty( $wpinv_options['vat_disable_company_name_check'] ) || $valid_company ) {
1813
                $response['success'] = true;
1814
                $response['message'] = wp_sprintf( __( '%s number validated', 'invoicing' ), $vat_name );
1815
            } else {           
1816
                $vat_info['valid'] = false;
1817
                $wpi_session->set( 'user_vat_data', $vat_info );
1818
                
1819
                $response['success'] = false;
1820
                $response['message'] = wp_sprintf( __( 'The company name associated with the %s number provided is not the same as the company name provided.', 'invoicing' ), $vat_name );
1821
                wp_send_json( $response );
1822
            }
1823
        }
1824
        
1825
        if ( $is_checkout ) {
1826
            self::save_user_vat_details( $company, $vat_number );
1827
1828
            $vat_info = array('company' => $company, 'number' => $vat_number, 'valid' => true );
1829
            $wpi_session->set( 'user_vat_data', $vat_info );
1830
        }
1831
1832
        wp_send_json( $response );
1833
    }
1834
    
1835
    public static function ajax_vat_reset() {
1836
        global $wpi_session;
1837
        
1838
        $company    = is_user_logged_in() ? self::get_user_company() : '';
1839
        $vat_number = self::get_user_vat_number();
1840
        
1841
        $vat_info   = array('company' => $company, 'number' => $vat_number, 'valid' => false );
1842
        $wpi_session->set( 'user_vat_data', $vat_info );
1843
        
1844
        $response                       = array();
1845
        $response['success']            = true;
1846
        $response['data']['company']    = $company;
1847
        $response['data']['number']     = $vat_number;
1848
        
1849
        wp_send_json( $response );
1850
    }
1851
    
1852
    public static function checkout_vat_validate( $valid_data, $post ) {
0 ignored issues
show
Unused Code introduced by
The parameter $valid_data is not used and could be removed. ( Ignorable by Annotation )

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

1852
    public static function checkout_vat_validate( /** @scrutinizer ignore-unused */ $valid_data, $post ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $post is not used and could be removed. ( Ignorable by Annotation )

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

1852
    public static function checkout_vat_validate( $valid_data, /** @scrutinizer ignore-unused */ $post ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1853
        global $wpinv_options, $wpi_session;
1854
1855
        $vat_name  = __( self::get_vat_name(), 'invoicing' );
1856
1857
        if ( !isset( $_POST['_wpi_nonce'] ) || !wp_verify_nonce( $_POST['_wpi_nonce'], 'vat_validation' ) ) {
1858
            wpinv_set_error( 'vat_validation', wp_sprintf( __( "Invalid %s validation request.", 'invoicing' ), $vat_name ) );
1859
            return;
1860
        }
1861
1862
        $vat_saved = $wpi_session->get( 'user_vat_data' );
1863
        $wpi_session->set( 'user_vat_data', null );
1864
        
1865
        $invoice        = wpinv_get_invoice_cart();
1866
        $amount         = $invoice->get_total();
0 ignored issues
show
Unused Code introduced by
The assignment to $amount is dead and can be removed.
Loading history...
1867
        $is_digital     = self::invoice_has_digital_rule( $invoice );
1868
        $no_vat         = !self::requires_vat( 0, false, $is_digital );
1869
        
1870
        $company        = !empty( $_POST['wpinv_company'] ) ? $_POST['wpinv_company'] : null;
1871
        $vat_number     = !empty( $_POST['wpinv_vat_number'] ) ? $_POST['wpinv_vat_number'] : null;
1872
        $country        = !empty( $_POST['wpinv_country'] ) ? $_POST['wpinv_country'] : $invoice->country;
1873
        if ( empty( $country ) ) {
1874
            $country = wpinv_default_billing_country();
1875
        }
1876
        
1877
        if ( !$is_digital && $no_vat ) {
1878
            return;
1879
        }
1880
            
1881
        $vat_data           = array( 'company' => '', 'number' => '', 'valid' => false );
1882
        
1883
        $ip_country_code    = self::get_country_by_ip();
1884
        $is_eu_state        = self::is_eu_state( $country );
1885
        $is_eu_state_ip     = self::is_eu_state( $ip_country_code );
1886
        $is_non_eu_user     = !$is_eu_state && !$is_eu_state_ip;
1887
1888
        if ( !empty( $wpinv_options['vat_prevent_b2c_purchase'] ) && !$is_non_eu_user && ( empty( $vat_number ) || $no_vat ) ) {
1889
            if ( $is_eu_state ) {
1890
                wpinv_set_error( 'vat_validation', wp_sprintf( __( 'Please enter and validate your %s number to verify your purchase is by an EU business.', 'invoicing' ), $vat_name ) );
1891
            } else if ( $is_digital && $is_eu_state_ip ) {
1892
                wpinv_set_error( 'vat_validation', wp_sprintf( __( 'Sales to non-EU countries cannot be completed because %s must be applied.', 'invoicing' ), $vat_name ) );
1893
            }
1894
        }
1895
1896
        if ( !$is_eu_state || $no_vat || empty( $vat_number ) ) {
1897
            return;
1898
        }
1899
1900
        if ( !empty( $vat_saved ) && isset( $vat_saved['valid'] ) ) {
1901
            $vat_data['valid']  = $vat_saved['valid'];
1902
        }
1903
            
1904
        if ( $company !== null ) {
1905
            $vat_data['company'] = $company;
1906
        }
1907
1908
        $message = '';
1909
        if ( $vat_number !== null ) {
1910
            $vat_data['number'] = $vat_number;
1911
            
1912
            if ( !$vat_data['valid'] || ( $vat_saved['number'] !== $vat_data['number'] ) || ( $vat_saved['company'] !== $vat_data['company'] ) ) {
1913
                if ( !empty( $wpinv_options['vat_vies_check'] ) ) {            
1914
                    if ( empty( $wpinv_options['vat_offline_check'] ) && !self::offline_check( $vat_number ) ) {
1915
                        $vat_data['valid'] = false;
1916
                    }
1917
                } else {
1918
                    $result = self::check_vat( $vat_number );
1919
                    
1920
                    if ( !empty( $result['valid'] ) ) {                
1921
                        $vat_data['valid'] = true;
1922
                        $vies_company = !empty( $result['company'] ) ? $result['company'] : '';
1923
                        $vies_company = apply_filters( 'wpinv_vies_company_name', $vies_company );
1924
                    
1925
                        $valid_company = $vies_company && $company && ( $vies_company == '---' || strcasecmp( trim( $vies_company ), trim( $company ) ) == 0 ) ? true : false;
1926
1927
                        if ( !( !empty( $wpinv_options['vat_disable_company_name_check'] ) || $valid_company ) ) {         
1928
                            $vat_data['valid'] = false;
1929
                            
1930
                            $message = wp_sprintf( __( 'The company name associated with the %s number provided is not the same as the company name provided.', 'invoicing' ), $vat_name );
1931
                        }
1932
                    } else {
1933
                        $message = wp_sprintf( __( 'Fail to validate the %s number: EU Commission VAT server (VIES) check fails.', 'invoicing' ), $vat_name );
1934
                    }
1935
                }
1936
                
1937
                if ( !$vat_data['valid'] ) {
1938
                    $error = wp_sprintf( __( 'The %s %s number %s you have entered has not been validated', 'invoicing' ), '<a href="#wpi-vat-details">', $vat_name, '</a>' ) . ( $message ? ' ( ' . $message . ' )' : '' );
1939
                    wpinv_set_error( 'vat_validation', $error );
1940
                }
1941
            }
1942
        }
1943
1944
        $wpi_session->set( 'user_vat_data', $vat_data );
1945
    }
1946
    
1947
    public static function checkout_vat_fields( $billing_details ) {
0 ignored issues
show
Unused Code introduced by
The parameter $billing_details is not used and could be removed. ( Ignorable by Annotation )

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

1947
    public static function checkout_vat_fields( /** @scrutinizer ignore-unused */ $billing_details ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1948
        global $wpi_session, $wpinv_options, $wpi_country, $wpi_requires_vat;
1949
        
1950
        $ip_address         = wpinv_get_ip();
1951
        $ip_country_code    = self::get_country_by_ip();
1952
        
1953
        $tax_label          = __( self::get_vat_name(), 'invoicing' );
1954
        $invoice            = wpinv_get_invoice_cart();
1955
        $is_digital         = self::invoice_has_digital_rule( $invoice );
1956
        $wpi_country        = $invoice->country;
1957
        
1958
        $requires_vat       = !self::hide_vat_fields() && !$invoice->is_free() && self::requires_vat( 0, false, $is_digital );
1959
        $wpi_requires_vat   = $requires_vat;
1960
        
1961
        $company            = self::get_user_company();
1962
        $vat_number         = self::get_user_vat_number();
1963
        
1964
        $validated          = $vat_number ? self::get_user_vat_number( '', 0, true ) : 1;
1965
        $vat_info           = $wpi_session->get( 'user_vat_data' );
1966
1967
        if ( is_array( $vat_info ) ) {
1968
            $company    = isset( $vat_info['company'] ) ? $vat_info['company'] : '';
1969
            $vat_number = isset( $vat_info['number'] ) ? $vat_info['number'] : '';
1970
            $validated  = isset( $vat_info['valid'] ) ? $vat_info['valid'] : false;
1971
        }
1972
        
1973
        $selected_country = $invoice->country ? $invoice->country : wpinv_default_billing_country();
1974
1975
        if ( $ip_country_code == 'UK' ) {
1976
            $ip_country_code = 'GB';
1977
        }
1978
        
1979
        if ( $selected_country == 'UK' ) {
1980
            $selected_country = 'GB';
1981
        }
1982
        
1983
        if ( $requires_vat && ( self::same_country_rule() == 'no' && wpinv_is_base_country( $selected_country ) || !self::allow_vat_rules() ) ) {
1984
            $requires_vat = false;
1985
        }
1986
1987
        $display_vat_details    = $requires_vat ? 'block' : 'none';
1988
        $display_validate_btn   = 'none';
1989
        $display_reset_btn      = 'none';
1990
        
1991
        if ( !empty( $vat_number ) && $validated ) {
1992
            $vat_vailidated_text    = wp_sprintf( __( '%s number validated', 'invoicing' ), $tax_label );
1993
            $vat_vailidated_class   = 'wpinv-vat-stat-1';
1994
            $display_reset_btn      = 'block';
1995
        } else {
1996
            $vat_vailidated_text    = empty( $vat_number ) ? '' : wp_sprintf( __( '%s number not validated', 'invoicing' ), $tax_label );
1997
            $vat_vailidated_class   = empty( $vat_number ) ? '' : 'wpinv-vat-stat-0';
1998
            $display_validate_btn   = 'block';
1999
        }
2000
        
2001
        $show_ip_country        = $is_digital && ( empty( $vat_number ) || !$requires_vat ) && $ip_country_code != $selected_country ? 'block' : 'none';
2002
        ?>
2003
        <div id="wpi-vat-details" class="wpi-vat-details clearfix" style="display:<?php echo $display_vat_details; ?>">
2004
            <div id="wpi_vat_info" class="clearfix panel panel-default">
2005
                <div class="panel-heading"><h3 class="panel-title"><?php echo wp_sprintf( __( '%s Details', 'invoicing' ), $tax_label );?></h3></div>
2006
                <div id="wpinv-fields-box" class="panel-body">
2007
                    <p id="wpi_show_vat_note">
2008
                        <?php echo wp_sprintf( __( 'Validate your registered %s number to exclude tax.', 'invoicing' ), $tax_label ); ?>
2009
                    </p>
2010
                    <div id="wpi_vat_fields" class="wpi_vat_info">
2011
                        <p class="wpi-cart-field wpi-col2 wpi-colf">
2012
                            <label for="wpinv_company" class="wpi-label"><?php _e( 'Company Name', 'invoicing' );?></label>
2013
                            <?php
2014
                            echo wpinv_html_text( array(
2015
                                    'id'            => 'wpinv_company',
2016
                                    'name'          => 'wpinv_company',
2017
                                    'value'         => $company,
2018
                                    'class'         => 'wpi-input form-control',
2019
                                    'placeholder'   => __( 'Company name', 'invoicing' ),
2020
                                ) );
2021
                            ?>
2022
                        </p>
2023
                        <p class="wpi-cart-field wpi-col2 wpi-coll wpi-cart-field-vat">
2024
                            <label for="wpinv_vat_number" class="wpi-label"><?php echo wp_sprintf( __( '%s Number', 'invoicing' ), $tax_label );?></label>
2025
                            <span id="wpinv_vat_number-wrap">
2026
                                <label for="wpinv_vat_number" class="wpinv-label"></label>
2027
                                <input type="text" class="wpi-input form-control" placeholder="<?php echo esc_attr( wp_sprintf( __( '%s number', 'invoicing' ), $tax_label ) );?>" value="<?php esc_attr_e( $vat_number );?>" id="wpinv_vat_number" name="wpinv_vat_number">
2028
                                <span class="wpinv-vat-stat <?php echo $vat_vailidated_class;?>"><i class="fa"></i>&nbsp;<font><?php echo $vat_vailidated_text;?></font></span>
2029
                            </span>
2030
                        </p>
2031
                        <p class="wpi-cart-field wpi-col wpi-colf wpi-cart-field-actions">
2032
                            <button class="btn btn-success btn-sm wpinv-vat-validate" type="button" id="wpinv_vat_validate" style="display:<?php echo $display_validate_btn; ?>"><?php echo wp_sprintf( __("Validate %s Number", 'invoicing'), $tax_label ); ?></button>
2033
                            <button class="btn btn-danger btn-sm wpinv-vat-reset" type="button" id="wpinv_vat_reset" style="display:<?php echo $display_reset_btn; ?>"><?php echo wp_sprintf( __("Reset %s", 'invoicing'), $tax_label ); ?></button>
2034
                            <span class="wpi-vat-box wpi-vat-box-info"><span id="text"></span></span>
2035
                            <span class="wpi-vat-box wpi-vat-box-error"><span id="text"></span></span>
2036
                            <input type="hidden" name="_wpi_nonce" value="<?php echo wp_create_nonce( 'vat_validation' ) ?>" />
2037
                        </p>
2038
                    </div>
2039
                </div>
2040
            </div>
2041
        </div>
2042
        <div id="wpinv_adddress_confirm" class="wpi-vat-info clearfix panel panel-info" value="<?php echo $ip_country_code; ?>" style="display:<?php echo $show_ip_country; ?>;">
2043
            <div id="wpinv-fields-box" class="panel-body">
2044
                <span id="wpinv_adddress_confirmed-wrap">
2045
                    <input type="checkbox" id="wpinv_adddress_confirmed" name="wpinv_adddress_confirmed" value="1">
2046
                    <label for="wpinv_adddress_confirmed"><?php _e('The country of your current location must be the same as the country of your billing location or you must confirm the billing address is your home country.', 'invoicing'); ?></label>
2047
                </span>
2048
            </div>
2049
        </div>
2050
        <?php if ( empty( $wpinv_options['hide_ip_address'] ) ) { 
2051
            $ip_link = '<a title="' . esc_attr( __( 'View more details on map', 'invoicing' ) ) . '" target="_blank" href="' . esc_url( admin_url( 'admin-ajax.php?action=wpinv_ip_geolocation&ip=' . $ip_address ) ) . '" class="wpi-ip-address-link">' . $ip_address . '&nbsp;&nbsp;<i class="fa fa-external-link-square" aria-hidden="true"></i></a>';
2052
        ?>
2053
        <div class="wpi-ip-info clearfix panel panel-info">
2054
            <div id="wpinv-fields-box" class="panel-body">
2055
                <span><?php echo wp_sprintf( __( "Your IP address is: %s", 'invoicing' ), $ip_link ); ?></span>
2056
            </div>
2057
        </div>
2058
        <?php }
2059
    }
2060
    
2061
    public static function show_vat_notice( $invoice ) {
2062
        if ( empty( $invoice ) ) {
2063
            return NULL;
2064
        }
2065
        
2066
        $label      = wpinv_get_option( 'vat_invoice_notice_label' );
2067
        $notice     = wpinv_get_option( 'vat_invoice_notice' );
2068
        if ( $label || $notice ) {
2069
        ?>
2070
        <div class="row wpinv-vat-notice">
2071
            <div class="col-sm-12">
2072
                <?php if ( $label ) { ?>
2073
                <strong><?php _e( $label, 'invoicing' ); ?></strong>
2074
                <?php } if ( $notice ) { ?>
2075
                <?php echo wpautop( wptexturize( __( $notice, 'invoicing' ) ) ) ?>
2076
                <?php } ?>
2077
            </div>
2078
        </div>
2079
        <?php
2080
        }
2081
    }
2082
}
2083
2084
global $wpinv_euvat;
2085
$wpinv_euvat = WPInv_EUVat::get_instance();
2086