Issue: Excise Tax Engine — Location Normalization & missingData Fix
Business Summary
What happened? Starting around Feb 9, 2026, two issues in the excise tax engine could cause $0 tax on some orders that should have been taxed: products with incomplete catalog metadata were being silently exempted, and minor differences in how location names are formatted — capitalization, extra spaces, or suffixes like "County" and "Borough" — could prevent tax rules from matching either related to entry in ecommerce stores or on our HQ screen.
What's the impact? The impact of the metadata issue is tightly restricted to products with partial data, which is rare since products tend to have all their metadata or none, and further limited to jurisdictions with rules that could have been fulfilled with the data provided. The location matching issue is similarly narrow — it only surfaces when there's a formatting discrepancy between the rule definition and the incoming address data (AFTER normalization) which appears to have been possible with City fields in Shopify and on our own HQ screen by entering CHICAGO vs Chicago for example.
What's the change? Going forward, the tax engine calculates on a best-effort basis — products are taxed whenever the data a rule needs is present, regardless of other missing fields. Location matching is now resilient to capitalization, whitespace, and common geographic suffixes. Conflicting rule entries are caught at startup rather than silently applying the wrong rate.
Problem
Two issues were causing incorrect tax calculations in production:
- Case/whitespace sensitivity on location lookups. County and locality rule lookups used exact string matching (
rule[locationElement]). A mismatch in case, whitespace, or county-equivalent suffix between the rule spec key and the incoming location — whether from a geocoding provider, HQ input, directly from an ecommerce store or API consumer — would cause the lookup to miss and no tax to be applied. For example,"boulder"vs"Boulder"," Chicago "vs"Chicago", or"Cook County"vs"Cook". missingDataproducts treated as tax-exempt. Commit61dec3afd(Feb 5, 2026) addedproduct.missingData === truetoproductIsExempt(). Products failing strict validation (e.g., pods/coils missingvolumeInMl) were blanket-exempted from all tax — even when the applicable rule only neededretailPrice, which was present. This caused vape SKUs across multiple states to stop being taxed starting ~Feb 9.
Changes
Branch: issue/6705-giantvapescom-discrepancies-on-order-129964
Files: 3 changed (+307, -29)
Location key normalization (ExciseTaxService.node.js)
- Introduced
normalizeKey()— collapses all whitespace (including\u00A0,\u200B,\uFEFF), trims, and uppercases. Applied at both storage time (doInitializeRules) and lookup time (findRegionCodeRules,normalizeLocation) across all hierarchy levels: country code, region code, county, and locality. - Introduced
stripCountySuffix()— removes county-equivalent suffixes (County,Parish,Borough,Municipality,Census Area) so that"Cook County"and"Cook"both resolve to the same rule. Applied to both rule spec keys and incoming location data. - Added collision detection at init time — if two rule spec keys normalize to the same value (e.g.,
"Boulder"and"BOULDER"), the service throws immediately rather than silently shadowing one.
Remove missingData exemption (ExciseTaxService.node.js)
- Removed
product.missingData === truefromproductIsExempt(). Products with incomplete metadata now proceed to rule evaluation. The existing per-rule try/catch handles attribute-level failures — rules needing a missing attribute record an error while rules that don't still calculate tax (best-effort).
Risk
- Jurisdiction rule paths (e.g.,
US.regionCode.AK.county.Juneau) now use normalized keys (US.regionCode.AK.county.JUNEAU). These paths appear in verbose API responses as thejurisdictionRulefield. They are not persisted to any database or used as external identifiers. Tax labels (e.g., "Juneau Excise Tax") are unaffected — they use the original rule spec casing. - missingData products will now be taxed where rules can execute. This is the intended correction. Products that truly can't be taxed (all required attributes missing) will have errors recorded on the tax line rather than being silently exempted.
Test Coverage
- 25 Jest tests (3 new: case-insensitive match, whitespace match, collision detection; 3 new: missingData best-effort)
- 108 nodeunit assertions (18 updated jurisdiction path expectations)