Expanding a SaaS product into global markets requires more than translation — it demands infrastructure changes (edge deployment for low latency), compliance adaptation (GDPR, PIPL, CCPA), payment localization (Alipay, SEPA, Stripe), and cultural UX adjustments. This guide breaks down the technical and business preparation needed for the three largest SaaS markets: the US, Europe, and China. Each section includes actionable code examples, configuration snippets, and deployment patterns you can implement immediately. See the architecture in action at tanstackship.com, a production SaaS shipping globally on Cloudflare Workers.
Why Globalization Must Be Built, Not Bolted On
The biggest mistake SaaS founders make with globalization is treating it as a phase-two feature. Retrofitting i18n, multi-currency pricing, and regional compliance onto a monolithic codebase typically costs 3-5x more than building it in from the start — and often requires a full rewrite of the database schema, routing layer, and deployment pipeline.
The three largest SaaS markets have fundamentally different requirements:
| Requirement | US Market | European Market | Chinese Market |
|---|---|---|---|
| Language | English | Multi (DE, FR, ES, etc.) | Chinese (Simplified) |
| Data regulation | CCPA/State laws | GDPR (strictest) | PIPL + CSL |
| Payment methods | Credit cards, Stripe | SEPA, PayPal, credit cards | Alipay, WeChat Pay, UnionPay |
| Infrastructure | AWS/US-East | GDPR-compliant hosting | ICP license, China-hosted |
| Authentication | Google, GitHub | EU-specific (eIDAS) | WeChat, Alipay, phone SMS |
| Pricing display | $ (tax excl.) | € (VAT incl.) | ¥ (tax incl.) |
| Content delivery | Global CDN | Edge (300ms latency target) | China CDN (ICP + DDoS shield) |
| Social login | Google, Apple, GitHub | Google, Apple, LinkedIn | WeChat, Alipay, phone OTP |
| Email deliverability | SendGrid, AWS SES | SendGrid (DPA signed) | QQ Mail, Alibaba Direct Mail |
Market 1: European Market Preparation
GDPR Compliance Checklist
GDPR applies to any SaaS processing data of EU residents — regardless of where your company is incorporated. Here is the technical implementation:
// src/lib/gdpr.ts
// GDPR-compliant data handling utilities
// 1. Explicit consent tracking
interface ConsentRecord {
userId: string
consentType: "analytics" | "marketing" | "functional" | "third_party"
granted: boolean
timestamp: number
ipAddress: string // Anonymized
}
export async function recordConsent(
userId: string,
consentType: ConsentRecord["consentType"],
granted: boolean
) {
await env.DB.prepare(
`INSERT INTO consent_records (user_id, consent_type, granted, timestamp, ip_address)
VALUES (?, ?, ?, unixepoch(), ?)`
).bind(
userId,
consentType,
granted ? 1 : 0,
anonymizeIp(getClientIp())
).run()
}
// 2. Data portability export
export const exportUserData = createServerFn({ method: "GET" }).handler(
async (_, { request }) => {
const userId = await getUserId(request)
const [profile, subscriptions, invoices, events] = await Promise.all([
env.DB.prepare("SELECT * FROM users WHERE id = ?").bind(userId).first(),
env.DB.prepare("SELECT * FROM subscriptions WHERE user_id = ?").bind(userId).all(),
env.DB.prepare("SELECT * FROM invoices WHERE user_id = ?").bind(userId).all(),
env.DB.prepare("SELECT * FROM analytics_events WHERE user_id = ?").bind(userId).all(),
])
return Response.json({
exportedAt: new Date().toISOString(),
data: { profile, subscriptions, invoices, events },
})
}
)
// 3. Right to erasure (GDPR Article 17)
export const deleteUserData = createServerFn({ method: "POST" }).handler(
async (_, { request }) => {
const userId = await getUserId(request)
// Anonymize personal data (keep transactional records for legal retention)
await env.DB.batch([
env.DB.prepare(`UPDATE users SET
email = 'deleted-' || id || '@redacted.com',
name = 'Deleted User',
avatar_url = NULL,
deleted_at = unixepoch()
WHERE id = ?`).bind(userId),
env.DB.prepare("DELETE FROM analytics_events WHERE user_id = ?").bind(userId),
env.DB.prepare("DELETE FROM session_tokens WHERE user_id = ?").bind(userId),
])
return { success: true }
}
)
EU Data Residency
GDPR requires that EU user data stays within the EU or in countries with adequate protection. With Cloudflare Workers, use data localization:
# wrangler.jsonc
{
"name": "tanstack-ship",
"compatibility_date": "2026-06-01",
"workers_dev": false,
"routes": [
{ "pattern": "tanstackship.com", "zone_id": "YOUR_ZONE_ID" },
{ "pattern": "eu.tanstackship.com", "zone_id": "YOUR_ZONE_ID", "custom_domain": true }
],
"d1_databases": [
{ "binding": "DB", "database_id": "GLOBAL_DB", "database_name": "tanstack-ship" },
{ "binding": "EU_DB", "database_id": "EU_DB", "database_name": "tanstack-ship-eu" }
]
}
// Route EU users to EU D1 instance
async function getDb(request: Request) {
const country = request.cf?.country
if (country && EU_COUNTRIES.has(country)) {
return env.EU_DB // GDPR-compliant D1 instance
}
return env.DB
}
EU Payment: SEPA Direct Debit
// With Stripe, SEPA is handled via Payment Methods
const paymentIntent = await stripe.paymentIntents.create({
amount: 2999, // €29.99
currency: "eur",
payment_method_types: ["sepa_debit", "card"],
customer: customerId,
})
Key EU-specific pricing considerations:
- Display prices including VAT (unlike the US)
- Register for VAT MOSS to handle cross-border EU sales
- Issue VAT-compliant invoices (required by law for B2B)
- SEPA transfers can take 1-3 business days — do not grant access until payment clears
Market 2: Chinese Market Preparation
Entering China is the most technically demanding market expansion. Here is what you need:
ICP License (Required for Any Hosted Service)
Any website or app hosted on servers in mainland China or using a Chinese CDN must have an ICP (Internet Content Provider) license. The process takes 4-8 weeks and requires:
- Registered Chinese company (WFOE or joint venture) or partnership with a Chinese ICP-licensed entity
- ICP filing submission through your cloud provider (Alibaba Cloud, Tencent Cloud, AWS China)
- Domain name already ICP-filed
Without an ICP license, your site will be blocked by the Great Firewall of China (GFW).
Infrastructure: Bypassing the Great Firewall
// src/lib/geo-routing.ts
// Route Chinese users to China-hosted instances
const CHINA_REGIONS = new Set(["CN", "HK", "MO"])
export async function getRegionalEndpoint(request: Request): Promise<string> {
const country = request.cf?.country
if (CHINA_REGIONS.has(country ?? "")) {
// Route to China-hosted instance via Cloudflare China Network
return "https://tanstack-ship.cn"
}
// Global instance
return "https://tanstackship.com"
}
Cloudflare China Network provides a GFW-compliant CDN layer without requiring your own Chinese servers. All major Chinese cloud providers also offer GPU/CPU instances:
- Alibaba Cloud (aliyun.com) — largest market share
- Tencent Cloud — second largest, WeChat integration
- AWS China (Beijing/Ningxia) — requires separate account
Chinese Payment Integration
// src/lib/payment/china.ts
// Alipay integration via Stripe or direct API
// Option 1: Stripe (simplest, higher fees)
const paymentIntent = await stripe.paymentIntents.create({
amount: 19900, // ¥199.00
currency: "cny",
payment_method_types: ["alipay", "card"],
payment_method_options: {
alipay: {},
},
})
// Option 2: Direct Alipay API (lower fees, more setup)
import AlipaySdk from "alipay-sdk"
const alipaySdk = new AlipaySdk({
appId: process.env.ALIPAY_APP_ID,
privateKey: process.env.ALIPAY_PRIVATE_KEY,
})
const result = await alipaySdk.exec("alipay.trade.create", {
bizContent: {
out_trade_no: orderId,
total_amount: "199.00",
subject: "TanStack Ship - Pro Plan",
product_code: "FAST_INSTANT_TRADE_PAY",
},
})
Chinese Social Login
// src/lib/auth/china.ts
// WeChat OAuth
// Step 1: Redirect user to WeChat authorization
export function getWeChatAuthUrl(redirectUri: string): string {
const params = new URLSearchParams({
appid: process.env.WECHAT_APP_ID,
redirect_uri: redirectUri,
response_type: "code",
scope: "snsapi_login",
state: crypto.randomUUID(),
})
return `https://open.weixin.qq.com/connect/qrconnect?${params}`
}
// Step 2: Exchange code for access token
export async function getWeChatToken(code: string) {
const response = await fetch(
`https://api.weixin.qq.com/sns/oauth2/access_token?` +
new URLSearchParams({
appid: process.env.WECHAT_APP_ID,
secret: process.env.WECHAT_APP_SECRET,
code,
grant_type: "authorization_code",
})
)
return response.json() as Promise<{
access_token: string
openid: string
unionid: string
}>
}
Chinese Content Delivery Considerations
- Censored keywords: Your content will be scanned. Common SaaS-blocked terms include sensitive political topics, VPN references, and cryptocurrency mentions
- WeChat ecosystem: WeChat Mini Programs, WeChat Pay, and WeChat Official Accounts are essential for B2C SaaS
- Email deliverability: QQ Mail and 163.com are the dominant email providers — ensure your SPF, DKIM, and DMARC are configured for Chinese domains
-
Domain blocking: Some domains are blocked without clear reason. Register a
.cndomain as backup
Market 3: US Market Preparation
The US market is the most straightforward but still requires specific attention:
Tax Compliance: Sales Tax by State
// src/lib/tax/us-sales-tax.ts
// Simplified sales tax calculation (use a service like TaxJar for production)
const STATE_TAX_RATES: Record<string, number> = {
"CA": 0.0725, // California base rate
"NY": 0.04, // New York state (county/city adds more)
"TX": 0.0625, // Texas
"WA": 0.065, // Washington
// ... 45+ more states
}
export function calculateSalesTax(
amount: number,
state: string
): { taxRate: number; taxAmount: number; total: number } {
const taxRate = STATE_TAX_RATES[state.toUpperCase()] ?? 0
const taxAmount = Math.round(amount * taxRate * 100) / 100
return { taxRate, taxAmount, total: amount + taxAmount }
}
Key considerations:
- Economic nexus: If you sell >$100K or 200+ transactions in a state, you must collect sales tax
- Use a service like TaxJar, Stripe Tax, or Anrok for automated calculation
- Display prices excluding tax (unlike Europe)
US Privacy Compliance (CCPA)
// src/lib/ccpa.ts
// CCPA opt-out mechanism
export async function handleCcpOptOut(userId: string) {
await env.DB.prepare(
`INSERT INTO privacy_opt_outs (user_id, regulation, opted_out_at)
VALUES (?, 'CCPA', unixepoch())`
).bind(userId).run()
}
// Do not sell my personal information endpoint
export const optOutOfSale = createServerFn({ method: "POST" }).handler(
async (_, { request }) => {
const userId = await getUserId(request)
await handleCcpOptOut(userId)
return { success: true }
}
)
Architectural Pattern: Regional Deployment with Edge Workers
For SaaS products targeting all three markets simultaneously, use a regional deployment architecture:
┌─────────────────────────────────────────────────┐
│ Cloudflare │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ US Worker │ │ EU Worker │ │ CN Edge │ │
│ │ us-east │ │ eu-west │ │ (CF CN) │ │
│ └──────┬───────┘ └──────┬───────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌──────▼───────┐ ┌──────▼───────┐ ┌────▼─────┐ │
│ │ US D1 DB │ │ EU D1 DB │ │ CN D1 DB │ │
│ │ (Virginia) │ │ (Frankfurt) │ │ (Beijing) │ │
│ └──────────────┘ └──────────────┘ └──────────┘ │
│ │ │ │ │
│ ┌──────▼───────┐ ┌──────▼───────┐ │
│ │ US R2 │ │ EU R2 │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────┘
// src/middleware/geo-router.ts
// Edge middleware that routes to the correct regional deployment
export const geoRouter = createServerFn({ method: "GET" }).handler(
async (_, { request }) => {
const country = request.cf?.country ?? "US"
if (["CN", "HK", "MO"].includes(country)) {
return env.CN_DB
}
if (EU_COUNTRIES.has(country)) {
return env.EU_DB
}
return env.US_DB
}
)
Multi-Currency Pricing Database Schema
CREATE TABLE price_tiers (
id TEXT PRIMARY KEY,
product_id TEXT NOT NULL REFERENCES products(id),
region TEXT NOT NULL CHECK (region IN ('US', 'EU', 'CN', 'ROW', 'GLOBAL')),
currency TEXT NOT NULL,
unit_amount INTEGER NOT NULL, -- In smallest currency unit (cents, fen, etc.)
interval TEXT NOT NULL CHECK (interval IN ('month', 'year')),
tax_behavior TEXT NOT NULL DEFAULT 'exclusive' CHECK (tax_behavior IN ('inclusive', 'exclusive')),
is_active INTEGER NOT NULL DEFAULT 1,
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
UNIQUE(product_id, region, interval)
);
CREATE INDEX idx_price_tiers_region ON price_tiers(region);
// src/lib/pricing/regional.ts
export async function getRegionalPrice(
productId: string,
region: "US" | "EU" | "CN" | "ROW",
interval: "month" | "year"
) {
const price = await env.DB.prepare(
`SELECT unit_amount, currency, tax_behavior
FROM price_tiers
WHERE product_id = ? AND region = ? AND interval = ? AND is_active = 1
ORDER BY created_at DESC
LIMIT 1`
)
.bind(productId, region, interval)
.first()
return price
}
Globalization Readiness Checklist
Before launching in a new market, verify each item:
- [ ] Content is fully translated (not just UI, but transactional emails, docs, error pages)
- [ ] Date/time/number formatting uses
Intl.*APIs (never hardcoded) - [ ] Currency symbols and formats match local expectations (€29,99 vs ¥199 vs $29.99)
- [ ] Regional payment methods are integrated and tested with sandbox credentials
- [ ] Tax calculation is automated per region (VAT, sales tax, consumption tax)
- [ ] Data residency requirements are met (GDPR EU, PIPL China, CCPA California)
- [ ] Privacy policy covers all applicable regulations (separate versions per region)
- [ ] CDN/edge routing directs users to the nearest regional deployment
- [ ] Email deliverability is configured for regional providers (QQ, 163, GMX, Gmail)
- [ ] Social login options include region-specific providers (WeChat, LINE, LinkedIn)
- [ ] Terms of Service are localized and legally reviewed per jurisdiction
- [ ] Customer support channels cover regional time zones (email, chat, phone)
- [ ] Pricing is localized to market expectations (not just currency conversion)
- [ ] ICP license is obtained for China (allow 4-8 weeks processing time)
- [ ] Cookie consent mechanism is GDPR-compliant (granular, documented)
Conclusion
Globalizing a SaaS product is a multi-dimensional challenge that touches every part of your stack — from database sharding and edge deployment to payment processing and legal compliance. The key takeaways:
- Build globalization into your architecture from day one — retrofitting regional routing, multi-currency pricing, and data residency onto a monolithic system is prohibitively expensive
- Each market has a dominant constraint: GDPR (data privacy) in Europe, ICP/GFW (infrastructure) in China, and sales tax nexus (tax complexity) in the US
- Payments are the hardest part — you need at least 3 payment providers (Stripe, Alipay/WeChat, SEPA) to cover all three markets
- Edge deployment is your friend — Cloudflare Workers' regional routing makes data residency compliance much simpler than traditional VPS-based architectures
- Test with real users in each market — localization bugs (wrong date formats, truncated text in Chinese characters, missing RTL support) only surface with native users
The companies that win at globalization are not the ones with the most features — they are the ones that make every user feel like the product was built for their market.













