Stripe 和 Next.js 无头商务集成指南

你的第一个 Stripe webhook 在费用成功后的 847 毫秒内到达。你的数据库仍然显示"待处理"。客户刷新,再次点击"支付",现在他们被重复收费了。我在凌晨 2 点在三个生产商店中调试过这种竞态条件。Stripe 的 API 已经演进 -- Checkout Sessions 现在支持一键 Link,Payment Intents 原生处理 Apple Pay 和 Google Pay,如果你的无服务器函数重写了原始正文,webhook 签名验证会无声地失败。本指南涵盖了八种在生产环境中能够保证稳定运行的集成模式:Checkout Sessions、Payment Intents、webhook 幂等性、订阅生命周期钩子、钱包支付设置,以及防止 847ms 噩梦的三个架构决策。我们从 webhook 处理器开始,因为那是大多数集成首先失败的地方。

截至 2026 年中期,Stripe 的 API 版本为 2025-12-18.acacia,Next.js 15.x 已稳定,App Router 成为默认选择,@stripe/stripe-js@stripe/react-stripe-js 包已经成熟了很多。如果你在较旧的版本上构建,大部分内容仍然适用,但一些服务器操作模式会有所不同。

目录

Stripe + Next.js 无头商务:2026 年集成指南

为什么选择 Stripe + Next.js 用于无头商务

Stripe 每年处理超过 1 万亿美元的支付交易量。Next.js 为越来越多的电商店面提供支持 -- Vercel 报告称 2026 年超过 40% 的新 Next.js 项目在某种形式上具有商务功能。这种组合出于以下几个具体原因而有意义:

  • 服务器组件和服务器操作让你在没有构建单独 API 层的情况下在服务器端调用 Stripe SDK。
  • Edge 和无服务器部署在 Vercel、Netlify 或 AWS 上意味着你的支付端点会自动扩展。
  • React 服务器组件将你的 Stripe 密钥保留在服务器端,不需要额外的处理。
  • App Router 提供了与结账流程很好映射的布局、加载状态和错误边界。

如果你正在评估无头商务架构,我们在 Social Animal 已经构建了数十个这样的项目 -- 查看我们的 Next.js 开发能力无头 CMS 开发以获取有关这些部分如何组合在一起的更多信息。

架构概览

在编写任何代码之前,让我们先了解正确的架构。以下是典型无头商务设置中这些部分如何连接的方式:

┌─────────────────┐     ┌──────────────────┐     ┌─────────────┐
│   Next.js 应用   │────▶│  Stripe API      │────▶│  Webhooks   │
│  (App Router)   │◀────│  (服务器端)      │     │  端点       │
└─────────────────┘     └──────────────────┘     └──────┬──────┘
        │                                                │
        │                                                ▼
        ▼                                        ┌─────────────┐
┌─────────────────┐                              │  数据库 /    │
│  无头 CMS       │                              │  订单管理    │
│  (产品)         │                              └─────────────┘
└─────────────────┘

关键决策是使用 Checkout Sessions(Stripe 托管或嵌入式)还是 Payment Intents(完全自定义 UI)。以下是何时使用各个的说明:

特性 Checkout Sessions Payment Intents
开发速度 快速 -- 几天 较慢 -- 几周
UI 定制 有限(Stripe 主题) 完全控制
PCI 合规范围 SAQ A(最简单) SAQ A-EP
支付方法支持 自动(40+ 种方法) 每种方法手动配置
订阅支持 内置 需要额外代码
Apple Pay / Google Pay 自动 通过 Payment Request API 手动
转化率优化 Stripe 优化 你自己负责
价格影响 相同 Stripe 费用 相同 Stripe 费用

我的诚实建议:**除非有具体原因,否则从 Checkout Sessions 开始。**你可以稍后随时迁移到 Payment Intents,Stripe 的嵌入式结账在 2026 年变得非常出色。

在 Next.js 15 项目中设置 Stripe

让我们设置基础。我假设你有一个带有 App Router 的 Next.js 15 项目。

npm install stripe @stripe/stripe-js @stripe/react-stripe-js

创建你的环境变量:

