--- title: "禮品碼" description: "驗證並兌換 Gift Codes、發放獎勵,以及處理兌換 webhook。" source: "https://docs.tokenz.one/zh-TW/v1/campaigns/gift-codes" api_version: "v1" locale: "zh-TW" version_status: "legacy" docs_stage: "prod" --- # 禮品碼 原名為兌換碼。API 端點與事件名稱維持不變。 了解如何透過 Tokenz 兌換碼 API 驗證和兌換活動碼。 兌換碼 API 幫助您開展玩家輸入代碼並獲得獎勵的活動。您的伺服器驗證代碼、確認資格、執行兌換,然後處理 `redemption.completed` Webhook 進行獎勵發放。 ## 概述 兌換流程包括: - **驗證**:檢查代碼是否可兌換並返回獎勵預覽 - **兌換**:為特定玩家消耗代碼並建立兌換記錄 - **Webhook 發放**:接收 `redemption.completed` 並在您的遊戲後端發放獎勵 ## 活動設定與管理 活動和兌換碼在 Tokenz 商家後台中建立和管理。商家使用後台進行以下操作: - 建立具有活動期間和獎勵設定的活動 - 產生或管理兌換碼 - 暫停、恢復或結束活動 ## 活動狀態 活動可以處於以下狀態之一: - `draft`:進行中。兌換不允許。所有欄位可編輯 - `scheduled`:已發布並排隊。在設定的 `start_at` 時間自動轉為 `active` - `active`:活動已上線,兌換可以成功 - `paused`:已手動暫停。兌換將被拒絕。可恢復至 `active` - `ended`:活動已結束,兌換將被拒絕。此狀態不可逆 `validate` 和 `redeem` 在活動處於 `active` 狀態時會成功;活動處於 `scheduled` 狀態且已到達 `startAt` 時同樣會成功。自 `endAt` 起會被拒絕。`scheduled` 活動會在 `startAt` 自動轉為 `active`,因此這一點只在狀態切換前後的短暫時間內有影響。 ## 兌換碼狀態 兌換碼狀態根據活動狀態和兌換碼自身狀態計算得出: - `pending`:兌換碼有效,但活動當前未啟用(例如 draft、scheduled 或 paused)。不允許兌換,但狀態為 `scheduled` 且已到達 `startAt` 的活動可以兌換。 - `active`:兌換碼可用。需要活動處於 active 狀態。可兌換的狀態不止這一種:`scheduled` 活動到達 `startAt` 後,`pending` 狀態的兌換碼同樣可以兌換。 - `deactivated`:兌換碼已停用。商家手動停用或活動結束時顯示此狀態。 顯示的兌換碼狀態取決於活動狀態: | 活動狀態 | 兌換碼狀態 | | --- | --- | | `active` | `active` | | `draft`、`scheduled`、`paused` | `pending` | | `ended` | `deactivated` | 活動超過 `endAt` 後,即使兌換碼狀態仍顯示為 `active`,也無法兌換。此時 API 將返回 `redemptionCode.campaign-invalid` 錯誤;尚未開始的活動也會返回同一錯誤。 如果商家手動停用了兌換碼,無論活動狀態如何,該兌換碼始終顯示為 `deactivated`。 此外,當 `currentRedemptions >= maxRedemptions` 時,Tokenz Dashboard會將兌換碼顯示為**已兌換**,但 API 狀態仍為 `active`。這僅是後台的顯示狀態,不會由 API 返回。 ## 兌換碼運作流程 1. 玩家在您的促銷活動介面中輸入代碼 2. 您的伺服器呼叫 `POST /v1/redemption-codes/validate` 3. 您的伺服器向玩家顯示獎勵預覽 4. 玩家確認兌換 5. 您的伺服器呼叫 `POST /v1/redemption-codes/redeem` 6. Tokenz 返回成功的兌換回應 7. Tokenz 發送 `redemption.completed` Webhook 8. 您的後端發放獎勵,並應使用 `redemptionId` 防止重複發放 ```mermaid sequenceDiagram; autonumber; participant P as 玩家; participant M as 商家伺服器; participant T as Tokenz API; participant W as 商家 Webhook 處理器; P->>M: 輸入兌換碼; M->>T: POST /v1/redemption-codes/validate; T-->>M: 活動 + rewardPreview; P->>M: 確認兌換; M->>T: POST /v1/redemption-codes/redeem; T-->>M: redemptionId + reward; T->>W: webhook: redemption.completed; W->>W: 發放獎勵,使用 redemptionId 防止重複發放; ``` ## 驗證代碼 驗證檢查活動和代碼的資格,並返回活動預覽和獎勵預覽。驗證成功需要活動處於 active 狀態,或處於 `scheduled` 狀態且已到達 `startAt`,並且在 `endAt` 之前。 ```bash curl --request POST \ --url https://api.tokenz.one/v1/redemption-codes/validate \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer secret_test_YOUR_KEY_HERE' \ --data '{ "redemptionCode": "SPRING2026FREE", "playerId": "player_98765" }' ``` ### 請求欄位 - `redemptionCode`(必填):玩家輸入的代碼 - `playerId`(選填):商家玩家識別碼。如果提供,還將檢查每位玩家的限制。如果省略,則僅驗證兌換碼和活動的全域限制 ### 成功回應(`200`) ```json { "campaign": { "id": "campaign_1AbCdEfGh12", "name": "Spring Campaign 2026" }, "rewardPreview": { "name": "SSR Reimu Card", "imageUrl": "https://images.example.com/rewards/reimu.png", "quantity": 1 } } ``` ## 兌換代碼 兌換為玩家消耗代碼並建立兌換記錄。兌換成功需要活動處於 active 狀態,或處於 `scheduled` 狀態且已到達 `startAt`,並且在 `endAt` 之前。 ```bash curl --request POST \ --url https://api.tokenz.one/v1/redemption-codes/redeem \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer secret_test_YOUR_KEY_HERE' \ --data '{ "redemptionCode": "SPRING2026FREE", "playerId": "player_98765" }' ``` ### 請求欄位 - `redemptionCode`(必填):玩家輸入的代碼 - `playerId`(必填):商家玩家識別碼 ### 成功回應(`201`) ```json { "redemptionId": "redemption_1XyZaBcDe34", "code": "SPRING2026FREE", "playerId": "player_98765", "campaign": { "id": "campaign_1AbCdEfGh12", "name": "Spring Campaign 2026" }, "reward": { "skuRedemptionReward": { "type": "EXTERNAL_SKU", "sku": "ITEM_SSR_REIMU", "name": "SSR Reimu Card", "quantity": 1 } }, "redeemedAt": "2026-03-25T08:10:00Z", "kind": "CODE_REDEMPTION", "source": "CODE_REDEMPTION" } ``` ## 兌換 Webhook 兌換成功後,Tokenz 將向訂閱此事件的 Webhook 端點發送 `redemption.completed`。 **發放獎勵前必須先驗證簽章。** 每次投遞都包含 `Tokenz-Signature` 標頭。如果略過驗證,任何找到或猜到該端點 URL 的人都可能取得獎勵。請針對原始請求主體進行驗證,然後再解析。詳細步驟請參閱[保護你的 webhook 端點](https://docs.tokenz.one/zh-TW/v1/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-03-24T23:10:00Z", "test": false, "eventData": { "type": "redemption", "version": "v1", "data": { "redemption": { "redemptionId": "redemption_1XyZaBcDe34", "campaign": { "id": "campaign_1AbCdEfGh12", "name": "Spring Campaign 2026" }, "code": "SPRING2026FREE", "playerId": "player_98765", "reward": { "skuRedemptionReward": { "type": "EXTERNAL_SKU", "sku": "ITEM_SSR_REIMU", "name": "SSR Reimu Card", "quantity": 1 } }, "redeemedAt": "2026-03-25T08:10:00Z", "kind": "CODE_REDEMPTION", "source": "CODE_REDEMPTION" } } } } ``` 使用頂層事件 `id` 進行 Webhook 投遞追蹤,使用 `redemptionId` 防止後端中的重複發放。 [免費道具領取](https://docs.tokenz.one/zh-TW/v1/free-items)也會發送同一事件。兌換碼的 `kind` 為 `CODE_REDEMPTION`,免費道具為 `FREE_ITEM_CLAIM`。`source` 已棄用,僅為相容保留;新整合使用 `kind`。較早進入佇列的事件可能省略 `kind`。免費道具領取不包含 `code`。 ## 錯誤處理 兌換端點返回帶有結構化錯誤物件的標準 HTTP 狀態碼。 ### 常見錯誤 - `401 Unauthorized`:無效或缺少 API 金鑰 - `403 Forbidden`:API 金鑰沒有所需權限範圍 - `404 Not Found`(`entity.not-found`):兌換碼未找到 - `422 Unprocessable Entity`(`campaign.limit-reached`):活動已達到其兌換總數限制 - `422 Unprocessable Entity`(`campaign.player-limit-reached`):玩家已達到此活動的玩家兌換限制 - `422 Unprocessable Entity`(`redemptionCode.campaign-invalid`):活動未啟用、超出有效時間窗口,或代碼已停用。錯誤的 `message` 欄位可區分這些原因 - `422 Unprocessable Entity`(`redemptionCode.limit-reached`):兌換碼已達到自身的使用限制 - `422 Unprocessable Entity`(`redemptionCode.player-limit-reached`):玩家已達到此兌換碼的每碼兌換限制 - `429 Too Many Requests`:超出速率限制 - `500 Server Error`:意外的伺服器錯誤 ## 測試 使用測試模式開發和測試您的兌換整合: 1. 使用測試 API 金鑰(以 `test_` 為前綴)來操作測試活動和兌換碼 2. 在 Tokenz 商家後台中使用測試模式建立測試活動 3. 測試兌換與正式資料完全隔離 — 測試 API 金鑰只能兌換測試碼,正式 API 金鑰只能兌換正式碼 4. 測試實體 ID 包含 `_t` 後綴(例如 `redemption_1XyZaBcDe34_t`) 5. 測試 Webhook 僅發送到您的測試 Webhook 端點,酬載中包含 `"test": true`