zoukankan      html  css  js  c++  java
  • IOS IAP 自动续订 之 利用rabbitmq延时队列自动轮询检查是否续订成功

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

    - 官方地址:

      - 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"},
            ]
    队列声明

      

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

  • 相关阅读:
    【MISC】ctf-想要种子吗?——jpg图片隐写
    【MISC】ctf-白哥的鸽子——jpg图片隐写
    关于MySQL递归查询,从子类ID查询所有父类
    关于lambdaQueryWrapper中常用方法
    Jmeter之正则表达式提取器+BeanShell断言--将返回结果导出到本地文件
    SQLMap开源渗透工具-SQLMap注入安全测试
    jmeter之配置元件-HTTP授权管理器(HTTP Authorization Manager)
    Jmeter实战大全(持续更新)
    Jmeter 之Ramp-up Period&固定定时器
    《从一万到一千万没有秘密》
  • 原文地址:https://www.cnblogs.com/Fushengliangnian/p/11263995.html
Copyright © 2011-2022 走看看