# .env.local
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

设置一个服务器端 Stripe 实例。我总是将其放在 lib/stripe.ts 文件中:

// lib/stripe.ts
import Stripe from 'stripe';

export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2025-12-18.acacia',
  typescript: true,
});

以及一个客户端加载器:

// lib/stripe-client.ts
import { loadStripe } from '@stripe/stripe-js';

export const stripePromise = loadStripe(
  process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
);

有一件事让人困惑:永远不要在客户端组件中导入 lib/stripe.ts stripe npm 包包含你的密钥,应该只在服务器端运行。如果你不小心在 'use client' 文件中导入它,Next.js 15 会抛出构建错误,这实际上是一个很好的防护措施。

Stripe + Next.js 无头商务:2026 年集成指南 - 架构

Checkout Sessions:快速路径

Checkout Sessions 是接受付款的最快方式。Stripe 托管支付表单(或者你嵌入它),处理 PCI 合规,并自动支持数十种支付方法,包括 Apple Pay、Google Pay 和 Link。

使用服务器操作创建 Checkout Session

// app/actions/checkout.ts
'use server';

import { stripe } from '@/lib/stripe';
import { redirect } from 'next/navigation';

export async function createCheckoutSession(formData: FormData) {
  const priceId = formData.get('priceId') as string;
  const quantity = Number(formData.get('quantity')) || 1;

  const session = await stripe.checkout.sessions.create({
    mode: 'payment',
    line_items: [
      {
        price: priceId,
        quantity,
      },
    ],
    success_url: `${process.env.NEXT_PUBLIC_URL}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/checkout/canceled`,
    automatic_tax: { enabled: true },
    // 启用所有相关支付方法
    payment_method_types: undefined, // 让 Stripe 自动检测
  });

  redirect(session.url!);
}

嵌入式结账(2026 年推荐方法)

Stripe 的嵌入式结账让用户留在你的域名上。这具有更好的转化率 -- Stripe 近年来的数据显示,对于回头客,与重定向式结账相比改进了 10-15%。

// app/checkout/embedded/page.tsx
'use client';

import { useCallback } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import {
  EmbeddedCheckoutProvider,
  EmbeddedCheckout,
} from '@stripe/react-stripe-js';

const stripePromise = loadStripe(
  process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
);

export default function CheckoutPage() {
  const fetchClientSecret = useCallback(async () => {
    const res = await fetch('/api/checkout/embedded', {
      method: 'POST',
      body: JSON.stringify({ priceId: 'price_xxx', quantity: 1 }),
    });
    const { clientSecret } = await res.json();
    return clientSecret;
  }, []);

  return (
    <div className="max-w-lg mx-auto py-12">
      <EmbeddedCheckoutProvider
        stripe={stripePromise}
        options={{ fetchClientSecret }}
      >
        <EmbeddedCheckout />
      </EmbeddedCheckoutProvider>
    </div>
  );
}

以及 API 路由:

// app/api/checkout/embedded/route.ts
import { stripe } from '@/lib/stripe';
import { NextResponse } from 'next/server';

