PAYATHON 2026

鸿蒙使用java生成的orderInfo发起支付宝支付返回6001

支付阿杰

结论

支付宝返回 6001,标准含义是“用户中途取消支付”。但如果支付页面根本没有正常打开,调用后就立即返回 6001,不能直接判定是用户主动取消。此时应检查:

  • 鸿蒙端是否原样传递了 orderInfo
  • orderInfo 是否经过 URL 解码、重复编码,或在拼接时被破坏
  • 服务端签名密钥、app_id 与支付宝应用配置是否匹配
  • 鸿蒙端使用的支付宝支付 SDK 是否支持当前系统和设备环境
  • 支付过程中是否发生页面拉起失败、应用切换失败或生命周期异常

从题目给出的字符串来看,其格式符合 Java SDK 生成的 App 支付请求结构。不过,仅凭字符串格式无法确认签名是否正确。排查时先确认支付宝收银台有没有出现,再查看完整的支付结果对象和服务端签名过程。

先确认 6001 出现在哪个阶段

已经进入支付宝收银台

如果支付宝收银台已经正常打开,之后用户点击返回、关闭页面或取消付款,返回 6001 是正常结果。

客户端应将它作为取消状态处理,而不是系统异常:

if (result.resultStatus === '6001') {
  // 用户取消支付
}

支付结果的字段名称应以项目当前接入的鸿蒙支付宝 SDK 为准。

没有进入支付宝收银台

如果调用支付接口后立即返回 6001,或者页面一闪而过,需要继续排查参数传递、SDK 集成和应用切换过程。此时,6001 可能只是客户端最终收到的取消状态,具体原因一般需要从支付结果中的 memo、resultMemo 或 SDK 日志中查找。

建议记录以下信息:

console.info(`resultStatus=${result.resultStatus}`);
console.info(`memo=${result.memo ?? result.resultMemo ?? ''}`);
console.info(`result=${result.result ?? ''}`);

不要只打印 resultStatus,否则可能遗漏有用的错误信息。

鸿蒙端必须原样传递 orderInfo

服务端生成的 orderInfo 已经是最终的支付请求字符串,其中 biz_content、sign、timestamp 等字段已经完成编码。鸿蒙端收到后应直接传给支付宝 SDK,不要再做任何处理。

以下操作都可能破坏签名:

// 错误:对整个 orderInfo 再编码
const value = encodeURIComponent(orderInfo);

// 错误:提前解码
const value = decodeURIComponent(orderInfo);

// 错误:按 & 拆分后重新排序或拼接
const value = rebuildOrderInfo(orderInfo);

// 错误:把 + 自动转换为空格
const value = orderInfo.replace(/\+/g, ' ');

正确的处理方式是:

const orderInfo: string = response.data.orderInfo;

// 将 orderInfo 原样传给当前支付宝鸿蒙 SDK 的支付方法
payWithAlipay(orderInfo);

这里的 payWithAlipay 只表示支付调用的位置。实际 API 名称、参数和返回结构,应以项目所用的支付宝鸿蒙 SDK 文档为准。

还要特别注意示例中的这些内容:

sign=...%2F...%2B...%3D%3D
timestamp=2024-12-25+09%3A56%3A33

%2F、%2B、%3D 以及时间中的 +,都是最终请求字符串的一部分。任何中间层对它们进行解码或二次编码,都可能造成验签失败。

服务端应使用 sdkExecute 生成请求字符串

App 支付请求应由服务端创建,并通过 Java SDK 的 sdkExecute 生成最终 orderInfo。不要自行排序、拼接或签名。

import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;

import java.math.BigDecimal;

public class AlipayOrderService {

    public String createOrderInfo() throws Exception {
        String gateway = "https://openapi.alipay.com/gateway.do";
        String appId = "你的支付宝应用APP_ID";
        String appPrivateKey = "你的应用私钥";
        String alipayPublicKey = "支付宝公钥";

        AlipayClient alipayClient = new DefaultAlipayClient(
                gateway,
                appId,
                appPrivateKey,
                "json",
                "UTF-8",
                alipayPublicKey,
                "RSA2"
        );

        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setOutTradeNo("唯一商户订单号");
        model.setProductCode("QUICK_MSECURITY_PAY");
        model.setSubject("订单标题");
        model.setTotalAmount(new BigDecimal("0.01").toPlainString());
        model.setTimeoutExpress("30m");

        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        request.setBizModel(model);
        request.setNotifyUrl("https://example.com/alipay/notify");

        AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);

        if (!response.isSuccess()) {
            throw new IllegalStateException(
                    "生成支付宝订单失败,code=" + response.getCode()
                            + ", msg=" + response.getMsg()
                            + ", subCode=" + response.getSubCode()
                            + ", subMsg=" + response.getSubMsg()
            );
        }

        return response.getBody();
    }
}

服务端直接将 response.getBody() 作为普通字符串返回:

{
  "orderInfo": "alipay_sdk=...&app_id=...&biz_content=...&sign=..."
}

不要先把 response.getBody() 转成参数对象,再交给客户端重新拼接。

检查密钥和应用配置

服务端的各项配置必须对应同一个支付宝应用:

  • app_id 是当前支付宝开放平台应用的 App ID
  • 签名使用该应用对应的应用私钥
  • Java SDK 中配置的是支付宝公钥,不是应用公钥
  • 签名算法与 sign_type=RSA2 一致
  • 沙箱应用、沙箱网关和沙箱账号必须配套使用
  • 正式应用应使用正式网关和正式应用密钥

