/// <summary>
        /// Gets a tax rate
        /// </summary>
        /// <param name="province">province</param>
        /// <param name="UserID">UserID</param>
        /// <param name="Password">Password</param>
        /// <param name="Error">Error</param>
        /// <returns>Tax rate</returns>
        public decimal GetTaxRateCanada(string province, string UserID, string Password, ref string Error)
        {
            decimal result = decimal.Zero;
            string key = string.Format(TAXRATECANADA_KEY, province);
            object obj2 = NopCache.Get(key);
            if (StrikeIronTaxProvider.CacheEnabled && (obj2 != null))
            {
                return (decimal)obj2;
            }

            try
            {
                NopSolutions.NopCommerce.Tax.TaxDataBasic.TaxDataBasic taxService = new NopSolutions.NopCommerce.Tax.TaxDataBasic.TaxDataBasic();
                taxService.LicenseInfoValue = new LicenseInfo();
                taxService.LicenseInfoValue.RegisteredUser = new RegisteredUser();
                taxService.LicenseInfoValue.RegisteredUser.UserID = UserID;
                taxService.LicenseInfoValue.RegisteredUser.Password = Password;

                /// The GetTaxRateCanada operation can now be called.  The output type for this operation is SIWSOutputOfTaxRateCanadaData.
                /// Note that for simplicity, there is no error handling in this sample project.  In a production environment, any
                /// web service call should be encapsulated in a try-catch block.
                /// 
                SIWsOutputOfTaxRateCanadaData wsOutput = taxService.GetTaxRateCanada(province);

                /// The output objects of this StrikeIron web service contains two sections: ServiceStatus, which stores data
                /// indicating the success/failure status of the the web service request; and ServiceResult, which contains the
                /// actual data returne as a result of the request.
                /// 
                /// ServiceStatus contains two elements - StatusNbr: a numeric status code, and StatusDescription: a string
                /// describing the status of the output object.  As a standard, you can apply the following assumptions for the value of
                /// StatusNbr:
                ///   200-299: Successful web service call (data found, etc...)
                ///   300-399: Nonfatal error (No data found, etc...)
                ///   400-499: Error due to invalid input
                ///   500+: Unexpected internal error; contact [email protected]
                ///     
                if ((wsOutput.ServiceStatus.StatusNbr >= 200) && (wsOutput.ServiceStatus.StatusNbr < 300))
                {
                    //Successfully called SalesTax service...
                    StringBuilder sb = new StringBuilder();
                    sb.AppendLine("Abbreviation: " + wsOutput.ServiceResult.Abbreviation);
                    sb.AppendLine("GST: " + wsOutput.ServiceResult.GST.ToString());
                    sb.AppendLine("province: " + wsOutput.ServiceResult.Province);
                    sb.AppendLine("PST: " + wsOutput.ServiceResult.PST.ToString());
                    sb.AppendLine("tax_shipping_handling: " + wsOutput.ServiceResult.TaxShippingHandling);
                    sb.AppendLine("total: " + wsOutput.ServiceResult.Total.ToString());
                    string debug = sb.ToString();
                    result = Convert.ToDecimal(wsOutput.ServiceResult.Total);
                }
                else
                {
                    // StrikeIron does not return SoapFault for invalid data when it cannot find a zipcode. 
                    Error = String.Format("[{0}] - {1}", wsOutput.ServiceStatus.StatusNbr.ToString(), wsOutput.ServiceStatus.StatusDescription);
                }
            }
            catch (Exception ex)
            {
                Error = ex.Message;
            }

            if (StrikeIronTaxProvider.CacheEnabled)
            {
                NopCache.Max(key, result);
            }

            return result;
        }
        /// <summary>
        /// Gets a tax rate
        /// </summary>
        /// <param name="province">province</param>
        /// <param name="userID">UserID</param>
        /// <param name="password">Password</param>
        /// <param name="error">Error</param>
        /// <returns>Tax rate</returns>
        public decimal GetTaxRateCanada(string province, string userID,
                                        string password, ref string error)
        {
            decimal result = decimal.Zero;
            string  key    = string.Format(TAXRATECANADA_KEY, province);
            object  obj2   = NopStaticCache.Get(key);

            if (StrikeIronTaxProvider.CacheEnabled && (obj2 != null))
            {
                return((decimal)obj2);
            }

            try
            {
                NopSolutions.NopCommerce.Tax.TaxDataBasic.TaxDataBasic taxService = new NopSolutions.NopCommerce.Tax.TaxDataBasic.TaxDataBasic();
                taxService.LicenseInfoValue = new LicenseInfo();
                taxService.LicenseInfoValue.RegisteredUser          = new RegisteredUser();
                taxService.LicenseInfoValue.RegisteredUser.UserID   = userID;
                taxService.LicenseInfoValue.RegisteredUser.Password = password;

                // The GetTaxRateCanada operation can now be called.  The output type for this operation is SIWSOutputOfTaxRateCanadaData.
                // Note that for simplicity, there is no error handling in this sample project.  In a production environment, any
                // web service call should be encapsulated in a try-catch block.
                //
                var wsOutput = taxService.GetTaxRateCanada(province);

                // The output objects of this StrikeIron web service contains two sections: ServiceStatus, which stores data
                // indicating the success/failure status of the the web service request; and ServiceResult, which contains the
                // actual data returne as a result of the request.
                //
                // ServiceStatus contains two elements - StatusNbr: a numeric status code, and StatusDescription: a string
                // describing the status of the output object.  As a standard, you can apply the following assumptions for the value of
                // StatusNbr:
                //   200-299: Successful web service call (data found, etc...)
                //   300-399: Nonfatal error (No data found, etc...)
                //   400-499: Error due to invalid input
                //   500+: Unexpected internal error; contact [email protected]
                //
                if ((wsOutput.ServiceStatus.StatusNbr >= 200) && (wsOutput.ServiceStatus.StatusNbr < 300))
                {
                    //Successfully called SalesTax service...
                    var sb = new StringBuilder();
                    sb.AppendLine("Abbreviation: " + wsOutput.ServiceResult.Abbreviation);
                    sb.AppendLine("GST: " + wsOutput.ServiceResult.GST.ToString());
                    sb.AppendLine("province: " + wsOutput.ServiceResult.Province);
                    sb.AppendLine("PST: " + wsOutput.ServiceResult.PST.ToString());
                    sb.AppendLine("tax_shipping_handling: " + wsOutput.ServiceResult.TaxShippingHandling);
                    sb.AppendLine("total: " + wsOutput.ServiceResult.Total.ToString());
                    string debug = sb.ToString();
                    result = Convert.ToDecimal(wsOutput.ServiceResult.Total);
                }
                else
                {
                    // StrikeIron does not return SoapFault for invalid data when it cannot find a zipcode.
                    error = String.Format("[{0}] - {1}", wsOutput.ServiceStatus.StatusNbr.ToString(), wsOutput.ServiceStatus.StatusDescription);
                }
            }
            catch (Exception ex)
            {
                error = ex.Message;
            }

            if (StrikeIronTaxProvider.CacheEnabled)
            {
                NopStaticCache.Max(key, result);
            }

            return(result);
        }