<?php
/**
* Stripe Tax Plugin Helper service
* Contains currencies, countries, for origin addresses, tax registrations, tax calculations and methods to retrieve it
*
* @package Stripe\StripeTaxForWooCommerce\Stripe
*/
namespace Stripe\StripeTaxForWooCommerce\Stripe;
defined( 'ABSPATH' ) || exit;
use Exception;
use Stripe\StripeTaxForWooCommerce\SDK\lib\Exception\ApiErrorException;
use Stripe\StripeTaxForWooCommerce\SDK\lib\Exception\InvalidRequestException;
/**
* Stripe Tax Plugin Helper service
*/
class StripeTaxPluginHelper {
/**
* Array keys, used in "locks" logics
*
* @see TaxRegistrations::get_locks()
*/
public const LOCK_BLOOMINGTON = 'bloomington';
public const LOCK_CA_PROVINCES = 'ca_provinces';
public const LOCK_CHICAGO_AMUSEMENT = 'chicago_amusement';
public const LOCK_CHICAGO_LEASE = 'chicago_lease';
public const LOCK_COUNTRIES = 'countries';
public const LOCK_EAST_DUNDEE = 'east_dundee';
public const LOCK_EVANSTON = 'evanston';
public const LOCK_LOCAL_COMMUNICATIONS = 'local_communications';
public const LOCK_IOSS = 'ioss';
public const LOCK_OSS_UNION = 'oss_union';
public const LOCK_OSS_NON_UNION = 'oss_non_union';
public const LOCK_SCHILLER_PARK = 'schiller_park';
public const LOCK_US_STATES = 'us_states';
/**
* Array keys, used to display Tax Registrations in List Table
*/
public const DISPLAY_LOCAL_AMUSEMENT_TAX_PREFIX = 'local_amusement_tax_';
public const DISPLAY_LOCAL_LEASE_TAX_PREFIX = 'local_lease_tax_';
public const DISPLAY_STATE_COMMUNICATIONS_TAX = 'state_communications_tax';
/**
* FIPS codes
*/
public const FIPS_CHICAGO = '14000';
public const FIPS_BLOOMINGTON = '06613';
public const FIPS_EAST_DUNDEE = '21696';
public const FIPS_EVANSTON = '24582';
public const FIPS_SCHILLER_PARK = '68081';
/**
* Supported presentment currencies for all countries, but without currencies for the United Arab Emirates
*
* @see https://stripe.com/docs/currencies#presentment-currencies
* @var array
*/
public const CURRENCY_INTERSECT = array(
'usd' => '',
'aed' => '',
'afn' => '',
'all' => '',
'amd' => '',
'ang' => '',
'aoa' => '',
'ars' => '',
'aud' => '',
'awg' => '',
'azn' => '',
'bam' => '',
'bbd' => '',
'bdt' => '',
'bgn' => '',
'bif' => '',
'bmd' => '',
'bnd' => '',
'bob' => '',
'brl' => '',
'bsd' => '',
'bwp' => '',
'byn' => '',
'bzd' => '',
'cad' => '',
'cdf' => '',
'chf' => '',
'clp' => '',
'cny' => '',
'cop' => '',
'crc' => '',
'cve' => '',
'czk' => '',
'djf' => '',
'dkk' => '',
'dop' => '',
'dzd' => '',
'egp' => '',
'etb' => '',
'eur' => '',
'fjd' => '',
'fkp' => '',
'gbp' => '',
'gel' => '',
'gip' => '',
'gmd' => '',
'gnf' => '',
'gtq' => '',
'gyd' => '',
'hkd' => '',
'hnl' => '',
'hrk' => '',
'htg' => '',
'huf' => '',
'idr' => '',
'ils' => '',
'inr' => '',
'isk' => '',
'jmd' => '',
'jpy' => '',
'kes' => '',
'kgs' => '',
'khr' => '',
'kmf' => '',
'krw' => '',
'kyd' => '',
'kzt' => '',
'lak' => '',
'lbp' => '',
'lkr' => '',
'lrd' => '',
'lsl' => '',
'mad' => '',
'mdl' => '',
'mga' => '',
'mkd' => '',
'mmk' => '',
'mnt' => '',
'mop' => '',
'mur' => '',
'mvr' => '',
'mwk' => '',
'mxn' => '',
'myr' => '',
'mzn' => '',
'nad' => '',
'ngn' => '',
'nio' => '',
'nok' => '',
'npr' => '',
'nzd' => '',
'pab' => '',
'pen' => '',
'pgk' => '',
'php' => '',
'pkr' => '',
'pln' => '',
'pyg' => '',
'qar' => '',
'ron' => '',
'rsd' => '',
'rub' => '',
'rwf' => '',
'sar' => '',
'sbd' => '',
'scr' => '',
'sek' => '',
'sgd' => '',
'shp' => '',
'sle' => '',
'sos' => '',
'srd' => '',
'std' => '',
'szl' => '',
'thb' => '',
'tjs' => '',
'top' => '',
'try' => '',
'ttd' => '',
'twd' => '',
'tzs' => '',
'uah' => '',
'ugx' => '',
'uyu' => '',
'uzs' => '',
'vnd' => '',
'vuv' => '',
'wst' => '',
'xaf' => '',
'xcd' => '',
'xof' => '',
'xpf' => '',
'yer' => '',
'zar' => '',
'zmw' => '',
);
/**
* Supported presentment currencies, added for the United Arab Emirates
*
* @see https://stripe.com/docs/currencies?presentment-currency=AE#zero-decimal
* @var array
*/
public const CURRENCIES_ADDED_FOR_AE = array(
'bhd' => '',
'jod' => '',
'kwd' => '',
'omr' => '',
'tnd' => '',
);
/**
* Zero-decimal currencies
*
* @see https://stripe.com/docs/currencies#zero-decimal
* @var array
*/
public const CURRENCIES_ZERO_DECIMAL = array(
'bif' => '',
'clp' => '',
'djf' => '',
'gnf' => '',
'jpy' => '',
'kmf' => '',
'krw' => '',
'mga' => '',
'pyg' => '',
'rwf' => '',
'ugx' => '',
'vnd' => '',
'vuv' => '',
'xaf' => '',
'xof' => '',
'xpf' => '',
);
/**
* Three-decimal currencies
*
* @see https://stripe.com/docs/currencies#three-decimal
* @var array
*/
public const CURRENCIES_THREE_DECIMAL = array(
'bhd' => '',
'jod' => '',
'kwd' => '',
'omr' => '',
'tnd' => '',
);
/**
* Supported presentment currencies for each country
*
* @see https://stripe.com/docs/currencies#presentment-currencies
* @var array
*/
public const CURRENCIES_PER_SUPPORTED_COUNTRY = array(
'AE' => self::CURRENCY_INTERSECT + self::CURRENCIES_ADDED_FOR_AE,
'AT' => self::CURRENCY_INTERSECT,
'AU' => self::CURRENCY_INTERSECT,
'BE' => self::CURRENCY_INTERSECT,
'BG' => self::CURRENCY_INTERSECT,
'BR' => self::CURRENCY_INTERSECT,
'CA' => self::CURRENCY_INTERSECT,
'CH' => self::CURRENCY_INTERSECT,
'CY' => self::CURRENCY_INTERSECT,
'CZ' => self::CURRENCY_INTERSECT,
'DE' => self::CURRENCY_INTERSECT,
'DK' => self::CURRENCY_INTERSECT,
'EE' => self::CURRENCY_INTERSECT,
'ES' => self::CURRENCY_INTERSECT,
'FI' => self::CURRENCY_INTERSECT,
'FR' => self::CURRENCY_INTERSECT,
'GB' => self::CURRENCY_INTERSECT,
'GI' => self::CURRENCY_INTERSECT,
'GR' => self::CURRENCY_INTERSECT,
'HK' => self::CURRENCY_INTERSECT,
'HR' => self::CURRENCY_INTERSECT,
'HU' => self::CURRENCY_INTERSECT,
'IE' => self::CURRENCY_INTERSECT,
'IN' => self::CURRENCY_INTERSECT,
'IT' => self::CURRENCY_INTERSECT,
'JP' => self::CURRENCY_INTERSECT,
'LI' => self::CURRENCY_INTERSECT,
'LT' => self::CURRENCY_INTERSECT,
'LU' => self::CURRENCY_INTERSECT,
'LV' => self::CURRENCY_INTERSECT,
'MT' => self::CURRENCY_INTERSECT,
'MX' => self::CURRENCY_INTERSECT,
'MY' => self::CURRENCY_INTERSECT,
'NL' => self::CURRENCY_INTERSECT,
'NO' => self::CURRENCY_INTERSECT,
'NZ' => self::CURRENCY_INTERSECT,
'PL' => self::CURRENCY_INTERSECT,
'PT' => self::CURRENCY_INTERSECT,
'RO' => self::CURRENCY_INTERSECT,
'SE' => self::CURRENCY_INTERSECT,
'SG' => self::CURRENCY_INTERSECT,
'SI' => self::CURRENCY_INTERSECT,
'SK' => self::CURRENCY_INTERSECT,
'TH' => self::CURRENCY_INTERSECT,
'US' => self::CURRENCY_INTERSECT,
);
/**
* List of all supported currencies in case we need to check only currency without country in which it supported.
*
* @see https://stripe.com/docs/currencies#presentment-currencies
* @var array
*/
public const CURRENCIES_ALL_SUPPORTED = self::CURRENCY_INTERSECT + self::CURRENCIES_ADDED_FOR_AE;
/**
* List of currencies to be applied special rules before tax calculation API call
*
* @see https://stripe.com/docs/currencies#special-cases
* @var array
*/
public const CURRENCIES_SPECIAL_CASE_ROUND_AND_MULTIPLY_100 = array(
'isk' => '',
'huf' => '',
'twd' => '',
'ugx' => '',
);
/**
* List of supported Stripe Customer Tax ID's.
* Current list of ID's prepended with country code, that should be removed before API call.
* This is made so, for duplication "eu_vat" for different european countries.
*
* @see https://stripe.com/docs/billing/customer/tax-ids#supported-tax-id
*/
public const TAX_IDS = array(
'AD_ad_nrt' => 'Andorra - Andorran NRT number',
'AR_ar_cuit' => 'Argentina - Argentinian tax ID number',
'AU_au_abn' => 'Australia - Australian Business Number (AU ABN)',
'AU_au_arn' => 'Australia - Australian Taxation Office Reference Number',
'AT_eu_vat' => 'Austria - European VAT number', // Austria.
'BE_eu_vat' => 'Belgium - European VAT number', // Belgium.
'BO_bo_tin' => 'Bolivia - Bolivian tax ID',
'BR_br_cnpj' => 'Brazil - Brazilian CNPJ number',
'BR_br_cpf' => 'Brazil - Brazilian CPF number',
'BG_bg_uic' => 'Bulgaria - Bulgaria Unified Identification Code',
'BG_eu_vat' => 'Bulgaria - European VAT number', // Bulgaria.
'CA_ca_bn' => 'Canada - Canadian BN',
'CA_ca_gst_hst' => 'Canada - Canadian GST/HST number',
'CA_ca_pst_bc' => 'Canada - Canadian PST number (British Columbia)',
'CA_ca_pst_mb' => 'Canada - Canadian PST number (Manitoba)',
'CA_ca_pst_sk' => 'Canada - Canadian PST number (Saskatchewan)',
'CA_ca_qst' => 'Canada - Canadian QST number (Québec)',
'CL_cl_tin' => 'Chile - Chilean TIN',
'CN_cn_tin' => 'China - Chinese tax ID',
'CO_co_nit' => 'Colombia - Colombian NIT number',
'CR_cr_tin' => 'Costa Rica - Costa Rican tax ID',
'HR_eu_vat' => 'Croatia - European VAT number', // Croatia.
'CY_eu_vat' => 'Cyprus - European VAT number', // Cyprus.
'CZ_eu_vat' => 'Czech Republic - European VAT number', // Czech Republic.
'DK_eu_vat' => 'Denmark - European VAT number', // Denmark.
'DO_do_rcn' => 'Dominican Republic - Dominican RCN number',
'EC_ec_ruc' => 'Ecuador - Ecuadorian RUC number',
'EG_eg_tin' => 'Egypt - Egyptian Tax Identification Number',
'SV_sv_nit' => 'El Salvador - El Salvadorian NIT number',
'EE_eu_vat' => 'Estonia - European VAT number', // Estonia.
'EU_eu_oss_vat' => 'EU - European One Stop Shop VAT number for non-Union scheme', // EU.
'FI_eu_vat' => 'Finland - European VAT number', // Finland.
'FR_eu_vat' => 'France - European VAT number', // France.
'GE_ge_vat' => 'Georgia - Georgian VAT',
'DE_eu_vat' => 'Germany - European VAT number', // Germany.
'GR_eu_vat' => 'Greece - European VAT number', // Greece.
'HK_hk_br' => 'Hong Kong - Hong Kong BR number',
'HU_eu_vat' => 'Hungary - European VAT number', // Hungary.
'HU_hu_tin' => 'Hungary - Hungary tax number (adószám)',
'IS_is_vat' => 'Iceland - Icelandic VAT',
'IN_in_gst' => 'India - Indian GST number',
'ID_id_npwp' => 'Indonesia - Indonesian NPWP number',
'IE_eu_vat' => 'Ireland - European VAT number', // Ireland.
'IL_il_vat' => 'Israel - Israel VAT',
'IT_eu_vat' => 'Italy - European VAT number', // Italy.
'JP_jp_cn' => 'Japan - Japanese Corporate Number (*Hōjin Bangō*)',
'JP_jp_rn' => 'Japan - Japanese Registered Foreign Businesses\' Registration Number (*Tōroku Kokugai Jigyōsha no Tōroku Bangō*)',
'JP_jp_trn' => 'Japan - Japanese Tax Registration Number (*Tōroku Bangō*)',
'KE_ke_pin' => 'Kenya - Kenya Revenue Authority Personal Identification Number',
'LV_eu_vat' => 'Latvia - European VAT number', // Latvia.
'LI_li_uid' => 'Liechtenstein - Liechtensteinian UID number',
'LT_eu_vat' => 'Lithuania - European VAT number', // Lithuania.
'LU_eu_vat' => 'Luxembourg - European VAT number', // Luxembourg.
'MY_my_frp' => 'Malaysia - Malaysian FRP number',
'MY_my_itn' => 'Malaysia - Malaysian ITN',
'MY_my_sst' => 'Malaysia - Malaysian SST number',
'MT_eu_vat' => 'Malta - European VAT number', // Malta.
'MX_mx_rfc' => 'Mexico - Mexican RFC number',
'NL_eu_vat' => 'Netherlands - European VAT number', // Netherlands.
'NZ_nz_gst' => 'New Zealand - New Zealand GST number',
'NO_no_vat' => 'Norway - Norwegian VAT number',
'PE_pe_ruc' => 'Peru - Peruvian RUC number',
'PH_ph_tin' => 'Philippines - Philippines Tax Identification Number',
'PL_eu_vat' => 'Poland - European VAT number', // Poland.
'PT_eu_vat' => 'Portugal - European VAT number', // Portugal.
'RO_eu_vat' => 'Romania - European VAT number', // Romania.
'RO_ro_tin' => 'Romania - Romanian tax ID number',
'RU_ru_inn' => 'Russia - Russian INN',
'RU_ru_kpp' => 'Russia - Russian KPP',
'SA_sa_vat' => 'Saudi Arabia - Saudi Arabia VAT',
'RS_rs_pib' => 'Serbia - Serbian PIB number',
'SG_sg_gst' => 'Singapore - Singaporean GST',
'SG_sg_uen' => 'Singapore - Singaporean UEN',
'SK_eu_vat' => 'Slovakia - European VAT number', // Slovakia.
'SI_eu_vat' => 'Slovenia - European VAT number', // Slovenia.
'SI_si_tin' => 'Slovenia - Slovenia tax number (davčna številka)',
'ZA_za_vat' => 'South Africa - South African VAT number',
'KR_kr_brn' => 'South Korea - Korean BRN',
'ES_es_cif' => 'Spain - Spanish NIF number (previously Spanish CIF number)',
'ES_eu_vat' => 'Spain - European VAT number', // Spain.
'SE_eu_vat' => 'Sweden - European VAT number', // Sweden.
'CH_ch_vat' => 'Switzerland - Switzerland VAT number',
'TW_tw_vat' => 'Taiwan - Taiwanese VAT',
'TH_th_vat' => 'Thailand - Thai VAT',
'TR_tr_tin' => 'Turkey - Turkish Tax Identification Number',
'UA_ua_vat' => 'Ukraine - Ukrainian VAT',
'AE_ae_trn' => 'United Arab Emirates - United Arab Emirates TRN',
'GB_eu_vat' => 'United Kingdom (UK) - Northern Ireland VAT number', // United Kingdom.
'GB_gb_vat' => 'United Kingdom (UK) - United Kingdom VAT number',
'US_us_ein' => 'United States (US) - United States EIN',
'UY_uy_ruc' => 'Uruguay - Uruguayan RUC number',
'VE_ve_rif' => 'Venezuela - Venezuelan RIF number',
'VN_vn_tin' => 'Vietnam - Vietnamese tax ID number',
);
/**
* Allowed countries for Tax Registration
*
* @var array
*/
public const TAX_REGISTRATION_ALLOWED_COUNTRIES = array(
'AU' => '',
'AT' => '',
'BE' => '',
'BG' => '',
'CA' => '',
'CL' => '',
'CO' => '',
'HR' => '',
'CY' => '',
'CZ' => '',
'DK' => '',
'EE' => '',
'FI' => '',
'FR' => '',
'DE' => '',
'GR' => '',
'HU' => '',
'IS' => '',
'ID' => '',
'IE' => '',
'IT' => '',
'JP' => '',
'LV' => '',
'LT' => '',
'LU' => '',
'MY' => '',
'MT' => '',
'MX' => '',
'NL' => '',
'NZ' => '',
'NO' => '',
'PL' => '',
'PT' => '',
'RO' => '',
'SA' => '',
'SG' => '',
'SK' => '',
'SI' => '',
'ZA' => '',
'KR' => '',
'ES' => '',
'SE' => '',
'CH' => '',
'TH' => '',
'TR' => '',
'AE' => '',
'GB' => '',
'US' => '',
'VN' => '',
);
/**
* Allowed US states for Tax Registration
*
* @var array
*/
public const TAX_REGISTRATION_ALLOWED_US_STATES = array(
'AL' => '',
'AK' => '',
'AZ' => '',
'AR' => '',
'CA' => '',
'CO' => '',
'CT' => '',
'DE' => '', // Delaware doesn't have sales tax.
'DC' => '',
'FL' => '', // Communications Tax.
'GA' => '',
'HI' => '',
'ID' => '',
'IL' => '', // Lease tax, amusement tax.
'IN' => '',
'IA' => '',
'KS' => '',
'KY' => '', // Communication tax.
'LA' => '',
'ME' => '',
'MD' => '',
'MA' => '',
'MI' => '',
'MN' => '',
'MS' => '',
'MO' => '',
'MT' => '', // Montana doesn't have sales tax.
'NE' => '',
'NV' => '',
'NH' => '', // New Hampshire doesn't have sales tax.
'NJ' => '',
'NM' => '',
'NY' => '',
'NC' => '',
'ND' => '',
'OH' => '',
'OK' => '',
'OR' => '', // Oregon doesn't have sales tax.
'PA' => '',
'RI' => '',
'SC' => '',
'SD' => '',
'TN' => '',
'TX' => '',
'UT' => '',
'VT' => '',
'VA' => '',
'WA' => '',
'WV' => '',
'WI' => '',
'WY' => '',
);
/**
* Allowed Canada provinces for Tax Registration
*
* @var array
*/
public const TAX_REGISTRATION_ALLOWED_CA_PROVINCES = array(
'BC' => '',
'MB' => '',
'SK' => '',
'QC' => '',
);
/**
* US states that has no sales tax
*
* @var array
*/
public const TAX_REGISTRATION_NO_SALES_TAX_US_STATES = array(
'DE' => '',
'MT' => '',
'NH' => '',
'OR' => '',
);
/**
* US states that has Local Communication tax
*
* @var array
*/
public const TAX_REGISTRATION_LOCAL_COMMUNICATIONS_TAX_US_STATES = array(
'FL' => '',
'KY' => '',
);
/**
* US states that has lease and amusement tax
*
* @var array
*/
public const TAX_REGISTRATION_LEASE_AND_AMUSEMENT_TAX_US_STATES = array(
'IL' => '',
);
/**
* EU countries
*
* @var array
*/
public const TAX_REGISTRATION_EU_TAX_COUNTRIES = array(
'AT' => '',
'BE' => '',
'BG' => '',
'CY' => '',
'CZ' => '',
'DE' => '',
'DK' => '',
'EE' => '',
'ES' => '',
'FI' => '',
'FR' => '',
'GR' => '',
'HR' => '',
'HU' => '',
'IE' => '',
'IT' => '',
'LT' => '',
'LU' => '',
'LV' => '',
'MT' => '',
'NL' => '',
'PL' => '',
'PT' => '',
'RO' => '',
'SE' => '',
'SI' => '',
'SK' => '',
);
/**
* Countries, that allowed only selling digital goods (simplified tax type)
*
* @var array
*/
public const TAX_REGISTRATION_DIGITAL_TAX_COUNTRIES = array(
'CL' => '',
'ID' => '',
'CO' => '',
'KR' => '',
'MX' => '',
'MY' => '',
'SA' => '',
'TH' => '',
'TR' => '',
'VN' => '',
);
/**
* Countries, that has standard sales taxes (standard tax type)
*
* @var array
*/
public const TAX_REGISTRATION_STANDARD_TAX_COUNTRIES = array(
'AE' => '',
'AU' => '',
'CH' => '',
'GB' => '',
'IS' => '',
'JP' => '',
'NO' => '',
'NZ' => '',
'SG' => '',
'ZA' => '',
);
/**
* Names for some US tax names, that needed to mention (because others are just Sales tax)
*
* @var array
*/
public const TAX_REGISTRATION_US_TAX_NAMES = array(
self::DISPLAY_STATE_COMMUNICATIONS_TAX => 'State and Local Communications Tax',
self::DISPLAY_LOCAL_LEASE_TAX_PREFIX . self::FIPS_CHICAGO => 'Chicago Lease Tax',
self::DISPLAY_LOCAL_AMUSEMENT_TAX_PREFIX . self::FIPS_CHICAGO => 'Chicago Amusement Tax',
self::DISPLAY_LOCAL_AMUSEMENT_TAX_PREFIX . self::FIPS_BLOOMINGTON => 'Bloomington Amusement Tax',
self::DISPLAY_LOCAL_AMUSEMENT_TAX_PREFIX . self::FIPS_EAST_DUNDEE => 'East Dundee Amusement Tax',
self::DISPLAY_LOCAL_AMUSEMENT_TAX_PREFIX . self::FIPS_EVANSTON => 'Evanston Amusement Tax',
self::DISPLAY_LOCAL_AMUSEMENT_TAX_PREFIX . self::FIPS_SCHILLER_PARK => 'Schiller Park Streaming Services Surcharge',
'state_sales_tax' => '',
);
/**
* Possible keys of US states tax with names (usually just Sales tax)
*
* @var array
*/
public const TAX_REGISTRATION_US_TAX_TYPES = array(
'state_sales_tax' => 'Sales tax',
'state_communications_tax' => 'Sales tax',
'local_lease_tax' => 'Sales tax',
'local_amusement_tax' => 'Sales tax',
);
/**
* Possible US states tax types
*
* @var array
*/
public const TAX_REGISTRATION_US_TYPE_OF_REGISTRATION = array(
'state_sales_tax' => 'Standard',
'state_communications_tax' => 'Communications Tax',
'local_lease_tax' => 'Lease Tax',
'local_amusement_tax' => 'Amusement Tax',
);
/**
* Names of Canada taxes
*
* @var array
*/
public const TAX_REGISTRATION_CA_TAX_NAMES = array(
'standard' => 'GST/HST',
'simplified' => 'simplified GST/HST',
'province_standard_BC' => 'PST',
'province_standard_MB' => 'PST',
'province_standard_SK' => 'PST',
'province_standard_QC' => 'QST',
);
/**
* Types of taxes in Canada with names
*
* @var array
*/
public const TAX_REGISTRATION_CA_TAX_TYPES = array(
'standard' => 'GST/HST',
'simplified' => 'GST/HST',
'province_standard' => 'GST/HST',
);
/**
* Types of registration in Canada
*
* @var array
*/
public const TAX_REGISTRATION_CA_TYPE_OF_REGISTRATION = array(
'standard' => 'Standard',
'simplified' => 'Simplified GST/HST',
'province_standard' => 'Standard',
);
/**
* Possible EU countries tax names
*
* @var array
*/
public const TAX_REGISTRATION_EU_TAX_NAMES = array(
'standard' => '',
'oss_union' => 'OSS (EU)',
'oss_non_union' => 'OSS (EU)',
'ioss' => 'IOSS (EU)',
);
/**
* Countries with tax type "VAT" and type of registration "Standard"
*
* @var array
*/
public const TAX_REGISTRATION_VAT_STANDARD_COUNTRIES = array(
'AT' => '',
'ES' => '',
'BE' => '',
'BG' => '',
'HR' => '',
'CY' => '',
'CZ' => '',
'EE' => '',
'FI' => '',
'FR' => '',
'DE' => '',
'GR' => '',
'HU' => '',
'IS' => '',
'DK' => '',
'IE' => '',
'IT' => '',
'LV' => '',
'LT' => '',
'LU' => '',
'MT' => '',
'NL' => '',
'NO' => '',
'PL' => '',
'PT' => '',
'RO' => '',
'SG' => '',
'SK' => '',
'SI' => '',
'ZA' => '',
'SE' => '',
'CH' => '',
'AE' => '',
'GB' => '',
);
/**
* Countries with tax type "VAT" and type of registration "Simplified"
*
* @var array
*/
public const TAX_REGISTRATION_VAT_SIMPLIFIED_COUNTRIES = array(
'CL' => '',
'CO' => '',
'ID' => '',
'MX' => '',
'SA' => '',
'KR' => '',
'TH' => '',
'TR' => '',
'VN' => '',
);
/**
* Countries with tax type "GST" and type of registration "Standard"
*
* @var array
*/
public const TAX_REGISTRATION_GST_STANDARD_COUNTRIES = array(
'AU' => '',
'NZ' => '',
);
/**
* Countries with tax type "JCT" and type of registration "Standard"
*
* @var array
*/
public const TAX_REGISTRATION_JCT_STANDARD_COUNTRIES = array(
'JP' => '',
);
/**
* Countries with tax type "Service Tax" and type of registration "Simplified"
*
* @var array
*/
public const TAX_REGISTRATION_SERVICE_TAX_SIMPLIFIED_COUNTRIES = array(
'MY' => '',
);
/**
* List of allowed origin address countries
*
* @var array
*/
public const ORIGIN_ADDRESS_ALLOWED_COUNTRIES = array(
'AE' => '',
'AT' => '',
'AU' => '',
'BE' => '',
'BG' => '',
'CA' => '',
'CH' => '',
'CY' => '',
'CZ' => '',
'DE' => '',
'DK' => '',
'EE' => '',
'ES' => '',
'FI' => '',
'FR' => '',
'GB' => '',
'GR' => '',
'HK' => '',
'HR' => '',
'HU' => '',
'IE' => '',
'IT' => '',
'JP' => '',
'LT' => '',
'LU' => '',
'LV' => '',
'MT' => '',
'NL' => '',
'NO' => '',
'NZ' => '',
'PL' => '',
'PT' => '',
'RO' => '',
'SE' => '',
'SG' => '',
'SI' => '',
'SK' => '',
'US' => '',
);
/**
* List of allowed origin address US states
*
* @var array
*/
public const ORIGIN_ADDRESS_ALLOWED_US_STATES = array(
'AA' => '',
'AE' => '',
'AL' => '',
'AK' => '',
'AP' => '',
'AZ' => '',
'AR' => '',
'CA' => '',
'CO' => '',
'CT' => '',
'DE' => '',
'DC' => '',
'FL' => '',
'GA' => '',
'HI' => '',
'ID' => '',
'IL' => '',
'IN' => '',
'IA' => '',
'KS' => '',
'KY' => '',
'LA' => '',
'ME' => '',
'MD' => '',
'MA' => '',
'MI' => '',
'MN' => '',
'MS' => '',
'MO' => '',
'MT' => '',
'NE' => '',
'NV' => '',
'NH' => '',
'NJ' => '',
'NM' => '',
'NY' => '',
'NC' => '',
'ND' => '',
'OH' => '',
'OK' => '',
'OR' => '',
'PA' => '',
'RI' => '',
'SC' => '',
'SD' => '',
'TN' => '',
'TX' => '',
'UT' => '',
'VT' => '',
'VA' => '',
'WA' => '',
'WV' => '',
'WI' => '',
'WY' => '',
);
/**
* List of allowed origin address United Arab Emirates provinces
*
* @var array
*/
public const ORIGIN_ADDRESS_ALLOWED_AE_PROVINCES = array(
'AZ' => 'Abu Dhabi',
'SH' => 'Sharjah',
'FU' => 'Fujairah',
'UQ' => 'Umm Al Quwain',
'DU' => 'Dubai',
'RK' => 'Ras al Khaimah',
'AJ' => 'Ajman',
);
/**
* ID, used to distinguish, when "customer ID not received" versus "customer ID not exists" (like for guests).
*
* @var string
*/
public const GUEST_CUSTOMER_ID = 'guest';
/**
* Check if country is supported for Stripe Calculate Tax API call
*
* @param string $country ISO 3166-1 alpha-2 country code.
*
* @return bool true if country supported and false if not.
*/
public static function is_country_supported( string $country ): bool {
return array_key_exists( $country, self::CURRENCIES_PER_SUPPORTED_COUNTRY );
}
/**
* Returns supported currencies
*
* @see https://stripe.com/docs/currencies#presentment-currencies
* @return array<string, string> Array of supported currencies (as array key) in ISO 4217 alphabetic code lowercase.
*/
public static function get_all_supported_currencies(): array {
return self::CURRENCIES_ALL_SUPPORTED;
}
/**
* Get supported currencies for the country in ISO 4217 alphabetic code lowercase format
*
* @param string $country ISO 3166-1 alpha-2 country code.
*
* @return array<string, string>
* @throws Exception In case country is not supported.
*/
public static function get_supported_currencies_for_country( string $country ): array {
if ( ! self::is_country_supported( $country ) ) {
throw new Exception( esc_html__( 'Country code not supported', 'stripe-tax-for-woocommerce' ) );
}
return self::CURRENCIES_PER_SUPPORTED_COUNTRY[ $country ];
}
/**
* Check if currency is supported
*
* @param string $currency Currency in ISO 4217 alphabetic code lowercase.
*
* @return bool true if supported and false otherwise
* @see https://stripe.com/docs/currencies#presentment-currencies
*/
public static function is_currency_supported( string $currency ): bool {
return array_key_exists( $currency, self::get_all_supported_currencies() );
}
/**
* Check if currency is zero decimal
*
* @param string $currency Currency in ISO 4217 alphabetic code lowercase.
*
* @return bool true if zero decimal and false otherwise
* @throws Exception If currency is not valid.
* @see https://stripe.com/docs/currencies?presentment-currency=AE#zero-decimal
*/
public static function is_currency_zero_decimal( string $currency ): bool {
Validate::validate_currency( $currency );
return array_key_exists( $currency, self::CURRENCIES_ZERO_DECIMAL );
}
/**
* Check if currency is three decimal
*
* @param string $currency Currency in ISO 4217 alphabetic code lowercase.
*
* @return bool true if three decimal and false otherwise
* @throws Exception In case of incorrect data.
* @see https://stripe.com/docs/currencies#three-decimal
*/
public static function is_currency_three_decimal( string $currency ): bool {
Validate::validate_currency( $currency );
return array_key_exists( $currency, self::CURRENCIES_THREE_DECIMAL );
}
/**
* Check if currency is special case round
*
* @param string $currency Currency in ISO 4217 alphabetic code lowercase.
*
* @return bool true if special rules applied and false otherwise
* @throws Exception If currency is not correct.
* @see https://stripe.com/docs/currencies#special-cases
*/
public static function is_currency_special_case_round_and_multiply_100( string $currency ): bool {
Validate::validate_currency( $currency );
return array_key_exists( $currency, self::CURRENCIES_SPECIAL_CASE_ROUND_AND_MULTIPLY_100 );
}
/**
* Get allowed tax registration countries
*
* @return array
*/
public static function get_allowed_tax_registration_countries(): array {
$all_countries = WC()->countries->get_countries();
return array_intersect_key( $all_countries, self::TAX_REGISTRATION_ALLOWED_COUNTRIES );
}
/**
* Get allowed tax registration US States
*
* @return array
*/
public static function get_allowed_tax_registration_us_states(): array {
$all_us_states = WC()->countries->get_states( 'US' ) ?? array();
return array_intersect_key( $all_us_states, self::TAX_REGISTRATION_ALLOWED_US_STATES );
}
/**
* Get allowed tax registration CA provinces
*
* @return array
*/
public static function get_allowed_tax_registration_ca_provinces(): array {
$all_ca_provinces = WC()->countries->get_states( 'CA' ) ?? array();
return array_intersect_key( $all_ca_provinces, self::TAX_REGISTRATION_ALLOWED_CA_PROVINCES );
}
/**
* Get tax registration US states
*
* @return string[]
*/
public static function get_tax_registration_us_states(): array {
return array_keys( self::TAX_REGISTRATION_ALLOWED_US_STATES );
}
/**
* Get registration no sales tax US states
*
* @return string[]
*/
public static function get_tax_registration_no_sales_tax_us_states(): array {
return array_keys( self::TAX_REGISTRATION_NO_SALES_TAX_US_STATES );
}
/**
* Get tax registration local communications tax US states
*
* @return string[]
*/
public static function get_tax_registration_local_communications_tax_us_states(): array {
return array_keys( self::TAX_REGISTRATION_LOCAL_COMMUNICATIONS_TAX_US_STATES );
}
/**
* Get tax registration lease and amusement tax US states
*
* @return string[]
*/
public static function get_tax_registration_lease_and_amusement_tax_us_states(): array {
return array_keys( self::TAX_REGISTRATION_LEASE_AND_AMUSEMENT_TAX_US_STATES );
}
/**
* Get tax registration digital countries
*
* @return string[]
*/
public static function get_tax_registration_digital_countries(): array {
return array_keys( self::TAX_REGISTRATION_DIGITAL_TAX_COUNTRIES );
}
/**
* Get tax registration EU countries
*
* @return string[]
*/
public static function get_tax_registration_eu_countries(): array {
return array_keys( self::TAX_REGISTRATION_EU_TAX_COUNTRIES );
}
/**
* Get tax registration standard tax countries
*
* @return array
*/
public static function get_tax_registration_standard_tax_countries(): array {
return array_keys( self::TAX_REGISTRATION_STANDARD_TAX_COUNTRIES );
}
/**
* Get tax registration US tax names
*
* @return string[]
*/
public static function get_tax_registration_us_tax_names(): array {
return self::TAX_REGISTRATION_US_TAX_NAMES;
}
/**
* Get tax registration US tax types
*
* @return string[]
*/
public static function get_tax_registration_us_tax_types(): array {
return self::TAX_REGISTRATION_US_TAX_TYPES;
}
/**
* Get tax registration US type of registration
*
* @return string[]
*/
public static function get_tax_registration_us_type_of_registration(): array {
return self::TAX_REGISTRATION_US_TYPE_OF_REGISTRATION;
}
/**
* Get tax registration CA tax names
*
* @return string[]
*/
public static function get_tax_registration_ca_tax_names(): array {
return self::TAX_REGISTRATION_CA_TAX_NAMES;
}
/**
* Get tax registration CA tax types
*
* @return string[]
*/
public static function get_tax_registration_ca_tax_types(): array {
return self::TAX_REGISTRATION_CA_TAX_TYPES;
}
/**
* Get tax registration CA type of registration
*
* @return string[]
*/
public static function get_tax_registration_ca_type_of_registration(): array {
return self::TAX_REGISTRATION_CA_TYPE_OF_REGISTRATION;
}
/**
* Get tax registration flag image file contents for supported Tax Registrations country
*
* @param string $country Country.
*
* @return string
*/
public static function get_tax_registration_flag_image_file_contents( string $country ): string {
$allowed_countries = self::get_allowed_tax_registration_countries();
$file_name = STRIPE_TAX_FOR_WOOCOMMERCE_ASSETS_IMG_DIR . 'tax_registration_flag_' . strtolower( $country ) . '.svg';
if ( array_key_exists( strtoupper( $country ), $allowed_countries ) && file_exists( $file_name ) ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
return file_get_contents( $file_name );
}
return '';
}
/**
* Get tax registration flag EU image file contents
*
* @return string
*/
public static function get_tax_registration_flag_eu_image_file_contents(): string {
$file_name = STRIPE_TAX_FOR_WOOCOMMERCE_ASSETS_IMG_DIR . 'tax_registration_flag_eu.svg';
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
return file_get_contents( $file_name );
}
/**
* Get allowed origin address countries
*
* @return array
*/
public static function get_allowed_origin_address_countries(): array {
$all_countries = WC()->countries->get_countries();
return array_intersect_key( $all_countries, self::ORIGIN_ADDRESS_ALLOWED_COUNTRIES );
}
/**
* Get allowed origin address states for Australia
*
* @return array
*/
public static function get_allowed_origin_address_au_states(): array {
return WC()->countries->get_states( 'AU' ) ?? array();
}
/**
* Get allowed origin address provinces for Canada
*
* @return array
*/
public static function get_allowed_origin_address_ca_provinces(): array {
return WC()->countries->get_states( 'CA' ) ?? array();
}
/**
* Get allowed origin address areas for Hong Kong
*
* @return array
*/
public static function get_allowed_origin_address_hk_areas(): array {
$woo_hk_areas = WC()->countries->get_states( 'HK' );
return array(
'Hong Kong' => $woo_hk_areas['HONG KONG'],
'Kowloon' => $woo_hk_areas['KOWLOON'],
'New Territories' => $woo_hk_areas['NEW TERRITORIES'],
);
}
/**
* Get allowed origin address counties for Ireland
*
* @return array
*/
public static function get_allowed_origin_address_ie_counties(): array {
return WC()->countries->get_states( 'IE' ) ?? array();
}
/**
* Get allowed origin address Italy provinces
*
* @return array
*/
public static function get_allowed_origin_address_it_provinces(): array {
return WC()->countries->get_states( 'IT' ) ?? array();
}
/**
* Get allowed origin address prefectures for Japan
*
* @return array
*/
public static function get_allowed_origin_address_jp_prefectures(): array {
$woo_jp_prefectures = WC()->countries->get_states( 'JP' );
$mapped_for_stripe_jp_prefectures = array();
// Replace from WooCommerce's 'JPXX' array keys to Stripe's 'XX' array keys.
for ( $i = 1; $i <= 47; $i++ ) {
$padded_number = sprintf( '%02d', $i );
$mapped_for_stripe_jp_prefectures[ $padded_number ] = $woo_jp_prefectures[ 'JP' . $padded_number ];
}
return $mapped_for_stripe_jp_prefectures;
}
/**
* Get allowed origin address provinces for Spain
*
* @return array
*/
public static function get_allowed_origin_address_es_provinces(): array {
return WC()->countries->get_states( 'ES' ) ?? array();
}
/**
* Get allowed origin address provinces for AE
*
* @return string[]
*/
public static function get_allowed_origin_address_ae_provinces(): array {
return self::ORIGIN_ADDRESS_ALLOWED_AE_PROVINCES;
}
/**
* Get allowed origin address US states
*
* @return array
*/
public static function get_allowed_origin_address_us_states(): array {
$all_us_states = WC()->countries->get_states( 'US' ) ?? array();
$allowed_us_states = array_intersect_key( $all_us_states, self::ORIGIN_ADDRESS_ALLOWED_US_STATES );
$add_countries_to_us_states = array(
'AS' => '', // American Samoa.
'GU' => '', // Guam.
'MH' => '', // Marshall Islands.
'FM' => '', // Micronesia.
'MP' => '', // Northern Mariana Islands.
'PW' => '', // Palau.
'PR' => '', // Puerto Rico.
'VI' => '', // Virgin Islands (US).
);
$all_countries = WC()->countries->get_countries();
$allowed_added_us_states = array_intersect_key( $all_countries, $add_countries_to_us_states );
$result = array_merge( $allowed_us_states, $allowed_added_us_states );
asort( $result );
return $result;
}
/**
* Get countries, where city field called "Town"
*
* @return string[]
*/
public static function get_city_is_town_or_city_countries(): array {
return array(
'GB' => '',
'NO' => '',
'SE' => '',
);
}
/**
* Get countries, where city field called "District"
*
* @return string[]
*/
public static function get_city_is_district_countries(): array {
return array(
'HK' => '',
);
}
/**
* Get countries where postal code field called "Eircode"
*
* @return string[]
*/
public static function get_postal_code_is_eircode_countries(): array {
return array(
'IE' => '',
);
}
/**
* Get countries where postal code field called "ZIP"
*
* @return string[]
*/
public static function get_postal_code_is_zip_countries(): array {
return array(
'US' => '',
);
}
/**
* Get countries where no city field exist
*
* @return string[]
*/
public static function get_no_city_countries(): array {
return array(
'AE' => '',
'SG' => '',
);
}
/**
* Get countries where no postal code field exist.
*
* @return string[]
*/
public static function get_no_postal_code_countries(): array {
return array(
'AE' => '',
'HK' => '',
);
}
/**
* Checks and fills Stripe Tax Settings object with proper properties
*
* @param mixed $settings Stripe Tax Settings object to check.
*
* @return object Filled with proper fields Stripe Tax object
*/
public static function fill_stripe_tax_settings_object( $settings ): object {
if ( ! is_object( $settings ) ) {
$settings = new \stdClass();
}
if ( ! isset( $settings->defaults ) || ! is_object( $settings->defaults ) ) {
$settings->defaults = new \stdClass();
}
if ( empty( $settings->defaults->tax_code ) || ! is_string( $settings->defaults->tax_code ) ) {
$settings->defaults->tax_code = null;
}
if ( ! isset( $settings->head_office ) || ! is_object( $settings->head_office ) ) {
$settings->head_office = new \stdClass();
}
if ( ! isset( $settings->head_office->address ) || ! is_object( $settings->head_office->address ) ) {
$settings->head_office->address = new \stdClass();
}
// City, country, line1, line2 and postal_code cannot be empty string.
// Regardless if it is an empty string or not a string at all - we make it null.
if ( empty( $settings->head_office->address->city ) || ! is_string( $settings->head_office->address->city ) ) {
$settings->head_office->address->city = null;
}
if ( empty( $settings->head_office->address->country ) || ! is_string( $settings->head_office->address->country ) ) {
$settings->head_office->address->country = null;
}
if ( empty( $settings->head_office->address->line1 ) || ! is_string( $settings->head_office->address->line1 ) ) {
$settings->head_office->address->line1 = null;
}
if ( empty( $settings->head_office->address->line2 ) || ! is_string( $settings->head_office->address->line2 ) ) {
$settings->head_office->address->line2 = null;
}
if ( empty( $settings->head_office->address->postal_code ) || ! is_string( $settings->head_office->address->postal_code ) ) {
$settings->head_office->address->postal_code = null;
}
return $settings;
}
/**
* Get report link
*
* @return string
*/
public static function get_report_link(): string {
return 'https://dashboard.stripe.com/tax/registrations';
}
/**
* Returns html element where, required field needs to be marked (usually by adding asterisk "*" character).
*
* @return string
*/
public static function get_required_field_mark_html(): string {
ob_start();
include STRIPE_TAX_FOR_WOOCOMMERCE_TEMPLATES_PARTS_DIR . 'admin-required-field-mark.php';
return ob_get_clean();
}
/**
* Sets transient data flag on Stripe Settings update error
*
* @return void
*/
public static function set_stripe_settings_update_error_flag(): void {
set_transient( 'stripe_tax_for_woocommerce_settings_update_error_flag', true );
}
/**
* Returns true if Stripe Settings update error occurred last time
*
* @return bool true if Stripe Settings update error occurred last time and false otherwise
*/
public static function get_stripe_settings_update_error_flag(): bool {
$flag = get_transient( 'stripe_tax_for_woocommerce_settings_update_error_flag' );
delete_transient( 'stripe_tax_for_woocommerce_settings_update_error_flag' );
return $flag;
}
/**
* Gets cart shipping methods property.
*
* @param \WC_Cart|\WC_Order $wc_cart_or_order WooCommerce Cart object.
*
* @return array
*/
public static function get_cart_shipping_methods( $wc_cart_or_order ): array {
if ( $wc_cart_or_order instanceof \WC_Cart ) {
$cart_reflection = new \ReflectionObject( $wc_cart_or_order );
$shipping_methods_property = $cart_reflection->getProperty( 'shipping_methods' );
$shipping_methods_property->setAccessible( true );
$value = $shipping_methods_property->getValue( $wc_cart_or_order );
} elseif ( $wc_cart_or_order instanceof \WC_Order ) {
$value = $wc_cart_or_order->get_shipping_methods();
} else {
return array();
}
if ( is_null( $value ) ) {
return array();
}
return $value;
}
/**
* Gets cart or order taxable shipping methods property.
*
* @param \WC_Cart|\WC_Order $wc_cart_or_order WooCommerce Cart object.
*
* @return array
*/
public static function get_cart_or_order_taxable_shipping_methods( $wc_cart_or_order ): array {
$shipping_methods = self::get_cart_shipping_methods( $wc_cart_or_order );
$taxable_shipping_methods = array();
$all_shipping_methods = array();
if ( $wc_cart_or_order instanceof \WC_Cart ) {
$packages = $wc_cart_or_order->get_shipping_packages();
WC()->shipping()->calculate_shipping( $packages );
$all_shipping_methods = WC()->shipping()->load_shipping_methods( isset( $packages[0] ) ? $packages[0] : array() );
} elseif ( $wc_cart_or_order instanceof \WC_Order ) {
WC()->shipping()->load_shipping_methods(
array(
'contents' => array(),
'user' => array(
'ID' => (int) $wc_cart_or_order->get_customer_id( 'edit' ),
),
'destination' => array(
'country' => $wc_cart_or_order->get_shipping_country( 'edit' ),
'state' => $wc_cart_or_order->get_shipping_state( 'edit' ),
'postcode' => $wc_cart_or_order->get_shipping_postcode( 'edit' ),
'city' => $wc_cart_or_order->get_shipping_city( 'edit' ),
'address' => $wc_cart_or_order->get_shipping_address_1( 'edit' ),
'address_1' => $wc_cart_or_order->get_shipping_address_1( 'edit' ), // Provide both address and address_1 for backwards compatibility.
'address_2' => $wc_cart_or_order->get_shipping_address_2( 'edit' ),
),
)
);
$all_shipping_methods = WC()->shipping()->get_shipping_methods();
}
foreach ( $shipping_methods as $shipping_method_key => $shipping_method ) {
if ( ( (int) $shipping_method->get_instance_id() === 0 ) ) {
$taxable_shipping_methods[ $shipping_method_key ] = $shipping_method;
continue;
}
foreach ( $all_shipping_methods as $selectable_shipping_method ) {
if ( $wc_cart_or_order instanceof \WC_Cart ) {
/**
* Types in case of WC_Cart.
*
* @var \WC_Shipping_Rate $shipping_method Shipping rates applied to cart (not provide taxability information).
* @var \WC_Shipping_Method $selectable_shipping_method All selectable shipping methods (provide taxability information)
*/
if ( (int) $shipping_method->get_instance_id() === (int) $selectable_shipping_method->get_instance_id() && $selectable_shipping_method->is_taxable() ) {
if ( ! in_array( $shipping_method, $taxable_shipping_methods, true ) ) {
$taxable_shipping_methods[ $shipping_method_key ] = $shipping_method;
}
}
} elseif ( $wc_cart_or_order instanceof \WC_Order ) {
/**
* Types in case of WC_Order.
*
* @var \WC_Order_Item_Shipping $shipping_method Shipping methods applied to order.
* @var \WC_Shipping_Method $selectable_shipping_method All selectable shipping methods (provide taxability information)
*/
$is_shipping_method_taxable = 'taxable' === $selectable_shipping_method->tax_status;
if ( $is_shipping_method_taxable ) {
$customer_id = $wc_cart_or_order->get_customer_id();
if ( $customer_id ) {
$customer = new \WC_Customer( $customer_id );
if ( $customer->get_id() && $customer->get_is_vat_exempt() ) {
$is_shipping_method_taxable = false;
}
}
}
if ( (int) $shipping_method->get_instance_id() === (int) $selectable_shipping_method->get_instance_id() && $is_shipping_method_taxable ) {
if ( ! in_array( $shipping_method, $taxable_shipping_methods, true ) ) {
$taxable_shipping_methods[ $shipping_method_key ] = $shipping_method;
}
}
}
}
}
return $taxable_shipping_methods;
}
/**
* Get not taxable shipping methods for cart or order.
*
* @param \WC_Cart|\WC_Order $wc_cart_or_order Cart or order.
*
* @return array
*/
public static function get_cart_not_taxable_shipping_methods( $wc_cart_or_order ): array {
$shipping_methods = self::get_cart_shipping_methods( $wc_cart_or_order );
$not_taxable_shipping_methods = array();
$all_shipping_methods = array();
if ( $wc_cart_or_order instanceof \WC_Cart ) {
$packages = $wc_cart_or_order->get_shipping_packages();
WC()->shipping()->calculate_shipping( $packages );
$all_shipping_methods = WC()->shipping()->load_shipping_methods( isset( $packages[0] ) ? $packages[0] : array() );
} elseif ( $wc_cart_or_order instanceof \WC_Order ) {
WC()->shipping()->load_shipping_methods(
array(
'contents' => array(),
'user' => array(
'ID' => (int) $wc_cart_or_order->get_customer_id( 'edit' ),
),
'destination' => array(
'country' => $wc_cart_or_order->get_shipping_country( 'edit' ),
'state' => $wc_cart_or_order->get_shipping_state( 'edit' ),
'postcode' => $wc_cart_or_order->get_shipping_postcode( 'edit' ),
'city' => $wc_cart_or_order->get_shipping_city( 'edit' ),
'address' => $wc_cart_or_order->get_shipping_address_1( 'edit' ),
'address_1' => $wc_cart_or_order->get_shipping_address_1( 'edit' ), // Provide both address and address_1 for backwards compatibility.
'address_2' => $wc_cart_or_order->get_shipping_address_2( 'edit' ),
),
)
);
$all_shipping_methods = WC()->shipping()->get_shipping_methods();
}
foreach ( $shipping_methods as $shipping_method_key => $shipping_method ) {
if ( (int) $shipping_method->get_instance_id() === 0 ) {
continue;
}
foreach ( $all_shipping_methods as $selectable_shipping_method ) {
if ( $wc_cart_or_order instanceof \WC_Cart ) {
/**
* Types in case of WC_Cart.
*
* @var \WC_Shipping_Rate $shipping_method Shipping rates applied to cart (not provide taxability information).
* @var \WC_Shipping_Method $selectable_shipping_method All selectable shipping methods (provide taxability information)
*/
if ( (int) $shipping_method->get_instance_id() === (int) $selectable_shipping_method->get_instance_id() && ! $selectable_shipping_method->is_taxable() ) {
if ( ! in_array( $shipping_method, $not_taxable_shipping_methods, true ) ) {
$not_taxable_shipping_methods[ $shipping_method_key ] = $shipping_method;
}
}
} elseif ( $wc_cart_or_order instanceof \WC_Order ) {
/**
* Types in case of WC_Order.
*
* @var \WC_Order_Item_Shipping $shipping_method Shipping methods applied to order.
* @var \WC_Shipping_Method $selectable_shipping_method All selectable shipping methods (provide taxability information)
*/
$is_shipping_method_taxable = 'taxable' === $selectable_shipping_method->tax_status;
if ( $is_shipping_method_taxable ) {
$customer_id = $wc_cart_or_order->get_customer_id();
if ( $customer_id ) {
$customer = new \WC_Customer( $customer_id );
if ( $customer->get_id() && $customer->get_is_vat_exempt() ) {
$is_shipping_method_taxable = false;
}
}
}
if ( (int) $shipping_method->get_instance_id() === (int) $selectable_shipping_method->get_instance_id() && ! $is_shipping_method_taxable ) {
if ( ! in_array( $shipping_method, $not_taxable_shipping_methods, true ) ) {
$not_taxable_shipping_methods[ $shipping_method_key ] = $shipping_method;
}
}
}
}
}
return $not_taxable_shipping_methods;
}
/**
* Sets cart shipping methods property.
*
* @param \WC_Cart $wc_cart WooCommerce Cart object.
* @param array $shipping_methods Array of shipping methods.
*/
public static function set_cart_shipping_methods( \WC_Cart $wc_cart, array $shipping_methods ) {
$cart_reflection = new \ReflectionObject( $wc_cart );
$shipping_methods_property = $cart_reflection->getProperty( 'shipping_methods' );
$shipping_methods_property->setAccessible( true );
$shipping_methods_property->setValue( $wc_cart, $shipping_methods );
}
/**
* Allowed html tags for escaping.
*/
public static function get_admin_allowed_html() {
$allowed_html = array(
'a' => array(
'href' => true,
'title' => true,
'target' => true,
),
'b' => true,
'strong' => true,
'i' => true,
'em' => true,
'p' => true,
'br' => true,
'ul' => true,
'ol' => array(
'start' => true,
'type' => true,
),
'li' => true,
'h1' => true,
'h2' => true,
'h3' => true,
'h4' => true,
'h5' => true,
'h6' => true,
'img' => array(
'src' => true,
'alt' => true,
'title' => true,
'width' => true,
'height' => true,
),
'span' => array(
'class' => true,
'style' => true,
),
'div' => array(
'class' => true,
'style' => true,
),
'blockquote' => array(
'cite' => true,
),
'code' => true,
'pre' => true,
);
return $allowed_html;
}
/**
* Allowed svg tags for escaping.
*/
public static function get_admin_allowed_svg_tags() {
$allowed_svg = array(
'svg' => array(
'class' => true,
'xmlns' => true,
'xmlns:xlink' => true,
'x' => true,
'y' => true,
'viewbox' => true,
'style' => array(
'type' => true,
'enable-background' => true,
'position' => true,
'top' => true,
'width' => true,
'height' => true,
),
'xml:space' => true,
),
'style' => array(
'type' => true,
),
'g' => true,
'path' => array(
'class' => true,
'd' => true,
),
'polygon' => array(
'class' => true,
'points' => true,
),
'rect' => array(
'x' => true,
'y' => true,
'class' => true,
'width' => true,
'height' => true,
),
);
return $allowed_svg;
}
/**
* Function does exit. Useful, when PHPUnit unit test needs to check "exit" calls.
*
* @return void
*/
public static function do_exit() {
exit;
}
/**
* Normalize Stripe API error into user-friendly message
*
* @param mixed $err Error.
*
* @return string
*/
public static function format_api_error_message( $err ): string {
list( $message, $code, $param ) = static::get_stripe_error_details( $err );
if ( 'parameter_missing' === $code && 'line_items' === $param ) {
return __( 'Please add at least one line item before calculating taxes.', 'stripe-tax-for-woocommerce' );
}
if ( 'parameter_invalid_integer' === $code || false !== stripos( $message, 'Invalid non-negative integer' ) ) {
return __( 'Please enter a valid non-negative amount before calculating taxes.', 'stripe-tax-for-woocommerce' );
}
if ( $err instanceof ApiErrorException ) {
return __( 'A problem occurred: invalid request.', 'stripe-tax-for-woocommerce' );
}
return $message;
}
/**
* Extracts message, code, and param from a Stripe API error
*
* @param mixed $err Error.
*
* @return array Array with message, code and parameter.
*/
protected static function get_stripe_error_details( $err ): array {
$message = $err instanceof \Throwable ? $err->getMessage() : (string) $err;
if ( ! ( $err instanceof ApiErrorException ) ) {
return array( $message, null, null );
}
$error = $err->getError();
$code = ( $error && isset( $error->code ) && is_string( $error->code ) ) ? $error->code : null;
$param = ( $error && isset( $error->param ) && is_string( $error->param ) ) ? $error->param : null;
if ( $error && isset( $error->message ) && is_string( $error->message ) ) {
$message = $error->message;
}
$code = $code ?? ( method_exists( $err, 'getStripeCode' ) ? $err->getStripeCode() : null );
$param = $param ?? ( $err instanceof InvalidRequestException ? $err->getStripeParam() : null );
return array( $message, $code, $param );
}
}