跳转到内容

Unity LevelPlay

  1. 自己生成一个私钥:至少 8 个字符、不含空格,最好是 32 位以上的随机字符串。把它和 LevelPlay App Key 一起存进 Kasskeep:
终端窗口
npx @kasskeep/cli ads credentials --id app_xxx --levelplay-private-key-file - --levelplay-app-key <App Key> < ./levelplay-private-key.txt

LevelPlay 每个平台有一个 App Key。如果你的 iOS 和 Android App 都上报到同一个 Kasskeep App,就把两个 key 用逗号隔开一起填(--levelplay-app-key <iOS key>,<Android key>,最多 10 个)。没有私钥时,回调返回 422 levelplay_not_configured。

  1. 在 LevelPlay 的 Apps 里打开你的 App 的操作菜单,选择 Set S2S callback:
    • Callback URL,方括号里的宏请原样保留:
https://api.kasskeep.com/v1/ads/levelplay/<app_id>/s2s?appUserId=[USER_ID]&rewards=[REWARDS]&eventId=[EVENT_ID]&itemName=[ITEM_NAME]&placementName=[PLACEMENT_NAME]&adNetwork=[AD_NETWORK]&appKey=[APP_KEY]&auctionId=[AUCTION_ID]

LevelPlay 会自己追加 timestamp 和 signature,以及带 custom_ 前缀的自定义参数。

  • Private key:与第 1 步相同的字符串。
  • 保存前先运行 Callback test;只有应答正确,LevelPlay 才会启用回调。测试用的 eventId 会像真实奖励一样被记录,所以请在开发用的 App 上测,或者在映射广告位之前测。
  1. 映射广告位,每个平台一个 id:
终端窗口
npx @kasskeep/cli ads unit-set --id app_xxx --name revive --levelplay ios:<iOS ad unit id> --levelplay android:<Android ad unit id>
  1. 在客户端用 App Key 和用户 id 初始化 LevelPlay,每次展示前设置动态用户 id 和自定义参数 ad_unit、ad_unit_id、data。下面的 provider 就是这么做的。
  • signature 必须等于 md5(timestamp + eventId + appUserId + rewards + privateKey) 的十六进制值,其中 appUserId 要先做 URL 解码。否则返回 400 invalid_signature。
  • timestamp(YYYYMMDDHHMM,按 UTC 解读;也接受 10 位秒级和 13 位毫秒级时间戳)必须在服务器时钟 ±1 小时以内,否则返回 400 timestamp_out_of_window 或 400 invalid_timestamp。可用 --levelplay-timestamp-window <seconds>(60 到 604800)调整窗口。
  • 设置了 app_key 且回调带有 appKey 时,它必须是已配置的 key 之一,否则返回 400 app_key_mismatch。
  • 可选的来源 IP 允许列表:--levelplay-ip-allowlist levelplay 放行 LevelPlay 公布的回调地址;也可以列出 IP 和 IPv4 CIDR。其他来源收到 403 ip_not_allowed。传空值即关闭。
  • 每个通过校验的回调(包括重复的和未映射的)都应答 200,JSON 体里带 "ack": "<eventId>:OK",这正是 LevelPlay 要找的内容。

没有收到正确应答的回调,LevelPlay 会重试(24 小时内 20 次,之后每天一次,持续 7 天)。

LevelPlay 参数 事件字段
dynamicUserId,没有则用 appUserId app_user_id(32 位十六进制的动态 id 会还原为 UUID)
rewards reward_amount
itemName reward_type
eventId network_transaction_id
custom_data custom_data
timestamp occurred_at

客户端发送 ad_unit=<your name>、ad_unit_id=<LevelPlay ad unit id> 和 data=<customData>;它们以 custom_ad_unit、custom_ad_unit_id 和 custom_data 回传。Kasskeep 先按 custom_ad_unit_id 查找映射,再按 placementName,最后按 custom_ad_unit 里的名字。如果映射到的名字与 custom_ad_unit 不同,回调按未映射处理。

import { LevelPlay, LevelPlayInitRequest, LevelPlayRewardedAd } from "unity-levelplay-mediation";
import { Kasskeep, type RewardedProvider } from "@kasskeep/react-native";
const ads = new Map<string, LevelPlayRewardedAd>();
const levelplayRewarded: RewardedProvider = {
network: "levelplay",
init: ({ appKey, appUserId }) =>
new Promise((resolve, reject) => {
const request = LevelPlayInitRequest.builder(appKey!).withUserId(appUserId).build();
LevelPlay.init(request, { onInitSuccess: () => resolve(), onInitFailed: (e) => reject(new Error(String(e?.errorMessage ?? e))) });
}),
setUserId: (appUserId) => LevelPlay.setDynamicUserId(appUserId.replace(/-/g, "")),
showRewarded: ({ networkAdUnitId, serverParams }) =>
new Promise((resolve) => {
const ad = ads.get(networkAdUnitId);
if (!ad) return resolve({ status: "failed" });
let rewarded = false;
ad.setListener({
onAdRewarded: () => (rewarded = true),
onAdClosed: () => setTimeout(() => resolve({ status: rewarded ? "completed" : "skipped" }), 500),
onAdDisplayFailed: () => resolve({ status: "failed" }),
onAdLoaded: () => {},
onAdLoadFailed: () => {},
onAdDisplayed: () => {},
});
LevelPlay.setMetaData("LevelPlay_Rewarded_Server_Params", Object.entries(serverParams).map(([k, v]) => `${k}=${v}`));
ad.showAd();
}),
};
Kasskeep.configure({
apiKey, baseUrl, appUserId,
ads: { rewardedProvider: levelplayRewarded, appKey: "<App Key>", adUnits: { revive: "<ad unit id>" }, testAds: __DEV__ },
});

初始化完成后,为每个 LevelPlayRewardedAd 创建实例并 loadAd(),存进 ads;每次展示后重新加载。onAdRewarded 可能在 onAdClosed 之后才到,所以加了一点延迟。方法名请以你锁定的插件版本为准。