DEV Community

kevin
kevin

Posted on

Stop Hardcoding 5-Digit ZIP Codes: How We Test Multi-Country Checkout & Tax-Free Edge Cases

Nothing hurts a checkout conversion rate quite like a validation error telling a London customer that SW1A 1AA isn't a valid ZIP code.

If you've worked on e-commerce checkouts or SaaS onboarding flows, you've probably seen forms that secretly assume every user lives in a 5-digit US ZIP code with a standard 123 Main St layout.

Testing address validation with real customer data is a privacy landmine under GDPR, while hardcoding basic dummy strings like "Test St" leaves your staging environment blind to real-world edge cases.

Here is a breakdown of the 4 most common international address bugs we've caught in production, along with a practical E2E testing workflow using Playwright.


1. The "5-Digit ZIP" Trap

Assuming every postal code is a 5-digit integer is probably the #1 internationalization bug on the web.

  • UK Postcodes: Alphanumeric with spaces (SW1A 1AA, EC1A 1BB).
  • Canadian Postal Codes: 6-character alternating alpha-numeric (K1A 0B1).
  • Japanese Postcodes: 7 digits separated by a hyphen (100-0001).
  • Hong Kong & UAE: They don't use postal codes at all. Mandatory ZIP fields will literally block entire countries from completing checkout.

💡 Pro-tip for QA: When switching country selectors in your frontend, ensure your validation schema (whether using Zod, Yup, or custom regex) dynamically adjusts both required rules and input masks.


2. Street Name vs House Number Ordering

In North America, address layouts almost always start with the house number followed by the street name (122 Mitte St).

In continental Europe—especially Germany, Austria, and the Netherlands—the convention is reversed:

German Standard:  Hauptstraße 12
US Standard:      122 Mitte St
Enter fullscreen mode Exit fullscreen mode

If your database or backend API parses street addresses by splitting strings on the first space, a German address like Bäckerstraße 4B will store Bäckerstraße as the number and 4B as the street name.

Always test how your shipping labels, invoice PDFs, and CRM integrations render non-ASCII characters (ä, ö, ü, ß) and reversed address structures.


3. Testing 0% Sales Tax Edge Cases (The 5 US Tax-Free States)

If your checkout integrates a tax engine (like Stripe Tax, TaxJar, or Avalara), you need to verify that orders shipped to US states without a statewide sales tax calculate a $0.00 tax line item.

The 5 tax-free US states are:

  1. Oregon (OR)
  2. Delaware (DE)
  3. Montana (MT)
  4. New Hampshire (NH)
  5. Alaska (AK) (Note: Alaska has no state sales tax, but some local municipalities impose local taxes)

Instead of manually guessing valid ZIP codes for Oregon or Delaware during testing, we generate synthetic address datasets using AddressLab's Tax-Free State Generator or pull random US format samples via their US Address Generator.


4. Building a Robust Playwright E2E Test

Here’s how we structure an automated Playwright test in TypeScript to validate tax-free checkout logic and address card rendering:

import { test, expect } from '@playwright/test';

// Synthetic Oregon address record for zero-tax verification
const mockTaxFreeAddress = {
  firstName: 'Jordan',
  lastName: 'Miller',
  street: '1050 SW 6th Ave',
  city: 'Portland',
  state: 'OR',
  zip: '97204',
  country: 'US',
};

test.describe('Checkout Address & Tax Calculation', () => {
  test('should render $0.00 sales tax for Oregon shipping address', async ({ page }) => {
    await page.goto('/checkout');

    // Populate shipping address form
    await page.fill('input[name="firstName"]', mockTaxFreeAddress.firstName);
    await page.fill('input[name="lastName"]', mockTaxFreeAddress.lastName);
    await page.fill('input[name="address1"]', mockTaxFreeAddress.street);
    await page.fill('input[name="city"]', mockTaxFreeAddress.city);
    await page.selectOption('select[name="state"]', mockTaxFreeAddress.state);
    await page.fill('input[name="zip"]', mockTaxFreeAddress.zip);

    // Trigger tax calculation blur event
    await page.dispatchEvent('input[name="zip"]', 'change');

    // Assert sales tax calculates as $0.00
    const taxElement = page.locator('.checkout-summary__tax-value');
    await expect(taxElement).toHaveText('$0.00');

    // Assert full address renders correctly on review card
    const reviewCard = page.locator('.address-review-card');
    await expect(reviewCard).toContainText('1050 SW 6th Ave, Portland, OR 97204');
  });
});
Enter fullscreen mode Exit fullscreen mode

For international testing (e.g., verifying German PLZ formats or Japanese prefecture selectors), we also test with localized formats from Germany Address Generator and Japan Address Generator.


Summary Checklist for International Address Testing

Before shipping a checkout or registration form to production, run through this quick checklist:

  • [ ] Does the postal code field handle spaces and letters (UK/Canada)?
  • [ ] Is postal code optional when Hong Kong or UAE is selected?
  • [ ] Do UTF-8 characters (ä, ö, ü, ñ, 漢字) display correctly on invoice receipts?
  • [ ] Does the tax calculation engine accurately return $0.00 for Oregon, Delaware, and Montana?
  • [ ] Does long text wrap cleanly on mobile screens without overflowing container bounds?

What's the weirdest internationalization or address validation bug you've ran into in production? Let me know in the comments below!

Top comments (0)