跨端自动续费流程方案设计

立即使用

客户端服务端请求服务器下单苹果服务器返回订单号向苹果服务器下单并进行扣款同步返回请求下单信息异步返回票据信息发送票据信息(TransactionId、OriginalTransactionId)调用票据校验接口,检查票据信息同步返回票据信息1、保存票据信息2、结算购买的会员3、入库订阅信息4、更改订单状态5、通知用户(MQ+SMS)返回结算结果第一次购买1、存储latest_receipt作为token,轮询使用2、检查is_in_intro_offer_period=1?则首次优惠已使用保存通知取消订阅200,告知已处理保存通知 检查是否有续订成功的交易,有则结算1、更新用户订阅信息为status=4已关闭2、更新用户订阅状态为continuous_in_use=0200,告知已处理通知类型:INITIAL_BUY正常续订不会有通知,依靠脚本轮询查询通知类型:CANCEL成功续订已过期的订阅通知类型:RENEWAL/DID_RECOVER保存通知 1、更新用户订阅信息为status=1待扣款2、更新用户订阅状态为continuous_in_use=1200,告知已处理通知类型:INTERACTIVE_RENEWAL以交互方式续订订阅保存通知 1、更新用户订阅信息为status=1待扣款2、更新用户订阅状态为continuous_in_use=1200,告知已处理通知类型:DID_CHANGE_RENEWAL_STATUS订阅状态改变保存通知 根据pending_renewal_info信息作处理1、auto_renew_status=1,表明处于自动续订中,忽略2、auto_renew_status=0,is_in_billing_retry_period=1则表明扣款失败,处于60日尝试扣款窗口期,此时: 2.1 更新用户订阅信息为status=3,扣款失败 2.2 记录本次扣款失败时间(60日后发短信用)3、auto_renew_status=0,is_in_billing_retry_period=0则表明用户关闭了订阅或订阅已失效 3.1 更新用户订阅信息为status=4,已关闭 3.2 更新用户订阅状态为continuous_in_use=0200,告知已处理检查是否有续订成功的交易,有则结算检查是否有续订成功的交易,有则结算检查是否有续订成功的交易,有则结算检查是否有续订成功的交易,有则结算有,mqtt通知类型:DID_FAIL_TO_RENEW由于账单问题续费失败检查是否有续订成功的交易,有则结算保存通知 根据pending_renewal_info信息作处理is_in_billing_retry_period=1则表明扣款失败,处于60日尝试扣款窗口期,此时:1、更新用户订阅信息为status=3,扣款失败2、记录本次扣款失败时间(60日后发短信用)200,告知已处理定时任务脚本轮询:1、时间点: 每日6、10、23点整2、要轮询的记录(一批用户): 2.1 status=1待扣款并且expires_date_ms在当前时间前后24h内的 2.2 status=3扣款失败的3、更新1天前status=2扣款成功的为status=1等待扣费使用latest_receipt请求票据信息有,mqtt有,mqtt有,mqtt有,mqtt有,mqtt返回票据信息续订成功与否检查检查是否有续订成功的交易对于status=3的还需要检查latest_deduction_fail_date_ms没有成功且在60天前则:1、更新用户订阅信息为status=4已关闭,关闭原因为账单原因2、sms告知关闭订阅有续订成功的则:1、入库最新一笔交易的信息:购买时间+到期时间+最新票据2、更新用户订阅信息为status=2,扣费成功3、首次折扣使用判断4、continuous_in_use=15、会员结算6、sms告知用户续订成功用户进入vip页面,静默触发“续订成功与否检查”有,mqtt备注:1、判断用户首单优惠:is_in_intro_offer_period字段值为true2、判断用户关闭订阅标识:pending_renewal_info,该字段是续订状态的说明。auto_renew_status为0,则说明已经关闭订阅3、判断用户在订阅周期内切换订阅的商品:pending_renewal_info,取product_id字段,字段内为最新一期续订的商品ID,注意一定不要取auto_renew_product_id。4、判断苹果扣费失败:对于扣费失败的用户,苹果仍会尝试扣款60天,解析pending_renewal_info中auto_renew_status为1并且is_in_billing_retry_period为1,此时用户的状态不能标记为已关闭,需要标记为扣费失败,以待每天的定时任务检查是否扣费成功。5、服务端轮询方案说明: a. 状态为等待扣费的,并且当前周期结束时间再当前时间之后一天的记录,苹果会在24小时内发起扣款,所以只查询这段时间内的即可,减少无用的轮询。 b. 所有状态为扣费失败的都应该每日检查是否重新扣费成功6、数据库中维护的字段订阅表如下:CREATE TABLE `ecm_auto_renewable` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL DEFAULT '0' COMMENT '用户id', `origin_transaction_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '原始事务ID,该ID不会变', `purchase_date_ms` int(11) DEFAULT NULL COMMENT '订阅开始时间', `expires_date_ms` int(11) DEFAULT NULL COMMENT '订阅结束时间', `latest_receipt` text COLLATE utf8mb4_unicode_ci COMMENT '该用户上一期的receipt', `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '1-等待扣费 2-扣费成功 3-扣费失败 4-已关闭', `latest_deduction_date_ms` int(11) DEFAULT NULL COMMENT '最近扣费成功的时间', `latest_deduction_fail_date_ms` int(11) DEFAULT NULL COMMENT '最近查询到处于60天扣费期的时间', `expiration_intent` tinyint(1) NOT NULL DEFAULT '0' COMMENT '订阅过期原因: 1-用户取消,2-账单错误(如用户支付信息不再有效),3-客户不同意最近提价,4-续订时无法购买,5-未知错误', `created_at` int(11) DEFAULT NULL COMMENT '创建时间', `updated_at` int(11) DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`), KEY `user_id` (`user_id`), KEY `origin_transaction_id` (`origin_transaction_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会员自动续费表'客户端服务端支付宝钱包APP用户点击下单(签约)请求服务器下单返回签约报文组装加前缀协议,schema方式唤起钱包签约支付宝服务端请求服务器显示签约页面确认签约处理签约显示签约结果同步返回app签约结果异步通知签约结果如果是签约,则:1、更新签约状态 2.1 填写agreement_no 2.2 status=2,签约成功 2.3 continuous_in_use=12、进行首次扣款5、mqtt通知签约成功。根据agreement_no、external_agreement_no判定为签约返回请求进行第一次扣款同步返回扣款结果异步通知扣款结果如果是交易,则:1、更新订单状态2、会员结算3、首单判断4、签约信息更新 4.1 计算下次扣款时间 4.2 重置扣费失败次数 4.3 continuous_in_use=15、mqtt+sms通知用户根据trade_status、out_trade_no、trade_no判定为交易返回mqttmqtt定时任务脚本轮询:1、时间点:每日8 22点整2、要轮询的用户: status=2签约成功并且next_pay_date>=今天到<=后三天的自动扣款过程有效,则请求进行扣款同步返回扣款结果异步通知扣款结果1、更新订单状态2、会员结算3、首单判断4、签约信息更新 4.1 计算下次扣款时间 4.2 重置扣费失败次数 4.3 continuous_in_use=15、mqtt+sms通知用户如果扣款失败:1、累计扣款失败2、检查pay_fail_count,如果>=2则调用解约接口解约5、解约成功后sms通知用户mqtt点击解约调用解约接口sdk调用解约接口解约同步返回解约结果同步返回解约结果异步通知解约结果消息提示解约如果是解约,则:1、更新用户签约状态 1.1 status=4,已解约 1.2 continuous_in_use=0处理解约根据agreement_no、external_agreement_no判定为签约返回点解解约处理解约消息提示解约异步通知解约结果处理解约根据agreement_no、external_agreement_no判定为签约返回如果是解约,则:1、更新用户签约状态 1.1 status=4,已解约 1.2 continuous_in_use=0调用协约检查接口同步返回检查结果1、调用sdk,生成签约报文2、保存签约信息status=1待签约first_pay_date、next_pay_date、external_agreement_no备注:1、支付宝的周期扣款有准入限制,请确保支付宝账号满足条件2、各个接口的入参和返回,以及消息的通知的格式,文档上有很多注意点,请仔细阅读周期扣款的接口调用事项。我们唤起钱包App那块由于唤起方式问题联调了很久。3、服务端在调用生成签约参数的方法时,alipayClient.pageExecute($request, "get")返回的字符串中只取gateway.do?后面的查询字符串,客户端encode以后再在字符串之前添加alipays://协议信息。4、签约和交易使用的是同一个notify_url,判断方法是如果有trade_status/out_trade_no为交易;如果有agreement_no/external_agreement_no则为签约,notify_type标识是签约还是解5、服务端轮询方案说明: a. 扣费主动权在我们服务端手上,但能否扣成功支付宝有限制(到期前3天开始才可以扣成功) b. 只轮询状态为签约成功的,在下次扣款3天前开始即可进行扣费6、数据库中维护的字段订阅表如下:CREATE TABLE `ecm_user_agreements` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL DEFAULT '0' COMMENT '用户id', `external_agreement_no` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '业务系统中用以唯一标识用户签约记录的编号,即订单号', `agreement_no` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '支付宝系统中用以唯一标识用户签约记录的编号', `agreement_type` tinyint(1) DEFAULT NULL COMMENT '协议类型:1-周期扣款', `status` tinyint(1) DEFAULT NULL COMMENT '签约状态: 1-待校验 2-签约成功 3-签约失败 4-已解约', `first_pay_date` date DEFAULT NULL COMMENT '第一次扣款时间', `next_pay_date` date DEFAULT NULL COMMENT '下一次扣款时间', `pay_fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '扣款失败次数', `created_at` int(11) DEFAULT NULL COMMENT '创建时间', `updated_at` int(11) DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`), KEY `user_id` (`user_id`), KEY `agreement_no` (`agreement_no`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='支付宝签约信息'苹果订阅自动续费流程支付宝周期扣款
818
2
27
发布时间: 2020-09-02