export async function POST(req: Request) {
  const { priceId, quantity } = await req.json();

  const session = await stripe.checkout.sessions.create({
    mode: 'payment',
    line_items: [{ price: priceId, quantity }],
    ui_mode: 'embedded',
    return_url: `${process.env.NEXT_PUBLIC_URL}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,
  });

  return NextResponse.json({ clientSecret: session.client_secret });
}

Payment Intents:完全控制模式

当你需要完全自定义的结账 UI 时 -- 也许你正在构建一个单页结账,或者你的设计团队有特定的要求 -- Payment Intents 给你完全的控制权。

权衡是实际的:你会写更多的代码,处理更多的边缘情况,并承担稍微更高的 PCI 合规负担。但对于某些产品,这是值得的。

服务器端:创建 Payment Intents

// app/api/payment-intent/route.ts
import { stripe } from '@/lib/stripe';
import { NextResponse } from 'next/server';

export async function POST(req: Request) {
  const { amount, currency = 'usd', metadata } = await req.json();

  const paymentIntent = await stripe.paymentIntents.create({
    amount, // 以美分为单位
    currency,
    metadata,
    automatic_payment_methods: {
      enabled: true, // 这启用了 Apple Pay、Google Pay、Link 等
    },
  });

  return NextResponse.json({
    clientSecret: paymentIntent.client_secret,
  });
}

客户端:支付表单

// components/PaymentForm.tsx
'use client';

import { useState } from 'react';
import {
  PaymentElement,
  useStripe,
  useElements,
} from '@stripe/react-stripe-js';

export function PaymentForm() {
  const stripe = useStripe();
  const elements = useElements();
  const [error, setError] = useState<string | null>(null);
  const [processing, setProcessing] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!stripe || !elements) return;

    setProcessing(true);
    setError(null);

    const { error: submitError } = await stripe.confirmPayment({
      elements,
      confirmParams: {
        return_url: `${window.location.origin}/checkout/success`,
      },
    });

    if (submitError) {
      setError(submitError.message ?? '支付失败');
      setProcessing(false);
    }
    // 如果没有错误,Stripe 会自动重定向
  };

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement
        options={{
          layout: 'accordion',
          wallets: {
            applePay: 'auto',
            googlePay: 'auto',
          },
        }}
      />
      {error && <p className="text-red-500 mt-2">{error}</p>}
      <button
        type="submit"
        disabled={!stripe || processing}
        className="mt-4 w-full bg-black text-white py-3 rounded-lg disabled:opacity-50"
      >
        {processing ? '处理中...' : '立即支付'}
      </button>
    </form>
  );
}

注意服务器端的 automatic_payment_methods: { enabled: true }。这是 2026 年处理支付方法支持的方式。Stripe 将根据客户的设备、位置和货币自动显示正确的支付方法。不再需要手动列出 payment_method_types

真正有效的 Webhook 处理

Webhooks 是大多数 Stripe 集成失败的地方。我看到过生产系统丢失订单,因为有人忘记验证 webhook 签名,或者因为处理器在返回 200 之前抛出了错误。

这是我在战斗中测试过的 webhook 处理器:

// app/api/webhooks/stripe/route.ts
import { stripe } from '@/lib/stripe';
import { headers } from 'next/headers';
import { NextResponse } from 'next/server';
import type Stripe from 'stripe';

export async function POST(req: Request) {
  const body = await req.text();
  const headersList = await headers();
  const signature = headersList.get('stripe-signature');

  if (!signature) {
    return NextResponse.json({ error: '缺少签名' }, { status: 400 });
  }

  let event: Stripe.Event;

  try {
    event = stripe.webhooks.constructEvent(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err) {
    console.error('Webhook 签名验证失败:', err);
    return NextResponse.json({ error: '无效的签名' }, { status: 400 });
  }

  try {
    switch (event.type) {
      case 'checkout.session.completed': {
        const session = event.data.object as Stripe.Checkout.Session;
        await handleCheckoutComplete(session);
        break;
      }
      case 'payment_intent.succeeded': {
        const paymentIntent = event.data.object as Stripe.PaymentIntent;
        await handlePaymentSuccess(paymentIntent);
        break;
      }
      case 'payment_intent.payment_failed': {
        const paymentIntent = event.data.object as Stripe.PaymentIntent;
        await handlePaymentFailure(paymentIntent);
        break;
      }
      case 'customer.subscription.created':
      case 'customer.subscription.updated':
      case 'customer.subscription.deleted': {
        const subscription = event.data.object as Stripe.Subscription;
        await handleSubscriptionChange(subscription);
        break;
      }
      case 'invoice.payment_failed': {
        const invoice = event.data.object as Stripe.Invoice;
        await handleInvoiceFailure(invoice);
        break;
      }
      default:
        console.log(`未处理的事件类型:${event.type}`);
    }
  } catch (err) {
    console.error(`处理 ${event.type} 时出错:`, err);
    // 仍然返回 200 以防止 Stripe 重试
    // 记录错误以供手动调查
  }

  return NextResponse.json({ received: true });
}

我艰难学到的 Webhook 陷阱

  1. 始终返回 200,即使你的处理失败。否则 Stripe 重试,你可能会多次处理相同的事件。记录错误并异步处理它。

  2. 使处理器幂等。 Stripe 可以并且会多次发送相同的事件。使用事件 ID 或对象的元数据来检查你是否已经处理过它。

  3. 使用 req.text() 而不是 req.json() 进行签名验证。签名是通过原始正文字符串计算的。如果你先解析它,验证将总是失败。

  4. **设置 Stripe CLI 进行本地测试。**这是不可协商的。

stripe listen --forward-to localhost:3000/api/webhooks/stripe
  1. **在 Vercel 上,webhook 路由需要特定配置。**确保你的路由不在任何修改请求正文的中间件后面。在 Next.js 15 中,App Router 中的 API 路由默认正确处理此问题,但如果你有自定义中间件,请仔细检查。

订阅和循环计费

订阅增加了复杂性层。你不仅处理一次性付款 -- 你还在管理生命周期:试用、升级、降级、取消、失败的付款、催收。

通过结账创建订阅

最简单的方法:

// app/actions/subscribe.ts
'use server';

import { stripe } from '@/lib/stripe';
import { redirect } from 'next/navigation';

export async function createSubscriptionCheckout(
  customerId: string,
  priceId: string
) {
  const session = await stripe.checkout.sessions.create({
    mode: 'subscription',
    customer: customerId,
    line_items: [{ price: priceId, quantity: 1 }],
    success_url: `${process.env.NEXT_PUBLIC_URL}/account/billing?success=true`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`,
    subscription_data: {
      trial_period_days: 14,
      metadata: {
        plan: 'pro', // 你自己的元数据
      },
    },
    allow_promotion_codes: true,
    tax_id_collection: { enabled: true },
  });

  redirect(session.url!);
}

