支付宝沙箱环境使用buyer_open_id转账提示openid不合法
结论
alipay.trade.query 返回的 buyer_open_id 不能直接作为 alipay.fund.trans.uni.transfer 的收款方支付宝用户 ID。
buyer_open_id 是当前应用下的用户 OpenID。单笔转账接口中的 payee_info.identity 必须与 payee_info.identity_type 匹配。如果接口要求 ALIPAY_USER_ID,应传入沙箱买家账号的支付宝用户 ID,通常是交易查询结果中的 buyer_user_id。传入 buyer_open_id 会因标识类型不匹配而返回“openid 不合法”或“参数不合法”。
原因
支付宝的用户标识分为不同类型:
buyer_open_id:当前应用下的用户 OpenID。buyer_user_id:支付宝用户 ID。- 登录账号:手机号、邮箱等支付宝登录标识。
调用转账接口时,支付宝会按照 identity_type 校验 identity 的格式和类型。例如:
{
"identity": "用户ID",
"identity_type": "ALIPAY_USER_ID"
}
此处的 identity 必须是支付宝用户 ID。即使 buyer_open_id 来自一笔已经完成的沙箱交易,填入后仍会因标识类型不符而校验失败。
沙箱账号与生产账号也相互隔离。生产环境中的用户 ID、OpenID、手机号或邮箱不能用于沙箱转账,沙箱环境中的标识同样不能用于生产环境。
解决步骤
1. 检查交易查询结果
调用 alipay.trade.query 后,分别检查以下字段:
{
"buyer_user_id": "沙箱买家的支付宝用户ID",
"buyer_open_id": "当前应用下的买家OpenID"
}
字段是否返回以及实际字段名,应以当前接口文档和接口响应为准。
2. 使用接口支持的身份类型
如果 alipay.fund.trans.uni.transfer 的当前文档支持 ALIPAY_USER_ID,应使用:
{
"identity": "alipay.trade.query返回的buyer_user_id",
"identity_type": "ALIPAY_USER_ID"
}
不要写成:
{
"identity": "alipay.trade.query返回的buyer_open_id",
"identity_type": "ALIPAY_USER_ID"
}
后一种写法虽然字段齐全,但 identity 的实际类型与 identity_type 不匹配。
如果需要按沙箱买家的登录账号转账,且当前接口支持 ALIPAY_LOGON_ID,也可以使用该沙箱买家账号的登录名:
{
"identity": "沙箱买家登录账号",
"identity_type": "ALIPAY_LOGON_ID"
}
3. 确认整个调用链使用同一套沙箱环境
支付、查询和转账都必须使用同一套沙箱配置,包括:
- 沙箱网关;
- 同一个沙箱应用的
app_id; - 沙箱应用私钥;
- 对应的支付宝公钥或支付宝公钥证书;
- 沙箱买家和商家账号;
- 沙箱环境生成的交易号和用户标识。
Java 示例
以下示例使用 buyer_user_id 作为收款方身份。支付宝 Java SDK 不同版本的类名和方法签名可能有所区别,请以项目实际使用的依赖为准。
AlipayFundTransUniTransferRequest request =
new AlipayFundTransUniTransferRequest();
AlipayFundTransUniTransferModel model =
new AlipayFundTransUniTransferModel();
model.setOutBizNo("TRANSFER_202609090001");
model.setTransAmount("1.00");
model.setProductCode("TRANS_ACCOUNT_NO_PWD");
model.setBizScene("DIRECT_TRANSFER");
model.setOrderTitle("沙箱转账测试");
Participant payeeInfo = new Participant();
payeeInfo.setIdentity(buyerUserId); // 使用 buyer_user_id,不是 buyer_open_id
payeeInfo.setIdentityType("ALIPAY_USER_ID");
payeeInfo.setName("沙箱买家姓名"); // 是否必填以当前接口要求为准
model.setPayeeInfo(payeeInfo);
request.setBizModel(model);
AlipayFundTransUniTransferResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
System.out.println("转账成功:" + response.getOrderId());
} else {
System.out.println("转账失败");
System.out.println("code=" + response.getCode());
System.out.println("subCode=" + response.getSubCode());
System.out.println("subMsg=" + response.getSubMsg());
}
对应的业务参数结构大致如下:
{
"out_biz_no": "TRANSFER_202609090001",
"trans_amount": "1.00",
"product_code": "TRANS_ACCOUNT_NO_PWD",
"biz_scene": "DIRECT_TRANSFER",
"order_title": "沙箱转账测试",
"payee_info": {
"identity": "buyer_user_id的值",
"identity_type": "ALIPAY_USER_ID",
"name": "沙箱买家姓名"
}
}
注意事项
- 不要只看字段值的长度或前缀来判断它是 OpenID 还是支付宝用户 ID,应以响应中的字段名为准。
identity和identity_type必须相互匹配,不能只修改其中一个。- 如果查询响应中没有
buyer_user_id,可以尝试使用沙箱买家账号和ALIPAY_LOGON_ID,但当前转账产品必须明确支持该身份类型。 - 如果接口会校验收款方姓名,必须填写沙箱账号对应的实名信息,不能随意填写。
- 参数正确并不代表一定能转账成功,沙箱应用还需要具备相应的转账产品权限。如果修正身份参数后仍然失败,应查看响应中的
sub_code和sub_msg,进一步排查产品权限、账户状态、余额或身份校验等问题。 - 如果当前接口文档明确支持
ALIPAY_OPEN_ID,还要确认该 OpenID 是否属于发起转账的同一应用和同一环境。不能默认其他接口返回的buyer_open_id可以跨产品直接使用。
备注:内容仅供参考。
