crazy-games-js-sdk

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CrazyGames JavaScript SDK

CrazyGames JavaScript SDK

Integration Checklist

集成检查清单

Follow this order when integrating the SDK into a game:
  1. Load SDK — add the CDN
    <script>
    tag before your game bootstraps
  2. Init — call
    SDK.init()
    and await resolution before touching any SDK module
  3. Lifecycle hooks — wire
    loadingStart/Stop
    and
    gameplayStart/Stop
    throughout the game loop
  4. Ads — add interstitial and/or rewarded ad calls at natural break points
  5. Optional: Auth & Storage — add sign-in and persistent key/value storage if needed
  6. Optional: Payments — Xsolla Pay Station integration (separate concern; skip if not applicable)

将 SDK 集成到游戏时,请按以下顺序操作:
  1. 加载 SDK — 在游戏 bootstrap 之前添加 CDN
    <script>
    标签
  2. 初始化 — 调用
    SDK.init()
    并等待 Promise 完成,然后再使用任何 SDK 模块
  3. 生命周期钩子 — 在整个游戏循环中接入
    loadingStart/Stop
    和
    gameplayStart/Stop
  4. 广告 — 在自然发生的游戏断点添加插页式和/或激励广告调用
  5. 可选:身份验证与存储 — 根据需要添加登录和持久化键/值存储
  6. 可选:支付 — 集成 Xsolla Pay Station(独立事项;如果不适用则跳过)

Installation

安装

Load the SDK from the official CDN before bootstrapping the game.
html
<script src="https://sdk.crazygames.com/crazygames-sdk-v3.js"></script>
If you load it dynamically, wait until
window.CrazyGames.SDK.init
is defined before calling it. If the script is blocked (e.g. by CSP), the
window.CrazyGames
global will be undefined — add
sdk.crazygames.com
to your
script-src
directive.
通过官方 CDN 加载 SDK,并确保在游戏 bootstrap 之前完成加载。
html
<script src="https://sdk.crazygames.com/crazygames-sdk-v3.js"></script>
如果动态加载脚本,请等待
window.CrazyGames.SDK.init
完成定义后再调用。如果脚本被阻止(例如被 CSP 阻止),
window.CrazyGames
全局变量将为 undefined——请将
sdk.crazygames.com
添加到
script-src
指令中。

Initialization

初始化

Call
SDK.init()
once. It resolves when the SDK is ready. Only after resolution are
SDK.user
,
SDK.data
,
SDK.ad
,
SDK.banner
,
SDK.game
, and
SDK.analytics
safe to use.
javascript
async function initCrazyGames() {
    if (!window.CrazyGames?.SDK) {
        throw new Error('CrazyGames SDK script not loaded');
    }
    const sdk = window.CrazyGames.SDK;
    try {
        await sdk.init();
    } catch (err) {
        throw new Error(`CrazyGames SDK init failed: ${err.message}`);
    }

    const isUserAccountAvailable = sdk.user.isUserAccountAvailable;

    if (isUserAccountAvailable) {
        const language = sdk.user.systemInfo.countryCode.toLowerCase();
        const deviceType = sdk.user.systemInfo.device.type.toLowerCase(); // 'desktop' | 'mobile' | 'tablet'
        console.log('lang/device', language, deviceType);
    }

    return sdk;
}
调用一次
SDK.init()
。SDK 就绪后,该调用会 resolve。只有在 resolve 之后,才能安全使用
SDK.user
、
SDK.data
、
SDK.ad
、
SDK.banner
、
SDK.game
和
SDK.analytics
。
javascript
async function initCrazyGames() {
    if (!window.CrazyGames?.SDK) {
        throw new Error('CrazyGames SDK script not loaded');
    }
    const sdk = window.CrazyGames.SDK;
    try {
        await sdk.init();
    } catch (err) {
        throw new Error(`CrazyGames SDK init failed: ${err.message}`);
    }

    const isUserAccountAvailable = sdk.user.isUserAccountAvailable;

    if (isUserAccountAvailable) {
        const language = sdk.user.systemInfo.countryCode.toLowerCase();
        const deviceType = sdk.user.systemInfo.device.type.toLowerCase(); // 'desktop' | 'mobile' | 'tablet'
        console.log('lang/device', language, deviceType);
    }

    return sdk;
}

Verification

验证

After calling
initCrazyGames()
, confirm the SDK is working before proceeding:
  • Console check — no
    CrazyGames SDK init failed
    error should appear;
    sdk.user.isUserAccountAvailable
    should return a boolean without throwing.
  • Lifecycle hooks — open the CrazyGames sandbox URL for your game and verify that
    loadingStart
    /
    loadingStop
    /
    gameplayStart
    /
    gameplayStop
    calls appear in the CrazyGames developer dashboard event log.
  • Ad callbacks — use the CrazyGames sandbox environment (append
    ?crazygames_sandbox=1
    to the URL) to trigger test ads and confirm
    adStarted
    and
    adFinished
    fire correctly.
  • Adblock — call
    sdk.ad.hasAdblock()
    while running with an ad blocker enabled and confirm it resolves
    true
    .

调用
initCrazyGames()
后,继续操作前请确认 SDK 正常工作:
  • 控制台检查 — 不应出现
    CrazyGames SDK init failed
    错误;
    sdk.user.isUserAccountAvailable
    应返回布尔值且不抛出异常。
  • 生命周期钩子 — 打开游戏的 CrazyGames sandbox URL,并验证
    loadingStart
    /
    loadingStop
    /
    gameplayStart
    /
    gameplayStop
    调用出现在 CrazyGames 开发者控制台的事件日志中。
  • 广告回调 — 使用 CrazyGames sandbox 环境(在 URL 后附加
    ?crazygames_sandbox=1
    )触发测试广告,并确认
    adStarted
    和
    adFinished
    正确触发。
  • 广告拦截 — 启用广告拦截器后运行游戏,调用
    sdk.ad.hasAdblock()
    ,并确认其 resolve 为
    true
    。

Player / Authorization

玩家 / 授权

Use
showAuthPrompt()
to ask the player to sign in, then
getUser()
to read profile data, and optionally
getUserToken()
for a signed JWT to verify on your backend.
javascript
async function authorizePlayer(sdk, { useUserToken = false } = {}) {
    if (!sdk.user.isUserAccountAvailable) {
        // Guest session — no auth possible on this domain
        return null;
    }

    await sdk.user.showAuthPrompt();

    const user = await sdk.user.getUser();
    if (!user) {
        return null;
    }

    const profile = {
        name: user.username || null,
        photo: user.profilePictureUrl || null,
        extra: user
    };

    if (useUserToken) {
        profile.extra.jwt = await sdk.user.getUserToken();
    }

    return profile;
}
使用
showAuthPrompt()
提示玩家登录,然后使用
getUser()
读取个人资料,并可选使用
getUserToken()
获取已签名的 JWT,以便在后端进行验证。
javascript
async function authorizePlayer(sdk, { useUserToken = false } = {}) {
    if (!sdk.user.isUserAccountAvailable) {
        // Guest session — no auth possible on this domain
        return null;
    }

    await sdk.user.showAuthPrompt();

    const user = await sdk.user.getUser();
    if (!user) {
        return null;
    }

    const profile = {
        name: user.username || null,
        photo: user.profilePictureUrl || null,
        extra: user
    };

    if (useUserToken) {
        profile.extra.jwt = await sdk.user.getUserToken();
    }

    return profile;
}

Storage

存储

CrazyGames provides synchronous key/value storage via
SDK.data
. Values are strings; serialize JSON yourself.
javascript
function storageSet(sdk, key, value) {
    const data = typeof value === 'string' ? value : JSON.stringify(value);
    sdk.data.setItem(key, data);
}

function storageGet(sdk, key, tryParseJson = true) {
    const raw = sdk.data.getItem(key);
    if (!tryParseJson || raw == null) {
        return raw;
    }
    try {
        return JSON.parse(raw);
    } catch (_) {
        return raw;
    }
}

function storageRemove(sdk, key) {
    sdk.data.removeItem(key);
}

// Batch helpers — just iterate, the SDK has no native multi-key API
function storageSetMany(sdk, keys, values) {
    for (let i = 0; i < keys.length; i++) {
        storageSet(sdk, keys[i], values[i]);
    }
}
CrazyGames 通过
SDK.data
提供同步键/值存储。值为字符串;请自行序列化 JSON。
javascript
function storageSet(sdk, key, value) {
    const data = typeof value === 'string' ? value : JSON.stringify(value);
    sdk.data.setItem(key, data);
}

function storageGet(sdk, key, tryParseJson = true) {
    const raw = sdk.data.getItem(key);
    if (!tryParseJson || raw == null) {
        return raw;
    }
    try {
        return JSON.parse(raw);
    } catch (_) {
        return raw;
    }
}

function storageRemove(sdk, key) {
    sdk.data.removeItem(key);
}

// Batch helpers — just iterate, the SDK has no native multi-key API
function storageSetMany(sdk, keys, values) {
    for (let i = 0; i < keys.length; i++) {
        storageSet(sdk, keys[i], values[i]);
    }
}

Advertisement

广告

Interstitial ad (
midgame
)

插页式广告 (
midgame
)

Call
SDK.ad.requestAd('midgame', callbacks)
. The callbacks fire synchronously as the ad lifecycle progresses.
javascript
function showInterstitial(sdk) {
    sdk.ad.requestAd('midgame', {
        adStarted: () => {
            // Mute audio and pause the game here
        },
        adFinished: () => {
            // Resume audio and gameplay here
        },
        adError: (error) => {
            console.warn('interstitial failed', error);
        }
    });
}
调用
SDK.ad.requestAd('midgame', callbacks)
。回调会在广告生命周期推进时同步触发。
javascript
function showInterstitial(sdk) {
    sdk.ad.requestAd('midgame', {
        adStarted: () => {
            // Mute audio and pause the game here
        },
        adFinished: () => {
            // Resume audio and gameplay here
        },
        adError: (error) => {
            console.warn('interstitial failed', error);
        }
    });
}

Rewarded ad

激励广告

Same API, pass
'rewarded'
as the type.
adFinished
means the user watched to completion and should receive the reward.
javascript
function showRewarded(sdk, onReward) {
    sdk.ad.requestAd('rewarded', {
        adStarted: () => {
            // Pause the game
        },
        adFinished: () => {
            onReward();
        },
        adError: (error) => {
            console.warn('rewarded failed', error);
        }
    });
}
使用相同的 API,将
'rewarded'
作为类型传入。
adFinished
表示用户已完整观看广告,此时应发放奖励。
javascript
function showRewarded(sdk, onReward) {
    sdk.ad.requestAd('rewarded', {
        adStarted: () => {
            // Pause the game
        },
        adFinished: () => {
            onReward();
        },
        adError: (error) => {
            console.warn('rewarded failed', error);
        }
    });
}

Adblock detection

广告拦截检测

javascript
async function isAdblockActive(sdk) {
    return sdk.ad.hasAdblock();
}
javascript
async function isAdblockActive(sdk) {
    return sdk.ad.hasAdblock();
}

Responsive banners

响应式横幅广告

Banners are bound to existing DOM containers by id. CrazyGames renders into them and picks an appropriate size.
javascript
async function showBanner(sdk, containerId) {
    // The container must already be in the DOM and visible
    try {
        await sdk.banner.requestResponsiveBanner([containerId]);
    } catch (e) {
        console.warn('banner failed', e);
    }
}

async function showAdvancedBanners(sdk, containerIds) {
    // Request several banners in parallel
    await Promise.all(containerIds.map((id) => sdk.banner.requestResponsiveBanner(id)));
}

function hideBanners(sdk) {
    sdk.banner.clearAllBanners();
}
横幅广告通过 id 绑定到已有的 DOM 容器。CrazyGames 会将广告渲染到这些容器中,并选择合适的尺寸。
javascript
async function showBanner(sdk, containerId) {
    // The container must already be in the DOM and visible
    try {
        await sdk.banner.requestResponsiveBanner([containerId]);
    } catch (e) {
        console.warn('banner failed', e);
    }
}

async function showAdvancedBanners(sdk, containerIds) {
    // Request several banners in parallel
    await Promise.all(containerIds.map((id) => sdk.banner.requestResponsiveBanner(id)));
}

function hideBanners(sdk) {
    sdk.banner.clearAllBanners();
}

Lifecycle / gameplay events

生命周期 / 游戏事件

CrazyGames REQUIRES the host game to call gameplay and loading hooks at the right moments — they drive ad pacing, in-game UI dimming, and analytics.
Map generic game lifecycle events to SDK calls as follows:
Game eventSDK call
level_started
,
level_resumed
,
gameplay_started
sdk.game.gameplayStart()
level_paused
,
level_completed
,
level_failed
,
gameplay_stopped
sdk.game.gameplayStop()
in_game_loading_started
sdk.game.loadingStart()
in_game_loading_stopped
sdk.game.loadingStop()
player_got_achievement
sdk.game.happytime()
There are no separate
audio
/
pause
/
visibility
callbacks on the CrazyGames SDK — drive your audio and pause state from the ad callbacks (
adStarted
/
adFinished
) and the standard
document.visibilitychange
event.

