PC 转二维码唤起支付宝支付并签约时 signStr 应取什么内容?
结论
这里的 signStr 不是请求字符串中的 sign 参数,也不是支付宝支付接口返回的某个业务字段。
它指的是调用 alipay.trade.app.pay 时,由支付宝 SDK 生成的完整已签名订单请求字符串,通常就是 AlipayTradeAppPayResponse#getBody() 返回的内容,例如:
app_id=...&biz_content=...&charset=...&method=alipay.trade.app.pay&sign=...&sign_type=RSA2×tamp=...&version=1.0
具体处理步骤如下:
- 由 SDK 生成完整请求字符串
orderInfo。 - 对整个
orderInfo做一次 URL Encode,并将结果作为外层orderStr的值。 - 拼接成:
alipays://platformapi/startApp?appId=60000157&orderStr=<URL Encode 后的完整 orderInfo>
- 将这条完整的
alipays://URL 写入二维码。
问题中那段以:
alipay_sdk%3D...%26app_id%3D...%26biz_content%3D...
开头的长字符串,从结构来看,就是完整订单请求字符串经过一次外层 URL Encode 后的结果。字符串很长并不代表它有问题。
三种内容不要混淆
假设 SDK 生成的完整请求字符串是:
app_id=2021000000000000&biz_content=%7B%22subject%22%3A%22test%22%7D&charset=UTF-8&method=alipay.trade.app.pay&sign=AbCd%2BEf%3D&sign_type=RSA2×tamp=2026-09-09+10%3A00%3A00&version=1.0
其中:
sign=AbCd%2BEf%3D只是完整请求中的签名参数。- 整条字符串才是 SDK 生成的
orderInfo。 - 对整条
orderInfo再编码一次,得到的才是要放入orderStr的内容。
不能只取:
AbCd+Ef=
也不能只取:
sign=AbCd%2BEf%3D
支付宝 App 还需要 app_id、method、biz_content、timestamp、sign_type 等参数,才能还原并校验完整的支付请求。
为什么会出现 %25 这样的双重编码
SDK 输出的 orderInfo 通常已经采用表单参数格式,各参数值也已经过编码。例如:
biz_content=%7B%22subject%22%3A%22test%22%7D
把完整的 orderInfo 作为 orderStr 的值再次编码时,原有的 % 会变成 %25:
biz_content%3D%257B%2522subject%2522%253A%2522test%2522%257D
虽然看起来像编码了两次,但这两次编码分别属于不同层级:
- 第一层是 SDK 对
alipay.trade.app.pay请求参数的编码。 - 第二层是把完整请求字符串放入
orderStr查询参数时进行的外层编码。
这种情况通常是正常的。不要先解码 SDK 的输出,也不要对外层连续执行两次 URL Encode。
Java 示例
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.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class AlipayQrCodeExample {
public static String buildAlipayQrContent() throws Exception {
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do",
"YOUR_APP_ID",
"YOUR_MERCHANT_PRIVATE_KEY",
"json",
"UTF-8",
"ALIPAY_PUBLIC_KEY",
"RSA2"
);
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo("202609090001");
model.setSubject("测试商品");
model.setTotalAmount("0.31");
model.setProductCode("QUICK_MSECURITY_PAY");
// 根据实际签约产品设置 agreement_sign_params。
// 字段名称和取值必须以当前接入产品文档及商户签约能力为准。
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setBizModel(model);
request.setNotifyUrl("https://merchant.example.com/alipay/pay-notify");
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
if (!response.isSuccess()) {
throw new IllegalStateException(
"生成支付宝订单字符串失败:"
+ response.getCode() + " / "
+ response.getSubCode() + " / "
+ response.getSubMsg()
);
}
// 完整、已签名的 App 支付请求字符串
String orderInfo = response.getBody();
// 只对完整 orderInfo 做一次外层编码
String encodedOrderInfo =
URLEncoder.encode(orderInfo, StandardCharsets.UTF_8);
return "alipays://platformapi/startApp"
+ "?appId=60000157"
+ "&orderStr="
+ encodedOrderInfo;
}
}
不同版本的 Java SDK 在方法重载和返回对象方面可能略有差异,但判断标准相同:要取得 SDK 为 alipay.trade.app.pay 生成的完整请求正文,不能只读取 sign 字段。
正确的数据流
业务参数
↓
Alipay SDK 组装参数并使用商户私钥签名
↓
完整 orderInfo
↓
对整个 orderInfo 做一次 URL Encode
↓
alipays://platformapi/startApp?appId=60000157&orderStr=...
↓
将完整 URL 写入二维码
最终格式应类似:
alipays://platformapi/startApp?appId=60000157&orderStr=app_id%3D2021000000000000%26biz_content%3D%257B...%257D%26charset%3DUTF-8%26method%3Dalipay.trade.app.pay%26sign%3D...%26sign_type%3DRSA2%26timestamp%3D...%26version%3D1.0
不要把最终 URL 整体再次编码成:
alipays%3A%2F%2Fplatformapi%2FstartApp...
二维码中保存的应是能够直接识别的 alipays:// URL。
常见错误
只拼接 sign 参数
下面这种格式是错误的:
alipays://platformapi/startApp?appId=60000157&orderStr=<RSA2签名值>
单独的签名值不能代表完整订单。
对 SDK 返回值重复编码
错误示例:
String value1 = URLEncoder.encode(orderInfo, StandardCharsets.UTF_8);
String value2 = URLEncoder.encode(value1, StandardCharsets.UTF_8);
如果把 value2 放入 orderStr,支付宝 App 解码一层后得到的仍是编码文本,无法按照正常的 key=value&key=value 格式解析请求。
手工拼装或改写 SDK 返回字符串
SDK 完成签名后,不要调整参数顺序、替换字符或修改 biz_content,也不要手工转换 +、%2B、空格等字符。签名校验以签名前的规范参数为依据,改动内容可能造成验签失败。
使用错误字符集
业务参数、SDK 配置、外层编码和二维码文本建议统一使用 UTF-8:
URLEncoder.encode(orderInfo, StandardCharsets.UTF_8)
示例中出现 charset=GBK 不一定会直接导致失败,但可能造成商品名称、签约参数或回调数据使用不同字符集。除非现有接入明确要求 GBK,否则优先统一使用 UTF-8。
把 JSON 中的 URL 转义错了
sign_notify_url 和 notify_url 应当是合法且可通过公网访问的回调地址。需要检查签名前的实际 JSON,确认其中没有多余的反斜杠,例如:
"http:\\/\\/www.merchant.com\\/receive_sign_notify"
JSON 序列化时对 / 的转义有时可以正常解析,但不要人为添加额外的反斜杠。最好交给 SDK 或正规的 JSON 序列化库处理。
二维码过长时的检查方向
完整支付订单中包含业务 JSON 和 RSA2 签名,所以生成的二维码内容确实可能很长。遇到问题时,可优先检查:
- 二维码生成器是否完整保留了
alipays://URL。 - 是否误做了第二次外层编码。
- 二维码尺寸是否足够,内容过长是否导致模块过密。
- 页面是否对字符串做了 HTML 转义、截断或字符替换。
- 是否使用支付宝 App 扫码。部分通用扫码工具无法正确处理
alipays://Scheme。 - SDK 返回的
response.getBody()是否为空,接口生成过程是否成功。 agreement_sign_params是否符合已开通产品的字段要求。external_agreement_no、sign_scene、personal_product_code等值是否符合当前商户的签约配置。- 支付回调地址和签约回调地址是否使用可访问的 HTTPS 地址。
调试时,可以分别保存并比较以下三个值的长度和内容:
System.out.println("orderInfo: " + orderInfo);
System.out.println("encodedOrderInfo: " + encodedOrderInfo);
System.out.println("qrContent: " + qrContent);
它们应当满足以下条件:
orderInfo中可以看到普通的&参数分隔符。encodedOrderInfo中的参数分隔符变为%26,等号变为%3D。qrContent仍以未编码的alipays://platformapi/startApp?开头。
signStr 这个名称很容易让人误以为它只包含签名。更准确的命名是 orderInfo 或 signedOrderString,因为它表示 SDK 生成的完整已签名请求字符串,而不是单独的 RSA2 签名。
备注:内容仅供参考。
