安卓PayUCheckout Pro集成报错:无法将字符串转为JSONObject
PayU Checkout Pro 调用支付触发onError:String转JSONObject失败
问题详情
已从官网获取MerchantID和SaltKey,在build.gradle中引入依赖:
implementation 'in.payu:payu-checkout-pro:2.0.23'
调用支付时触发onError方法,错误日志:
E/ErrorMessage: Value <!DOCTYPE of type java.lang.String cannot be converted to JSONObject
尝试过本地生成Hash,也试过服务端生成,相关Kotlin代码如下:
btnPay.setOnClickListener { val additionalParamsMap: HashMap<String, Any?> = HashMap() additionalParamsMap[PayUCheckoutProConstants.CP_UDF1] = "udf1" additionalParamsMap[PayUCheckoutProConstants.CP_UDF2] = "udf2" additionalParamsMap[PayUCheckoutProConstants.CP_UDF3] = "udf3" additionalParamsMap[PayUCheckoutProConstants.CP_UDF4] = "udf4" additionalParamsMap[PayUCheckoutProConstants.CP_UDF5] = "udf5" additionalParamsMap[PayUCheckoutProConstants.SODEXO_SOURCE_ID] = "srcid123" val payUPaymentParams = PayUPaymentParams.Builder() .setAmount("10") .setIsProduction(true) .setKey("Key") .setProductInfo("Macbook Pro") .setPhone("9874563210") .setTransactionId(System.currentTimeMillis().toString()) .setFirstName("firstname") .setEmail("email") .setSurl("https://payuresponse.firebaseapp.com/success") .setFurl("https://payuresponse.firebaseapp.com/failure") .setUserCredential("key:email") .setAdditionalParams(additionalParamsMap) .build() PayUCheckoutPro.open( this, payUPaymentParams, object : PayUCheckoutProListener { override fun onPaymentSuccess(response: Any) { response as HashMap<*, *> val payUResponse = response[PayUCheckoutProConstants.CP_PAYU_RESPONSE] val merchantResponse = response[PayUCheckoutProConstants.CP_MERCHANT_RESPONSE] } override fun onPaymentFailure(response: Any) { response as HashMap<*, *> val payUResponse = response[PayUCheckoutProConstants.CP_PAYU_RESPONSE] val merchantResponse = response[PayUCheckoutProConstants.CP_MERCHANT_RESPONSE] } override fun onPaymentCancel(isTxnInitiated: Boolean) { } override fun onError(errorResponse: ErrorResponse) { val errorMessage: String if (errorResponse != null && errorResponse.errorMessage != null && errorResponse.errorMessage!!.isNotEmpty() ) errorMessage = errorResponse.errorMessage!! else errorMessage = "Error Message" Log.e("ErrorMessage",""+errorResponse.errorMessage); } override fun setWebViewProperties(webView: WebView?, bank: Any?) { //For setting webview properties, if any. // Check Customized Integration section for more details on this } override fun generateHash( valueMap: HashMap<String, String?>, hashGenerationListener: PayUHashGenerationListener ) { if (valueMap.containsKey(CP_HASH_STRING) && valueMap.containsKey(CP_HASH_NAME) ) { val hashData = valueMap[CP_HASH_STRING] val hashName = valueMap[CP_HASH_NAME] val hashString: String? = HashGenerationUtils.generateHashFromSDK( hashData.toString(), "Saltkey" ) //Do not generate hash from local, // it needs to be calculated from server side only. Here, // hashString contains hash created from your server side. val hash: String? = hashString if (!TextUtils.isEmpty(hash)) { val dataMap: HashMap<String, String?> = HashMap() dataMap[hashName!!] = hash!! hashGenerationListener.onHashGenerated(dataMap) } } } }) }
排查与解决方案
这个错误的核心是SDK尝试解析HTML内容(以<!DOCTYPE开头)为JSONObject,说明请求返回的不是预期的JSON响应,而是错误页或重定向页。按以下步骤排查:
1. 校验环境与密钥配置
- 确认
setIsProduction(true)是否匹配当前环境:如果使用测试密钥,必须设置为false;生产环境才用true。错误的环境配置会导致请求到错误的PayU服务器,返回HTML错误页。 - 确保
setKey("Key")中的Key和HashGenerationUtils中使用的SaltKey是对应环境(测试/生产)的配对密钥,不能混用。
2. 修正非法参数
setEmail("email")是无效邮箱格式,必须改为合法邮箱(如test@example.com),PayU会拒绝非法参数并返回错误页。- 检查手机号
9874563210是否符合PayU的格式要求(必须是有效10位数字)。
3. 确保Hash生成正确
- 本地测试:使用
HashGenerationUtils.generateHashFromSDK时,确保hashData是PayU要求的正确拼接字符串,且SaltKey无误。 - 服务端生成:服务端生成Hash时,必须严格使用和客户端完全一致的参数(amount、txnId、productInfo等内容/大小写完全匹配),并使用正确的SaltKey生成SHA-512 Hash。
4. 检查回调地址可用性
- 确认
surl和furl是公开可访问的地址,且能返回PayU要求的响应格式。如果地址不可达、返回HTML或被拦截,会导致SDK解析失败。
修正后的关键代码示例
// 修正环境与参数 val payUPaymentParams = PayUPaymentParams.Builder() .setAmount("10") .setIsProduction(false) // 测试环境设为false,生产环境改为true .setKey("YOUR_TEST_KEY") // 替换为实际测试/生产Key .setProductInfo("Macbook Pro") .setPhone("9874563210") .setTransactionId(System.currentTimeMillis().toString()) .setFirstName("firstname") .setEmail("test@example.com") // 修正为合法邮箱 .setSurl("https://your-public-success-url.com") // 替换为实际可访问的回调地址 .setFurl("https://your-public-failure-url.com") // 替换为实际可访问的回调地址 .setUserCredential("YOUR_TEST_KEY:test@example.com") // 匹配Key和邮箱 .setAdditionalParams(additionalParamsMap) .build() // 服务端Hash生成示例(替换本地生成逻辑) override fun generateHash( valueMap: HashMap<String, String?>, hashGenerationListener: PayUHashGenerationListener ) { if (valueMap.containsKey(CP_HASH_STRING) && valueMap.containsKey(CP_HASH_NAME)) { // 调用你的服务端接口获取Hash val hashData = valueMap[CP_HASH_STRING] val hashName = valueMap[CP_HASH_NAME] // 这里替换为服务端请求逻辑,比如用Retrofit获取Hash yourApiService.getPayUHash(hashData!!) .enqueue(object : Callback<String> { override fun onResponse(call: Call<String>, response: Response<String>) { val hash = response.body() if (!TextUtils.isEmpty(hash)) { val dataMap = HashMap<String, String?>() dataMap[hashName!!] = hash!! hashGenerationListener.onHashGenerated(dataMap) } } override fun onFailure(call: Call<String>, t: Throwable) { hashGenerationListener.onHashGenerationFailed(t.message) } }) } }
内容的提问来源于stack exchange,提问作者prabhakaran
相关产品推荐
相关产品推荐

