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

  

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

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM