Alipay Easy SDK 如何调用周期扣款签约接口
结论
如果当前版本的 Alipay Easy SDK 没有封装 alipay.user.agreement.page.sign,就无法像调用付款接口那样,通过 Factory.Payment... 直接发起周期扣款签约。
这是一个页面类接口。服务端需要生成带签名的支付宝跳转地址或表单,再由用户在浏览器中完成授权。可以在项目中同时引入支付宝通用 Java SDK,通过 AlipayClient.pageExecute() 调用该接口。Easy SDK 不必移除,其他已经封装的接口仍可继续使用。
为什么 Easy SDK 中找不到该接口
Easy SDK 只封装了一部分常用的开放平台接口,并不覆盖支付宝开放平台的所有 API。
alipay.user.agreement.page.sign 是页面签约接口,调用结果需要交给浏览器跳转或渲染,处理方式与普通的服务端 JSON 接口不同。即使某个版本的 Easy SDK 提供了通用调用能力,也需要确认它是否支持页面接口所需的 pageExecute 行为,不能直接把这个接口当作普通接口提交。
使用通用 SDK 发起签约
以下使用 Java 演示。依赖版本应根据项目现有的依赖管理,以及支付宝开放平台提供的版本确定,避免同时引入多个不兼容的版本。
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>${alipay-sdk.version}</version>
</dependency>
初始化 AlipayClient:
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do",
appId,
appPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
使用沙箱环境时,需要配置对应的沙箱网关、应用 ID 和密钥,不能与正式环境的配置混用。
调用 alipay.user.agreement.page.sign
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayUserAgreementPageSignRequest;
import com.alibaba.fastjson.JSON;
import java.util.LinkedHashMap;
import java.util.Map;
public String createAgreementSignForm(
AlipayClient alipayClient,
String externalAgreementNo,
String returnUrl,
String signNotifyUrl) throws Exception {
AlipayUserAgreementPageSignRequest request =
new AlipayUserAgreementPageSignRequest();
request.setReturnUrl(returnUrl);
Map<String, Object> bizContent = new LinkedHashMap<>();
bizContent.put("external_agreement_no", externalAgreementNo);
bizContent.put("personal_product_code", "CYCLE_PAY_AUTH");
// sign_scene 必须使用应用实际签约产品允许的场景值。
bizContent.put("sign_scene", "YOUR_SIGN_SCENE");
Map<String, Object> accessParams = new LinkedHashMap<>();
accessParams.put("channel", "ALIPAYAPP");
bizContent.put("access_params", accessParams);
Map<String, Object> periodRuleParams = new LinkedHashMap<>();
periodRuleParams.put("period_type", "MONTH");
periodRuleParams.put("period", 1);
periodRuleParams.put("execute_time", "2026-10-01");
periodRuleParams.put("single_amount", 99.00);
bizContent.put("period_rule_params", periodRuleParams);
bizContent.put("sign_notify_url", signNotifyUrl);
request.setBizContent(JSON.toJSONString(bizContent));
// 返回支付宝签约页面的 HTML form。
return alipayClient.pageExecute(request).getBody();
}
控制器可以把生成的表单直接返回给浏览器:
@GetMapping(value = "/agreement/sign", produces = "text/html;charset=UTF-8")
@ResponseBody
public String sign() throws Exception {
String externalAgreementNo = generateUniqueAgreementNo();
return createAgreementSignForm(
alipayClient,
externalAgreementNo,
"https://example.com/alipay/agreement/return",
"https://example.com/alipay/agreement/notify"
);
}
浏览器收到 HTML 后,会显示或自动提交支付宝签约表单。根据所用 SDK 提供的重载方法,也可以生成 GET 跳转地址,例如:
String redirectUrl = alipayClient
.pageExecute(request, "GET")
.getBody();
不同 SDK 版本返回的内容可能不同。需要先确认结果是完整 URL 还是 HTML 表单,再选择使用 HTTP 重定向或直接输出页面。
参数需要按已开通产品配置
示例参数只用于说明调用结构,不能直接作为所有商户的通用配置:
external_agreement_no:商户侧唯一的签约编号,需要持久化并避免重复。personal_product_code:周期扣款产品通常使用CYCLE_PAY_AUTH,具体取值仍以应用实际开通的产品为准。sign_scene:必须填写签约产品允许的业务场景值,不能保留示例中的占位符。access_params.channel:表示签约入口渠道,需要根据实际接入方式填写。period_rule_params:用于定义扣款周期、间隔、执行时间和单次金额等规则。sign_notify_url:支付宝异步通知地址,支付宝服务器必须能通过公网访问。return_url:用户完成签约后的同步跳转地址,不能替代异步通知。
字段是否必填、可用的枚举值以及金额限制,可能因商户签约产品和开放平台规则而异,应以应用控制台中对应产品的接口文档为准。
签约完成后的处理
同步跳转只适合用于页面展示,不能仅凭 return_url 判断签约是否成功。服务端还需要处理异步通知:
- 使用支付宝公钥验证通知签名。
- 校验应用 ID、商户签约编号等关键字段。
- 根据通知中的协议号更新本地签约状态。
- 对通知做幂等处理,避免重复更新。
- 必要时调用协议查询接口,再次确认签约状态。
完成周期扣款签约,只表示用户授予了代扣权限,并不会自动执行后续扣款。实际扣款仍需要按照已开通产品的扣款接口、协议号和周期规则另行发起。
注意事项
不要自行拼接支付宝网关 URL,也不要在前端生成签名。应用私钥只能保存在服务端。
如果准备继续使用 Easy SDK 的通用调用功能,需要先确认当前语言和版本明确支持页面类 API。普通的 generic execute 调用通常无法代替 pageExecute 生成页面跳转地址或表单。因此,调用 alipay.user.agreement.page.sign 时,使用支付宝通用 SDK 提供的 AlipayClient.pageExecute() 更合适。
备注:内容仅供参考。
