跳至内容

浏览文档

用 AI 构建
营销活动免费商品

免费道具

让玩家无需输入兑换码或付款即可领取活动奖励。您的服务器查询活动状态、为玩家提交领取请求,再处理 redemption.completed Webhook,在游戏中发放奖励。

完整的请求与响应结构请参阅免费道具 API 参考

身份验证与玩家标识符

从服务器使用密钥调用 API。查询状态需要 FreeItemStatus 权限,领取需要 FreeItemClaim 权限。密钥只保存在后端,playerId 应来自游戏中已登录并通过身份验证的玩家。

playerId 是您自己的玩家标识符。Tokenz 不会查询游戏账号,只根据这个字符串识别玩家。

  • 会去除首尾空白,不允许空白值。
  • 最多 100 个字符。超长值会返回 request.decoding-failed,不会被截断。
  • 区分大小写。Player_123player_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 /v1/free-items/{campaignId}?playerId=...,显示奖励与剩余额度。
  3. 玩家点击领取后,服务器调用 POST /v1/free-items/claim
  4. Tokenz 创建领取记录,返回 201,并发送 redemption.completed Webhook。
  5. 后端验证签名,使用 redemptionId 防止重复发放,再发放奖励。

查询状态

campaignId 是必需的路径参数,playerId 是可选的查询参数。状态查询不会预留额度,提交领取时可用额度可能已改变。额度耗尽时,状态接口返回剩余次数 0,而不是额度错误。

bash
curl --request GET \
  --url 'https://api.tokenz.one/v1/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:两个额度下次重置的绝对时刻。一次性活动省略。

领取免费道具

请求体中的 campaignIdplayerId 均为必填项。活动必须可领取,且两个额度都未耗尽。API 创建记录;实际奖励由您的后端处理 Webhook 时发放。

bash
curl --request POST \
  --url https://api.tokenz.one/v1/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 是本次领取后的剩余次数,未设置玩家额度时省略。一次性活动省略 nextResetAtredemption 对象字段与下方 Webhook 相同。

Webhook 与奖励发放

Tokenz 向订阅此事件的端点发送 redemption.completed,与兑换码兑换使用同一事件。

发放奖励前先验证 Tokenz-Signature 针对原始请求体验证,再解析内容。跳过验证会使伪造请求获得奖励。请参阅保护你的 webhook 端点

json
{
  "id": "f75ff6f0-bdf9-4b25-bb96-968f8733a7e3",
  "object": "redemption.completed",
  "createdAt": "2026-09-10T08:10:00Z",
  "test": true,
  "eventData": {
    "type": "redemption",
    "version": "v1",
    "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 投递时间计算。一次性领取和兑换码兑换均省略。

通常只需根据 redemptionIdplayerIdreward 共用一套发放逻辑。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 只发放一次奖励。