PAYATHON 2026

支付宝移动端SDK是否支持周期扣款签约后代扣?

支付阿杰

结论

支付宝移动端支付 SDK 的普通支付入口通常不能直接处理周期扣款中的「先签约、后代扣」请求。将签约页面地址或签约参数传给 payV2 等支付方法,通常会因参数类型或业务场景不匹配而失败。

一般按以下流程接入:

  1. 服务端调用支付宝周期扣款产品对应的签约接口。
  2. 获取支付宝返回的页面跳转内容或签约地址。
  3. App 通过系统浏览器、H5 容器或支付宝文档指定的 URL Scheme 打开签约页面。
  4. 用户在支付宝完成签约。
  5. 服务端根据异步通知确认签约结果。
  6. 服务端按照协议执行后续代扣。

是否可以使用专用 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 打开签约页面,服务端确认结果」的流程。

备注:内容仅供参考。