IOS IAP 自動續訂 之 利用rabbitmq延時隊列自動輪詢檢查是否續訂成功

啓用針對自動續期訂閱的服務器通知: 

- 官方地址:python

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

  - 相關字段, 相關類型地址: https://developer.apple.com/documentation/storekit/in-app_purchase/enabling_server-to-server_notificationsapp

 

- 蘋果針對自動續訂:ide

App Store會向您的服務器發送訂閱狀態實時更改的通知。statusUpdateNotificationsspa

  - 6種通知類型: 設計

INITIAL_BUYcode

在最初購買訂閱時發生。經過在App Store中驗證,能夠隨時您的服務器存儲在服務器上以驗證用戶的訂閱狀態。latest_receiptorm

CANCELserver

表示當用戶升級訂閱時,Apple客戶支持或App Store已取消訂閱。密鑰包含的日期和訂購被取消或升級的時間。cancellation_dateblog

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"},
        ]
隊列聲明

  

- 附贈一份初始設計只有三個隊列的思惟導圖:

相關文章
相關標籤/搜索