启用针对自动续期订阅的服务器通知:

- 官方地址:

  - https://help.apple.com/app-store-connect/#/dev0067a330b

  - 相关字段, 相关类型地址: https://developer.apple.com/documentation/storekit/in-app_purchase/enabling_server-to-server_notifications

- 苹果针对自动续订:

App Store会向您的服务器发送订阅状态实时更改的通知。statusUpdateNotifications

  - 6种通知类型:

INITIAL_BUY

在最初购买订阅时发生。通过在App Store中验证,可以随时将您的服务器存储在服务器上以验证用户的订阅状态。latest_receipt

CANCEL

表示当用户升级订阅时,Apple客户支持或App Store已取消订阅。该密钥包含的日期和订购被取消或升级的时间。cancellation_date

RENEWAL

表示成功自动续订过去未能续订的过期订阅。检查以确定下一个续订日期和时间。expires_date

INTERACTIVE_RENEWAL

表示客户通过使用应用程序界面或在帐户设置中的App Store上以交互方式续订订阅。立即提供服务。

DID_CHANGE_RENEWAL_PREF

表示客户对其订阅计划进行了更改,该更改将在下次续订时生效。当前有效的计划不受影响。

DID_CHANGE_RENEWAL_STATUS

表示订阅续订状态的更改。检查JSON中的和,以了解上次更新状态的日期和时间以及当前的续订状态。auto_renew_status_change_date_msauto_renew_status

  

  - 上面的类型中缺少了用户无操作自动订阅通知:

    虽然没有自动续订的通知, 但是, 我们可以拿任意一段周期的订单的收据去Apple服务器校验就可以拿到全部的订阅信息, 所以可以根据这一思路来对用户的续订手否成功做出判断;

    针对这种情况, 我想到了三种情况来针对苹果自动续订成功的问题:

      - 1. 利用 Apple 发给我们的消息中的 auto_renew_status 字段来判断, 默认用户续订成功, 当接收到 CANCEL 或者 DID_CHANGE_RENEWAL_STATUS 类型的消息通知时, 更改用户续订的状态 auto_renew_status。 这样可能会出现当用户过期时, 苹果续费未成功却仍然再尝试续费, 会出现一段时间免费了的时间。

      - 2. 项目中利用用户的过期时间来进行判断该用户是否有权限访问项目; 在查询用户的过期时间的接口中添加逻辑: 当用户到期后向Apple 服务器请求订单信息判断最新的订单是否成功, 成功了则更新库中保存的用户的新的过期时间, 反之则取消用户的权限。 这种方式相对来说比较简单, 但是这种情况需要依靠用户来触发, 而且在触发时会给用户的响应时间会增长, 降低体验; 而且当用户长时间不登录时, 便获取不到用户的最新的订阅状态, 也算是弊端之一。

      - 3. 利用定时任务来定时想Apple服务器发送请求, 校验用户是否有续订的状态。这个也是有一定弊端的, 要时刻小心你的定时任务挂掉。

RabbitMQ的延时队列(死信队列):

定时任务选用的是RabbitMQ的延时队列来做的, 具体RabbitMQ的延时队列这里不再叙述, 网上有很多详细的教程, 这里只记录几点注意事项:

- 简述实现流程:

  因为订阅分为: 3天试用, 7天试用, 28天月度订阅, 30天月度订阅, 31天月度订阅, 365天年度订阅, 366天年度订阅; 为了能够适配, 保证任意天数的订阅都可以再相对应的时间到期后进行多次轮询, 我再这里使用了9个延时队列与一个正常的 worker:

  - 延时队列:

    - 过期时间为一小时的队列;

    - 过期时间为 1 天的队列;

    - 过期时间为 2 天的队列;

    - 过期时间为 4 天的队列;

    - 过期时间为 8 天的队列;

    - 过期时间为 16 天的队列;

    - 过期时间为 32 天的队列;

    - 过期时间为 64 天的队列;

    - 过期时间为 128 天的队列;

    - 过期时间为 256 天的队列;

  - worker:

    - 正常队列

  - 基本流程:

    在首次订阅的时候, 判断该订单的过期时间的天数选择最近的天数的延时队列, 将其放入, 等在该队列过期后根据剩余的天数再次选择放入的队列, 依次类推, 若续订成功则更新库中存的过期时间, 若依旧没有新的续订订单则继续再队列中等待, 直到进入小时队列后超过固定的时间后依然没有续订成功, 则视为续订失败。