管理订阅

对于客户门户(升级、降级、取消、更新付款方法),Stripe 的客户门户在 2026 年确实很好用:

// app/actions/billing.ts
'use server';

import { stripe } from '@/lib/stripe';
import { redirect } from 'next/navigation';

export async function createBillingPortalSession(customerId: string) {
  const session = await stripe.billingPortal.sessions.create({
    customer: customerId,
    return_url: `${process.env.NEXT_PUBLIC_URL}/account/billing`,
  });

  redirect(session.url);
}

关键订阅 Webhook 事件

事件 何时触发 操作内容
customer.subscription.created 新订阅 配置访问权限
customer.subscription.updated 计划更改、续期 更新访问级别
customer.subscription.deleted 取消(期末) 撤销访问权限
invoice.payment_succeeded 成功续期 更新计费记录
invoice.payment_failed 续期失败 发送催收电子邮件、标记账户
customer.subscription.trial_will_end 试用结束前 3 天 发送提醒电子邮件

不要仅依赖 API 调用中的订阅状态。Webhooks 是订阅状态更改的事实来源。我见过团队轮询 Stripe API 而不是使用 webhooks,这既更慢又更不稳定。

Stripe 2026 年 Payment Element 的妙处在于钱包支付大多只需要正常工作。但有一些人们会错过的设置要求。

Apple Pay 设置

  1. **需要域验证。**你需要在你的域根部托管一个 .well-known/apple-developer-merchantid-domain-association 文件。Stripe 在你的仪表板中的"设置"→"支付方法"→"Apple Pay"下提供此文件。

  2. 在 Next.js 中,将文件放在 public/.well-known/apple-developer-merchantid-domain-association

  3. 在 Stripe 仪表板中注册你的域。

  4. Apple Pay 仅在 Safari/iOS 上显示。在测试中不出现在 Chrome 中时不要惊慌。

Google Pay 设置

Google Pay 需要的设置较少 -- 只要你的 Stripe 账户配置得当,它就可以通过 Payment Element 自动工作。它在 Chrome 和 Android 设备上显示。

Link 是 Stripe 对 Shop Pay 的回答。客户保存他们的支付信息一次,可以在使用 Link 的任何 Stripe 商家中一键结账。

截至 2026 年,Link 在新 Stripe 账户上默认启用。转化率提升是真实的 -- Stripe 报告当 Link 可用时,结账完成率有显著改善。对于回头 Link 用户,改善更高。

