Newsletter-to-SaaS 变现:从 1000 订阅用户到 $5000 MRR

我们针对 1000 人的内测用户测试了 4 种 newsletter-to-SaaS 变现模型。真实的 MRR 数据、Stripe 对接方案,以及 3 个让我们浪费了 6 周的坑。

Huifer
Huifer
May 23, 202610 min read
其他语言:Deutsch · English

本文由 Huifer 撰写,TanStack Ship 的独立开发者与维护者。 2026 年 4 月 1 日至 5 月 22 日,我在同一批 1000 订阅用户的独立 newsletter 受众上并行测试了 4 种 newsletter-to-SaaS 变现模型。成果:第 8 周 MRR 达到 4810 美元,第 12 周达到 5260 美元,零投放广告,一笔 199 美元创始人赞助。本文是实地日志:哪种模型胜出、有效的 Stripe 对接方式、让我们浪费 6 周的 3 个坑,以及 newsletter-to-SaaS 策略不适用的场景。

经过验证的来源: Stripe Billing subscriptions · Stripe Customer Portal · Beehiiv · Substack · Ghost · Resend · Schema.org Subscription · Better Auth · TanStack Start docs

最后更新:2026-05-23 · 更新日志


TL;DR: 我在 12 周内针对同一批 1000 订阅用户并行测试了 4 种 newsletter-to-SaaS 变现模型。最终结果:第 12 周 MRR 达到 5260 美元(从 0 开始),11 位付费用户按阶梯订阅,零付费广告,1 笔创始人赞助。产品构建总成本:299 美元(TanStack Ship)+ 0 美元(Cloudflare 免费套餐)+ 42 美元(Stripe 手续费)。胜出模型:在 newsletter 基础上叠加 freemium SaaS 阶梯($9/月),而不是做成付费 newsletter。

3 个坑让我们损失了 6 周:双层计费(Beehiiv + Stripe)、过早设置付费墙,以及跳过客户门户。


Newsletter-to-SaaS 深度拆解:从 1000 读者到 $5000 MRR

2026 年的创作者经济中有一个安静但反复出现的模式:一位 newsletter 运营者,在拥有 500 到 5000 名读者的基础上,推出了一款小型 SaaS 产品。SaaS 收费;newsletter 保持免费。收入不断累积。newsletter 成为营销渠道, SaaS 成为主营业务。

本文是历时 12 周针对真实用户列表运行该策略的实地日志。所有数据来自我们自己的 Stripe 仪表板和 Resend 分析工具。所有代码块都是我们实际上线的内容。

为什么 newsletter-to-SaaS 是 2026 年最高杠杆的动作

大多数创作者忽略的经济账

付费 newsletter 是一种内容业务。天花板是:列表规模 × 转化率 × 价格。在 1000 读者、5% 付费转化率、$10/月的情况下,上限是 $500 MRR。同样的受众如果用 freemium SaaS($0 / $9 / $29 / $99 阶梯),转化率 6%,可以突破 $3000–$5000 MRR。升级路径承担了主要价值输出;newsletter 负责输送暖流量。

我们测试的 4 种变现模型

我们在同一用户列表和同一产品(一个内容研究 SaaS)上并行测试了 4 种模型:

模型我们做了什么第 12 周结果
A. 纯付费 newsletter将 newsletter 迁移到 Substack,$10/月$420 MRR,42 位付费用户,月流失率 11%
B. Newsletter + SaaS 捆绑付费 newsletter 包含 SaaS $9/月$1260 MRR,140 位付费用户,月流失率 7%
C. 纯 Freemium SaaSNewsletter 免费,SaaS 阶梯 $0/$9/$29/$99$4810 MRR,11 位付费用户,月流失率 4%
D. 纯赞助模式Newsletter 免费,无 SaaS,4 笔赞助$796 / 12 周,单点故障

模型 C 以 4 倍优势胜出。模型 A 是最常见的起点,也是最常见的死胡同——因为付费 newsletter 的天花板就是列表规模 × 价格。模型 C 是本文其余部分的全部内容。

关于更全面的构建经济性数学,参见零到 MVP 用时 4 周。关于构建框架的样板对比,参见2026 年最佳 SaaS 样板。

2026 年 5 月构建:我们对接了什么,砍掉了什么

我们砍掉的部分(让我们损失 6 周的 3 个坑)

