PaymentsStripeAlipayWeChat PaySEPAGlobalSaaS

Global Payment Integration: Stripe, Alipay, SEPA, and WeChat Pay

A comprehensive guide to integrating global payment methods for SaaS products — covering credit cards, SEPA Direct Debit, Alipay, WeChat Pay, PayPal, Stripe Checkout, and localization strategies.

Priya Sharma
Priya Sharma
June 8, 202613 min read

TL;DR: 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 for supporting multiple payment methods across geographic markets, drawing on cross-cultural UX design principles.


Payment Methods by Market

A well-planned globalization strategy is essential for determining which payment methods to support in each region.

MarketPrimary Methods% of TransactionsAverage Ticket
US/CanadaCredit Card, PayPalCard: 79%, PayPal: 14%$49/mo
EuropeCredit Card, PayPal, SEPACard: 45%, PayPal: 25%, SEPA: 20%€39/mo
GermanySEPA, PayPal, Credit CardSEPA: 35%, PayPal: 30%, Card: 25%€35/mo
ChinaAlipay, WeChat PayAlipay: 55%, WeChat: 40%¥149/mo
UKCredit Card, PayPalCard: 65%, PayPal: 25%£39/mo
JapanCredit Card, KonbiniCard: 70%, Konbini: 15%¥980/mo

Stripe Integration with Multiple Payment Methods

Setting Up Stripe Checkout with Payment Method Types

typescript
// 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

Setting prices for each market requires careful consideration of local purchasing power, competitive positioning, and perceived value. SaaS pricing psychology — concepts like anchoring and localization — plays a crucial role in how users perceive your price points across different regions. The configuration below demonstrates per-currency price definitions and how to create corresponding Stripe prices programmatically.

Price Configuration

typescript
// 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
}

This approach ensures each user sees prices in their local currency, reducing friction during checkout. The createPricesForLocale function automates Stripe price creation with appropriate currency settings, making it straightforward to add new markets or adjust pricing without manual Stripe dashboard work.


SEPA Direct Debit (German Market)

Mandate Collection Flow

typescript
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

typescript
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

typescript
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

CountryTax TypeRateSaaS-SpecificNotes
USSales tax0-10% (varies by state)Taxed in most statesOrigin-based for digital
GermanyVAT (Umsatzsteuer)19%TaxableReverse charge for B2B
ChinaVAT6% (digital services)TaxableWithholding requirements
UKVAT20%TaxableVAT MOSS scheme
JapanConsumption tax10%TaxableInvoice system 2023+

Subscription Management

typescript
// 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

typescript
// 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

typescript
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.

A smooth payment and checkout experience should be paired with an effective customer onboarding process to ensure new users succeed from day one.