免密支付API、商户权限开通及沙箱环境操作咨询
结论
如果这里的“免密支付”是指支付宝商户在用户授权后主动扣款,那么通常不存在一个独立的“免密支付 API”。整个流程由以下环节组成:
- 使用
alipay.user.agreement.page.sign引导用户签署代扣协议。 - 使用
alipay.user.agreement.query查询签约状态。 - 取得有效协议号后,通过相应代扣产品支持的支付接口发起扣款。常见场景使用
alipay.trade.pay,具体仍要以商户已开通产品的官方接入文档为准。 - 用户解约时调用
alipay.user.agreement.unsign,同时更新业务系统中的协议状态。
免密代扣是一项受控支付能力。商户通常要先申请相应的产品权限,再将该能力绑定到实际使用的应用和商户账号,而不是在后台为某件商品打开一个“免密”开关。
沙箱可以验证接口签名、请求格式、协议状态流转和异步通知,但沙箱测试通过不等于生产权限已经开通。部分代扣产品、签约渠道或真实扣款链路无法在沙箱中完整模拟,需要按平台要求申请生产联调,或采用官方提供的测试方案。
以下内容以支付宝开放平台的“签约后代扣”模式为前提。如果接入的是微信支付、银联或其他支付平台,API 名称和准入流程会有所不同。
应调用哪些 API
完整链路一般包括签约、查询、扣款和解约四个部分。
1. 用户签约
调用:
alipay.user.agreement.page.sign
该接口用于生成签约页面。用户需要在支付宝提供的页面中阅读并确认协议,商户不能自行把用户标记为已签约。
业务系统应为每次签约生成唯一的外部协议号,例如:
external_agreement_no = AGR_20260913_100001
签约请求中的产品码、签约场景等参数由商户申请的代扣产品决定,不能自行猜测或混用。
2. 查询协议状态
调用:
alipay.user.agreement.query
收到页面跳转或异步通知后,建议主动查询一次协议状态。只有平台确认协议有效,业务系统才能启用后续代扣。
不能只根据前端跳转页面中的“签约成功”提示直接扣款。
3. 发起扣款
常见的签约代扣产品会通过以下接口发起支付:
alipay.trade.pay
请求中还要提交有效的协议标识。具体使用 agreement_no、外部协议号还是其他凭据,以及 product_code 应填写什么值,取决于商户实际开通的产品。
因此,不能简单地认为“调用 alipay.trade.pay 就能免密扣款”。扣款请求要成功,通常需要同时满足以下条件:
- 商户已取得相应代扣产品的权限;
- 发起请求的应用、签约应用和商户主体相互匹配;
- 用户协议当前有效;
- 请求中的产品码和场景参数与已开通产品一致;
- 金额、频率和业务用途符合签约协议及平台风控要求。
4. 用户解约
调用:
alipay.user.agreement.unsign
解约成功后,系统必须停止扣款。业务系统还应保存解约时间、协议状态和平台返回结果,防止本地状态更新延迟造成重复扣款。
商户是否需要开通权限
需要。免密代扣通常按照商户主体、应用、行业场景和产品能力进行审核,不是针对某件商品单独配置权限。
准入条件会随代扣产品和行业变化,常见审核内容包括:
- 企业主体及经营资质;
- 网站、App 或小程序已有真实业务;
- 使用场景属于连续服务、周期性服务或其他允许代扣的场景;
- 用户可以清楚了解扣款规则、金额或计算方式、扣款周期和解约入口;
- 商户具有退款、投诉及异常扣款处理机制;
- 商品或服务不在平台限制或禁止范围内;
- 应用已经创建,并完成必要的签约、密钥和回调地址配置。
具体入口名称和审核材料可能调整,请以支付宝开放平台控制台当前显示的产品申请页面为准。
推荐的开通流程
- 在开放平台创建应用,确认应用所属的商户账号和签约主体。
- 在产品中心查找符合实际业务的代扣产品,例如周期扣款或其他签约代扣能力。
- 阅读产品准入规则,确认所在行业、商品类型和扣款模式是否符合要求。
- 提交业务说明、用户签约页面、服务协议、解约路径及必要资质。
- 审核通过后,把产品能力添加到应用。
- 配置应用公钥或证书、支付宝公钥、网关地址和异步通知地址等信息。
- 对签约、协议查询、扣款、退款、解约和异常处理进行完整测试。
- 上线前检查生产应用的
app_id、密钥、网关和产品权限,不能继续使用沙箱配置。
如果控制台中没有相应产品,或者添加接口后仍返回“无权限”,问题通常不在代码,而在商户主体、应用或产品权限尚未开通。此时应根据返回的 code、sub_code 和 sub_msg 排查权限,必要时联系平台技术支持确认准入情况。
代码示例
下面的 Java 示例只展示调用结构。product_code、sign_scene、协议参数名称及取值必须从当前已开通产品的官方文档中获取,不能把示例中的占位值直接用于生产环境。
AlipayClient alipayClient = new DefaultAlipayClient(
gatewayUrl,
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayUserAgreementPageSignRequest request =
new AlipayUserAgreementPageSignRequest();
request.setReturnUrl(returnUrl);
request.setNotifyUrl(notifyUrl);
request.setBizContent("""
{
"external_agreement_no": "AGR_20260913_100001",
"product_code": "<以已开通产品文档为准>",
"sign_scene": "<以已开通场景为准>"
}
""");
AlipayUserAgreementPageSignResponse response =
alipayClient.pageExecute(request);
if (response.isSuccess()) {
String signPage = response.getBody();
// 将表单或跳转内容返回给浏览器,由用户完成签约。
} else {
System.err.println(response.getCode());
System.err.println(response.getSubCode());
System.err.println(response.getSubMsg());
}
查询协议时,需要校验平台返回的状态,并将平台协议号与本地用户、业务合同绑定:
AlipayUserAgreementQueryRequest request =
new AlipayUserAgreementQueryRequest();
request.setBizContent("""
{
"external_agreement_no": "AGR_20260913_100001"
}
""");
AlipayUserAgreementQueryResponse response =
alipayClient.execute(request);
if (response.isSuccess()) {
// 读取并保存协议号、协议状态及有效期。
// 只有协议有效时才允许进入代扣流程。
} else {
System.err.println(response.getCode());
System.err.println(response.getSubCode());
System.err.println(response.getSubMsg());
}
不同 SDK 版本生成的类名或字段封装方式可能不同,应以项目实际使用的官方 SDK 和相应接口模型为准。
沙箱环境配置方法
1. 创建沙箱应用
在开放平台沙箱控制台获取以下测试信息:
- 沙箱
app_id; - 沙箱支付宝账号;
- 沙箱网关地址;
- 应用私钥;
- 支付宝公钥或平台证书。
沙箱与生产环境的账号、密钥和 app_id 相互隔离,不能混用。
2. 切换沙箱网关
调用接口时,将网关设置为控制台当前提供的沙箱网关。例如:
String gatewayUrl = "<沙箱控制台提供的网关地址>";
不要按照旧示例硬编码网关地址。上线时要切换到生产网关,并同时更换生产环境的 app_id 和密钥。
3. 配置回调地址
notify_url 必须是支付平台能够访问的地址。本机的 localhost 或内网地址通常无法接收平台通知。
调试时可以使用支持 HTTPS 的测试域名,或经过安全控制的内网穿透地址。收到通知后必须:
- 验证签名;
- 校验
app_id、商户身份和业务订单号; - 校验金额和币种;
- 根据通知标识进行幂等处理;
- 按接口要求返回确认内容。
页面返回地址 return_url 只影响用户体验,不能用来判断最终的支付或签约结果。
4. 验证完整状态流转
测试至少应覆盖以下场景:
未签约 → 发起签约 → 签约成功 → 查询协议有效
协议有效 → 发起扣款 → 查询订单结果
协议有效 → 用户解约 → 查询协议失效
重复通知 → 业务系统只处理一次
重复扣款请求 → 相同 out_trade_no 不重复入账
协议失效或无权限 → 扣款被拒绝
每次请求都应保存:
app_id
method
out_trade_no
external_agreement_no
agreement_no
code
sub_code
sub_msg
trade_no
notify_time
日志中的用户标识、协议号及其他敏感信息需要脱敏。
沙箱无法完成代扣时怎么办
如果普通支付接口可以在沙箱中使用,但签约或代扣接口返回产品不存在、权限不足、场景不支持等错误,应依次检查:
- 当前沙箱应用中是否显示相应的产品能力;
- 使用的
product_code是否属于已经申请的产品; - SDK 请求是否发送到了沙箱网关;
app_id、密钥和支付宝公钥是否来自同一个沙箱应用;- 测试账号是否为沙箱账号;
- 该产品是否明确支持沙箱测试。
如果产品无法在沙箱中完整模拟,不能通过修改参数绕过权限。可以先在本地模拟测试签名、幂等、状态机和通知验签,再按照平台提供的生产联调流程,使用受控金额和测试账号验证真实链路。
注意事项
- 用户明确授权是免密支付的前提,不能把普通支付授权视为代扣授权。
- 除用户 ID 外,还要保存平台协议号、协议状态和有效期。
- 每笔扣款都要使用唯一的
out_trade_no,并在服务端实现幂等。 - 扣款结果不明确时,应调用订单查询接口确认,不能立即创建新订单并再次扣款。
- 退款不能替代解约。退款完成后,协议可能仍然有效。
- 用户解约、协议过期或平台暂停协议后,必须立即停止扣款。
- 生产环境需要提供扣款提醒、账单查询、退款、投诉处理和清晰的解约入口。
- API 调用失败时,应根据
sub_code和sub_msg定位问题,不能只记录最外层的code。 - 产品权限、准入行业和沙箱支持范围可能变化,最终应以商户控制台当前可申请的产品及相应官方接口文档为准。
备注:内容仅供参考。