OWolf

BlogToolsProjectsAboutContact
© 2025 owolf.com
HomeAboutNotesContactPrivacy

2025-07-07 Web Development, Ecommerce

Stripe Subscriptions & Multi-Currency in Next.js 15

By O. Wolfson

This article expands on our Stripe + Next.js 15 integration, showing how to support recurring payments (subscriptions) and multi-currency checkout. You’ll learn best practices, see example code, and understand the tradeoffs in international SaaS/e-commerce pricing.


Table of Contents

  1. Overview
  2. Stripe Subscriptions: How They Work
  3. Adding a Subscription Checkout Session
  4. Multi-Currency Support
  5. Example: Multi-Currency Subscription Flow
  6. Appendix: Useful Stripe Dashboard Tips

1. Overview

With the basics in place (one-time payments using Stripe Elements), most modern apps will want to support:

  • Recurring subscriptions (e.g., memberships, SaaS plans)
  • Multi-currency (localized pricing for US, EU, Asia, etc.)

Stripe supports both with a secure, scalable API. Here’s how to add these features to your Next.js 15 (App Router) project.


2. Stripe Subscriptions: How They Work

  • Subscriptions are powered by recurring Prices attached to a Product in Stripe.
  • The recommended flow is to redirect the user to Stripe Checkout (mode: "subscription").
  • Stripe handles billing, tax, SCA, invoices, retries, and more.
  • You can manage and sync subscription status using Stripe webhooks.

3. Adding a Subscription Checkout Session

A. Create Subscription Prices in Stripe:

  • Go to Products in the Stripe Dashboard.
  • Add a new product (e.g., "Pro Membership").
  • Add one or more Prices, each with Type: Recurring and a currency (USD, THB, EUR, etc.).
  • Each price has a unique Price ID.

B. API Route for Subscription Checkout:

Create a new API route, e.g. app/api/create-subscription-session/route.ts:

ts
import { NextResponse } from "next/server";
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(request: Request) {
  const { priceId, customerEmail } = await request.json();
  try {
    const session = await stripe.checkout.sessions.create({
      mode: "subscription",
      payment_method_types: ["card"],
      line_items: [{ price: priceId, quantity: 1 }],
      success_url:
        "https://yourdomain.com/success?session_id={CHECKOUT_SESSION_ID}",
      cancel_url: "https://yourdomain.com/cancel",
      customer_email: customerEmail,
    });
    return NextResponse.json({ url: session.url });
  } catch (err: any) {
    return NextResponse.json({ error: err.message }, { status: 400 });
  }
}

C. Client Component to Subscribe:

tsx
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";

export default function SubscribeButton({
  priceId,
  customerEmail,
}: {
  priceId: string;
  customerEmail?: string;
}) {
  const [loading, setLoading] = useState(false);

  const handleSubscribe = async () => {
    setLoading(true);
    const res = await fetch("/api/create-subscription-session", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ priceId, customerEmail }),
    });
    const { url, error } = await res.json();
    setLoading(false);
    if (url) {
      window.location.href = url;
    } else {
      alert(error ?? "Subscription failed.");
    }
  };

  return (
    <Button onClick={handleSubscribe} disabled={loading}>
      {loading ? "Redirecting..." : "Subscribe"}
    </Button>
  );
}

4. Multi-Currency Support

A. How Stripe Multi-Currency Works

  • Each Price in Stripe has a fixed currency (USD, EUR, THB, etc.).
  • You can add multiple prices (one per region/currency) to a single product.
  • At checkout, pass the correct Price ID (and thus currency) depending on the user’s region or preference.
  • Stripe handles all currency-specific processing and conversion.

B. Adding Multi-Currency to Your UI

  1. Create all needed prices in Stripe:

    • E.g., "Pro Plan, USD" (price_xxx_usd)
    • "Pro Plan, THB" (price_xxx_thb)
  2. Let users choose their currency (dropdown/radio):

tsx
const priceIds = {
  usd: "price_xxx_usd",
  thb: "price_xxx_thb",
  // more...
};

const [currency, setCurrency] = useState<"usd" | "thb">("usd");
<SubscribeButton priceId={priceIds[currency]} />;
  1. Display the correct price/currency label in the button/UI.

5. Example: Multi-Currency Subscription Flow

tsx
"use client";
import { useState } from "react";
import SubscribeButton from "@/components/subscribe-button";

const priceIds = {
  usd: "price_xxx_usd",
  thb: "price_xxx_thb",
};

export default function SubscriptionPage() {
  const [currency, setCurrency] = useState<"usd" | "thb">("usd");

  return (
    <div>
      <h1>Choose your plan</h1>
      <div>
        <label>
          <input
            type="radio"
            value="usd"
            checked={currency === "usd"}
            onChange={() => setCurrency("usd")}
          />
          USD $10/month
        </label>
        <label>
          <input
            type="radio"
            value="thb"
            checked={currency === "thb"}
            onChange={() => setCurrency("thb")}
          />
          THB 350/month
        </label>
      </div>
      <SubscribeButton priceId={priceIds[currency]} />
    </div>
  );
}

6. Appendix: Useful Stripe Dashboard Tips

  • Adding new prices: In your product, click “Add price”, choose currency and amount.
  • Copying Price IDs: On the price row, click the three dots > Copy Price ID.
  • Test with test cards: Same as with one-time payments.
  • Webhooks: For production apps, listen for customer.subscription.created, updated, deleted events to update user status in your DB.

Summary

  • Subscriptions are built around recurring Prices. Use Stripe Checkout for secure, best-practice flows.
  • Multi-currency is handled by creating separate prices for each currency/region and letting the user (or your logic) select the right Price ID at checkout.
  • Stripe does all the heavy lifting for payment, compliance, currency, and security.

Questions or need code for specific regions, currencies, or subscription models? Just ask.


Official Stripe Docs for Reference

  • Stripe: Recurring Subscriptions
  • Stripe: Multi-Currency Pricing
  • Stripe Next.js Example (GitHub)

Related Posts

  • Stripe + Next.js 15 Test App: Step-by-Step Walkthrough