PC 浏览器网页能否接入支付宝自动续费
结论
支付宝支持 PC 网页会员自动续费,但普通扫码支付不能用于后续自动代扣。
alipay.trade.precreate 生成的付款二维码只能完成当前订单。用户扫码付款后,商户不能根据这次支付的结果直接扣取下个月的费用。连续包月需要单独申请支付宝的周期扣款或代扣产品,取得用户明确授权后,才能按照协议发起后续扣款。
支付宝开放平台可能调整具体产品名称、准入行业和可用接口,请以商户后台实际可申请的产品及对应文档为准。
推荐接入流程
1. 申请周期扣款能力
在支付宝开放平台创建网页应用,完成企业认证、应用签约、密钥配置和上线审核,然后申请适用于会员订阅业务的周期扣款产品。
这类产品通常对行业、经营内容和签约主体有限制。如果应用后台没有相应的产品入口,应先向支付宝开放平台确认当前业务是否符合准入要求。普通当面付或电脑网站支付权限不能代替代扣权限。
2. 设计订阅协议
签约页面应明确展示以下内容:
- 会员名称和服务内容;
- 每期扣款金额;
- 扣款周期及首次扣款时间;
- 自动续费规则;
- 免费试用或优惠期结束后的价格;
- 取消自动续费的方法;
- 扣款失败后的处理方式。
用户必须主动确认授权。一次扫码付款不代表用户同意后续自动扣款。
3. 发起签约
获得相关产品权限后,通过支付宝提供的签约接口引导用户完成授权。常见流程会使用 alipay.user.agreement.page.sign,但具体接口、product_code 和业务参数必须以商户获批产品的接口文档为准。
下面的结构示例仅用于说明需要保存的业务信息,不能直接复制到生产环境:
AlipayUserAgreementPageSignRequest request =
new AlipayUserAgreementPageSignRequest();
request.setReturnUrl("https://www.example.com/alipay/agreement/return");
request.setNotifyUrl("https://www.example.com/alipay/agreement/notify");
JSONObject bizContent = new JSONObject();
bizContent.put("external_agreement_no", "AGR_20260909_10001");
bizContent.put("personal_product_code", "<支付宝分配的产品代码>");
bizContent.put("sign_scene", "<获批产品对应的签约场景>");
request.setBizContent(bizContent.toJSONString());
String signUrl = alipayClient.pageExecute(request).getBody();
服务端生成签约地址后,可让 PC 浏览器跳转至支付宝签约页面。页面是否直接显示二维码、支持哪些确认方式,取决于支付宝的产品页面。商户不应自行模拟授权页面。
4. 处理签约结果
不能只根据浏览器的 return_url 判断签约是否成功。前端跳转可能被关闭、重复访问或篡改,签约结果应以支付宝异步通知和主动查询为准。
处理通知时,至少要完成以下检查:
- 使用支付宝 SDK 验证签名;
- 检查应用、商户和协议编号是否匹配;
- 校验
external_agreement_no是否属于当前用户; - 检查协议状态是否有效;
- 对重复通知进行幂等处理;
- 保存支付宝协议号、协议状态、签约时间和用户关系。
如果获批产品支持,可通过 alipay.user.agreement.query 主动查询协议状态。查询接口的必填参数以对应产品文档为准。
AlipayUserAgreementQueryRequest request =
new AlipayUserAgreementQueryRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("external_agreement_no", "AGR_20260909_10001");
bizContent.put("personal_product_code", "<支付宝分配的产品代码>");
request.setBizContent(bizContent.toJSONString());
AlipayUserAgreementQueryResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
String agreementNo = response.getAgreementNo();
String status = response.getStatus();
// 将协议状态更新到订阅记录中
}
示例中的方法名和返回字段只适用于支持该接口的产品,不能据此认定所有代扣产品都使用相同字段。
5. 按协议发起扣款
签约成功后,服务端定时任务根据订阅到期时间创建续费订单,并调用获批产品指定的扣款接口。部分周期扣款方案会通过 alipay.trade.pay 携带协议信息完成扣款。能否采用这种方式以及需要传入哪些参数,仍以签约产品文档为准。
下面仅展示服务端扣款的基本结构:
AlipayTradePayRequest request = new AlipayTradePayRequest();
request.setNotifyUrl("https://www.example.com/alipay/payment/notify");
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", "RENEW_20261009_10001");
bizContent.put("total_amount", "29.00");
bizContent.put("subject", "会员连续包月");
bizContent.put("product_code", "<支付宝分配的代扣产品代码>");
JSONObject agreementParams = new JSONObject();
agreementParams.put("agreement_no", "<用户签约后取得的支付宝协议号>");
bizContent.put("agreement_params", agreementParams);
request.setBizContent(bizContent.toJSONString());
AlipayTradePayResponse response = alipayClient.execute(request);
不要自行猜测 product_code、扣款次数、扣款日期或重试参数。支付宝通常会限制扣款金额、周期、执行时间和重试方式,提交内容也必须与用户签约时确认的规则一致。
6. 根据最终支付状态开通会员
同步响应只能说明接口调用结果,不一定代表最终资金状态。续费订单应以可靠的交易状态查询结果或验签后的异步通知为准。
可以采用以下状态流转:
待扣款 -> 扣款处理中 -> 扣款成功
-> 扣款失败
-> 状态待确认
每一期续费都应使用唯一的 out_trade_no,并在数据库中设置唯一约束,防止任务重试导致重复扣款。如果请求超时或状态不明确,应先查询原订单,不要直接创建第二笔扣款。
普通扫码支付和自动续费的区别
普通 PC 扫码支付的流程通常是:
创建订单 -> 生成二维码 -> 用户扫码并确认 -> 完成本次付款
自动续费的流程则是:
展示续费规则 -> 用户签约授权 -> 保存有效协议
-> 到期创建续费订单 -> 按协议扣款 -> 处理结果
-> 用户解约后停止扣款
扫码支付可以用于购买首期会员,但如果首次支付时没有同时完成支付宝认可的代扣签约,下一个周期就不能自动扣款。
解约与异常处理
系统需要提供清晰的自动续费关闭入口。用户取消订阅时,应调用获批产品对应的解约接口,并立即停止在本地创建新的续费任务。常见的解约接口是 alipay.user.agreement.unsign,具体仍以实际产品文档为准。
系统还要处理以下情况:
- 用户已经在支付宝端解约;
- 协议失效或被冻结;
- 余额不足或支付工具不可用;
- 扣款金额超过协议限制;
- 异步通知重复或乱序到达;
- 扣款请求超时,但支付宝侧已经成功;
- 解约与定时扣款同时发生。
解约操作和扣款任务之间需要做好并发控制。每次扣款前,最好再次检查本地订阅状态。要求较高的业务还应查询支付宝协议状态。
注意事项
- 不要保存用户的支付宝密码、支付密码或银行卡信息。
- 私钥只能保存在服务端,不能写入网页 JavaScript。
- 所有支付宝通知都必须验签,不能仅凭请求参数更新会员状态。
- 金额应使用精确的十进制类型,避免用浮点数计算。
- 签约协议、扣款订单和会员权益应分别建模,不要让一张订单表承担所有状态。
- 扣款失败后能否重试、何时重试及最多重试几次,必须遵守获批产品的规则。
- 调整价格通常需要重新取得用户确认,不能直接修改已有授权对应的扣款金额。
- 上线前应完整验证签约、首扣、续扣、失败、查询、解约、退款和重复通知等流程。
如果当前商户无法申请周期扣款产品,只能在每个周期到期前提醒用户重新扫码支付,不能通过普通扫码支付接口自行实现”免确认自动续费”。
备注:内容仅供参考。