如果应用密钥已经重新生成,而服务端仍在使用旧私钥,签名也会无效。

可以使用支付宝开放平台提供的验签工具,验证生成订单时的原始待签名内容和签名值。验证时要分清编码后的请求字符串与编码前的待签名字符串,不能直接把整个 URL 编码结果当作待签名内容。

对比服务端和鸿蒙端收到的字符串

排查期间可以临时记录以下信息:

String orderInfo = response.getBody();

System.out.println("orderInfo.length=" + orderInfo.length());
System.out.println("orderInfo=" + orderInfo);

鸿蒙端也记录收到的字符串及其长度:

console.info(`orderInfo.length=${orderInfo.length}`);
console.info(`orderInfo=${orderInfo}`);

对比时重点查看:

  • 字符串长度是否一致
  • 开头或结尾是否多出引号
  • & 是否被转换为 &
  • %2B、%2F、%3D 是否发生变化
  • + 是否变成空格
  • 是否出现 %252F、%252B 等重复编码
  • 字符串中是否混入换行、制表符或首尾空格

例如,下面这些都不是原始的 orderInfo:

"alipay_sdk=...&app_id=..."
alipay_sdk=...&app_id=...
alipay_sdk=...&sign=abc%252Fdef

如果必须在生产环境中排查,不宜长期记录完整签名和订单参数。可以只记录字符串长度和哈希,用来判断传输链路中是否发生了变化:

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HexFormat;

public static String sha256(String value) throws Exception {
    byte[] digest = MessageDigest.getInstance("SHA-256")
            .digest(value.getBytes(StandardCharsets.UTF_8));
    return HexFormat.of().formatHex(digest);
}

只有服务端和客户端针对原始字符串计算出的 SHA-256 完全一致,才能说明传输过程中没有修改内容。

检查订单本身

还应确认以下业务条件:

  • 每次创建订单时,out_trade_no 都保持唯一
  • total_amount 使用字符串小数,例如 "0.01"
  • 订单没有过期、关闭或已经支付
  • 设备时间与服务端时间基本准确
  • 不要反复使用历史 orderInfo 进行测试
  • notify_url 可以通过公网 HTTPS 访问
  • subject 不为空,且内容符合支付宝接口要求

题目中的时间是 2024-12-25 09:56:33。如果它只是脱敏后的历史示例,不影响排查;如果实际测试仍在重复使用这段旧 orderInfo,则应重新创建订单并生成新的请求字符串。

检查鸿蒙 SDK 集成和页面拉起

如果服务端与客户端的参数完全一致,但仍然无法进入收银台,应检查:

  • 当前支付宝 SDK 是否明确支持项目使用的 HarmonyOS 版本
  • SDK 是否初始化成功
  • 应用签名、包名或 App Linking 配置是否与支付宝开放平台配置一致
  • 真机是否安装了可用的支付宝客户端
  • 调用支付时,页面是否仍处于有效的前台状态
  • 支付调用是否被重复触发
  • 从支付宝切回应用时,支付结果是否被生命周期代码覆盖
  • SDK 所需的权限、依赖和混淆配置是否完整

可以增加防重复点击逻辑:

let paying = false;

async function startPay(orderInfo: string) {
  if (paying) {
    return;
  }

  paying = true;
  try {
    const result = await payWithAlipay(orderInfo);
    console.info(`pay result=${JSON.stringify(result)}`);
  } finally {
    paying = false;
  }
}

其中 payWithAlipay 需要替换为当前 SDK 提供的真实支付接口。

不要仅凭客户端结果确认支付成功

无论客户端返回 9000、6001 还是其他状态,最终订单状态都要由服务端确认。服务端需要:

  1. 验证支付宝异步通知的签名。
  2. 校验 app_id、商户订单号、金额和收款方信息。
  3. 查询支付宝订单状态,处理通知延迟或客户端结果异常。
  4. 确认交易成功后,再将业务订单更新为已支付。

客户端收到 6001 时,可以提示“支付已取消”。如果用户反馈已经扣款,则不能直接重新创建订单或再次支付,应先让服务端查询原订单状态。

推荐排查顺序

  1. 确认支付宝收银台是否真正打开。
  2. 输出完整返回对象,查看 memo 或 resultMemo。
  3. 对比服务端生成的值与鸿蒙端收到的值,确认 orderInfo 完全一致。
  4. 删除客户端对 orderInfo 的编码、解码、参数拆分和重新拼接逻辑。
  5. 使用 sdkExecute 重新生成一笔全新的订单。
  6. 核对 app_id、应用私钥、支付宝公钥、RSA2 和运行环境。
  7. 检查鸿蒙支付宝 SDK 的兼容性与应用拉起配置。
  8. 如果仍未解决,结合同一笔订单的 SDK 日志、支付结果详情和支付宝服务端错误信息继续定位。

这类问题通常不在 Java 生成的字符串格式本身,而是 orderInfo 在接口序列化、网络传输或传给鸿蒙端 SDK 之前,经历了二次编码、解码或重组。先确认服务端输出与客户端入参逐字符一致,往往就能很快缩小排查范围。

备注:内容仅供参考。