支付宝移动端SDK是否支持周期扣款签约后代扣?
结论
支付宝移动端支付 SDK 的普通支付入口通常不能直接处理周期扣款中的「先签约、后代扣」请求。将签约页面地址或签约参数传给 payV2 等支付方法,通常会因参数类型或业务场景不匹配而失败。
一般按以下流程接入:
- 服务端调用支付宝周期扣款产品对应的签约接口。
- 获取支付宝返回的页面跳转内容或签约地址。
- App 通过系统浏览器、H5 容器或支付宝文档指定的 URL Scheme 打开签约页面。
- 用户在支付宝完成签约。
- 服务端根据异步通知确认签约结果。
- 服务端按照协议执行后续代扣。
是否可以使用专用 SDK 接口,要以当前签约产品的支付宝开放平台文档和商户已开通的能力为准。普通 App 支付 SDK 唤起失败,并不代表所有移动端 SDK 都不支持签约。
为什么普通支付 SDK 会失败
普通 App 支付 SDK 主要用于处理支付订单,例如把服务端生成并签名的订单字符串传给:
PayTask.payV2(orderInfo, true)
周期扣款签约不是普通支付订单。它使用的请求接口、业务参数、签名内容、回跳方式和结果确认流程都可能不同。
下面这些做法通常行不通:
- 把签约页面 URL 当作
orderInfo传给支付 SDK。 - 用普通支付接口的参数拼装签约请求。
- 只根据客户端同步返回的结果判断签约是否成功。
- 在 App 中保存支付宝应用私钥,直接生成签约请求。
接口返回的页面跳转内容可能是 URL,也可能是需要提交的 HTML Form。客户端应根据接口实际返回的类型进行处理,不能默认返回值一定是可以直接打开的链接。
推荐接入流程
1. 服务端创建签约请求
服务端调用支付宝为周期扣款产品提供的签约接口。部分产品可能使用类似 alipay.user.agreement.page.sign 的页面签约接口,具体接口名称和参数要以商户已开通产品的当前文档为准。
示意代码:
AlipayClient alipayClient = new DefaultAlipayClient(
gatewayUrl,
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayUserAgreementPageSignRequest request =
new AlipayUserAgreementPageSignRequest();
request.setNotifyUrl("https://example.com/alipay/agreement/notify");
request.setReturnUrl("https://example.com/alipay/agreement/return");
request.setBizContent("""
{
"personal_product_code": "PRODUCT_CODE_FROM_ALIPAY",
"sign_scene": "INDUSTRY|DEFAULT_SCENE",
"external_agreement_no": "AGREEMENT_202609090001",
"access_params": {
"channel": "ALIPAYAPP"
}
}
""");
AlipayUserAgreementPageSignResponse response =
alipayClient.pageExecute(request);
if (!response.isSuccess()) {
throw new IllegalStateException(
response.getCode() + ": " + response.getSubMsg()
);
}
String pageRedirectionData = response.getBody();
这段代码只用于说明接口的调用位置和处理流程。personal_product_code、sign_scene、access_params 等字段是否必填,以及可以填写哪些值,取决于商户开通的周期扣款产品,不能直接照搬示例。
2. 将跳转信息返回给 App
服务端可以返回统一的数据结构:
{
"agreementRequestId": "AGREEMENT_202609090001",
"openType": "URL",
"redirectUrl": "https://example-alipay-sign-page"
}
如果支付宝接口返回的是 HTML Form,而非 URL,应按照官方说明输出或承载该表单,不要自行从 HTML 中截取链接。
3. App 打开签约页面
Android 通过系统浏览器打开 HTTPS 地址的示例:
Uri uri = Uri.parse(redirectUrl);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
iOS 示例:
guard let url = URL(string: redirectUrl) else {
return
}
UIApplication.shared.open(url, options: [:]) { success in
if !success {
// 提示用户无法打开签约页面
}
}
如果支付宝文档明确要求使用特定 URL Scheme,应完整使用服务端或支付宝返回的地址,并配置对应的回跳 Scheme。不要自行拼接文档中没有定义的 alipays:// 参数。
4. 通过异步通知确认结果
用户从支付宝返回 App,只能说明签约页面流程已经结束,不能证明签约成功。服务端应结合以下信息确认最终状态:
- 支付宝异步通知;
- 签约协议查询接口;
- 商户自己的
external_agreement_no; - 支付宝返回的协议号和协议状态。
处理通知时必须验签,同时校验应用、商户和协议编号等字段:
boolean verified = AlipaySignature.rsaCheckV1(
requestParams,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!verified) {
throw new SecurityException("Invalid Alipay signature");
}
// 验证 app_id、商户身份、external_agreement_no 和协议状态后再更新本地记录
异步通知处理完成后,应按照支付宝接口要求返回成功响应。通知处理还要保证幂等,避免因重复通知多次更新同一条记录。
后续代扣
签约成功后,代扣请求由服务端发起。服务端需要保存业务订单与支付宝协议标识的关联,并在每次扣款前检查:
- 协议是否仍然有效;
- 是否符合约定的扣款周期和金额规则;
- 当前账期是否已经发起过扣款;
- 是否需要向用户发送扣款提醒;
- 当前请求是否有唯一的业务流水号。
客户端不应保存商户私钥,也不应直接调用服务端代扣接口。
注意事项
- 普通 App 支付 SDK 的订单支付能力与周期扣款签约能力使用不同的调用入口。
- 不要把页面签约返回值直接传给
payV2,除非对应产品文档明确允许这种调用方式。 - 不建议依赖 App 内 WebView。支付宝页面可能涉及外部 App 跳转、Cookie、重定向或安全限制,应优先采用官方推荐的浏览器或容器方案。
return_url只能改善用户的回跳体验,不能作为签约成功的可信依据。- 签约页面提示失败时,应记录支付宝返回的
code、sub_code、msg、sub_msg和请求流水号。失败还可能由产品未开通、签约场景不匹配、参数缺失、应用类型不符或签名错误引起。 - 周期、金额、解约和扣款提醒等规则应符合商户实际开通的产品能力。
- 如果当前支付宝产品文档提供了专用的客户端签约 SDK,应优先使用该入口。否则,应采用「服务端创建签约请求,App 打开签约页面,服务端确认结果」的流程。
备注:内容仅供参考。