- RabbitMQ 队列的特点:

  - 每个延时队列中的消息的过期时间必须一致, 因为只有在最顶部的消息过期后, 后面的消息才会被抛出, 倘若存在不一样的过期时间, 会出现第一个消息没有过期, 但是第二个消息已经过期了, 却无法出去的问题;

  - 正常队列中, 如果消息没有被消费, 或者在消费的过程中出现异常, 该消息会一直堵塞在队列的出口, 等待被消费。

  - 可以多个延时队列导向同一个正常队列

  - 队列声明的代码: 利用的是python的 aioamqp 模块

class InitDelayWorker(QueueInitWorker):

    def get_microservice_data(self, app):
return [
{"action": "exchange_declare", "exchange_name": "out.exchange.direct", "type_name": "direct"},
{"action": "exchange_declare", "exchange_name": "dlx.exchange.direct", "type_name": "direct"},
{"action": "queue_declare", 'queue_name': "buffer-queue",
"arguments": {"x-dead-letter-exchange": "dlx.exchange.direct",
"x-dead-letter-routing-key": "dlx.routing.key"}},
{"action": "queue_bind", "queue_name": "buffer-queue", "exchange_name": "out.exchange.direct",
"routing_key": "out.routing.key"}, # 1天查询一次
# 定义一个交换机 用与正常队列
{"action": "exchange_declare", "exchange_name": "oneDay.exchange.direct", "type_name": "direct"},
# {"action": "exchange_declare", "exchange_name": "oneDayDLX.exchange.direct", "type_name": "direct"},
{"action": "queue_declare", 'queue_name': "one-day-buffer-queue",
"arguments": {"x-dead-letter-exchange": "dlx.exchange.direct",
"x-dead-letter-routing-key": "dlx.routing.key"}},
{"action": "queue_bind", "queue_name": "one-day-buffer-queue", "exchange_name": "oneDay.exchange.direct",
"routing_key": "oneDay.routing.key"}, # 2天查询一次
{"action": "exchange_declare", "exchange_name": "twoDay.exchange.direct", "type_name": "direct"},
# {"action": "exchange_declare", "exchange_name": "twoDayDLX.exchange.direct", "type_name": "direct"},
{"action": "queue_declare", 'queue_name': "two-day-buffer-queue",
"arguments": {"x-dead-letter-exchange": "dlx.exchange.direct",
"x-dead-letter-routing-key": "dlx.routing.key"}},
{"action": "queue_bind", "queue_name": "two-day-buffer-queue", "exchange_name": "twoDay.exchange.direct",
"routing_key": "twoDay.routing.key"}, # 4天查询一次
{"action": "exchange_declare", "exchange_name": "fourDay.exchange.direct", "type_name": "direct"},
# {"action": "exchange_declare", "exchange_name": "fourDayDLX.exchange.direct", "type_name": "direct"},
{"action": "queue_declare", 'queue_name': "four-day-buffer-queue",
"arguments": {"x-dead-letter-exchange": "dlx.exchange.direct",
"x-dead-letter-routing-key": "dlx.routing.key"}},
{"action": "queue_bind", "queue_name": "four-day-buffer-queue", "exchange_name": "fourDay.exchange.direct",
"routing_key": "fourDay.routing.key"}, # 8天查询一次
{"action": "exchange_declare", "exchange_name": "eightDay.exchange.direct", "type_name": "direct"},
# {"action": "exchange_declare", "exchange_name": "eightDayDLX.exchange.direct", "type_name": "direct"},
{"action": "queue_declare", 'queue_name': "eight-day-buffer-queue",
"arguments": {"x-dead-letter-exchange": "dlx.exchange.direct",
"x-dead-letter-routing-key": "dlx.routing.key"}},
{"action": "queue_bind", "queue_name": "eight-day-buffer-queue", "exchange_name": "eightDay.exchange.direct",
"routing_key": "eightDay.routing.key"}, # 16天查询一次
{"action": "exchange_declare", "exchange_name": "sixteenDay.exchange.direct", "type_name": "direct"},
# {"action": "exchange_declare", "exchange_name": "sixteenDayDLX.exchange.direct", "type_name": "direct"},
{"action": "queue_declare", 'queue_name': "sixteen-day-buffer-queue",
"arguments": {"x-dead-letter-exchange": "dlx.exchange.direct",
"x-dead-letter-routing-key": "dlx.routing.key"}},
{"action": "queue_bind", "queue_name": "sixteen-day-buffer-queue", "exchange_name": "sixteenDay.exchange.direct",
"routing_key": "sixteenDay.routing.key"}, # 32天查询一次
{"action": "exchange_declare", "exchange_name": "thirtyDay.exchange.direct", "type_name": "direct"},
# {"action": "exchange_declare", "exchange_name": "thirtyDayDLX.exchange.direct", "type_name": "direct"},
{"action": "queue_declare", 'queue_name': "thirty-day-buffer-queue",
"arguments": {"x-dead-letter-exchange": "dlx.exchange.direct",
"x-dead-letter-routing-key": "dlx.routing.key"}},
{"action": "queue_bind", "queue_name": "thirty-day-buffer-queue", "exchange_name": "thirtyDay.exchange.direct",
"routing_key": "thirtyDay.routing.key"}, # 64天查询一次
{"action": "exchange_declare", "exchange_name": "sixtyDay.exchange.direct", "type_name": "direct"},
# {"action": "exchange_declare", "exchange_name": "sixtyDayDLX.exchange.direct", "type_name": "direct"},
{"action": "queue_declare", 'queue_name': "sixty-day-buffer-queue",
"arguments": {"x-dead-letter-exchange": "dlx.exchange.direct",
"x-dead-letter-routing-key": "dlx.routing.key"}},
{"action": "queue_bind", "queue_name": "sixty-day-buffer-queue", "exchange_name": "sixtyDay.exchange.direct",
"routing_key": "sixtyDay.routing.key"}, # 128天查询一次
{"action": "exchange_declare", "exchange_name": "twentyDay.exchange.direct", "type_name": "direct"},
# {"action": "exchange_declare", "exchange_name": "twentyDayDLX.exchange.direct", "type_name": "direct"},
{"action": "queue_declare", 'queue_name': "twenty-day-buffer-queue",
"arguments": {"x-dead-letter-exchange": "dlx.exchange.direct",
"x-dead-letter-routing-key": "dlx.routing.key"}},
{"action": "queue_bind", "queue_name": "twenty-day-buffer-queue", "exchange_name": "twentyDay.exchange.direct",
"routing_key": "twentyDay.routing.key"}, # 256天查询一次
{"action": "exchange_declare", "exchange_name": "fiftyDay.exchange.direct", "type_name": "direct"},
# {"action": "exchange_declare", "exchange_name": "fiftyDayDLX.exchange.direct", "type_name": "direct"},
{"action": "queue_declare", 'queue_name': "fifty-day-buffer-queue",
"arguments": {"x-dead-letter-exchange": "dlx.exchange.direct",
"x-dead-letter-routing-key": "dlx.routing.key"}},
{"action": "queue_bind", "queue_name": "fifty-day-buffer-queue", "exchange_name": "fiftyDay.exchange.direct",
"routing_key": "fiftyDay.routing.key"},
]