对于 Payment Element,Link 会自动显示。对于 Checkout Sessions,它也是自动的。你不需要做任何特殊的事情。

// Link 通过 Payment Element 自动显示,但你可以定制:
<PaymentElement
  options={{
    wallets: {
      applePay: 'auto',
      googlePay: 'auto',
    },
    // Link 在电子邮件字段中自动显示
  }}
/>

Link 值得独立成章,因为它已成为一个严肃的转化率驱动力。以下是它的工作原理:

  1. 客户在你的结账表单中输入他们的电子邮件。
  2. 如果他们有 Link 账户,他们会收到一条通过短信发送的验证码。
  3. 验证后,他们保存的地址和支付方法会自动填充。
  4. 他们点击"支付"-- 完成。

关键见解:**Link 在商家中工作。**如果你的客户在一个完全不同的网站上使用过 Link,他们在你的网站上也会获得一键体验。Stripe 的网络效应是真实的 -- 他们报告截至 2026 年初有超过 1 亿 Link 用户。

为了最大化 Link 采用,确保电子邮件字段是客户在你的结账流程中与之互动的第一件事。Payment Element 通过 accordion 布局很好地处理了这一点。

如果你想更进一步,你可以使用 Express Checkout Element 在你的表单上方显示 Apple Pay、Google Pay 和 Link 作为突出的按钮:

// components/ExpressCheckout.tsx
'use client';

import { ExpressCheckoutElement } from '@stripe/react-stripe-js';

export function ExpressCheckout() {
  return (
    <ExpressCheckoutElement
      onConfirm={async (event) => {
        // 处理快速支付确认
        console.log('快速结账已确认:', event);
      }}
      options={{
        buttonType: {
          applePay: 'buy',
          googlePay: 'buy',
        },
      }}
    />
  );
}

安全、测试和上线

