连续包月-周期扣款-自动续费业务整理总结

开篇

连续包月 - 周期扣款 - 自动续费名字很多，先简单解释这三个的意思:

支付宝官方定义的名称为 -周期扣款

连续包月是这个模式常用的行为 (也可以包天，包季度),

自动续费是在用户的眼里行为，可以不用手动付款

扣款场景

周期扣款有两种模式:

先签约后代扣场景

先跳转到签约页面

用户同意签约

支付宝签约异步回调 && 同步发起扣款

支付后签约场景

跳转到支付页面 (用户可以手动关闭自动续费)

用户支付

收到两个异步通知，支付通知 + 签约通知

支付后签约场景的模式虽然简单，但是用户可以手动关闭续费，所以很多时候我们选择先签约后代扣场景

申请信息

了解清楚基本逻辑之后，就是要申请功能，支付宝的可以参考此链接https://opendocs.alipay.com/open/20190319114403226822/intro

需要注意的是微信周期扣款申请比较复杂，还需要申请一个额外的模板 ID,

签约页面唤起逻辑

服务端根据SDK生成签名串 (值的注意的https://github.com/go-pay/gopay 这个包没有实现这个功能 我提交了新功能并于2023-01-01合并https://github.com/go-pay/gopay/pull/295)

这个SDK虽然包含了client.UserAgreementPageSign()方法，但是这个方法实际上会请求支付宝返回一个H5页面唤醒签约页面 (但是这个只能给前端使用)

如果是APP签约，我们不能请求支付宝，必须本地签名 (这里很坑，支付宝API文档没写，需要在接入指南查看)

官方提供SDK的代码示例如下 (如果是PHP,Java,Node可以直接用官方SDK)

Copy
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do","app_id","your private_key","json","GBK","alipay_public_key","RSA2");
AlipayUserAgreementPageSignRequest request = new AlipayUserAgreementPageSignRequest();
request.setBizContent(参考上面的bizContent值);
//无线端唤起签约
//pageExecute get方式，生成url
AlipayUserAgreementPageSignResponse response = alipayClient.pageExecute(request,"get");

//生成的url地址去除http://openapi.alipay.com/gateway.do
signParams = response.replace("http://openapi.alipay.com/gateway.do?","");

//生成唤起客户端。把signParams使用 UTF-8 字符集整体做一次 encode
return 'alipays://platformapi/startapp?appId=60000157&appClearTop=false&startMultApp=YES&sign_params='+ encode(signParams)

开发

由于我使用的是Go, 所以使用这个包 https://github.com/go-pay/gopay

值的注意的是，当前日期2022-12-29由于支付宝V3是还不支持周期扣款的，所以我们需要用V2

首先基于表设计，这边大致如下 (省略部分)

Copy
id, no(订单号), tarde_no(商户订单号), user_id(用户), goods_id(商品), contract_code(自定义签约号), contract_id(商户签约号)

订单流程

用户下单连续包月产品时候，创建订单，订单包含一个随机的contract_code

调用生成签约参数时，把contract_code当做商户签约号参数生成唤醒签约页面链接签名

下发订单号no和支付宝唤醒签约页面链接给客户端，客户端唤醒支付宝签约页面

签约成功之后，异步通知回调会把contract_code和contract_code返回给我们 (插入签约表)

通过contract_code查找到订单，然后调用支付接口alipay.trade.pay(同步调用), 实时获取是否扣费成功

客户端返回的时候，根据订单号no调用服务端，如果订单未支付，服务端再去主动扣款一次

增加定时任务，定时查询签约表，找到五天内到期的调用同步支付接口