PhonePe React-Native SDK 调用 UPI Intent 始终失败
结论
Transaction Interrupted! 是 SDK 对“支付流程未正常完成”的通用失败提示,不能据此认定 Base64 或 SHA-256 有问题。
这段调用有几个地方需要优先修正:
startPGTransaction()没有等待init()完成。init()的第三个参数通常是 PhonePe 分配的appId,不是 Android 签名证书的 SHA 指纹。- 不同 SDK 版本对
startPGTransaction()第五个参数的定义可能不同。它可能是商户应用的 package name 或 app schema,不一定是目标 PhonePe 应用的包名。 - UAT 凭据不等于真实 PhonePe App 的生产支付环境。Intent 流程必须使用 PhonePe 当前提供的 UAT 测试方式或测试应用。
- 每次发起支付都必须使用唯一的
merchantTransactionId。如果先调用 API,再把同一个transaction_123交给 SDK,就可能形成重复交易。 - 示例中的 JSON 是解码后的请求体,不是 PhonePe 返回的响应对象。
API 返回 redirectUrl,只说明 PhonePe 服务端接受了创建交易的请求。之后由 Android SDK 拉起 UPI App 并完成回跳的流程,仍可能存在问题。
先修正初始化顺序
当前代码中的两个 Promise 会并行启动。startPGTransaction() 执行时,SDK 可能还没有完成初始化。
应改为串行调用:
import PhonePePaymentSDK from 'react-native-phonepe-pg';
async function startPhonePePayment() {
try {
const initResult = await PhonePePaymentSDK.init(
"UAT",
"PGTESTPAYUAT",
"YOUR_PHONEPE_APP_ID",
true
);
console.log("SDK initialization successful", initResult);
const result = await PhonePePaymentSDK.startPGTransaction(
requestBodyBase64,
checksum,
"/pg/v1/pay",
{ "Content-Type": "application/json" },
"YOUR_EXPECTED_PACKAGE_OR_SCHEMA",
"https://your-domain.example/payment/callback"
);
console.log("Transaction result", result);
} catch (error) {
console.error("PhonePe transaction error", error);
}
}
不要在 init().then(...) 外部立即调用 startPGTransaction()。
不要把证书指纹当成 appId
下面这个值看起来是 Android 证书指纹:
F4:1E:2F:64:BA:E6:19:1A:A7:C2:29:6D:5F:5D:2F:0A:29:E8:6E:5B
init() 的第三个参数通常应填写 PhonePe 为商户应用配置或分配的 appId:
PhonePePaymentSDK.init(
"UAT",
"PGTESTPAYUAT",
"YOUR_PHONEPE_APP_ID",
true
);
证书指纹一般用于 PhonePe 后台的 Android 应用绑定或白名单校验,不应自行填入 appId 参数。不同版本的 react-native-phonepe-pg 可能采用不同的参数定义,请以项目实际安装版本对应的 PhonePe 文档为准。如果当前版本允许在 UAT 环境中传入 null 或空的 appId,也必须严格按照该版本的说明传递,不能用证书指纹代替。
还要确认 PhonePe 后台登记的信息与当前构建一致:
- Android application ID/package name
- Debug 或 release 签名证书指纹
- Merchant ID
- App ID
- 当前环境,即
UAT或生产环境
Debug APK 和 release APK 通常使用不同的证书。如果后台登记的是 release 指纹,设备上运行的却是 debug 包,也可能导致 SDK 中断。
核对第五个参数的含义
请求体中的:
"targetApp": "com.phonepe.app"
表示希望将 UPI Intent 定向到 PhonePe App。
这不代表 startPGTransaction() 的第五个参数也一定要传:
"com.phonepe.app"
在某些 React Native SDK 版本中,这个位置表示商户应用的 package name、app schema 或平台回跳信息,并不是目标 UPI App。把目标 App 的包名错误地传到这里,可能导致 SDK 无法返回商户应用。
如果当前版本要求填写商户 Android 包名,应传入类似下面的值:
"com.example.merchantapp"
如果要求填写 app schema,则应传入已经在 Android 或 iOS 中配置的 schema。请检查当前安装版本的函数声明,不要混用其他版本或原生 Android SDK 的示例。
目标 UPI App 应在请求体的 paymentInstrument.targetApp 中指定:
{
"paymentInstrument": {
"type": "UPI_INTENT",
"targetApp": "com.phonepe.app"
}
}
每次使用新的交易号
不要反复使用:
"merchantTransactionId": "transaction_123"
应由服务端为每次支付生成唯一值:
const merchantTransactionId =
`TXN_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
生产环境最好由后端通过数据库或可靠的 ID 生成器创建交易号,避免发生碰撞。
尤其不要采用下面的流程:
- 使用
transaction_123调用/pg/v1/pay,获得redirectUrl。 - 再把同一请求和
transaction_123传给 SDK。 - 把第二次调用失败理解为 SDK 不支持该参数。
这可能是在重复创建同一笔交易。测试 API 和测试 SDK 时,必须分别使用不同的 merchantTransactionId。
校验 Base64 和 checksum
checksum 必须根据最终实际发送的 Base64 字符串计算,不能基于格式化后的 JSON 对象重新计算。旧版 /pg/v1/pay 接口通常使用类似下面的逻辑:
SHA256(base64Payload + "/pg/v1/pay" + saltKey) + "###" + saltIndex
需要逐项确认:
- 参与签名的 endpoint 与传给 SDK 的参数完全一致,例如都使用
/pg/v1/pay。 - Base64 编码后没有出现换行、空格或其他字符变化。
saltKey和saltIndex属于当前 Merchant ID 和当前环境。- 没有混用 UAT 与生产环境的 key。
- 每次修改请求 JSON 后,都重新生成 Base64 和 checksum。
SaltKey 不能放在 React Native 代码中。请求体和 checksum 应由业务后端生成,再把以下数据交给客户端:
{
"request": "<base64-payload>",
"checksum": "<sha256>###<salt-index>",
"apiEndpoint": "/pg/v1/pay"
}
客户端只负责调用 SDK。否则,一旦 APK 或 JavaScript bundle 被反编译,支付密钥就会直接泄露。
正确区分 UAT 与真实 PhonePe App
Custom UPI Intent 不只是一次 HTTP 请求,它还包含完整的应用调用和回跳过程:
商户 App → PhonePe SDK → UPI App → 支付结果 → 商户 App
因此,服务端返回 redirectUrl 只验证了前半段。还需要确认以下配置:
- 当前 PhonePe SDK 版本是否支持 UAT 环境的
UPI_INTENT。 - PhonePe 是否要求使用专门的 UAT/Test App 或模拟器。
- 生产版
com.phonepe.app是否可以处理 UAT 交易。 - 设备上是否安装了符合测试要求的 PhonePe App。
- Android 11 及以上版本是否需要在 manifest 中声明 package visibility。
- 商户 App 的回跳 scheme、intent filter 和 package name 是否正确。
如果 UAT 文档指定了测试 App 的包名,就不能把生产 PhonePe App 的:
com.phonepe.app
用作 UAT 环境的 targetApp。测试时必须使用 PhonePe 为当前环境明确提供的包名。如果文档没有写明,应请 PhonePe 支持团队确认该商户账号是否已经开通 UPI Intent UAT 测试能力。
建议的请求构造方式
请求应由后端生成,并在每次支付时使用新的交易号:
const payload = {
merchantId: "PGTESTPAYUAT",
merchantTransactionId: createUniqueTransactionId(),
merchantUserId: "90223250",
amount: 1000,
mobileNumber: "9999999999",
callbackUrl: "https://your-domain.example/payment/callback",
paymentInstrument: {
type: "UPI_INTENT",
targetApp: "PHONEPE_UAT_TARGET_PACKAGE"
},
deviceContext: {
deviceOS: "ANDROID"
}
};
然后按顺序调用 SDK:
async function payWithPhonePe(paymentData) {
const initialized = await PhonePePaymentSDK.init(
"UAT",
paymentData.merchantId,
paymentData.appId,
true
);
if (!initialized) {
throw new Error("PhonePe SDK initialization failed");
}
return PhonePePaymentSDK.startPGTransaction(
paymentData.request,
paymentData.checksum,
paymentData.apiEndpoint,
{ "Content-Type": "application/json" },
paymentData.packageNameOrSchema,
paymentData.callbackUrl
);
}
其中,appId、targetApp 和 packageNameOrSchema 必须按照当前 SDK 版本和 PhonePe UAT 配置填写,不能直接照搬生产环境的包名。
排查顺序
可以按下面的顺序检查:
- 把
startPGTransaction()移到await init()之后。 - 使用全新的
merchantTransactionId,不要复用测试 API 时用过的交易号。 - 将初始化参数中的证书指纹替换为 PhonePe 分配的真实
appId。 - 核对第五个参数要求的是商户 package name 还是 app schema。
- 重新生成 Base64 和 checksum,确认 endpoint、salt key 和 salt index 都属于 UAT 环境。
- 确认 UAT Intent 应使用哪个测试 App,以及对应的
targetApp包名。 - 打开 SDK 日志并查看 Android
logcat,重点检查初始化、应用签名、目标 App、Intent resolution 和回跳失败信息。 - 支付结果应以服务端交易状态查询接口或 webhook 为最终依据,不要只依赖客户端返回的
SUCCESS或FAILURE。
如果修正调用顺序、唯一交易号和参数含义后,SDK 仍然只返回通用错误,应把 Merchant ID、App ID、商户应用包名、签名指纹、SDK 版本和失败交易号提供给 PhonePe 技术支持,请他们核对 UAT 配置。不要提供 saltKey。
备注:内容仅供参考。