安全检查清单

  • Stripe 密钥仅在服务器端使用
  • 在每个请求上验证 webhook 签名
  • 在生产环境中强制使用 HTTPS
  • 金额计算在服务器端进行(永远不要信任客户端发送的金额)
  • API 路由具有速率限制
  • 客户数据按照你的隐私政策处理
  • CSP 头允许 Stripe 的域(js.stripe.comapi.stripe.com

测试

Stripe 的测试模式很好。使用这些测试卡号:

卡号 情景
4242 4242 4242 4242 成功支付
4000 0000 0000 3220 需要 3D Secure
4000 0000 0000 9995 被拒绝
4000 0025 0000 3155 需要身份验证
4000 0000 0000 0341 附加失败(对于已保存的卡)

对于订阅测试,使用 Stripe 的测试时钟来模拟时间流逝,而无需实际等待。

上线

  1. 将你的密钥从 sk_test_ 切换到 sk_live_pk_test_pk_live_
  2. 在 Stripe 仪表板中设置你的实时 webhook 端点。
  3. 为生产验证你的 Apple Pay 域。
  4. 在 Stripe 仪表板中启用你想要的支付方法。
  5. 确保你的 Stripe 账户已完全激活(身份验证、银行账户等)。

性能考虑

Stripe.js 大约 40KB 压缩。这不是小数目。以下是一些提示:

  • **延迟加载 Stripe.js。**不要在每个页面上加载它 -- 仅在与结账相关的页面上加载。loadStripe 函数很好地处理了这一点;直到你调用它,它才会获取脚本。

  • 使用 @stripe/stripe-js/pure 如果你想精确控制脚本何时加载:

import { loadStripe } from '@stripe/stripe-js/pure';
// 脚本在调用 loadStripe() 之前不会加载
  • **产品页面的服务器组件。**将 Stripe 客户端代码保留在你的产品列表和详细页面之外。仅当用户实际启动结账时才引入客户端组件。

  • API 路由的 Edge 运行时。 Stripe 的 Node.js SDK 在 Edge 运行时上工作。你可以将 export const runtime = 'edge' 添加到你的 Stripe API 路由以获得更低的延迟。

对于构建高性能无头店面的团队,Astro 等框架对于内容繁重的页面也可以是一个很好的选择,而 Next.js 处理动态结账流程。我们在几个客户项目中做过这种混合方法 -- 我们的 Astro 开发Next.js 开发团队定期在这些架构上协作。

常见问题

Stripe 在 2026 年的交易费用是多少? Stripe 的标准定价是成功卡交易的 2.9% + $0.30(美国)。对于欧洲卡,为 1.5% + €0.25。年处理超过 100 万美元的企业可获得批量折扣。标准计划没有设置费用、月费或隐藏费用。Stripe 对手动输入的卡收取额外的 0.5%,对国际卡收取 1%。

我应该使用 Checkout Sessions 还是 Payment Intents? 在大多数情况下使用 Checkout Sessions。实现速度更快,自动支持 40+ 种支付方法,处理 PCI 合规,Stripe 不断优化转化率。当你需要无法通过嵌入式结账实现的完全自定义结账 UI 时,或当你需要对支付流程进行细粒度控制时使用 Payment Intents(例如分割支付或手动捕获)。

我如何在生产环境中处理 webhook 失败? 始终从你的 webhook 处理器返回 200 状态代码,即使你的业务逻辑失败。记录错误并异步处理它。通过检查事件 ID 是否与你的数据库中的事件 ID 相符来使处理器幂等,然后再处理。Stripe 使用指数退避重试 webhooks 最长 3 天。在 Stripe 仪表板中设置 webhook 失败警报,并考虑使用队列(如 AWS SQS 或 Inngest)来异步处理 webhook 负载。

我可以将 Stripe 与 Sanity 或 Contentful 等无头 CMS 一起使用吗? 绝对可以。典型的模式是:在你的无头 CMS 中存储产品信息和内容,在 Stripe 中存储价格和支付数据,并通过共享的产品 ID 或 SKU 连接它们。你的 Next.js 前端从 CMS 中获取内容,当客户准备购买时创建 Stripe Checkout Sessions 或 Payment Intents。我们在 无头 CMS 开发工作中广泛涵盖了这个模式。

我如何在本地测试 Apple Pay? 你不能轻易地在 localhost 上测试 Apple Pay,因为它需要 HTTPS 和域验证。最好的方法是使用 Stripe 的测试模式,在 Payment Element 中使用 4242 测试卡 -- 它模拟支付流程。对于实际的 Apple Pay 测试,部署到具有 HTTPS 的暂存环境。Stripe CLI 也支持为 Apple Pay 交易转发 webhook 事件。

Stripe Link 值得启用吗? 值得。Link 对商家是免费的 -- Stripe 不对其收取额外费用。它会在 Payment Element 和 Checkout Sessions 中自动显示。它会在 Payment Element 和 Checkout Sessions 中自动显示。没有启用的缺点,到 2026 年拥有 1 亿多 Link 用户,网络效应是显著的。

我如何在 Next.js 中处理带有计量计费的订阅? 在 Stripe 中创建带有计量价格的订阅。然后,使用使用记录 API 从你的后端报告使用情况。在每个计费周期结束时,Stripe 自动计算总额并向客户收费。你的 webhook 处理器应该侦听 invoice.payment_succeededinvoice.payment_failed 事件以保持你的系统同步。使用 cron 作业或事件驱动的架构从服务器端报告使用情况。

如何为国际客户处理货币和定价? Stripe 自适应定价(2025 年发布)在结账时自动将价格转换为客户的本地货币。你以你的基础货币设置价格,Stripe 处理转换、显示和结算。或者,你可以在 Stripe 中为不同货币的每个产品创建多个价格以获得更多控制。使用客户的 IP 或浏览器区域设置来确定在你的产品页面上显示哪种货币。

使用 Stripe 构建无头商务集成要花多少钱? 这取决于范围。基本的 Checkout Sessions 集成可以在几天内完成。完整功能的设置,包括订阅、客户门户、webhooks 和自定义 UI,通常需要 2-6 周的开发时间。如果你想讨论你的具体需求,请查看我们的 价格页面联系我们 -- 我们已经在各种行业中构建了这些集成,可以给你一个现实的估计。