CrazyGames 要求宿主游戏在正确的时间调用 gameplay 和 loading 钩子——这些钩子用于控制广告节奏、游戏内 UI 变暗和 analytics。
将通用游戏生命周期事件映射到 SDK 调用,如下所示:
游戏事件SDK 调用
level_started
,
level_resumed
,
gameplay_started
sdk.game.gameplayStart()
level_paused
,
level_completed
,
level_failed
,
gameplay_stopped
sdk.game.gameplayStop()
in_game_loading_started
sdk.game.loadingStart()
in_game_loading_stopped
sdk.game.loadingStop()
player_got_achievement
sdk.game.happytime()
CrazyGames SDK 没有独立的
audio
/
pause
/
visibility
回调——应通过广告回调(
adStarted
/
adFinished
)和标准的
document.visibilitychange
事件来驱动音频与暂停状态。

Payments (optional, via Xsolla Pay Station)

支付(可选,通过 Xsolla Pay Station)

Skip this section entirely if you do not have an Xsolla project ID. CrazyGames itself does not sell IAPs; this integration uses CrazyGames-issued Xsolla user tokens with the Xsolla Pay Station widget and Xsolla Store API.
Key constants:
javascript
const XSOLLA_PAYSTATION_EMBED_URL =
    'https://cdn.xsolla.net/payments-bucket-prod/embed/1.5.0/widget.min.js';
const XSOLLA_SDK_URL = 'https://store.xsolla.com/api/v2/project';
Obtain the Xsolla user token via
sdk.user.getXsollaUserToken()
. Use it as a
Bearer
token for all Xsolla Store API requests (catalog, purchase, inventory, consume).
Catalog —
GET /{projectId}/items?limit=50
Purchase flow:
  1. POST /{projectId}/payment/item/{sku}
    → returns
    { token, order_id }
  2. Load
    XSOLLA_PAYSTATION_EMBED_URL
    dynamically if not already present
  3. XPayStationWidget.init({ access_token: token, sandbox: isSandbox })
    then
    .open()
  4. Listen for
    XPayStationWidget.eventTypes.STATUS
    ; when
    paymentInfo.status
    matches
    /done|charged|success/i
    , fetch
    GET /{projectId}/order/{orderId}
    and call
    sdk.analytics.trackOrder('xsolla', order)
    to record the purchase
  5. Listen for the
    'close'
    event to handle cancellation
Inventory —
GET /{projectId}/user/inventory/items
Consume —
POST /{projectId}/user/inventory/item/consume
with
{ sku, quantity }
All fetch calls must include
Authorization: Bearer <xsollaToken>
. Throw on non-OK responses. Use
isSandbox: true
during development.
如果尚未拥有 Xsolla project ID,请完全跳过此节。 CrazyGames 本身不销售 IAP;此集成使用 CrazyGames 签发的 Xsolla user token,并结合 Xsolla Pay Station widget 和 Xsolla Store API。
关键常量:
javascript
const XSOLLA_PAYSTATION_EMBED_URL =
    'https://cdn.xsolla.net/payments-bucket-prod/embed/1.5.0/widget.min.js';
const XSOLLA_SDK_URL = 'https://store.xsolla.com/api/v2/project';
通过
sdk.user.getXsollaUserToken()
获取 Xsolla user token。将其作为所有 Xsolla Store API 请求(catalog、purchase、inventory、consume)的
Bearer
token 使用。
目录 —
GET /{projectId}/items?limit=50
购买流程
  1. POST /{projectId}/payment/item/{sku}
    → 返回
    { token, order_id }
  2. 如果尚未加载,则动态加载
    XSOLLA_PAYSTATION_EMBED_URL
  3. 执行
    XPayStationWidget.init({ access_token: token, sandbox: isSandbox })
    ,然后执行
    .open()
  4. 监听
    XPayStationWidget.eventTypes.STATUS
    ;当
    paymentInfo.status
    匹配
    /done|charged|success/i
    时,获取
    GET /{projectId}/order/{orderId}
    ,并调用
    sdk.analytics.trackOrder('xsolla', order)
    记录购买
  5. 监听
    'close'
    事件以处理取消操作
库存 —
GET /{projectId}/user/inventory/items
消耗 —
POST /{projectId}/user/inventory/item/consume
,请求内容为
{ sku, quantity }
所有 fetch 调用都必须包含
Authorization: Bearer <xsollaToken>
。对于非 OK 响应,请抛出异常。开发期间使用
isSandbox: true
。