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 支付很难稳定通过审核。
备注:内容仅供参考。