队列声明

  

- 附赠一份初始设计只有三个队列的思维导图:

IOS IAP 自动续订 之 利用rabbitmq延时队列自动轮询检查是否续订成功的更多相关文章

  1. RabbitMQ延时队列应用场景

    应用场景 我们系统未付款的订单,超过一定时间后,需要系统自动取消订单并释放占有物品 常用的方案 就是利用Spring schedule定时任务,轮询检查数据库 但是会消耗系统内存,增加了数据库的压力. ...

  2. rabbitmq 延时队列

    前言 某个产品 或者订单,有个有效期 过了有效期要取消 方法一 : 写个脚本,用crontab 定时扫描 改变状态 但是最低只能一分钟 ,不适合 方法二 : 用swoole得毫秒定时器,每秒钟去扫描表 ...

  3. java实现rabbitMQ延时队列详解以及spring-rabbit整合教程

    在实际的业务中我们会遇见生产者产生的消息,不立即消费,而是延时一段时间在消费.RabbitMQ本身没有直接支持延迟队列功能,但是我们可以根据其特性Per-Queue Message TTL和 Dead ...

  4. rabbitmq 延时队列 插件方式实现 每条消息都延时自己时间

    上篇文章的延时是加到队列上的 通过死信过时推送 ,缺点就是不能每条消息定义自己的过时时间而且每次有新的过时时间,要新建一个交换机和队列 https://www.cnblogs.com/brady-wa ...

  5. RabbitMQ 延时消息队列

    消息延时在日常随处可见: 1.订单创建10min之后不发起支付,自动取消. 2.30min定时推送一次邮件信息. 最常用到方式后台定时任务轮训,量小的时候可以使用,量大会出现数据读取会性能问题.Rab ...

  6. 基于rabbitMQ 消息延时队列方案 模拟电商超时未支付订单处理场景

    前言 传统处理超时订单 采取定时任务轮训数据库订单,并且批量处理.其弊端也是显而易见的:对服务器.数据库性会有很大的要求,并且当处理大量订单起来会很力不从心,而且实时性也不是特别好 当然传统的手法还可 ...

  7. 面试官:RabbitMQ过期时间设置、死信队列、延时队列怎么设计?

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 RabbitMQ我们经常的使用, ...

  8. 【RabbitMQ】一文带你搞定RabbitMQ延迟队列

    本文口味:鱼香肉丝   预计阅读:10分钟 一.说明 在上一篇中,介绍了RabbitMQ中的死信队列是什么,何时使用以及如何使用RabbitMQ的死信队列.相信通过上一篇的学习,对于死信队列已经有了更 ...

  9. 基于Redis实现延时队列服务

    背景 在业务发展过程中,会出现一些需要延时处理的场景,比如: a.订单下单之后超过30分钟用户未支付,需要取消订单 b.订单一些评论,如果48h用户未对商家评论,系统会自动产生一条默认评论 c.点我达 ...

