# 如何为 Expo / React Native 应用添加支付功能

> 了解如何使用 Dodo Payments 和 WebBrowser.openBrowserAsync() 为 Expo 或 React Native 应用添加支付功能，帮助 SaaS 产品避免 30% 的应用商店费用。
- **Author**: Aarthi Poonia
- **Published**: 2026-03-24
- **Category**: Payments, Mobile, How-To
- **URL**: https://dodopayments.com/blogs/zh/add-payments-react-native-expo

---

使用 Expo 或 React Native 构建移动应用，是通过单一代码库触达 iOS 和 Android 用户的绝佳方式。不过，在实现变现时，许多开发者都会遇到瓶颈。Apple App Store 和 Google Play Store 都会从所有应用内购买中抽取 30% 的分成。对于利润微薄的 SaaS 产品来说，这可能会成为无法接受的成本。通过自己的结账页面接入[数字钱包解决方案](https://dodopayments.com/blogs/digital-wallet-solution)，可以让移动端体验保持流畅，同时避免这 30% 的费用。

在本指南中，我们将探讨如何使用 Dodo Payments 为 Expo 或 React Native 应用添加支付功能。我们将重点介绍一种使用外部支付链接的方案，以避免 30% 的平台费用，同时遵守 SaaS 和数字服务相关的应用商店政策。内容涵盖从后端设置到深层链接处理的完整流程，帮助你为用户提供无缝体验。

## 30% 的问题与 SaaS 例外

Apple 和 Google 对应用内购买有严格规定。一般来说，如果你销售的是在应用内消费的数字商品，就必须使用它们的原生支付系统。不过，对于"多平台"服务，有一个广为人知的例外。如果你的应用是基于 Web 的服务的配套应用，就可以允许用户在网站上付款，然后在应用中使用这些功能。

> 我们为那些在午夜构建产品的独立开发者设计了 Dodo 的 API。如果你需要一整个支付工程师团队才能完成集成，那就说明我们没有做好自己的工作。
>
> \- Ayush Agarwal，Dodo Payments 联合创始人兼 CPTO

通过 Dodo Payments 生成结账链接，你可以将用户引导至安全的 Web 结账页面。这样，你可以保留 100% 的收入（扣除 Dodo 的少量交易费），同时提供专业的支付体验。Dodo Payments 还是 MoR（名义商户），可以处理跨境销售涉及的全球税务和合规问题。

## 设置 Expo 环境

开始之前，你需要一个现有的 Expo 或 React Native 项目。我们将使用 `expo-web-browser` 和 `expo-linking` 软件包来处理结账流程以及返回应用的过程。

```bash
bunx expo install expo-web-browser expo-linking
```

这些软件包可以在应用内提供接近原生的浏览器体验，并让你能够在用户完成付款后处理深层链接。

## 在后端生成结账链接

你绝不应该直接从移动应用生成支付链接。这样做需要暴露你的私有 API 密钥，会带来重大的安全风险。相反，你应该在后端创建一个简单的端点，调用 Dodo Payments API 并返回结账 URL。

```javascript
// Example Node.js/Express backend
const DodoPayments = require("dodopayments");
const client = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
});

app.post("/create-checkout", async (req, res) => {
  const { productId, userEmail } = req.body;

  try {
    const session = await client.checkoutSessions.create({
      product_cart: [{ product_id: productId, quantity: 1 }],
      customer: { email: userEmail },
      return_url: "myapp://payment-success",
    });
    res.json({ url: session.checkout_url });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
```

注意 `return_url`。这是一个自定义 scheme，付款完成后会触发应用打开。你需要在 Expo 项目中配置这个 scheme。

## 在 Expo 中配置深层链接

要允许通过自定义 URL scheme 打开应用，你需要在 `app.json` 或 `app.config.js` 文件中添加 `scheme` 属性。

```json
{
  "expo": {
    "name": "My App",
    "slug": "my-app",
    "scheme": "myapp",
    "ios": {
      "bundleIdentifier": "com.yourname.myapp"
    },
    "android": {
      "package": "com.yourname.myapp"
    }
  }
}
```

完成此配置后，任何以 `myapp://` 开头的链接都会尝试打开你的应用。这是实现从 Web 结账页面无缝返回原生界面的关键。

## 在 React Native 中实现结账流程

现在，让我们构建一个触发支付流程的 UI 组件。我们将使用 `WebBrowser.openBrowserAsync`，在安全的应用内浏览器窗口中打开 Dodo Payments 结账页面。

```tsx
import React from "react";
import { Button, Alert } from "react-native";
import * as WebBrowser from "expo-web-browser";
import * as Linking from "expo-linking";

export default function PaymentButton({ productId }) {
  const handlePress = async () => {
    try {
      // 1. Fetch the checkout URL from your own backend (not from Dodo directly)
      const response = await fetch(
        `${process.env.EXPO_PUBLIC_API_URL}/create-checkout`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ productId, userEmail: "user@example.com" }),
        },
      );
      const { url } = await response.json();

      // 2. Open the checkout URL in the in-app browser
      const result = await WebBrowser.openBrowserAsync(url);

      // 3. Handle the result (optional, deep links are more reliable)
      if (result.type === "cancel") {
        Alert.alert(
          "Payment Cancelled",
          "You closed the browser before finishing.",
        );
      }
    } catch (error) {
      Alert.alert("Error", "Could not start checkout process.");
    }
  };

  return <Button title="Upgrade to Pro" onPress={handlePress} />;
}
```

与标准的 `Linking.openURL` 调用相比，使用 `WebBrowser` 可以让用户留在应用的使用场景中。在 iOS 上，它使用 `SFSafariViewController`；在 Android 上，它使用 Chrome Custom Tabs。这两者都深受用户信任。

## 优化用户体验

支付流程的质量取决于用户体验。在移动应用中，用户期待快速、清晰的操作反馈。当他们点击"购买"或"升级"按钮时，应该立即看到反馈。

```tsx
const [loading, setLoading] = useState(false);

const handlePress = async () => {
  setLoading(true);
  try {
    const response = await fetch(
      `${process.env.EXPO_PUBLIC_API_URL}/create-checkout`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ productId, userEmail: "user@example.com" }),
      },
    );
    const { url } = await response.json();
    await WebBrowser.openBrowserAsync(url);
  } catch (error) {
    Alert.alert("Error", "Could not start checkout process.");
  } finally {
    setLoading(false);
  }
};
```

添加加载状态可以防止用户多次点击按钮，并让他们了解流程正在进行。你还应该考虑添加一个由深层链接回调触发的"成功"页面，而不是仅将结果记录到控制台。

## 移动支付的安全注意事项

处理支付时，安全性至关重要。虽然 Dodo Payments 会处理敏感的卡片数据，但你需要负责保护自己的 API 以及应用的完整性。

- **基于令牌的身份验证**：确保你的 `/create-checkout` 端点受到安全身份验证令牌（例如 JWT）的保护。只有经过身份验证的用户才能生成支付链接。
- **输入验证**：始终在后端验证 `productId` 和其他参数。绝不要信任直接来自移动应用的数据。
- **全程使用 HTTPS**：确保应用与后端之间的所有通信都通过 HTTPS 进行，防止中间人攻击拦截敏感数据。
- **证书固定**：对于高安全性应用，可以考虑实现证书固定，确保应用只与指定的后端服务器通信。

## 基于浏览器的方案与原生 SDK 方案对比

选择基于浏览器的结账方式还是原生 SDK，取决于你的优先事项。

| 功能             | 基于浏览器（WebBrowser） | 原生 SDK             |
| ---------------- | ------------------------ | -------------------- |
| **实现速度**     | 非常快                   | 中等                 |
| **用户体验**     | 良好（接近原生）         | 出色（完全原生）     |
| **规避平台费用** | 较高（更容易说明合理性） | 较低（审查更严格）   |
| **自定义能力**   | 受限于 Dodo Dashboard    | 高（原生 UI 组件）   |
| **维护成本**     | 低（Dodo 更新 Web UI）   | 中等（需要更新 SDK） |

对于大多数 SaaS 初创企业来说，基于浏览器的方案是最佳起点。它更容易实现，维护成本更低，也能清晰地帮助你规避 30% 的平台费用。随着业务规模扩大、需求变得更加复杂，你随时可以转向原生 SDK，获得更完善的体验。

## 案例研究：规避 30% 费用的 SaaS 应用

许多成功的 SaaS 公司都采用"阅读器应用"或"多平台"模式来规避平台费用。

- **Netflix**：用户无法在 Netflix 应用内注册或支付订阅费用，而必须在网站上完成这些操作，然后登录应用观看内容。
- **Spotify**：与 Netflix 类似，Spotify 长期以来一直将用户引导至网站购买高级订阅，以规避"Apple 税"。
- **Basecamp**：Basecamp 团队一直公开谈论他们如何为项目管理软件使用外部支付方式。

参考这些案例，你可以构建一种可持续的商业模式，而不必受应用商店把关者政策变化的影响。Dodo Payments 提供了让这一模式顺畅运行所需的基础设施。

## 处理支付回调

用户完成付款后，Dodo Payments 会将其重定向到你指定的 `return_url`。你需要在应用中监听这一事件，以更新 UI 并确认购买。

```tsx
import { useEffect } from "react";
import * as Linking from "expo-linking";

export function usePaymentHandler() {
  useEffect(() => {
    const handleUrl = (event) => {
      const { path, queryParams } = Linking.parse(event.url);
      if (path === "payment-success") {
        // Update user state, show success message, etc.
        console.log("Payment successful!", queryParams);
      }
    };

    const subscription = Linking.addEventListener("url", handleUrl);

    // Check if the app was opened via a deep link
    Linking.getInitialURL().then((url) => {
      if (url) handleUrl({ url });
    });

    return () => subscription.remove();
  }, []);
}
```

这个 hook 应该放在组件树的较高位置，例如 `App.js` 或主导航容器中。这样可以确保用户从结账页面返回后，应用立即做出响应。

## 支付流程架构

下图展示了典型支付会话期间系统各部分之间的交互方式。

```mermaid
flowchart LR
    A[Mobile App] -->|"POST /create-checkout"| B[Your Backend]
    B -->|"API Call"| C[Dodo Payments]
    C -->|"Session URL"| B
    B -->|"URL"| A
    A -->|"Open Browser"| D[Dodo Checkout Page]
    D -->|"Payment Success"| E[User Redirect]
    E -->|"myapp://success"| A
    C -->|"Webhook"| B
    B -->|"Update DB"| F[(Database)]
```

这一架构确保后端始终是事实来源。深层链接可以向用户提供即时反馈，但你应该依靠 Dodo Payments 的 webhook，在数据库中真正授予用户功能访问权限。

## 合规与应用商店政策

你需要谨慎考虑如何呈现这一支付选项。尤其是 Apple 对于"引导"用户使用外部支付方式的应用非常严格。为了降低风险：

- **不要使用"购买"按钮**，将销售纯数字商品的用户直接引导至网站。
- **使用"管理订阅"**或"账户设置"区域来提供链接。
- **将其定位为多平台服务**。说明用户也可以在网站上管理账户。
- **避免在应用 UI 中提及"App Store 费用"**或"更低价格"。

对于许多 SaaS 产品来说，这种方式完全合法，并且 Netflix、Spotify 和 Basecamp 等公司都在广泛使用。

## MoR（名义商户）对移动业务的优势

在移动端开展全球销售会增加另一层复杂性。每个国家都有自己的数字税规则（例如欧盟的 VAT 或印度的 GST）。如果由你自行处理，就需要在数十个司法管辖区进行税务登记。

使用 Dodo Payments 作为你的 MoR（名义商户），就可以将这些工作全部交给 Dodo。Dodo 会处理税款计算、征收和申报缴纳，也会处理当地法律法规的合规要求，确保你的应用始终符合法律规定。这对于没有专门法务或会计部门的独立创始人和小型团队尤其有价值。

## 进阶：使用 React Native SDK

虽然基于浏览器的方案非常适合规避费用，但 Dodo Payments 也提供原生 React Native SDK，以实现更深度的集成体验。对于在法律要求使用替代支付方式的地区运营的应用来说，这尤其有用，例如受《数字市场法案》（DMA）约束的欧盟地区。

SDK 提供了 `presentPaymentSheet` 函数，可以打开用于收集卡片信息的原生 UI。由于它与应用设计的融合度更高，因此可能带来更高的转化率。

```tsx
import { useCheckout } from "dodopayments-react-native-sdk";

const { initPaymentSession, presentPaymentSheet } = useCheckout();

const handleNativePayment = async () => {
  const { clientSecret } = await fetchPaymentParams();
  await initPaymentSession({ clientSecret });
  const result = await presentPaymentSheet();
  // Handle result...
};
```

不过请注意，根据所在地区以及所销售产品的类型，使用原生 SDK 仍可能触发平台费用要求。在确定实现方案之前，请务必查看最新的 [App Store Fee Bypass Guide](https://dodopayments.com/blogs/sell-software-without-app-store)。

## 测试移动端集成

测试移动支付可能比较棘手。Expo Go 应用为此提供了很好的环境。你可以直接在实体设备上测试深层链接和浏览器集成。

1. **使用测试模式**：确保你的 Dodo Payments API 密钥设置为 `test_mode`。
2. **测试深层链接**：使用 `bunx uri-scheme open` 命令，测试应用是否能正确响应自定义 scheme。
3. **验证 Webhook**：使用 `ngrok` 等工具，将 Dodo 的 webhook 转发到本地开发服务器。

## 结语

为 Expo 或 React Native 应用添加支付功能，并不意味着你必须放弃辛苦赚来的 30% 收入。借助 Dodo Payments 和明智的基于 Web 的结账策略，你可以构建能够在全球扩展的盈利型 SaaS。

使用 `expo-web-browser` 处理结账，并使用 `expo-linking` 处理返回应用的流程，可以提供流畅、接近原生的用户体验，同时让你掌控利润空间。由 Dodo Payments 处理税务和合规后，你可以专注于最重要的事情：为用户打造出色的产品。

想进一步了解移动端变现，请查看我们的[面向 SaaS 的嵌入式支付指南](https://dodopayments.com/blogs/embedded-payments-saas)和[订阅定价模型指南](https://dodopayments.com/blogs/subscription-pricing-models)。你还可以了解更多关于[MoR（名义商户）模式](https://dodopayments.com/blogs/merchant-of-record-for-saas)的信息，以及它如何简化全球商业交易。

准备好让你的移动应用更上一层楼了吗？[注册 Dodo Payments](https://dodopayments.com)，立即开始收款。

## 常见问题

### 这种方式是否违反 Apple 的 App Store 政策？

对于 SaaS 产品和多平台服务，只要你没有明确引导用户前往网站以"节省费用"或规避平台费用，这种方式通常符合政策。许多大型应用都成功采用了这种"阅读器应用"或"多平台"模式。

### 我可以通过这种方式使用 Apple Pay 或 Google Pay 吗？

可以！当你在 `WebBrowser` 窗口中打开 Dodo Payments 结账页面时，用户可以使用 Dodo Dashboard 中启用的所有支付方式，包括 Apple Pay 和 Google Pay，前提是这些方式在浏览器环境中受支持。

### 如何处理订阅取消？

理想情况下，订阅管理应在网站上进行，或者通过应用中的专用"管理订阅"链接打开 Dodo Payments 客户门户。你的后端会收到 `subscription.cancelled` webhook，从而可以实时更新用户的访问权限。

### React Native SDK 比浏览器方案更好吗？

SDK 能提供更接近原生的体验，有助于提高转化率。不过，在某些司法管辖区，浏览器方案通常更容易实现，也更灵活，更有利于规避平台费用。最佳选择取决于你的具体产品和目标市场。

### 如果用户在付款前关闭浏览器，会发生什么？

如果用户手动关闭窗口，`WebBrowser.openBrowserAsync` 函数会返回一个 `type: 'cancel'` 的结果对象。你可以利用这一结果向用户显示有帮助的消息，或提供支持。
---
- [More Payments articles](https://dodopayments.com/blogs/category/payments)
- [All articles](https://dodopayments.com/blogs)