Global Payment Integration: Stripe, Alipay, SEPA, and WeChat Pay
The payment method a user expects depends on their location — and offering the wrong ones dramatically reduces conversion. US users expect credit cards and PayPal. German users want SEPA Direct Debit and PayPal. Chinese users expect Alipay and WeChat Pay QR codes. This guide covers the global payment integration patterns used at tanstackship.com, supporting multiple payment methods across geographic markets.
Payment Methods by Market
| Market | Primary Methods | % of Transactions | Average Ticket |
|---|---|---|---|
| US/Canada | Credit Card, PayPal | Card: 79%, PayPal: 14% | $49/mo |
| Europe | Credit Card, PayPal, SEPA | Card: 45%, PayPal: 25%, SEPA: 20% | €39/mo |
| Germany | SEPA, PayPal, Credit Card | SEPA: 35%, PayPal: 30%, Card: 25% | €35/mo |
| China | Alipay, WeChat Pay | Alipay: 55%, WeChat: 40% | ¥149/mo |
| UK | Credit Card, PayPal | Card: 65%, PayPal: 25% | £39/mo |
| Japan | Credit Card, Konbini | Card: 70%, Konbini: 15% | ¥980/mo |
Stripe Integration with Multiple Payment Methods
Setting Up Stripe Checkout with Payment Method Types
// server/billing/stripe.ts
import Stripe from "stripe"
const stripe = new Stripe(env.STRIPE_SECRET_KEY, {
apiVersion: "2025-02-24.acacia",
})
export const createCheckoutSession = createServerFn({ method: "POST" }).handler(
async ({ data, context }: {
data: { priceId: string; locale: string }
}) => {
// Determine payment methods based on locale
const paymentMethods = getPaymentMethodsForLocale(data.locale)
const session = await stripe.checkout.sessions.create({
line_items: [{ price: data.priceId, quantity: 1 }],
mode: "subscription",
success_url: `https://tanstackship.com/${data.locale}/billing/success`,
cancel_url: `https://tanstackship.com/${data.locale}/billing/cancel`,
payment_method_types: paymentMethods,
customer_creation: "always",
locale: mapStripeLocale(data.locale),
metadata: {
locale: data.locale,
userId: context.user.id,
},
...(paymentMethods.includes("sepa_debit") && {
payment_intent_data: {
// SEPA requires mandate display
mandate_data: {
type: "online",
},
},
}),
})
return { sessionId: session.id, url: session.url }
}
)
function getPaymentMethodsForLocale(locale: string): string[] {
const methods: Record<string, string[]> = {
en: ["card", "paypal"],
de: ["card", "paypal", "sepa_debit"],
zh: ["card", "alipay", "wechat_pay"],
}
return methods[locale] ?? ["card"]
}
function mapStripeLocale(locale: string): Stripe.Checkout.SessionCreateParams.Locale {
const locales: Record<string, Stripe.Checkout.SessionCreateParams.Locale> = {
en: "auto",
de: "de",
zh: "zh",
}
return locales[locale] ?? "auto"
}
Managing Recurring Billing Globally
Price Configuration
// Define prices per market with appropriate currency
export const prices = {
starter: {
en: { price: 2900, currency: "usd" }, // $29
de: { price: 2900, currency: "eur" }, // €29
zh: { price: 12900, currency: "cny" }, // ¥129
},
pro: {
en: { price: 7900, currency: "usd" }, // $79
de: { price: 7900, currency: "eur" }, // €79
zh: { price: 34900, currency: "cny" }, // ¥349
},
}
// Create Stripe prices per currency
async function createPricesForLocale(locale: string) {
const stripePrices = []
for (const [plan, currencies] of Object.entries(prices)) {
const { price, currency } = currencies[locale as keyof typeof currencies]
const stripePrice = await stripe.prices.create({
unit_amount: price,
currency,
recurring: { interval: "month" },
product_data: {
name: plan.charAt(0).toUpperCase() + plan.slice(1),
metadata: { locale },
},
})
stripePrices.push({ plan, stripePriceId: stripePrice.id })
}
return stripePrices
}
SEPA Direct Debit (German Market)
Mandate Collection Flow
export const createSepaSubscription = createServerFn({ method: "POST" }).handler(
async ({ data, context }: { data: { priceId: string; iban: string } }) => {
// 1. Create a Stripe customer
const customer = await stripe.customers.create({
email: context.user.email,
metadata: { userId: context.user.id },
})
// 2. Create SEPA payment method
const paymentMethod = await stripe.paymentMethods.create({
type: "sepa_debit",
sepa_debit: { iban: data.iban },
billing_details: { email: context.user.email },
})
// 3. Attach payment method
await stripe.paymentMethods.attach(paymentMethod.id, { customer: customer.id })
// 4. Set as default
await stripe.customers.update(customer.id, {
invoice_settings: { default_payment_method: paymentMethod.id },
})
// 5. Create subscription
const subscription = await stripe.subscriptions.create({
customer: customer.id,
items: [{ price: data.priceId }],
default_payment_method: paymentMethod.id,
payment_settings: {
payment_method_types: ["sepa_debit"],
},
})
return { subscriptionId: subscription.id }
}
)
Alipay and WeChat Pay (Chinese Market)
QR Code Integration
export const createAlipaySession = createServerFn({ method: "POST" }).handler(
async ({ data, context }: { data: { priceId: string } }) => {
// Stripe Checkout handles Alipay/WeChat Pay QR code generation
const session = await stripe.checkout.sessions.create({
line_items: [{ price: data.priceId, quantity: 1 }],
mode: "payment",
payment_method_types: ["alipay", "wechat_pay"],
success_url: `https://tanstackship.com/zh/billing/success`,
cancel_url: `https://tanstackship.com/zh/billing/cancel`,
})
return {
url: session.url,
// Stripe Checkout shows QR code automatically on mobile
}
}
)
Tax Handling
Automatic Tax Calculation
export const createTaxAwareSession = createServerFn({ method: "POST" }).handler(
async ({ data, context }: { data: { priceId: string; locale: string } }) => {
// Stripe Tax automatically calculates VAT, GST, sales tax
const session = await stripe.checkout.sessions.create({
line_items: [{ price: data.priceId, quantity: 1 }],
mode: "subscription",
automatic_tax: { enabled: true },
customer_update: { address: "auto" },
// Billing address collected for tax calculation
billing_address_collection: "required",
})
return { url: session.url }
}
)
Tax Rates by Market
| Country | Tax Type | Rate | SaaS-Specific | Notes |
|---|---|---|---|---|
| US | Sales tax | 0-10% (varies by state) | Taxed in most states | Origin-based for digital |
| Germany | VAT (Umsatzsteuer) | 19% | Taxable | Reverse charge for B2B |
| China | VAT | 6% (digital services) | Taxable | Withholding requirements |
| UK | VAT | 20% | Taxable | VAT MOSS scheme |
| Japan | Consumption tax | 10% | Taxable | Invoice system 2023+ |
Subscription Management
// Handle cross-market subscription changes
export const updateSubscriptionLocale = createServerFn({ method: "POST" }).handler(
async ({ data, context }: {
data: { subscriptionId: string; newLocale: string }
}) => {
const subscription = await stripe.subscriptions.retrieve(data.subscriptionId)
// Update locale metadata
await stripe.subscriptions.update(data.subscriptionId, {
metadata: { locale: data.newLocale },
})
// If invoice locale changed, update default payment method
if (data.newLocale === "de") {
// Ensure SEPA mandate is active
} else if (data.newLocale === "zh") {
// Ensure Alipay/WeChat is available
}
return { updated: true }
}
)
Failed Payment Recovery by Market
// Localized dunning emails based on user locale
async function handleFailedPayment(invoice: Stripe.Invoice) {
const subscription = await stripe.subscriptions.retrieve(
invoice.subscription as string
)
const locale = subscription.metadata.locale ?? "en"
// Smart retry based on payment method
if (subscription.metadata.payment_method === "sepa_debit") {
// SEPA retries are handled by Stripe — wait 3-5 days
await scheduleEmail(invoice.customer, "sepa_pending", locale)
} else {
// Credit card: immediate retry with smart timing
await stripe.invoices.pay(invoice.id, {
payment_method: invoice.payment_method as string,
})
}
// Localize email template
const emailTemplates = {
en: "Your payment failed. Update your billing info here: {link}",
de: "Ihre Zahlung ist fehlgeschlagen. Aktualisieren Sie hier: {link}",
zh: "支付失败。点击此处更新账单信息:{link}",
}
await sendEmail({
to: invoice.customer_email,
subject: subjectTranslations[locale],
body: emailTemplates[locale as keyof typeof emailTemplates].replace(
"{link}",
`https://tanstackship.com/${locale}/billing`
),
})
}
Currency Display
function PriceDisplay({
amount,
locale,
currency,
}: {
amount: number
locale: string
currency: string
}) {
return new Intl.NumberFormat(locale, {
style: "currency",
currency,
}).format(amount)
// Result examples:
// en, USD → "$29.99"
// de, EUR → "29,99 €"
// zh, CNY → "¥129.99"
}
Global Payment Integration Checklist
[ ] Payment methods configured per market (card + local methods)
[ ] Prices defined in local currencies
[ ] Tax calculation automated with Stripe Tax
[ ] Invoice and receipt localization (language, format, VAT ID)
[ ] Failed payment recovery emails localized
[ ] Refund policy complies with local laws (e.g., 14-day cancellation in EU)
[ ] Chargeback handling process documented per market
[ ] Currency conversion displayed clearly
[ ] Payment confirmation pages localized
[ ] Dunning email sequences translated
[ ] Subscription cancellation flows comply with local regulations
Conclusion
Global payment integration is one of the most impactful localization investments a SaaS can make. The principle is simple: let users pay the way they are accustomed to paying. A German user who sees SEPA Direct Debit as an option is significantly more likely to convert than one who sees only credit card.
Stripe's platform makes multi-method, multi-currency, multi-tax support manageable through a single API. Combined with locale-specific price configuration and localized payment flows, you can support global users without managing separate payment providers for each market.
For a production SaaS with global payment integration across US, European, and Chinese markets, see tanstackship.com.