PAYATHON 2026

React Native 通过 WebView 集成第三方支付能否上架 Apple Store?

支付老李

明确结论

可以上架,但能否通过审核,主要取决于应用销售什么商品、采用什么支付流程,而不是 React Native 或 WebView 本身。

  • 销售服装、外卖、酒店、交通、上门服务等实体商品或线下服务时,通常可以在 WebView 中接入第三方支付网关。
  • 销售数字内容、会员权益、虚拟货币、订阅或应用内功能时,原则上应使用 Apple 的 In-App Purchase(StoreKit)。如果通过 WebView 跳转到第三方支付,通常会被视为绕过应用内购买机制,拒审风险较高。
  • 某些地区、应用类型或特定 entitlement 可能适用例外,但具体范围会随着 Apple 政策变化。提交审核前,需要查看当前版本的《App Review Guidelines》,特别是支付和应用内购买条款。

所以,仅仅把支付页面放进 WebView,并不能保证应用顺利上架。

Apple 审核主要看什么

商品是否属于数字商品

商品类型是最重要的判断标准。

通常可以使用第三方支付的场景有:

  • 实体商品
  • 外卖和生鲜配送
  • 酒店、机票和交通服务
  • 线下课程或线下活动
  • 需要在应用外实际履约的服务

通常需要使用 In-App Purchase 的场景有:

  • 应用会员和高级功能
  • 视频、音乐、电子书等数字内容
  • 游戏道具和虚拟货币
  • 云存储容量
  • 在线订阅
  • 在应用内消费的课程、权益或服务

判断依据不是支付页面的位置。即使用户在 WebView、Safari 或外部网站中付款,只要购买的是在应用内消费的数字商品,仍然可能需要使用 In-App Purchase。

WebView 是否只是网站包装

如果应用的主要功能只是打开一个移动网站,缺少原生功能、交互和实际应用价值,也可能因为功能过于简单或只是网站封装而被拒。

电商应用可以加入适合移动端使用的功能,例如:

  • 原生商品导航和搜索
  • 推送通知
  • 订单状态跟踪
  • 相机扫码
  • 地址管理
  • 原生客服入口
  • 稳定的登录状态和返回导航
  • WebView 加载失败后的错误处理

支付流程是否安全、清楚

无论是审核还是后续运营,都应注意以下事项:

  • 全程使用 HTTPS
  • 不在 React Native 代码中保存支付密钥
  • 不通过 WebView 注入脚本读取银行卡号、CVV 等敏感信息
  • 由服务端创建支付订单
  • 由服务端验证支付结果签名,或通过 webhook 确认结果
  • 用户取消支付、支付失败或网络中断后,可以正常返回
  • 页面清楚展示商品、金额、退款规则和商户信息

客户端跳转到“支付成功”页面可以改善交互体验,但不能作为服务端确认到账的唯一依据。

推荐实现流程

1. 先确定商品类别

如果销售的是实体商品或线下服务,可以继续设计第三方支付流程。

如果业务中包含数字商品,应为这部分商品单独接入 StoreKit。不要把实体商品和数字商品放在同一个 WebView 结算流程中,否则审核人员可能难以判断支付方式是否合规。

2. 由服务端创建支付会话

React Native 客户端只向业务服务器提交订单号。服务器核对商品、金额和用户身份,再调用支付网关创建一次性支付会话。

流程可以设计为:

React Native App
    -> Business Server
    -> Payment Gateway
    <- Checkout URL
    -> WebView opens Checkout URL
    -> Payment Gateway sends webhook to Business Server
    <- App queries verified order status

支付网关的 secret key 必须保存在服务端,不能写进 App。

3. 限制 WebView 可访问的地址

下面是一个简化示例,适用于实体商品的第三方结算页面:

import React, { useCallback } from 'react';
import { Alert, Linking } from 'react-native';
import { WebView } from 'react-native-webview';

const CHECKOUT_HOSTS = new Set([
  'checkout.example.com',
  'payments.example-gateway.com',
]);

