This repository has been archived on 2026-03-21. You can view files and clone it, but cannot push or open issues or pull requests.
payfrit-biz/api/setup/lookupTaxRate.cfm
John Mizerek 3cd7bbb8b7 Fix tax rate lookup and add price extraction from __OO_STATE__
- Tax rate: Use Zippopotam (free, no key) to get state, then lookup
  from built-in state+local rate tables instead of API Ninjas
- Prices: Extract prices from Toast __OO_STATE__ MenuItem objects
  when visible HTML prices are missing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-13 12:05:24 -08:00

94 lines
3.7 KiB
Text

<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfheader name="Cache-Control" value="no-store">
<cfset response = { "OK": false, "taxRate": 0, "message": "", "state": "" }>
<!--- State base sales tax rates (2024) - local rates may add 1-5% more --->
<cfset stateTaxRates = {
"AL": 4, "AK": 0, "AZ": 5.6, "AR": 6.5, "CA": 7.25,
"CO": 2.9, "CT": 6.35, "DE": 0, "FL": 6, "GA": 4,
"HI": 4, "ID": 6, "IL": 6.25, "IN": 7, "IA": 6,
"KS": 6.5, "KY": 6, "LA": 4.45, "ME": 5.5, "MD": 6,
"MA": 6.25, "MI": 6, "MN": 6.875, "MS": 7, "MO": 4.225,
"MT": 0, "NE": 5.5, "NV": 6.85, "NH": 0, "NJ": 6.625,
"NM": 4.875, "NY": 4, "NC": 4.75, "ND": 5, "OH": 5.75,
"OK": 4.5, "OR": 0, "PA": 6, "RI": 7, "SC": 6,
"SD": 4.2, "TN": 7, "TX": 6.25, "UT": 6.1, "VT": 6,
"VA": 5.3, "WA": 6.5, "WV": 6, "WI": 5, "WY": 4, "DC": 6
}>
<!--- Estimated average local rate addition by state (rough estimates) --->
<cfset stateLocalAdd = {
"AL": 5.2, "AZ": 2.8, "AR": 3, "CA": 1.5, "CO": 4.8,
"GA": 4, "IL": 2.5, "KS": 2.5, "LA": 5.5, "MO": 4,
"NV": 1.4, "NM": 2.7, "NY": 4.5, "NC": 2.3, "OH": 1.5,
"OK": 4.5, "SC": 2, "SD": 2, "TN": 2.5, "TX": 2,
"UT": 1.3, "WA": 3, "WI": 0.6
}>
<cftry>
<!--- Get ZIP code from URL or form --->
<cfset zipCode = "">
<cfif structKeyExists(url, "zip")>
<cfset zipCode = trim(url.zip)>
<cfelseif structKeyExists(form, "zip")>
<cfset zipCode = trim(form.zip)>
</cfif>
<!--- Validate ZIP code format (5 digits or 5+4) --->
<cfif NOT reFind("^\d{5}(-\d{4})?$", zipCode)>
<cfset response.message = "Invalid ZIP code format">
<cfoutput>#serializeJSON(response)#</cfoutput>
<cfabort>
</cfif>
<!--- Use just the 5-digit portion --->
<cfset zipCode = left(zipCode, 5)>
<!--- Use Zippopotam.us to get state from ZIP (free, no API key) --->
<cfhttp url="https://api.zippopotam.us/us/#zipCode#" method="GET" timeout="10" result="httpResult">
</cfhttp>
<cfif httpResult.statusCode CONTAINS "200">
<cfset zipData = deserializeJSON(httpResult.fileContent)>
<cfif structKeyExists(zipData, "places") AND isArray(zipData.places) AND arrayLen(zipData.places) GT 0>
<cfset place = zipData.places[1]>
<cfset stateAbbr = uCase(place["state abbreviation"])>
<cfset response.state = stateAbbr>
<!--- Look up state base rate --->
<cfif structKeyExists(stateTaxRates, stateAbbr)>
<cfset baseRate = stateTaxRates[stateAbbr]>
<!--- Add estimated local rate if available --->
<cfset localAdd = 0>
<cfif structKeyExists(stateLocalAdd, stateAbbr)>
<cfset localAdd = stateLocalAdd[stateAbbr]>
</cfif>
<cfset totalRate = baseRate + localAdd>
<!--- Round to 2 decimal places --->
<cfset response.taxRate = round(totalRate * 100) / 100>
<cfset response.OK = true>
<cfset response.stateRate = baseRate>
<cfset response.localRate = localAdd>
<cfset response.message = "Estimated rate for #place['place name']#, #stateAbbr#">
<cfelse>
<cfset response.message = "Unknown state: #stateAbbr#">
</cfif>
<cfelse>
<cfset response.message = "No location data for ZIP">
</cfif>
<cfelse>
<cfset response.message = "ZIP lookup failed: #httpResult.statusCode#">
</cfif>
<cfcatch type="any">
<cfset response.message = "Error: #cfcatch.message#">
</cfcatch>
</cftry>
<cfoutput>#serializeJSON(response)#</cfoutput>