- 官方地址: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會向您的服務器發送訂閱狀態實時更改的通知。statusUpdateNotifications
spa
設計
|
在最初購買訂閱時發生。經過在App Store中驗證,能夠隨時將您的服務器存儲在服務器上以驗證用戶的訂閱狀態。 |
|
表示當用戶升級訂閱時,Apple客戶支持或App Store已取消訂閱。該密鑰包含的日期和訂購被取消或升級的時間。 |
|
表示成功自動續訂過去未能續訂的過時訂閱。檢查以肯定下一個續訂日期和時間。 |
|
表示客戶經過使用應用程序界面或在賬戶設置中的App Store上以交互方式續訂訂閱。當即提供服務。 |
|
表示客戶對其訂閱計劃進行了更改,該更改將在下次續訂時生效。當前有效的計劃不受影響。 |
|
表示訂閱續訂狀態的更改。檢查JSON中的和,以瞭解上次更新狀態的日期和時間以及當前的續訂狀態。 |
- 上面的類型中缺乏了用戶無操做自動訂閱通知:
雖然沒有自動續訂的通知, 可是, 咱們能夠拿任意一段週期的訂單的收據去Apple服務器校驗就能夠拿到所有的訂閱信息, 因此能夠根據這一思路來對用戶的續訂手否成功作出判斷;
針對這種狀況, 我想到了三種狀況來針對蘋果自動續訂成功的問題:
- 1. 利用 Apple 發給咱們的消息中的 auto_renew_status 字段來判斷, 默認用戶續訂成功, 當接收到 CANCEL 或者 DID_CHANGE_RENEWAL_STATUS 類型的消息通知時, 更改用戶續訂的狀態 auto_renew_status。 這樣可能會出現當用戶過時時, 蘋果續費未成功卻仍然再嘗試續費, 會出現一段時間免費了的時間。
- 2. 項目中利用用戶的過時時間來進行判斷該用戶是否有權限訪問項目; 在查詢用戶的過時時間的接口中添加邏輯: 當用戶到期後向Apple 服務器請求訂單信息判斷最新的訂單是否成功, 成功了則更新庫中保存的用戶的新的過時時間, 反之則取消用戶的權限。 這種方式相對來講比較簡單, 可是這種狀況須要依靠用戶來觸發, 並且在觸發時會給用戶的響應時間會增加, 下降體驗; 並且當用戶長時間不登陸時, 便獲取不到用戶的最新的訂閱狀態, 也算是弊端之一。
- 3. 利用定時任務來定時想Apple服務器發送請求, 校驗用戶是否有續訂的狀態。這個也是有必定弊端的, 要時刻當心你的定時任務掛掉。
定時任務選用的是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"}, ]
- 附贈一份初始設計只有三個隊列的思惟導圖: