mirror of
https://github.com/supabase/supabase.git
synced 2026-10-12 04:45:08 +03:00
### Summary This PR adds support for Isle of Man customers to use GB VAT tax IDs. Isle of Man uses the UK VAT system, so Orb requires country: 'GB' for validation - but the billing address country is IM. - Adds an IM VAT entry to `TAX_IDS` with a new `taxCountryIso2` override field, so the UI shows "IM VAT" while sending country: 'GB' to Orb - Introduces `getEffectiveTaxCountry()` to resolve the correct country code for Orb - Introduces `resolveStoredTaxId()` to map stored Orb data back to the correct UI entry, using billing address country to disambiguate IM vs GB - Updates all three billing forms to use these utilities: Billing Settings page, Add Payment Method modal and Update Billing Address modal ### Testing #### Automated tests I've added unit tests in this PR #### Manual tests You can use the following country/city/postal code combinations to test: - Isle of Man, Douglas, IM1 2LE - United Kingdom, Crumlin, BT29 4AA **Billing Settings page** - Select Isle of Man as billing country → tax ID dropdown shows "Isle of Man - IM VAT" - Select United Kingdom as billing country → tax ID dropdown shows "United Kingdom - UK VAT" / "United Kingdom - GB VAT" (not "IM VAT") - Select IM VAT, enter a valid GB VAT number, save → check network tab, Orb receives country: 'GB' - Reload page for an IM customer with existing tax ID → "IM VAT" is pre-selected (not "UK VAT") - Reload page for a GB customer with existing tax ID → "UK VAT" is pre-selected (no regression) - Change country from IM to GB → available tax IDs update, IM VAT no longer shown **Add Payment Method modal** - Open modal for an IM customer with existing tax ID → "IM VAT" is pre-selected - Check "I'm purchasing as a business", select Isle of Man in Stripe address → tax ID dropdown shows "IM VAT" - Check "I'm purchasing as a business", select United Kingdom in Stripe address → tax ID dropdown shows "UK VAT" / "GB VAT" - Submit with IM VAT → check network tab, tax ID is saved with country: 'GB' - Open modal for a GB customer → no regression, "UK VAT" shown as before **Update Billing Address modal** Appears for paid orgs missing a billing address - Open modal for an IM customer with existing tax ID → "IM VAT" is pre-selected - Open modal for a GB customer with existing tax ID → "UK VAT" is pre-selected - Fill in IM address + IM VAT and save → saves correctly, modal closes --------- Co-authored-by: Ignacio Dobronich <ignacio@dobronich.com>
90 lines
3.0 KiB
TypeScript
90 lines
3.0 KiB
TypeScript
import { TAX_IDS } from 'components/interfaces/Organization/BillingSettings/BillingCustomerData/TaxID.constants'
|
|
import {
|
|
getEffectiveTaxCountry,
|
|
resolveStoredTaxId,
|
|
sanitizeTaxIdValue,
|
|
} from 'components/interfaces/Organization/BillingSettings/BillingCustomerData/TaxID.utils'
|
|
import { describe, test, expect } from 'vitest'
|
|
|
|
/**
|
|
* We're sanitizing EU tax ids. Stripe expects a prefixed tax id (ATU12345678),
|
|
* but users might not realize this and enter only the numbers (12345678) without the
|
|
* country code prefix.
|
|
*
|
|
* Our sanitize function should handle 3 cases:
|
|
* 1. take an un-prefixed tax id (12345678) and add the country prefix to it (ATU12345678)
|
|
* 2. take a correctly prefixed tax id (ATU12345678) and just pass it through
|
|
* 3. take a non-EU tax id and just pass it through
|
|
*/
|
|
describe('TaxID utils: sanitizeTaxID', () => {
|
|
test('should prefix an EU tax ID correctly', () => {
|
|
const austriaTaxID = {
|
|
id: 'txi_1LoPlYJDPojXS6LNVd1FOTk2',
|
|
type: 'eu_vat',
|
|
value: '12345678',
|
|
name: 'AT VAT',
|
|
}
|
|
|
|
const sanitizedID = sanitizeTaxIdValue(austriaTaxID)
|
|
expect(sanitizedID).toBe('ATU12345678')
|
|
})
|
|
|
|
test('should check that EU prefix is correct', () => {
|
|
const austriaTaxID = {
|
|
id: 'txi_1LoPlYJDPojXS6LNVd1FOTk2',
|
|
type: 'eu_vat',
|
|
value: 'ATU12345678',
|
|
name: 'AT VAT',
|
|
}
|
|
|
|
const sanitizedID = sanitizeTaxIdValue(austriaTaxID)
|
|
expect(sanitizedID).toBe('ATU12345678')
|
|
})
|
|
|
|
test('should not prefix an non-EU tax ID', () => {
|
|
const unitedStatesID = {
|
|
id: 'txi_1LoPlYJDPojXS6LNVd1FOTk2',
|
|
type: 'us_ein',
|
|
value: '12-3456789',
|
|
name: 'US EIN',
|
|
}
|
|
|
|
const sanitizedID = sanitizeTaxIdValue(unitedStatesID)
|
|
expect(sanitizedID).toBe('12-3456789')
|
|
})
|
|
})
|
|
|
|
describe('TaxID utils: getEffectiveTaxCountry', () => {
|
|
test('returns countryIso2 when no override is set', () => {
|
|
const ukVat = TAX_IDS.find((t) => t.name === 'UK VAT')!
|
|
expect(getEffectiveTaxCountry(ukVat)).toBe('GB')
|
|
})
|
|
|
|
test('returns taxCountryIso2 when override is set', () => {
|
|
const imVat = TAX_IDS.find((t) => t.name === 'IM VAT')!
|
|
expect(getEffectiveTaxCountry(imVat)).toBe('GB')
|
|
})
|
|
})
|
|
|
|
describe('TaxID utils: resolveStoredTaxId', () => {
|
|
test('resolves GB customer to UK VAT with billingCountry', () => {
|
|
const ukVat = TAX_IDS.find((t) => t.name === 'UK VAT')!
|
|
expect(resolveStoredTaxId('gb_vat', 'GB', 'GB')).toBe(ukVat)
|
|
})
|
|
|
|
test('resolves IM customer to IM VAT with billingCountry', () => {
|
|
const imVat = TAX_IDS.find((t) => t.name === 'IM VAT')!
|
|
expect(resolveStoredTaxId('gb_vat', 'GB', 'IM')).toBe(imVat)
|
|
})
|
|
|
|
test('resolves GB customer to UK VAT without billingCountry', () => {
|
|
// Without billingCountry, falls back to countryIso2 match — UK VAT has countryIso2 'GB'
|
|
const ukVat = TAX_IDS.find((t) => t.name === 'UK VAT')!
|
|
expect(resolveStoredTaxId('gb_vat', 'GB')).toBe(ukVat)
|
|
})
|
|
|
|
test('returns undefined for unknown type', () => {
|
|
expect(resolveStoredTaxId('unknown_type', 'XX')).toBeUndefined()
|
|
})
|
|
})
|