如何在 react-native expo 中配置第三方支付 SDK?
结论
Expo 项目中没有 ios 和 android 目录很正常,这表示项目目前采用 Expo 托管式结构。
如果支付 SDK 包含原生 iOS 或 Android 代码,通常不能直接在 Expo Go 中使用。可以按以下方式处理:
- 先确认支付服务商是否提供 Expo Config Plugin。
- 如果提供,安装插件并创建 Expo Development Build。
- 如果没有插件,使用
npx expo prebuild生成原生目录,再手动配置 SDK,或者自行编写 Config Plugin。 - 如果 SDK 只有原生版本,没有 React Native 封装,还要编写 Native Module 或 Turbo Module,作为 JavaScript 与原生 SDK 之间的桥接层。
不要把支付 SDK 仓库中的 ios、android 文件夹直接复制到 Expo 项目中。这些目录通常属于 SDK 源码、示例工程或 React Native 原生模块,并不是可以直接覆盖到项目根目录的应用工程。
为什么 Expo Go 无法直接加载支付 SDK
Expo Go 是预先编译的通用客户端,只包含 Expo 官方预置的原生模块。第三方支付 SDK 需要将新的原生代码编译进 App,而 Expo Go 无法在运行时加入 iOS Framework、CocoaPods 依赖、Android AAR 或 Maven 依赖。
因此,以下 SDK 通常需要通过 Development Build 或正式原生构建使用:
- 依赖 CocoaPods 的 iOS SDK
- 依赖 Gradle、Maven 或 AAR 的 Android SDK
- 包含
NativeModules或 Turbo Module 的 React Native 包 - 需要修改
Info.plist、AndroidManifest.xml或原生入口文件的 SDK - 需要配置 URL Scheme、支付回调 Activity 或 App Delegate 的 SDK
如果支付网关只提供 REST API,并且付款页面可以通过浏览器或 WebView 打开,则可能不需要安装原生 SDK。不过,支付平台是否允许使用 WebView,以及如何处理支付回调,仍要以其接入规范为准。
第一步:确认 SDK 的接入类型
问题中没有提供可访问的具体 SDK 地址,因此暂时无法判断它是否支持 Expo。安装前应查看 README、package.json、Podspec 和 Android Gradle 配置,确认以下内容:
- 是否提供 React Native npm 包
- 是否明确支持 Expo
- 是否提供 Config Plugin
- 是否要求运行
pod install - 是否需要修改
MainApplication、AppDelegate或AndroidManifest.xml - 是否支持项目当前使用的 React Native 新架构
- 支付结果通过 Promise、事件监听、URL Scheme 还是 Deep Link 返回
- 是否分别提供测试环境和生产环境
如果文档只介绍 Swift、Objective-C、Kotlin 或 Java 接入方式,它很可能只是原生 SDK,JavaScript 无法直接调用。
方案一:使用支付 SDK 提供的 Expo Config Plugin
如果支付 SDK 提供 Expo Config Plugin,接入会相对简单。假设支付服务商提供了名为 react-native-example-payment 的包,安装命令可能如下:
npx expo install react-native-example-payment
然后在 app.json 中注册插件:
{
"expo": {
"name": "MyApp",
"slug": "my-app",
"plugins": [
[
"react-native-example-payment",
{
"merchantId": "YOUR_MERCHANT_ID"
}
]
]
}
}
这里的包名、字段名和参数只用于展示配置结构,必须替换成支付 SDK 文档中真实存在的内容,不能直接使用。
安装原生依赖后,需要创建 Development Build,不能继续使用 Expo Go:
npx expo prebuild
npx expo run:android
iOS 可以运行:
npx expo run:ios
也可以使用 EAS 构建开发客户端。下面是一个 eas.json 示例:
{
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"production": {}
}
}
然后执行:
eas build --profile development --platform android
或者:
eas build --profile development --platform ios
每次新增或修改原生依赖后,通常都要重新构建 Development Build。只重启 Metro,无法把新的原生 SDK 加入已经安装的客户端。
方案二:SDK 没有 Config Plugin
如果 SDK 是 React Native 原生模块,但没有提供 Expo Config Plugin,可以先生成原生工程:
npx expo prebuild
运行后,项目中会出现:
android/
ios/
接下来按照支付 SDK 的原生文档完成配置,例如:
- 在 iOS 中添加 Pod、Framework、URL Scheme 或
Info.plist配置 - 在 Android 中添加 Maven 仓库、Gradle 依赖、Manifest 权限或回调 Activity
- 配置支付应用跳转和返回 App 的 Deep Link
- 添加支付平台要求的包名、签名、公钥或商户标识
完成 iOS 原生依赖配置后,通常还要运行:
npx pod-install
然后重新编译:
npx expo run:ios
npx expo run:android
直接修改生成的 ios 和 android 文件可以生效,但后续执行:
npx expo prebuild --clean
可能会重新生成原生工程,覆盖手动修改。对于需要长期维护的项目,最好将这些修改封装成自定义 Config Plugin。
下面的配置只用于说明插件结构:
// app.config.js
export default {
expo: {
name: "MyApp",
slug: "my-app",
plugins: [
"./plugins/withExamplePayment"
]
}
};
自定义插件可以通过 @expo/config-plugins 修改 Info.plist、Android Manifest、Gradle 文件及其他原生配置。具体代码必须按照支付 SDK 的真实安装要求编写,不能套用通用模板盲目添加依赖。
方案三:SDK 只有 iOS 和 Android 原生版本
如果支付平台完全没有 React Native 包,即使已经生成 ios 和 android 目录,JavaScript 代码也不能自动调用原生 SDK。项目还需要实现一层桥接:
React Native JavaScript
↓
Native Module / Turbo Module
↓
iOS SDK 和 Android SDK
JavaScript 侧可能采用类似的调用方式:
import { NativeModules } from "react-native";
const { ExamplePayment } = NativeModules;
async function startPayment(order) {
const result = await ExamplePayment.pay({
orderId: order.id,
paymentToken: order.paymentToken
});
return result;
}
ExamplePayment、pay 和相关参数都只是示意名称。对应的 iOS 与 Android 模块需要由项目自行实现,或者使用支付服务商提供的官方 React Native 封装。
对于刚接触 React Native 原生开发的团队,自行桥接支付 SDK 往往需要投入不少工作,还要处理:
- iOS 与 Android 接口差异
- App 跳转和回调
- Promise 与事件通知
- 生命周期变化
- 取消支付和重复回调
- 原生异常转换
- 新旧架构兼容
- SDK 升级后的维护
如果支付服务商提供官方维护的 React Native SDK,应优先采用官方方案。
JavaScript 侧的支付流程
无论使用哪种 SDK,都不应把商户密钥直接保存在 App 中。较合理的调用流程如下:
App 请求业务服务器创建订单
↓
服务器调用支付网关创建支付会话
↓
服务器把临时 paymentToken 返回给 App
↓
App 调用原生 SDK 发起支付
↓
支付网关通过 Webhook 通知服务器
↓
App 向服务器查询最终订单状态
示例代码:
async function pay(orderId) {
const response = await fetch("https://api.example.com/payments/session", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${userToken}`
},
body: JSON.stringify({ orderId })
});
if (!response.ok) {
throw new Error("创建支付会话失败");
}
const { paymentToken } = await response.json();
const sdkResult = await ExamplePayment.pay({
paymentToken
});
const statusResponse = await fetch(
`https://api.example.com/orders/${encodeURIComponent(orderId)}/status`,
{
headers: {
Authorization: `Bearer ${userToken}`
}
}
);
if (!statusResponse.ok) {
throw new Error("查询订单状态失败");
}
return statusResponse.json();
}
客户端收到”支付成功”回调,并不代表订单一定已经完成。最终状态应由服务器通过支付网关 API 或经过签名验证的 Webhook 确认,防止客户端回调遭到伪造、篡改或重复提交。
必须检查的配置
接入时需要逐项核对以下配置。
iOS
Info.plist是否需要 URL Scheme- 是否需要添加
LSApplicationQueriesSchemes - 是否要求修改
AppDelegate - CocoaPods 的最低 iOS 版本是否与项目一致
- 支付 SDK 是否支持模拟器
- Bundle Identifier 是否已在支付后台登记
- Universal Links 或回调域名是否正确
- 正式包的签名与证书是否符合要求
Android
AndroidManifest.xml是否需要权限、Activity、Service 或 Intent Filter- Gradle 是否需要额外的 Maven 仓库
minSdkVersion是否符合 SDK 要求- Application ID 和签名证书指纹是否已登记
- R8/ProGuard 是否需要 keep 规则
- Deep Link 的 scheme、host 和 path 是否正确
- 正式签名与调试签名是否分别配置
Expo
- 不再使用 Expo Go 测试该原生模块
- 安装新的原生依赖后,重新生成并安装 Development Build
- Config Plugin 是否与当前 Expo SDK 兼容
- 执行
expo prebuild --clean前,确认手动修改的原生代码已经保存或封装成插件 - 云端构建所需的文件、环境变量和证书是否已经配置
支付业务的额外注意事项
支付密钥、Webhook Secret 和私钥不能写入 React Native 代码、app.json 或公开环境变量。所有打包进客户端的数据都有可能被提取。
支付流程还要覆盖以下情况:
- 用户取消支付
- 支付应用未安装
- 网络中断
- App 进入后台后被系统终止
- SDK 返回成功,但服务器尚未收到通知
- Webhook 重复发送
- 同一订单被重复支付
- 沙箱与生产环境配置混用
- 金额和币种在客户端被篡改
如果销售的是 App 内数字内容、订阅或虚拟权益,还要先核对 Apple App Store 和 Google Play 的支付政策。部分场景要求使用应用商店的应用内购买机制,不能直接接入普通第三方支付网关。实体商品和线下服务通常适用不同的规则。
推荐实施顺序
建议按以下顺序实施:
- 查找支付平台官方 React Native SDK,不要直接使用原生示例工程。
- 确认 SDK 是否提供 Expo Config Plugin。
- 如果有插件,通过插件完成配置并构建 Development Build。
- 如果没有插件,评估社区插件是否可信,以及是否仍在维护。
- 必须手动配置时,运行
npx expo prebuild,再按照 iOS 和 Android 文档接入。 - 只有原生 SDK 时,实现 React Native 桥接层。
- 在沙箱环境测试支付成功、失败、取消、重复回调和 App 重启等流程。
- 由服务器验证支付结果,再更新订单状态。
- 最后检查应用商店政策、正式签名和生产环境配置。
项目缺少 ios 和 android 文件夹并不是问题。需要先确认支付 SDK 是否支持 React Native 或 Expo,以及它要求加入哪些原生代码。只要 SDK 包含原生模块,就不能使用 Expo Go,而要通过 Development Build、Prebuild 或完整的原生工作流进行编译。
备注:内容仅供参考。
