PAYATHON 2026

uniapp 调用支付宝支付提示商家订单参数异常

支付老李

结论

出现“商家订单参数异常,请重新发起付款”,通常不是 uni.requestPayment 的调用方式有误,而是传入的 orderInfo 不符合支付宝 App 支付要求。可以优先排查这些问题:

  • orderInfo 不是服务端生成并签名的完整订单字符串
  • app_id、私钥、支付宝公钥或应用环境不匹配
  • biz_content 缺少必填参数,或参数格式不合法
  • 签名算法、参数排序或字符编码不一致
  • 订单字符串被重复 URL 编码、转义或序列化
  • 订单号重复、金额格式错误或时间戳过期
  • 支付产品尚未签约,或应用配置还未生效

客户端应直接使用服务端返回的支付宝订单字符串,不要在 uniapp 中自行拼接参数或生成签名。

正确的调用方式

uni.requestPayment({
    provider: 'alipay',
    orderInfo: orderInfo,
    success(res) {
        console.log('success:', JSON.stringify(res));
    },
    fail(err) {
        console.log('fail:', JSON.stringify(err));
    }
});

这里的 orderInfo 一般是字符串,结构类似:

app_id=你的应用ID&biz_content=...&charset=utf-8&method=alipay.trade.app.pay&sign_type=RSA2&timestamp=2026-09-09%2012%3A00%3A00&version=1.0&sign=...

不要把普通 JavaScript 对象直接传给 orderInfo:

// 通常是错误用法
const orderInfo = {
    app_id: '...',
    biz_content: {
        out_trade_no: '...',
        total_amount: '0.01'
    }
};

可以先确认数据类型:

console.log('orderInfo type:', typeof orderInfo);
console.log('orderInfo:', orderInfo);

if (typeof orderInfo !== 'string' || !orderInfo.trim()) {
    console.error('支付宝 orderInfo 必须是非空订单字符串');
    return;
}

重点检查的订单参数

app_id

app_id 必须与生成签名时使用的应用完全一致。常见问题有:

  • 使用了其他支付宝应用的 app_id
  • 混用了沙箱环境和正式环境的参数
  • 服务端仍配置着旧的 app_id
  • 应用没有开通或签约 App 支付能力

如果更换过应用,还要同步检查私钥、支付宝公钥和应用证书。

method

App 支付通常使用:

alipay.trade.app.pay

如果服务端使用了手机网站支付、电脑网站支付或其他接口的 method,生成的订单不能直接交给 App 支付 SDK。

biz_content

biz_content 是 JSON 字符串,常用的必填参数包括:

{
  "out_trade_no": "PAY202609090001",
  "total_amount": "0.01",
  "subject": "测试商品",
  "product_code": "QUICK_MSECURITY_PAY"
}

需要检查:

  • out_trade_no 是否为空,是否包含不合适的字符,或是否已经使用
  • total_amount 是否为合法的金额字符串
  • subject 是否为空
  • product_code 是否为 QUICK_MSECURITY_PAY
  • JSON 是否有效,字段名是否拼写正确
  • 金额单位是否有误,例如把“分”直接当成“元”

金额应由服务端按照十进制定点方式处理,不要直接依赖 JavaScript 浮点数计算:

// 可能产生浮点精度问题
const amount = 0.1 + 0.2;

// 应由服务端按照业务规则转换并格式化
const totalAmount = '0.30';

timestamp

通常使用以下格式:

yyyy-MM-dd HH:mm:ss

例如:

2026-09-09 12:30:00

同时要确认服务器时间准确。时间戳格式错误、服务器时钟偏差较大,或重复使用缓存过久的订单字符串,都可能造成请求失败。

charset、sign_type 和 version

常见取值如下:

charset=utf-8
sign_type=RSA2
version=1.0

具体取值要与服务端支付宝 SDK 和应用配置一致。尤其要避免参数中填写了 RSA2,实际却采用另一套签名方式。

sign

签名是最常见的出错环节之一。需要逐项确认:

  • 使用的是支付宝应用私钥,而不是支付宝公钥
  • 私钥属于当前 app_id
  • 没有混用公钥模式和证书模式
  • 待签名参数的排序符合支付宝接口要求
  • 签名前使用的参数值与最终发送的值完全一致
  • 签名完成后没有再次修改 biz_content、金额或订单号
  • 字符集统一使用 UTF-8
  • 私钥内容没有丢失换行、头尾标记或关键字符