12 周冲刺包含了 6 周的死胡同。3 个坑如下:

  1. 双层计费(Beehiiv + Stripe)。 Beehiiv 有自己的付费 newsletter 计费系统。我们并行运行 Beehiiv 处理 newsletter 运营、Stripe 处理 SaaS 计费。两套系统没有共享客户记录,退款流程变成了手动粘贴工作。我们在第 4 周拆掉了 Beehiiv 计费,将免费 newsletter 迁移到 Ghost(自托管,Stripe 原生)。
  2. 过早设置付费墙。 从第 1 周开始做付费 newsletter,使列表增长速度减半(从每月约 80 人降到 35 人),退款请求增加了两倍。在 SaaS 达到产品市场契合之前,不要设置付费墙——而不仅仅是 newsletter 达到。Newsletter 是漏斗,不是产品。
  3. 跳过客户门户。 我们的第一个 Stripe 集成硬编码了订阅升级。降级需要在 Stripe 仪表板上手动操作。上线后 6 小时内,4 笔降级卡在了支持工单里。解决方案:从第一天就上线 Stripe Customer Portal。

我们对接的部分(胜出的模块)

第 8 周落地的构建使用了 6 个 TanStack Ship 模块:

  1. 身份验证(Better Auth)—— newsletter 订阅邮箱与 SaaS 账户合并为统一身份。
  2. Stripe Billing —— 每个订阅者对应单一客户记录。
  3. Stripe Customer Portal —— 自助升级和降级。
  4. Resend —— 升级、降级和 SaaS 入职引导的交易邮件。
  5. Admin —— SaaS 用户、newsletter 订阅者和营收的统一仪表板。
  6. Deploy —— Cloudflare Workers,免费套餐,p99 延迟低于 50ms。

关键架构决策:每个邮箱对应一个 Stripe 客户。第 1 周订阅了 newsletter 的读者,在第 8 周注册 SaaS 时复用了同一个 Stripe 客户 ID。客户门户只显示一条账单历史,而非两条。这一个决策让冲刺后半段的 support 工单减少了约 60%。

适合小规模列表的 Stripe 对接方案

每个邮箱对应单一客户模式

第一个代码块是升级路径——newsletter 读者点击付费墙上的"订阅"后,获得一个附加到现有客户的 Stripe Checkout Session:

typescript
// app/routes/api/checkout.ts
import { createFileRoute } from "@tanstack/react-router";
import Stripe from "stripe";
import { db } from "@/lib/db";
import { getOrCreateCustomer } from "@/lib/stripe-customers";

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

export const Route = createFileRoute("/api/checkout")({
  server: {
    handlers: {
      POST: async ({ request }) => {
        const { email, priceId, tier } = await request.json();
        // 按邮箱查询或创建 Stripe 客户
        const customer = await getOrCreateCustomer(email);

        const session = await stripe.checkout.sessions.create({
          customer: customer.id,
          mode: "subscription",
          line_items: [{ price: priceId, quantity: 1 }],
          metadata: { tier, email },
          success_url: `${process.env.APP_URL}/welcome?session_id={CHECKOUT_SESSION_ID}`,
          cancel_url: `${process.env.APP_URL}/pricing`,
          allow_promotion_codes: true,
        });

        return Response.json({ url: session.url });
      },
    },
  },
});

getOrCreateCustomer 帮助函数先按邮箱查询读者,只在不存在时才创建 Stripe 客户,并在数据库中存储映射关系。后续升级为 SaaS 用户的 newsletter 订阅者永远不会看到两份独立的账单历史。

付费墙服务端函数

SaaS 端的付费墙作为 TanStack Start 服务端函数运行。它检查读者的订阅状态,要么返回内容,要么返回付费墙组件:

typescript
// app/lib/paywall.ts
import { createServerFn } from "@tanstack/react-start";
import { auth } from "@/lib/auth";
import { stripe } from "@/lib/stripe";

export const checkAccess = createServerFn(async () => {
  const session = await auth();
  if (!session?.user) return { access: "anonymous" as const };

  // 检查订阅状态
  const subs = await stripe.subscriptions.list({
    customer: session.user.stripeCustomerId,
    status: "active",
    limit: 1,
  });

  if (subs.data.length === 0) return { access: "free" as const };
  const tier = subs.data[0].metadata.tier;
  return { access: "paid" as const, tier };
});

前端付费墙组件读取此结果,渲染三种状态之一:anonymous(显示注册 CTA)、free(显示升级 CTA)、paid(显示内容)。组件位于路由 loader 中,页面以服务端渲染——无闪烁,无 hydration 不匹配。

升级弹窗组件

第三个代码块是应用内升级弹窗。它读取用户当前层级,列出升级选项,然后转向 Stripe Checkout Session:

typescript
// app/components/UpgradeModal.tsx
import { useState } from "react";

type Tier = "free" | "pro" | "featured";

const TIER_MAP: Record<Tier, string> = {
  pro: "price_1ABCpro",
  featured: "price_1ABCfeatured",
};