export function CheckoutScreen() {
  const handleNavigation = useCallback((request: { url: string }) => {
    try {
      const url = new URL(request.url);

      if (url.protocol === 'https:' && CHECKOUT_HOSTS.has(url.hostname)) {
        return true;
      }

      if (url.protocol === 'myshop:') {
        // 这里只负责返回应用,不应直接认定订单已经支付。
        return false;
      }

      if (['tel:', 'mailto:'].includes(url.protocol)) {
        Linking.openURL(request.url);
        return false;
      }
    } catch {
      Alert.alert('无法打开链接', '支付页面返回了无效地址。');
      return false;
    }

    return false;
  }, []);

  return (
    <WebView
      source={{ uri: 'https://checkout.example.com/session/SESSION_ID' }}
      onShouldStartLoadWithRequest={handleNavigation}
      javaScriptEnabled
      domStorageEnabled
      sharedCookiesEnabled={false}
      thirdPartyCookiesEnabled={false}
      originWhitelist={['https://*']}
      setSupportMultipleWindows={false}
      onError={() => {
        Alert.alert('页面加载失败', '请检查网络后重试。');
      }}
    />
  );
}

示例中的域名、回调协议和会话地址需要替换为实际配置。域名白名单应尽可能精确,不要无条件放行所有跳转。

4. 通过服务端确认支付状态

支付网关发送 webhook 后,服务端验证签名并更新订单状态。App 返回订单页时,再向业务服务器查询:

async function fetchOrderStatus(orderId: string) {
  const response = await fetch(
    `https://api.example.com/orders/${encodeURIComponent(orderId)}`,
    {
      headers: {
        Authorization: `Bearer ${getAccessToken()}`,
      },
    },
  );

  if (!response.ok) {
    throw new Error('Unable to fetch order status');
  }

  return response.json();
}

不要信任 WebView 返回的 status=success、JavaScript 消息或 URL 查询参数。这些信息可能被伪造,也可能在款项尚未最终入账时提前触发。

5. 如果销售数字商品,使用 StoreKit

React Native 项目通常通过维护良好的原生桥接库调用 StoreKit。具体 API 取决于所选库,但整体流程应为:

App requests products from StoreKit
    -> User confirms purchase
    -> App receives transaction
    -> Server validates transaction data
    -> Server grants digital entitlement
    -> App finishes the transaction

不要使用隐藏按钮、远程配置或审核后切换页面等方式,将数字商品支付转移到 WebView。这种做法可能导致拒审,也可能影响后续版本审核和开发者账户状态。

提交审核时的准备

建议在 App Store Connect 的审核备注中写清楚:

  • 应用销售的是实体商品还是线下服务
  • 第三方支付用于哪些商品
  • 审核账号和登录步骤
  • 如何进入结算页面
  • 测试环境是否会真实扣款
  • 如何取消测试订单或申请退款
  • 如果应用同时提供数字内容,使用什么方式购买

例如:

The third-party checkout is used only for physical goods delivered to the
customer's shipping address. No digital content, subscription, virtual
currency, or in-app feature is unlocked through this checkout flow.

Review account:
Email: review@example.com
Password: ********

Steps:
1. Sign in with the review account.
2. Add any physical product to the cart.
3. Open Cart and tap Checkout.
4. Use the provided sandbox payment method.

审核备注可以帮助审核人员理解业务,但不能让原本不合规的支付方式变得合规。

容易导致拒审的问题

以下问题需要重点避免:

  • 使用 WebView 销售或解锁数字内容
  • WebView 支付完成后直接开放会员功能
  • 应用基本只是网站套壳
  • 审核账号无法登录,或无法完成测试订单
  • 支付页空白、返回按钮失效,或跳转到不受控的网站
  • 客户端包含支付网关的 secret key
  • 根据前端回调直接把订单标记为已支付
  • 商品类型、价格或退款规则展示不清楚
  • 支付页面要求下载其他 App,却没有正确处理跳转
  • 使用不受限制的 onMessage 或 URL scheme 执行敏感操作

上架前检查清单

提交前至少确认以下事项:

  • 商品属于实体商品,或属于允许使用外部支付的服务
  • 数字商品已经改用 In-App Purchase
  • 已重新核对当前 Apple 审核规则
  • 支付页面和 API 全部使用 HTTPS
  • 服务端负责创建订单和确认支付
  • WebView 只允许访问可信域名
  • 已处理支付成功、失败、取消和超时情况
  • 客户端没有保存支付密钥
  • App 提供了足够的原生功能和使用价值
  • 已准备可用的审核账号、测试步骤和测试支付方式

React Native 加 WebView 通常不会直接妨碍应用上架。如果第三方支付只用于实体商品或合规的线下服务,同时支付安全措施和审核资料准备齐全,应用就具备上架条件。如果销售的是应用内数字商品,应优先使用 StoreKit,否则仅靠 WebView 支付很难稳定通过审核。

备注:内容仅供参考。