随机推荐

  1. poj_3617 解题

    题意: 给定长度为N的字符串S,要构造一个长度为N的字符串T串. 从S的头部删除一个字符,加到T的尾部 从S的尾部删除一个字符,加到T的尾部 目标是构造字典序尽可能小的字符串. 示例输入: ACDBC ...

  2. API文档大集合

    [API文档大集合] sklearn API:http://sklearn.apachecn.org/cn/0.19.0/tutorial/index.html pandas API:http://p ...

  3. 基于MATLAB的手写公式识别(10)

    2公式分割 2.1投影分割法 12:23:00 完成水平和垂直两个方向上的投影 15:32:23 水平投影只投到水平方向的影,垂直投影只投到垂直方向上的影. 16:51:38  17:37:08 终其 ...

  4. qta自动化

    qta框架采用PO(page object)模式,即页面结构层和逻辑对象层,如图的用例结构:我们将页面结构放到lib层,将执行用例层放到test层,区分开方便维护:

  5. 使用EasySYS搭建驱动开发基本框架

    提供EasySYS的下载地址:http://bbs.pediy.com/showthread.php?p=956643,看雪上有提供下载,自行百度. EasySYS你能够帮我们快速的搭建驱动的开发框架 ...

  6. 如何让c语言使用结构体近似模拟c++中的类

    如今统治市场的主流编程语言,如c++,java,大都是面向对象类型的编程语言. 而众所周知,c语言是面向过程的编程语言,但是它拥有一个类似于类的结构,叫做结构体,主要的区别在于结构体无法定义函数. 因 ...

  7. Intel汇编语言程序设计学习-第五章 过程-下

    5.3.3  库测试程序 测试程序#1:整数I/O 该测试程序把输出文本的颜色改为蓝底黄字,然后以十六进制数显示七个数组的内容,最后提示用户输入一个有符号整数,再分别以十进制.十六进制和二进制格式重复 ...

  8. node-util

    Node.js 常用工具 util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScript 的功能 过于精简的不足. util.inherits util.inherit ...

  9. 一、HttpRunner学习汇总

    HttpRunner是一款面向Http和HTTPS协议的通用测试框架,只需编写维护一份YAML/JSON脚本即可实现自动化测试.性能测试.线上监控.持续集成等多种测试需求,是基于关键字驱动的框架,基于 ...

  10. 数据人必读!玩转数据可视化用这个就够了——高德LOCA API 2.0升级来袭!

    引言 "一图胜千言",大数据时代来临,数据与人们生活密切相关.复杂难懂且体量庞大的数据给人的感觉总是冷冰冰的,让人难以获取到重点信息,也找不出规律和特征,数据价值发挥不出来.空间数 ...