PAYATHON 2026

PhonePe React-Native SDK 调用 UPI Intent 始终失败

支付阿杰

结论

Transaction Interrupted! 是 SDK 对“支付流程未正常完成”的通用失败提示,不能据此认定 Base64 或 SHA-256 有问题。

这段调用有几个地方需要优先修正:

  1. startPGTransaction() 没有等待 init() 完成。
  2. init() 的第三个参数通常是 PhonePe 分配的 appId,不是 Android 签名证书的 SHA 指纹。
  3. 不同 SDK 版本对 startPGTransaction() 第五个参数的定义可能不同。它可能是商户应用的 package name 或 app schema,不一定是目标 PhonePe 应用的包名。
  4. UAT 凭据不等于真实 PhonePe App 的生产支付环境。Intent 流程必须使用 PhonePe 当前提供的 UAT 测试方式或测试应用。
  5. 每次发起支付都必须使用唯一的 merchantTransactionId。如果先调用 API,再把同一个 transaction_123 交给 SDK,就可能形成重复交易。
  6. 示例中的 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 生成器创建交易号,避免发生碰撞。

尤其不要采用下面的流程:

  1. 使用 transaction_123 调用 /pg/v1/pay,获得 redirectUrl。
  2. 再把同一请求和 transaction_123 传给 SDK。
  3. 把第二次调用失败理解为 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 配置填写,不能直接照搬生产环境的包名。

排查顺序

可以按下面的顺序检查:

  1. 把 startPGTransaction() 移到 await init() 之后。
  2. 使用全新的 merchantTransactionId,不要复用测试 API 时用过的交易号。
  3. 将初始化参数中的证书指纹替换为 PhonePe 分配的真实 appId。
  4. 核对第五个参数要求的是商户 package name 还是 app schema。
  5. 重新生成 Base64 和 checksum,确认 endpoint、salt key 和 salt index 都属于 UAT 环境。
  6. 确认 UAT Intent 应使用哪个测试 App,以及对应的 targetApp 包名。
  7. 打开 SDK 日志并查看 Android logcat,重点检查初始化、应用签名、目标 App、Intent resolution 和回跳失败信息。
  8. 支付结果应以服务端交易状态查询接口或 webhook 为最终依据,不要只依赖客户端返回的 SUCCESS 或 FAILURE。

如果修正调用顺序、唯一交易号和参数含义后,SDK 仍然只返回通用错误,应把 Merchant ID、App ID、商户应用包名、签名指纹、SDK 版本和失败交易号提供给 PhonePe 技术支持,请他们核对 UAT 配置。不要提供 saltKey。

备注:内容仅供参考。