--- title: "免費道具" description: "讓玩家無需輸入代碼或付款即可領取活動獎勵。您的伺服器查詢活動狀態、為玩家提交領取請求,再處理 redemption.completed Webhook,在遊戲中發放獎勵。" source: "https://docs.tokenz.one/zh-TW/v2/free-items" api_version: "v2" locale: "zh-TW" version_status: "current" docs_stage: "prod" --- # 免費道具 讓玩家無需輸入代碼或付款即可領取活動獎勵。您的伺服器查詢活動狀態、為玩家提交領取請求,再處理 `redemption.completed` Webhook,在遊戲中發放獎勵。 完整的請求與回應結構請參閱[免費道具 API 參考](https://docs.tokenz.one/zh-TW/v2/api/free-items)。 ## 身份驗證與玩家識別碼 從伺服器使用金鑰呼叫 API。查詢狀態需要 `FreeItemStatus` 許可權,領取需要 `FreeItemClaim` 許可權。金鑰只儲存在後端,`playerId` 應來自遊戲中已登入並通過身份驗證的玩家。 `playerId` 是您自己的玩家識別碼。Tokenz 不會查詢遊戲帳號,只根據這個字串識別玩家。 - 會去除首尾空白,不允許空白值。 - 最多 100 個字元。超長值會返回 `request.decoding-failed`,不會被截斷。 - 區分大小寫。`Player_123` 和 `player_123` 是不同玩家,各有獨立額度。 - 所有呼叫都應使用同一個穩定識別碼,不要轉為小寫或通過其他方式合併不同識別碼。 - 相同識別碼在不同裝置、會話或店面中共享領取額度。 ## 活動設定與狀態 在 Tokenz Dashboard 中配置活動時間、獎勵、活動總額度、每位玩家的額度以及重複週期,並管理暫停、恢復和結束操作。如果看不到免費道具設定,請聯絡 Tokenz 支援,確認您的帳號是否已開放此功能。 - `draft`:草稿,不能領取,所有欄位可編輯。 - `scheduled`:已釋出,等待開始;到達 `startAt` 即可領取。 - `active`:活動進行中,可以領取。 - `paused`:已暫停,不能領取,可恢復為 `active`。 - `ended`:已結束,不能領取,此狀態不可撤銷。 查詢與領取要求活動類型為免費道具,狀態為 `active`,或為 `scheduled` 且已到達 `startAt`,並且尚未到達 `endAt`。即使儲存的狀態尚未更新,已到開始時間的 `scheduled` 活動也可使用。時間按絕對時刻比較,玩家所在地不會改變活動結束時刻。 ## 額度與重複週期 每次領取都會檢查兩個獨立的額度: - **活動總額度**:所有玩家的領取次數總和。達到上限後返回 `campaign.limit-reached`。 - **每位玩家的額度**:同一 `playerId` 的領取次數。一次性活動達到上限返回 `campaign.player-limit-reached`;重複活動返回 `campaign.player-period-limit-reached`。其他玩家不受影響。 一次性活動的兩個額度均適用於活動整個生命週期,不會重置。重複活動的**兩個額度都會在每個週期開始時重置**。被拒絕的領取不會消耗任一額度。 | 頻率 | 週期開始時間 | | --- | --- | | 一次性 | 不重置 | | 每日 | 當地時間午夜 | | 每週 | 週一當地時間午夜 | | 每月 | 每月第一天當地時間午夜 | 使用活動的時區,而非玩家時區。API 不返回該時區,因此不要自行計算週期邊界。使用 `nextResetAt` 顯示倒計時,到達該時刻後重新查詢狀態。該時刻及之後的領取屬於新週期。重置不會延長 `endAt`。 例如,每日活動總額度為 100 次、每位玩家限 1 次,則每天最多 100 位玩家各領取一次。次日重置後,總額度恢復到 100 次,前一天已領取的玩家也可再次領取。 ## 領取流程 1. 在自己的系統中儲存 `campaignId`。 2. 伺服器呼叫 `GET /v2/free-items/{campaignId}?playerId=...`,顯示獎勵與剩餘額度。 3. 玩家點選領取後,伺服器呼叫 `POST /v2/free-items/claim`。 4. Tokenz 建立領取記錄,返回 `201`,並發送 `redemption.completed` Webhook。 5. 後端驗證簽名,使用 `redemptionId` 防止重複發放,再發放獎勵。 ## 查詢狀態 `campaignId` 是必需的路徑引數,`playerId` 是可選的查詢引數。狀態查詢不會預留額度,提交領取時可用額度可能已改變。額度耗盡時,狀態介面返回剩餘次數 `0`,而不是額度錯誤。 ```bash curl --request GET \ --url 'https://api.tokenz.one/v2/free-items/campaign_1p4LPTRKB5Z_t?playerId=player_98765' \ --header 'Authorization: Bearer secret_test_YOUR_KEY_HERE' ``` ### 成功回應(200) ```json { "campaign": { "id": "campaign_1p4LPTRKB5Z_t", "name": "Weekly Free Pack" }, "rewardPreview": { "name": "Marathon Energy Gift", "imageUrl": "https://images.example.com/rewards/energy.png", "quantity": 1 }, "campaignRemaining": 842, "remainingForPlayer": 1, "nextResetAt": "2026-09-14T00:00:00Z" } ``` - `campaignRemaining`:所有玩家可用的剩餘次數。重複活動為當前週期,一次性活動為整個生命週期。未設定活動總額度時省略。 - `remainingForPlayer`:此玩家的剩餘次數。未提供 `playerId` 或未設定玩家額度時省略。 - `nextResetAt`:兩個額度下次重置的絕對時刻。一次性活動省略。 ## 領取免費道具 請求主體中的 `campaignId` 和 `playerId` 均為必填項。活動必須可領取,且兩個額度都未耗盡。API 建立記錄;實際獎勵由您的後端處理 Webhook 時發放。 ```bash curl --request POST \ --url https://api.tokenz.one/v2/free-items/claim \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer secret_test_YOUR_KEY_HERE' \ --data '{ "campaignId": "campaign_1p4LPTRKB5Z_t", "playerId": "player_98765" }' ``` ### 成功回應(201) ```json { "redemption": { "redemptionId": "redemption_1p4LPTRKB5Z_t", "playerId": "player_98765", "campaign": { "id": "campaign_1p4LPTRKB5Z_t", "name": "Weekly Free Pack" }, "reward": { "skuRedemptionReward": { "type": "EXTERNAL_SKU", "sku": "ITEM_ENERGY_GIFT", "name": "Marathon Energy Gift", "quantity": 1 } }, "redeemedAt": "2026-09-10T08:10:00Z", "kind": "FREE_ITEM_CLAIM", "source": "FREE_ITEM_CLAIM", "nextPeriodStart": "2026-09-14T00:00:00Z" }, "remainingForPlayer": 0, "nextResetAt": "2026-09-14T00:00:00Z" } ``` `remainingForPlayer` 是本次領取後的剩餘次數,未設定玩家額度時省略。一次性活動省略 `nextResetAt`。`redemption` 物件欄位與下方 Webhook 相同。 ## Webhook 與獎勵發放 Tokenz 向訂閱此事件的端點發送 `redemption.completed`,與禮品碼(原名為兌換碼)兌換使用同一事件。 **發放獎勵前先驗證 `Tokenz-Signature`。** 針對原始請求主體驗證,再解析內容。跳過驗證會使偽造請求獲得獎勵。請參閱[保護你的 webhook 端點](https://docs.tokenz.one/zh-TW/v2/checkout/webhooks-get-started#3.-%E4%BF%9D%E8%AD%B7%E4%BD%A0%E7%9A%84-webhook-%E7%AB%AF%E9%BB%9E)。 ```json { "id": "f75ff6f0-bdf9-4b25-bb96-968f8733a7e3", "object": "redemption.completed", "createdAt": "2026-09-10T08:10:00Z", "test": true, "eventData": { "type": "redemption", "version": "v2", "data": { "redemption": { "redemptionId": "redemption_1p4LPTRKB5Z_t", "campaign": { "id": "campaign_1p4LPTRKB5Z_t", "name": "Weekly Free Pack" }, "playerId": "player_98765", "reward": { "skuRedemptionReward": { "type": "EXTERNAL_SKU", "sku": "ITEM_ENERGY_GIFT", "name": "Marathon Energy Gift", "quantity": 1 } }, "redeemedAt": "2026-09-10T08:10:00Z", "kind": "FREE_ITEM_CLAIM", "source": "FREE_ITEM_CLAIM", "nextPeriodStart": "2026-09-14T00:00:00Z" } } } } ``` - `kind`:免費道具為 `FREE_ITEM_CLAIM`,禮品碼為 `CODE_REDEMPTION`。新事件都會設定此欄位,但較早進入佇列的事件可能省略。 - `source`:已棄用,僅為相容現有整合保留。新整合使用 `kind`。 - `code`:僅禮品碼兌換包含此欄位,免費道具領取會省略。請檢查現有處理程序是否錯誤地要求其存在。 - `nextPeriodStart`:僅重複免費道具活動包含。它根據 `redeemedAt` 時的活動週期計算下一週期開始時刻,而非根據 Webhook 投遞時間計算。一次性領取和禮品碼兌換均省略。 通常只需根據 `redemptionId`、`playerId` 和 `reward` 共用一套發放邏輯。`kind` 缺失或未知時不要猜測類型,仍使用這三個欄位發放,並標記事件以供核對。頂層事件 `id` 用於追蹤投遞,`redemptionId` 用於防止重複發放。 ## 重試與回應丟失 領取請求沒有冪等性鍵。額度允許時,重複請求會建立另一條領取記錄。回應丟失後不要盲目重試。 Webhook 可確認領取成功,但只標識活動和玩家,未必能唯一對應某次超時請求。請結合自己的請求日誌核對。如果既無回應也無 Webhook,公開 API 目前無法重新查詢領取記錄,請在重試前聯絡 Tokenz 支援。Webhook 去重不能防止重複的領取請求。 端點未接受 Webhook 時,Tokenz 會逐漸增加間隔並加入隨機變化進行重試。重試安排在首次嘗試後的最長 72 小時視窗內;這不是保證在視窗內送達,實際執行可能延後。不要依賴固定次數或準確時間。投遞可能晚到數天,因此應將已發放的 `redemptionId` 持久儲存,而非放入短期快取,並保證同一 ID 的獎勵只發放一次。 ## 錯誤處理 - `400` / `request.decoding-failed`:無效 ID、空白或超長的 `playerId`,或格式錯誤的請求。 - `401`:缺少或無效的 API 金鑰。 - `403`:缺少所需許可權。 - `404` / `entity.not-found`:活動不存在、屬於其他商戶,或測試模式與金鑰不匹配。 - `422` / `campaign.kind-invalid`:不是免費道具活動。 - `422` / `campaign.status-invalid`:草稿、暫停、結束,或尚未開始的預定活動。 - `422` / `campaign.expired`:已到結束時刻;結束狀態也可能返回 `campaign.status-invalid`。 - `422` / `campaign.limit-reached`:總額度耗盡;重複活動為當前週期,一次性活動為整個生命週期。 - `422` / `campaign.player-limit-reached`:玩家額度耗盡。 - `422` / `campaign.player-period-limit-reached`:當前週期的玩家額度耗盡,可在 `nextResetAt` 後重試。 - `429`:超過速率限制。兩個介面均按 API 金鑰限制;提供玩家識別碼時,也按玩家限制。 - `500`:意外的伺服器錯誤。 ## 測試 使用以 `secret_test_` 開頭的金鑰,在 Dashboard 測試模式下建立活動。測試與正式資料隔離,金鑰只能操作模式匹配的活動。測試 ID 以 `_t` 結尾;測試 Webhook 僅傳送到測試端點,承載內容包含 `"test": true`。 測試成功領取與獎勵發放、兩個額度耗盡、週期重置、暫停與過期、重複投遞。也應驗證空白和超長 `playerId`、速率限制以及測試與正式模式隔離,並確認每個 `redemptionId` 只發放一次獎勵。