返回

解决PayU Android支付“Processing”页面闪退问题

Android

Android PayU Checkout Pro: “Processing” 页面后自动关闭问题的分析与解决

在使用 PayU Checkout Pro 进行 Android 应用支付集成时,有时会遇到支付页面短暂显示 “Processing” 后自动关闭的情况。 本文深入分析此问题,提供解决方案,并就相关安全实践提出建议。

问题分析

当应用集成 PayU Checkout Pro 时,若初始化支付后界面快速关闭,通常与哈希值(Hash)的生成和验证环节密切相关。

根据PayU官方文档,在发起支付流程时,SDK会在多个阶段请求哈希值,例如:

  • 在构建 PayUPaymentParams 时,使用 initial hash。
  • 在启动支付后,PayU SDK 也可能需要其他哈希值。

如果哈希值生成不正确,或者未能响应 PayU SDK 的多次哈希值请求,就会导致上述问题。

另一个常见原因:虽然成功调用了 onPaymentSuccessonPaymentFailureonPaymentCancelonError 其中一个,但界面在短暂停留后就消失了,可能是因为没有妥善处理这些回调函数返回的结果,没有给用户明显的反馈,或导致程序错误终止。

解决方案

方案一: 多次哈希值生成

如上所述,PayU SDK 在支付的不同阶段需要多个哈希值,所以必须妥善处理 generateHash 回调。

  • 问题所在: 在上述代码中 generateHash 回调里仅用 hashGenerator.generateHash(); 生成一个哈希,这与实际情况不符。PayU SDK可能会要求你生成多个不同目的的哈希值。
  • 解决思路: 你需要根据valueMap提供的 hashName 动态生成对应哈希,并使用hashGenerationListener回调方法onHashGenerated返回给 SDK。

以下示例代码展示如何响应 SDK 的哈希值请求:

  @Override
  public void generateHash(HashMap<String, String> valueMap, PayUHashGenerationListener hashGenerationListener) {
      String hashName = valueMap.get(PayUCheckoutProConstants.CP_HASH_NAME);
      String hashData = valueMap.get(PayUCheckoutProConstants.CP_HASH_STRING);

      if (!TextUtils.isEmpty(hashName) && !TextUtils.isEmpty(hashData)) {
           // 从服务器请求哈希值
           requestHashFromServer(hashName, hashData, hashGenerationListener);
      }
  }

    // 该方法用于从你的服务器获取哈希值
    private void requestHashFromServer(String hashName, String hashData, final PayUHashGenerationListener hashGenerationListener) {

        // 发送带有 hashData 和 hashName 的请求到服务器
        // 获取对应的服务器端哈希值

        // 以下代码是使用一个虚拟的请求
          String hash = generateLocalHash(hashData);  //生成Hash
          HashMap<String, String> dataMap = new HashMap<>();
          dataMap.put(hashName, hash);
          hashGenerationListener.onHashGenerated(dataMap);  //通过回调将哈希返回SDK
    }

    //仅供演示,实际需要在后端计算
   private String generateLocalHash(String data) {
        String salt = "mysalt";  //务必使用实际盐值
        String input = data + salt;
        return sha512(input);
   }
   // 此 sha512() 与你的 hashgenerator 代码中的方法相同,只是为了展示一个完整的例子
   private String sha512(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-512");
            byte[] hashBytes = md.digest(input.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : hashBytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
   }

步骤

  1. 修改 generateHash 回调: 将哈希生成逻辑从本地代码转移到 requestHashFromServer方法,该方法模拟请求服务器的响应,使用提供的hashData和实际的salt生成hash, 务必使用服务端实际哈希生成方式替换 generateLocalHash() 方法。
  2. 在服务器端实现哈希生成: 服务端接收hashNamehashData。服务端使用同样的salt和参数,通过相同的哈希算法生成hash, 返回给android客户端。
  3. 响应 onHashGenerated: 客户端接收服务器的哈希值后,通过hashGenerationListener.onHashGenerated(dataMap)回传给 SDK 。
    务必: 在实际生产中,切勿 在客户端本地生成哈希值。始终从您的服务器端生成和获取哈希值。客户端本地的代码安全较低,容易被反编译获取salt信息,产生安全风险。

方案二: 处理回调方法,提供用户反馈

如果快速关闭的问题与哈希无关,并且代码已经进入了 onPaymentSuccessonPaymentFailureonPaymentCancelonError 方法,但UI没有更新,那么请确认:

问题: 可能在回调中,开发者没有针对用户给出反馈或者 UI 的刷新操作。

解决: 需要在支付完成(无论成功与否)后,使用 toast,Snackbar或其他UI提示,或跳转到新的页面以提供明确的反馈。

以下代码片段展示了如何在回调方法中处理成功,失败和取消场景。

                    @Override
                    public void onPaymentSuccess(Object response) {
                          // Handle Payment Success, redirect the user to success page
                         Log.d("PayU", "Payment Success: " + response.toString());
                          Toast.makeText(thisActivity, "支付成功!", Toast.LENGTH_SHORT).show();
                         // e.g.:
                         Intent intent = new Intent(thisActivity, PaymentSuccessActivity.class);
                         startActivity(intent);
                         finish(); // 支付成功后,关闭当前页面
                    }

                    @Override
                    public void onPaymentFailure(Object response) {
                        // Handle Payment Failure and display proper error to user
                         Log.d("PayU", "Payment Failure: " + response.toString());
                        Toast.makeText(thisActivity, "支付失败!", Toast.LENGTH_SHORT).show();
                         Intent intent = new Intent(thisActivity, PaymentFailureActivity.class);
                          startActivity(intent);
                           finish();

                    }

                    @Override
                    public void onPaymentCancel(boolean isTxnInitiated) {
                       // handle transaction cancel scenario here and send user to proper screen.
                           Log.d("PayU", "Payment Cancel Initiated?: "+ isTxnInitiated );
                            Toast.makeText(thisActivity, "支付取消", Toast.LENGTH_SHORT).show();
                              // example if back is pressed:
                            finish();


                    }

                    @Override
                    public void onError(ErrorResponse errorResponse) {
                       // Display Payu Errors
                        Log.d("PayU", "Error:" + errorResponse.getMessage() +  " " + errorResponse.toString());
                        Toast.makeText(thisActivity, "支付发生错误: "+errorResponse.getMessage(), Toast.LENGTH_SHORT).show();
                            // Show error or go back:
                             finish();

                    }

步骤:

  1. onPaymentSuccess 方法里添加跳转至支付成功界面的代码,或者利用 Toast 组件给用户提示支付成功。
  2. onPaymentFailureonError 方法里处理错误状态并展示合适的用户界面提示。
  3. onPaymentCancel 方法里响应取消状态。
  4. 确保完成支付逻辑后调用 finish(); 以便销毁activity,避免回到checkout界面。

额外安全建议

  1. 避免本地生成哈希: 客户端的哈希生成逻辑应仅作为本地调试使用。正式环境中必须使用服务器端生成的哈希。
  2. 验证响应: 确保后端服务正确验证所有来自PayU的回调信息,防止篡改风险。
  3. HTTPS: 所有和支付相关的通讯务必通过HTTPS协议进行。
  4. API Key安全 : 切勿将 keysalt 等密钥信息直接嵌入到代码中。 建议通过安全的配置文件或者环境变量进行管理,并且避免上传至代码仓库。
  5. 数据校验: 确保前端和后端同时进行支付数据的校验,包括金额、产品信息和用户信息的校验。
  6. 混淆和代码优化 : 正式版本应该进行混淆,确保安全。同时及时升级payu sdk库,避免已知安全漏洞。

通过上述方案与建议,可以解决 PayU Checkout Pro 在 Android 集成中 “Processing” 后快速关闭的问题。
记住要将哈希生成移到服务器端进行, 并适当处理回调方法以便提供用户反馈。 安全第一,请在开发过程中牢记这些原则,构建一个稳定可靠的支付解决方案。