不要在客户端保存支付宝应用私钥。订单签名必须在可信服务端完成,否则私钥很容易被提取。

检查是否发生重复编码

支付宝订单字符串中的 biz_content、timestamp 和 sign 等内容通常需要按规则编码,但客户端不应对服务端返回的整个订单字符串再次执行 encodeURIComponent。

// 不建议:可能导致整个订单字符串被重复编码
orderInfo = encodeURIComponent(orderInfo);

也不要再次对订单字符串进行 JSON 序列化:

// 不建议:会在字符串外增加引号,并转义内部字符
orderInfo = JSON.stringify(orderInfo);

客户端通常应原样使用接口返回值:

const orderInfo = response.data.orderInfo;

uni.requestPayment({
    provider: 'alipay',
    orderInfo,
    success(res) {
        console.log('支付结果:', res);
    },
    fail(err) {
        console.error('支付调用失败:', err);
    }
});

如果后端接口返回的数据还包了一层 JSON,需要取出其中真正的订单字符串:

{
  "code": 0,
  "data": {
    "orderInfo": "app_id=...&biz_content=...&sign=..."
  }
}

对应的客户端代码是:

const orderInfo = response.data.data.orderInfo;

推荐的排查顺序

1. 打印客户端收到的原始数据

console.log('typeof orderInfo:', typeof orderInfo);
console.log('orderInfo length:', orderInfo && orderInfo.length);
console.log('orderInfo:', orderInfo);

查看是否存在以下情况:

  • 值为 undefined、null 或空字符串
  • 内容以 { 开头,可能传入了对象或 JSON
  • 字符串最外层带有多余的双引号
  • 整个内容都变成了 %26、%3D 等编码形式
  • 只有业务参数,没有公共参数和签名

打印日志时要遮盖 sign 等敏感内容,生产环境中不要记录完整支付订单。

2. 在服务端记录签名前参数

分别记录:

  • 公共请求参数
  • 原始 biz_content
  • 最终待签名字符串
  • 支付宝 SDK 生成的最终订单字符串

对比签名前后的业务参数,确认它们没有被二次修改。

3. 使用支付宝官方服务端 SDK 生成订单

尽量不要手写参数排序、拼接和 RSA2 签名逻辑。服务端调用支付宝 SDK 时,核心业务参数类似:

const bizContent = {
    out_trade_no: 'PAY202609090001',
    total_amount: '0.01',
    subject: '测试商品',
    product_code: 'QUICK_MSECURITY_PAY'
};

具体的 SDK 方法名和调用方式取决于后端语言及 SDK 版本,应以当前版本的官方文档为准。服务端最终需要返回可以直接传给 uni.requestPayment 的订单字符串。

4. 核对应用和支付环境

将以下配置放在一起逐项核对:

  • 支付宝 app_id
  • 应用私钥
  • 支付宝公钥或应用证书
  • 支付宝网关环境
  • App 支付签约状态
  • uniapp 应用的支付宝支付模块配置
  • Android/iOS 打包后的支付能力配置

沙箱密钥不能用于正式应用,正式应用的密钥也不能直接用于沙箱环境。

5. 更换订单号重新测试

每次测试支付时生成新的 out_trade_no:

const outTradeNo = `PAY${Date.now()}`;

这段代码只用于说明如何生成唯一订单号。正式业务中的订单号应由服务端生成,并通过数据库唯一约束防止重复。

支付结果处理注意事项

进入客户端的 success 回调,并不代表商户已经安全确认收款。最终支付状态应以服务端收到的异步通知或主动查询结果为准。

success(res) {
    console.log('支付宝客户端返回:', res);

    // 再请求业务服务器查询订单状态
    uni.request({
        url: 'https://example.com/api/payment/status',
        data: {
            outTradeNo: currentOrderNo
        },
        success(statusRes) {
            console.log('服务端订单状态:', statusRes.data);
        }
    });
}

服务端处理支付宝异步通知时,还要校验签名、应用 ID、商户身份、订单号、金额和订单状态,不能只根据客户端回调将订单修改为已支付。

如果所有必填字段都正确,但仍然出现该提示,应优先确认三点:orderInfo 是否被二次编码,app_id 与密钥是否属于同一应用,以及服务端是否确实按照 alipay.trade.app.pay 生成了完整的签名订单。

备注:内容仅供参考。