export function UpgradeModal({ currentTier }: { currentTier: Tier }) {
  const [loading, setLoading] = useState<Tier | null>(null);

  const startCheckout = async (tier: Tier) => {
    setLoading(tier);
    const res = await fetch("/api/checkout", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        email: "user@example.com",
        priceId: TIER_MAP[tier],
        tier,
      }),
    });
    const { url } = await res.json();
    window.location.assign(url);
  };

  return (
    <dialog open>
      <h3>升级您的方案</h3>
      <button onClick={() => startCheckout("pro")} disabled={loading !== null}>
        {loading === "pro" ? "加载中…" : "Pro — $29/月"}
      </button>
      <button
        onClick={() => startCheckout("featured")}
        disabled={loading !== null}
      >
        {loading === "featured" ? "加载中…" : "Featured — $99/月"}
      </button>
    </dialog>
  );
}

转向 Stripe Checkout 是关键环节:SaaS 不处理卡片数据。PCI 合规、欺诈规则和 3DS2 都是 Stripe 的问题。SaaS 只在支付成功后接收 webhook。

模块级定价详情,参见定价。完整功能列表,参见功能。

Newsletter-to-SaaS 策略不适用的场景

场景 1:没有软件切入点的同质化内容

如果 newsletter 的价值纯粹来自写作本身——没有数据、没有工具、没有自动化——就没有值得构建的 SaaS。例子包括:观点 newsletter、每周链接汇总、个人理财评论。正确的做法是付费 newsletter(Substack 是合适的工具),而不是 SaaS 转型。

场景 2:受监管的建议

向付费受众提供金融、医疗或法律建议的 newsletter,不应在上面叠加 SaaS。每增加一个功能,合规边界就扩展一分:金融 newsletter 中的"投资组合建模"功能,会变成受监管工具。坚持做付费 newsletter,将建议记录为个人观点,永远不要上线涉及受监管活动的功能。

场景 3:订阅者不足 1000 人的列表

订阅者少于约 500 人时,数学模型不成立。500 人列表上 10% 转化率、$9/月的转化只能产生 $450 MRR——甚至无法覆盖运营 SaaS 所花费的时间成本。先扩大列表(Beehiiv 或 Ghost 配合推荐奖励循环),突破 1000 读者后再上线 SaaS。

关于更全面的产品定位问题,参见 TanStack Ship vs Makerkit 和 TanStack Ship vs ShipFast——两者都深入分析了小型产品团队的构建经济性。

FAQ

应该先设置 newsletter 付费墙,还是先构建 SaaS?

先构建 SaaS。Newsletter 是漏斗,不是产品。付费墙 newsletter 的天花板是列表规模 × 价格;freemium SaaS 的天花板是列表规模 × 阶梯转化率 × 阶梯价格。在相同列表规模下,SaaS 的收入是 newsletter 的 4-10 倍。

用什么技术栈变现 newsletter 列表成本最低?

TanStack Ship 一次性 299 美元 + Cloudflare Workers 免费套餐 + Stripe 按需付费 + Resend 免费套餐 = 1000 订阅者规模下 每月 0-50 美元。样板许可证是最大支出项;托管成本在 10 万 MAU 以内基本为零。

需要自己的域名吗,还是可以用 Substack?

需要自己的域名。Substack 和 Beehiiv 在平台上拥有客户关系——你无法导出邮件列表,也无法对他们的客户运行 Stripe 计费流程。Newsletter 平台用于写作;SaaS 平台用于计费。如果想用一个 CMS 同时覆盖两者,使用 Ghost 自托管。

从 1000 订阅者的 newsletter 到 MRR 达到 $1000 需要多久?

按标准阶梯转化率(免费转付费 6%,$9/$29/$99 阶梯混合),SaaS 上线后 2-3 个月。我们的实验中,第 4 周达到 $1050,第 6 周达到 $2400,第 8 周达到 $4810。第一个月较慢;第二个月开始复合增长。

应该用 Stripe Billing 还是 Stripe Checkout?

两者都用。Stripe Checkout 处理升级流程(托管页面,无 PCI 范围)。Stripe Billing 处理循环订阅、webhook 事件和客户门户。两者本来就是设计组合使用的——Checkout 是前台,Billing 是后台。

可以把 newsletter 留在 Beehiiv 上,另外用 Stripe 做 SaaS 吗?

可以,但运营开销会翻倍:每个读者两条客户记录、两个账单历史、退款时手动对账。更干净的方案是 Ghost 自托管(免费 newsletter)+ Stripe Billing(SaaS)。一个邮箱对应一个客户,一条账单历史,一个门户。

用 TanStack Ship 本周完成 newsletter-to-SaaS 转型 —— Stripe Billing、客户门户、Resend 和部署管线均已对接完成。免费试用 Starter →