JoyGames TikTok SDK

Integration Guide Hướng dẫn tích hợp集成指南

Connect a TikTok Mini Game or H5 Webview to JoyGames Portal: install the SDK → login/sync the player → call pay().

Kết nối Mini Game TikTok hoặc H5 Webview với JoyGames Portal: cài SDK → đăng nhập/đồng bộ user → gọi pay().

将 TikTok 小游戏或 H5 Webview 接入 JoyGames Portal:安装 SDK → 登录/同步玩家 → 调用 pay()

Environment Môi trường环境: Sandbox API: https://devsdkapi.joyplay.vn Payment: https://devnap.joyplay.vn Portal dev URLs only — TikTok login + Beans stay real (same as Live). Chỉ đổi URL Portal dev — login + Beans TikTok vẫn là luồng thật (giống Live).仅切换 Portal 开发域名 — TikTok 登录与 Beans 仍为正式流程(与 Live 相同)。 Portal production URLs — same real TikTok login + Beans flow. URL Portal production — cùng luồng login + Beans TikTok thật.Portal 生产域名 — 同样是正式的 TikTok 登录与 Beans 流程。
Who does what in this guide Ai làm phần nào trong guide này本指南中谁做什么
Dev Game Dev Game — install SDK, init, call login()/pay(), implement game IPN addGold. Sections marked in red. Dev Game — cài SDK, init, gọi login()/pay(), implement IPN addGold phía game. Mục tô đỏ.Dev Game — 安装 SDK、初始化、调用 login()/pay()、实现游戏侧 IPN addGold。红色标记的章节。
SDK JOY SDK JOY does thistt.login, /tiktok/sync, create order, TTMinis.game.pay, poll. Dev Game does not call these APIs by hand. SDK JOY sẽ làmtt.login, /tiktok/sync, tạo đơn, TTMinis.game.pay, poll. Dev Game không cần gọi các API này thủ công.由 SDK JOY 完成tt.login/tiktok/sync、创建订单、TTMinis.game.pay、轮询。Dev Game 无需手动调用这些 API。
System Portal / TikTok explanation — how our backend + TikTok work (webhook, jscode2session, hosts). Read-only context. Giải thích hệ thống Portal / TikTok — cách backend của mình + TikTok hoạt động (webhook, jscode2session, host). Chỉ đọc để hiểu.Portal / TikTok 系统说明 — 我方后端与 TikTok 如何工作(webhook、jscode2session、域名)。只读了解。
Step 01

Install the SDK Cài đặt SDK安装 SDK

Dev Game · setup Dev Game · setupDev Game · 配置

Dev Game: download/ship the SDK file and init JoyGameSDK with your productCode + Portal URLs. Dev Game: tải/đóng gói file SDK và init JoyGameSDK với productCode + URL Portal.Dev Game:下载/打包 SDK 文件,并用 productCode + Portal 域名初始化 JoyGameSDK

Pick your runtime. TikTok native mini games cannot load remote scripts — you must ship a local copy of the SDK file.

Chọn môi trường chạy. Mini Game native TikTok không được load script từ xa — phải đóng gói file SDK local trong game.

选择运行环境。TikTok 原生小游戏不能加载远程脚本 — 必须随包提供 SDK 文件的本地副本。

Load the Portal-hosted CDN script. Use this for H5 games in a browser or embedded webview.

Load script CDN từ Portal. Dùng cho game H5 trên trình duyệt hoặc webview nhúng.

加载 Portal CDN 脚本。适用于浏览器或内嵌 Webview 中的 H5 游戏。

index.html
<script src="https://devsdkapi.joyplay.vn/sdk/joygames-tiktok-sdk.js"></script>
<script>
  const joySdk = new window.JoyGameSDK({
    productCode: 'YOUR_PRODUCT_CODE',
    mode: 'webview',
    env: 'live',
    apiEndpoint: 'https://devsdkapi.joyplay.vn',
    paymentPortalUrl: 'https://devnap.joyplay.vn'
  });
</script>
Step 02

Login & sync Đăng nhập & đồng bộ登录与同步

Dev Game · call login() Dev Game · gọi login()Dev Game · 调用 login()

Dev Game: call joySdk.login() once at start. Everything under that (tt.login → sync → JWT) is done by SDK JOY. Dev Game: gọi joySdk.login() một lần lúc vào game. Phần bên dưới (tt.login → sync → JWT) do SDK JOY làm.Dev Game:启动时调用一次 joySdk.login()。其下流程(tt.login → sync → JWT)由 SDK JOY 完成。

Both HTML5 and Native call the same Portal API POST /tiktok/sync. The response must include tokens.accessToken — without it, create-order and other authed APIs return 401/403.

Cả HTML5 và Native đều gọi cùng API Portal POST /tiktok/sync. Response bắt buộc có tokens.accessToken — thiếu token thì create-order và các API cần auth sẽ 401/403.

HTML5 与原生均调用同一 Portal API POST /tiktok/sync。 响应必须包含 tokens.accessToken — 否则创建-order 及其他鉴权接口会返回 401/403。

Production integration = real TikTok only. Configure App ID / App Secret / Client Key / Client Secret / Webhook Secret on Admin → Product. Sync uses tt.login → Portal jscode2session (real openid). Payment uses real Beans + TikTok webhook. Header Sandbox / Live only switches Portal URLs (dev vs prod) — same real TikTok flow. Tích hợp production = dữ liệu TikTok thật. Cấu hình App ID / App Secret / Client Key / Client Secret / Webhook Secret trên Admin → Product. Sync: tt.login → Portal jscode2session (openid thật). Payment: Beans thật + webhook TikTok. Header Sandbox / Live chỉ đổi URL Portal (dev vs prod) — cùng luồng TikTok thật. 正式集成 = 仅使用真实 TikTok 数据。 在 Admin → Product 配置 App ID / App Secret / Client Key / Client Secret / Webhook Secret。 同步:tt.login → Portal jscode2session(真实 openid)。支付:真实 Beans + TikTok webhook。 页头 Sandbox / Live 仅切换 Portal 域名(开发 / 生产)— TikTok 流程相同且均为正式。

Inside TikTok: tt.login → auth code → Portal jscode2session with App ID/Secret from Admin Product → real openid → JWT. Portal never trusts a client-supplied tiktokUserId for production.

Trong TikTok: tt.logincode → Portal jscode2session với App ID/Secret trên Admin Product → openid thật → JWT. Production Portal không tin tiktokUserId do client tự gửi.

在 TikTok 内:tt.login → 授权 code → Portal 使用 Admin Product 上的 App ID/Secret 做 jscode2session → 真实 openid → JWT。 正式环境 Portal 不信任客户端传入的 tiktokUserId

1joySdk.login()
1joySdk.login()
1joySdk.login()
2tt.logincode
2tt.logincode
2tt.logincode
3POST /tiktok/sync + code
3POST /tiktok/sync + code
3POST /tiktok/sync + code
4Portal → TikTok jscode2session → openid
4Portal → TikTok jscode2session → openid
4Portal → TikTok jscode2session → openid
5Create/link user + JWT
5Tạo/liên kết user + JWT
5创建/关联用户 + JWT
login() — Native
joySdk.login()
  .then((user) => console.log('Portal user id:', user.id))
  .catch((err) => console.error(err.message));
SDK JOY · Dev Game không cần SDK JOY · Dev Game không cầnSDK JOY · Dev Game 无需

SDK JOY: tt.login → send code to Portal → receive user + JWT. Portal (system) runs jscode2session with App ID/Secret from Admin Product. SDK JOY: tt.login → gửi code lên Portal → nhận user + JWT. Portal (hệ thống) chạy jscode2session với App ID/Secret trên Admin Product.SDK JOY:tt.login → 将 code 发给 Portal → 收到用户 + JWT。Portal(系统)使用 Admin Product 上的 App ID/Secret 执行 jscode2session

API: POST /tiktok/sync API: POST /tiktok/syncAPI:POST /tiktok/sync

Purpose: create or find the Portal user for this TikTok player, then issue a JWT used by payment APIs.

Mục đích: tạo hoặc tìm user Portal cho người chơi TikTok, rồi cấp JWT dùng cho các API thanh toán.

用途:为该 TikTok 玩家创建或查找 Portal 用户,并签发支付 API 使用的 JWT。

Base URL: https://devsdkapi.joyplay.vn (no auth header on this call). Base URL: https://devsdkapi.joyplay.vn (API này không cần header auth).Base URL:https://devsdkapi.joyplay.vn(此接口无需 auth header)。
POST https://devsdkapi.joyplay.vn/tiktok/sync
POST /tiktok/sync
Content-Type: application/json

{
  "productId": "YOUR_PRODUCT_CODE",
  "platform": "android",
  "code": "<auth code from tt.login — required>",
  "name": "Nickname",
  "avatarUrl": "https://...",
  "env": "live"
}
Field RequiredBắt buộc必填 MeaningÝ nghĩa含义
productId yes Game product code on Portal (same as SDK productCode). Mã game trên Portal (cùng SDK productCode).Portal 上的游戏 product code(与 SDK productCode 相同)。
platform ios | android | web (device OS; login method is registerBy=tiktok). Browser test → web. ios | android | web (OS thiết bị; phương thức đăng ký là registerBy=tiktok). Test browser → web.ios | android | web(设备系统;登录方式为 registerBy=tiktok)。浏览器测试 → web
code yes Auth code from tt.login. Portal exchanges it via TikTok jscode2session using App ID/Secret on Admin Product — this is the only trusted openid source. Auth code từ tt.login. Portal đổi qua TikTok jscode2session với App ID/Secret trên Admin Product — nguồn openid duy nhất được tin.来自 tt.login 的授权码。Portal 使用 Admin Product 上的 App ID/Secret 通过 TikTok jscode2session 换取 openid — 这是唯一可信的 openid 来源。
tiktokUserId Do not send for production. Openid comes from jscode2session only. (Internal demo tools may mock locally — not for partner games.) Production không gửi field này. Openid chỉ từ jscode2session. (Demo nội bộ có thể mock local — không dùng cho game partner.)正式环境不要发送。Openid 仅来自 jscode2session。(内部 demo 可在本地 mock — 不适用于合作方游戏。)
name, avatarUrl Display profile (optional). Profile hiển thị (tuỳ chọn).展示用资料(可选)。
env yes Use live for partner integration (real TikTok). Header Sandbox/Live in this guide only changes Portal URLs, not this meaning. Dùng live khi tích hợp partner (TikTok thật). Header Sandbox/Live trên guide chỉ đổi URL Portal, không đổi ý nghĩa này.合作方集成请使用 live(真实 TikTok)。本指南页头 Sandbox/Live 仅切换 Portal 域名,不改变此含义。
200 OK — must include tokens.accessToken
{
  "status": 0,
  "data": {
    "status": "created",
    "user": {
      "id": 1002452667,
      "username": "58932478@tiktok",
      "name": "Player Name",
      "balance": 0,
      "registerBy": "tiktok",
      "tiktokUserId": "tt_openid_xxx"
    },
    "tokens": {
      "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "accessTokenExpiredAt": "2026-07-17T13:00:00.000Z"
    }
  }
}
Field MeaningÝ nghĩa含义
data.status created / exists — new or returning user. created / exists — user mới hoặc đã có.created / exists — 新用户或回流用户。
data.user Portal user (id, tiktokUserId, balance, …). User Portal (id, tiktokUserId, số dư, …).Portal 用户(idtiktokUserId、余额等)。
data.tokens.accessToken JWT for later APIs. Send as Authorization: <token> (no Bearer). JWT cho API sau. Gửi Authorization: <token> (không Bearer).后续 API 使用的 JWT。以 Authorization: <token> 发送(不要加 Bearer)。
data.tokens.accessTokenExpiredAt Token expiry time. Thời điểm hết hạn token.Token 过期时间。
After login, every payment API needs header Authorization: <accessToken> (raw JWT, not Bearer …). Sau login, mọi API thanh toán cần header Authorization: <accessToken> (JWT thô, không Bearer …). 登录后,每个支付 API 都需要 header Authorization: <accessToken>(原始 JWT,不是 Bearer …)。
Step 03

Payment Thanh toán支付

Dev Game · call pay() Dev Game · gọi pay()Dev Game · 调用 pay()

Dev Game: call joySdk.pay({ googleProductId, priceBeans, charId, … }). Create-order, TikTok pay sheet, and poll are SDK JOY work. Dev Game: gọi joySdk.pay({ googleProductId, priceBeans, charId, … }). Tạo đơn, sheet trả Beans, poll là việc của SDK JOY.Dev Game:调用 joySdk.pay({ googleProductId, priceBeans, charId, … })。创建订单、TikTok 支付面板与轮询由 SDK JOY 完成。

Two payment paths. Pick the one that matches how you installed the SDK (or force H5 with paymentMethod: 'portal').

Có 2 luồng thanh toán. Chọn theo cách cài SDK (hoặc ép H5 bằng paymentMethod: 'portal').

两种支付路径。按 SDK 安装方式选择(或用 paymentMethod: 'portal' 强制 H5)。

Default for mode: 'minigame'. Create a Beans order with googleProductId + priceBeans, then call TTMinis.game.pay. TikTok (live) webhook marks the order paid on Portal — see §5. Header Sandbox = Portal dev host; Live = Portal prod host (TikTok flow is the same).

Mặc định mode: 'minigame'. Tạo đơn Beans với googleProductId + priceBeans, rồi gọi TTMinis.game.pay. Webhook TikTok (live) đánh dấu order paid trên Portal — xem §5. Header Sandbox = host Portal dev; Live = host Portal prod (luồng TikTok giống nhau).

mode: 'minigame' 的默认路径。 使用 googleProductId + priceBeans 创建 Beans 订单, 然后调用 TTMinis.game.pay。 TikTok(正式)webhook 在 Portal 上将订单标记为已支付 — 见 §5。 页头 Sandbox = Portal 开发域名;Live = Portal 生产域名(TikTok 流程相同)。

1POST /tiktok/orders/create
1POST /tiktok/orders/create
1POST /tiktok/orders/create
2Get tradeOrderId
2Nhận tradeOrderId
2获取 tradeOrderId
3TTMinis.game.pay
3TTMinis.game.pay
3TTMinis.game.pay
4TikTok webhook → paid + addGold
4Webhook TikTok → paid + addGold
4TikTok webhook → paid + addGold
5Poll GET /tiktok/orders/:id
5Poll GET /tiktok/orders/:id
5轮询 GET /tiktok/orders/:id
SDK JOY · Dev Game không gọi tay SDK JOY · Dev Game không gọi taySDK JOY · Dev Game 无需手调

Steps 1–3 and 5 above are executed inside joySdk.pay(). The request/response tables below are for understanding — mini-game code only passes package + character fields into pay(). Bước 1–3 và 5 ở trên chạy bên trong joySdk.pay(). Bảng request/response bên dưới chỉ để hiểu luồng — code mini game chỉ truyền gói + nhân vật vào pay().上面第 1–3、5 步在 joySdk.pay() 内部执行。下方请求/响应表仅供理解 — 小游戏代码只需把套餐与角色字段传给 pay()

priceBeans = Admin “Giá Beans”. Portal finds the package by googleProductId + priceBeans (not VND). priceBeans = “Giá Beans” trên Admin. Portal tìm gói theo googleProductId + priceBeans (không phải VND). priceBeans = Admin「Giá Beans」。Portal 按 googleProductId + priceBeans 查找套餐(不是 VND)。
POST https://devsdkapi.joyplay.vn/tiktok/orders/create
POST /tiktok/orders/create
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

{
  "googleProductId": "com.joyplay.tiennghich.29",
  "priceBeans": 20,
  "productCode": "b18tiennghich",
  "serverId": "60013",
  "charId": "60013000001775",
  "serverName": "S13",
  "charName": "HeroName",
  "packageName": "Gói 29k"
}

Request body Body gửi lên请求体

Field RequiredBắt buộc必填 MeaningÝ nghĩa含义
googleProductId yes* Package SKU on Portal (Admin). SKU gói trên Portal (Admin).Portal(Admin)上的套餐 SKU。
priceBeans yes* Beans amount. Must match Admin “Giá Beans”. Used with SKU to find the package. Số Beans. Khớp “Giá Beans” Admin. Dùng cùng SKU để tìm gói.Beans 数量。须与 Admin「Giá Beans」一致。与 SKU 一起用于查找套餐。
productCode yes* Game product code on Portal. Mã game trên Portal.Portal 上的游戏 product code。
packageId Portal package id. If set, can skip googleProductId + priceBeans. Id gói trên Portal. Có thì có thể bỏ googleProductId + priceBeans.Portal package id。若已传,可跳过 googleProductId + priceBeans。
serverId yes Game server id (for addGold / IPN). Id server game (dùng addGold / IPN).游戏区服 id(用于 addGold / IPN)。
charId yes Character id in game. Id nhân vật trong game.游戏内角色 id。
serverName Server display name. Tên server hiển thị.区服显示名。
charName Character display name. Tên nhân vật hiển thị.角色显示名。
packageName Package display name (UI / TikTok product name). Tên gói hiển thị (UI / tên sản phẩm TikTok).套餐显示名(UI / TikTok 商品名)。
gameOrderId Your game’s order id (optional). Mã đơn phía game (tuỳ chọn).游戏侧订单号(可选)。
deviceId Device id (optional). Id thiết bị (tuỳ chọn).设备 id(可选)。
openId TikTok open_id. SDK usually fills from login. TikTok open_id. SDK thường tự lấy sau login.TikTok open_id。SDK 通常在登录后自动填充。
tiktokAccessToken TikTok user token for Open API when needed (Beans live). Token TikTok user cho Open API khi cần (Beans live).需要时用于 Open API 的 TikTok user token(Beans 正式)。

* Required when resolving by SKU. Or send packageId instead.

* Bắt buộc khi tìm gói theo SKU. Hoặc gửi packageId thay thế.

* 按 SKU 解析时必填。或改为传 packageId

Response (success) Response (thành công)响应(成功)

200 OK
{
  "status": 0,
  "data": {
    "orderId": "a1b2c3d4-...",
    "tradeOrderId": "TOID_...",
    "tokenAmount": 20,
    "amount": 29000,
    "currency": "vnd",
    "sandbox": false,
    "status": "placed"
  }
}
Field MeaningÝ nghĩa含义
status (root) 0 = OK. 0 = thành công.0 = 成功。
data.orderId Portal order id — use for poll until paid. Id đơn trên Portal — dùng poll đến khi paid.Portal 订单 id — 用于轮询直至 paid
data.tradeOrderId TikTok trade id → pass to TTMinis.game.pay({ trade_order_id }). Id giao dịch TikTok → truyền vào TTMinis.game.pay({ trade_order_id }).TikTok 交易 id → 传给 TTMinis.game.pay({ trade_order_id })
data.tokenAmount Beans charged (= package priceBeans). Số Beans trừ (= priceBeans của gói).扣减的 Beans(= 套餐 priceBeans)。
data.amount Order revenue in VND (from package price / priceSDK). Doanh thu đơn (VND) lấy từ giá gói / priceSDK.订单 VND 收入(来自套餐 price / priceSDK)。
data.currency Always vnd. Luôn là vnd.始终为 vnd
data.sandbox false for partner production (real Beans). Demo tools may return true. false khi tích hợp partner (Beans thật). Demo nội bộ có thể trả true.合作方正式环境为 false(真实 Beans)。内部 demo 可能返回 true
data.status Order status right after create, usually placed. Trạng thái đơn ngay sau tạo, thường placed.创建后订单状态,通常为 placed
Dev Game · this is the only pay code to write Dev Game · đây là đoạn pay duy nhất cần viếtDev Game · 这是唯一需要写的 pay 代码
pay() — Native Beans
joySdk.pay({
  googleProductId: 'com.joyplay.tiennghich.29',
  priceBeans: 20, // or price: 20 — SDK sends priceBeans to the API
  serverId: '60013',
  serverName: 'S13',
  charId: '60013000001775',
  charName: 'HeroName',
  packageName: 'Gói 29k'
});
Full docs for create / poll / webhook → see §4 API reference. Grant gold only when order status is paid (after TikTok webhook). Chi tiết create / poll / webhook → xem §4 Tham chiếu API. Chỉ cộng vàng khi order paid (sau webhook TikTok). create / poll / webhook 完整说明 → 见 §4 API 参考。 仅在订单状态为 paid(TikTok webhook 之后)时发放游戏币。
Step 04

API reference Tham chiếu APIAPI 参考

System · read-only System · chỉ đọcSystem · 只读

Explains Portal endpoints. Most are called by SDK JOY — mini-game usually only implements the game IPN in §5. Giải thích endpoint Portal. Hầu hết do SDK JOY gọi — mini game thường chỉ cần implement IPN game ở §5.说明 Portal 接口。多数由 SDK JOY 调用 — 小游戏通常只需实现 §5 的游戏 IPN。

Base: https://devsdkapi.joyplay.vn. Authed endpoints need header Authorization: <accessToken> (raw JWT).

Base: https://devsdkapi.joyplay.vn. API cần auth: header Authorization: <accessToken> (JWT thô).

Base:https://devsdkapi.joyplay.vn。 鉴权接口需要 header Authorization: <accessToken>(原始 JWT)。

Method URL Who calls itAi gọi调用方 PurposeMục đích用途
POST /tiktok/sync SDK JOY Login — tt.login + jscode2session (real TikTok openid). Game only calls joySdk.login(). Đăng nhập — tt.login + jscode2session (openid TikTok thật). Game chỉ gọi joySdk.login().登录 — tt.login + jscode2session(真实 TikTok openid)。游戏只调用 joySdk.login()
GET /tiktok/me SDK JOY Refresh current Portal user (auth) Lấy lại user Portal hiện tại (cần auth)刷新当前 Portal 用户(需鉴权)
POST /tiktok/orders/create SDK JOY Create Beans order + TikTok tradeOrderId. Inside joySdk.pay(). Tạo đơn Beans + tradeOrderId TikTok. Bên trong joySdk.pay().创建 Beans 订单 + TikTok tradeOrderId。在 joySdk.pay() 内。
GET /tiktok/orders/:id SDK JOY Poll order status / addGold result Poll trạng thái đơn / kết quả addGold轮询订单状态 / addGold 结果
POST /tiktok/orders/:id/sandbox-complete Internal Not used in partner production — real pay uses TikTok webhook Không dùng production partner — thanh toán thật qua webhook TikTok合作方正式环境不使用 — 正式支付走 TikTok webhook
POST /tiktok/payment/webhook TikTok Real payment callback → mark paid + addGold Callback thanh toán thật → paid + addGold真实支付回调 → 标记 paid + addGold

POST /tiktok/orders/create

Purpose: create a Portal order and a TikTok Beans trade. Returns tradeOrderId for TTMinis.game.pay. Auth required. See request/response tables in §3 Native.

Mục đích: tạo đơn Portal + giao dịch TikTok Beans. Trả tradeOrderId để gọi TTMinis.game.pay. Cần auth. Xem bảng body/response ở §3 Native.

用途:创建 Portal 订单与 TikTok Beans 交易。 返回供 TTMinis.game.pay 使用的 tradeOrderId。 需要鉴权。请求/响应表见 §3 原生。

GET /tiktok/orders/:id

Purpose: after pay (or webhook), SDK polls this until status is paid (and checks addGoldStatus). :id = orderId from create. Auth required.

Mục đích: sau pay (hoặc webhook), SDK poll API này đến khi status = paid (và kiểm tra addGoldStatus). :id = orderId từ create. Cần auth.

用途:支付(或 webhook)后,SDK 轮询此接口直到 statuspaid(并检查 addGoldStatus)。 :id = create 返回的 orderId。需要鉴权。

GET /tiktok/orders/:id — response data
{
  "status": 0,
  "data": {
    "orderId": "a1b2c3d4-...",
    "status": "paid",
    "addGoldStatus": "success",
    "addGoldError": null,
    "addGoldCallbackUrl": "https://game-server/.../ipn",
    "tiktokTradeOrderId": "TOID_...",
    "amount": 29000,
    "tokenAmount": 20
  }
}
Field MeaningÝ nghĩa含义
status Order: placedpaid (or failed). Đơn: placedpaid (hoặc failed).订单:placedpaid(或失败)。
addGoldStatus Game IPN result: pending / success / failed. Kết quả IPN game: pending / success / failed.游戏 IPN 结果:pending / success / failed
addGoldError Error message if addGold failed. Nội dung lỗi nếu addGold thất bại.addGold 失败时的错误信息。
addGoldCallbackUrl Game callback URL used for IPN (if any). URL callback game dùng cho IPN (nếu có).用于 IPN 的游戏回调 URL(如有)。
tiktokTradeOrderId Same as create tradeOrderId. Giống tradeOrderId lúc create.与 create 的 tradeOrderId 相同。
amount / tokenAmount VND revenue / Beans charged. Doanh thu VND / số Beans đã trừ.VND 收入 / 扣减 Beans。

POST /tiktok/orders/:id/sandbox-complete

Internal / JoyGames demo only — not part of partner production. Partner games wait for TikTok’s real POST /tiktok/payment/webhook after Beans pay.

Chỉ demo nội bộ JoyGames — không nằm trong tích hợp partner production. Game partner chờ webhook thật POST /tiktok/payment/webhook sau khi trả Beans.

仅 JoyGames 内部 demo — 不属于合作方正式集成。 合作方游戏在 Beans 支付后等待 TikTok 真实的 POST /tiktok/payment/webhook

Production path: user pays Beans → TikTok calls webhook → Portal marks paid → addGold. Do not call sandbox-complete from a shipped game. Luồng production: user trả Beans → TikTok gọi webhook → Portal đánh dấu paid → addGold. Game ship không gọi sandbox-complete. 正式路径:用户支付 Beans → TikTok 调用 webhook → Portal 标记 paid → addGold。 已上线游戏不要调用 sandbox-complete。
POST /tiktok/orders/:id/sandbox-complete (demo only)
POST /tiktok/orders/a1b2c3d4-.../sandbox-complete
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
// no body required — demo tools only

POST /tiktok/payment/webhook

Purpose: TikTok server → Portal callback after a real Beans payment. Portal verifies the event (live Product requires Webhook Secret on Admin), marks the order paid, then runs addGold (game IPN). Your game never calls this — only TikTok servers after a real Beans payment.

Mục đích: TikTok server → Portal sau khi trả Beans thật. Portal xác minh event (Product bắt buộc Webhook Secret trên Admin), đánh dấu order paid, rồi chạy addGold (IPN game). Game không gọi API này — chỉ server TikTok sau thanh toán thật.

用途:真实 Beans 支付后,TikTok 服务器 → Portal 回调。 Portal 校验事件(正式 Product 在 Admin 必须配置 Webhook Secret), 将订单标记为 paid,然后执行 addGold(游戏 IPN)。 游戏不要调用此接口 — 仅 TikTok 服务器在真实支付后调用。

GET /tiktok/me

Purpose: return the current Portal user from the JWT (e.g. refresh balance after payment). Auth required. Prefer this over looking up users by id.

Mục đích: trả user Portal hiện tại từ JWT (ví dụ refresh số dư sau thanh toán). Cần auth. Dùng API này thay vì tra cứu user theo id.

用途:根据 JWT 返回当前 Portal 用户 (例如支付后刷新余额)。需要鉴权。 优先使用本接口,而不是按 id 查询用户。

Step 05

Beans payment flow Luồng thanh toán BeansBeans 支付流程

System · overview System · tổng quanSystem · 总览

Full flow from buy → paid → gold. Red = Dev Game setup; cyan = SDK JOY; other nodes = Portal / TikTok / game IPN. Sơ đồ toàn bộ luồng từ mua → paid → cộng vàng. Đỏ = Dev Game phải setup; cyan = SDK JOY; các bước khác = Portal / TikTok / IPN game.完整流程示意:购买 → 已支付 → 发币。红色 = Dev Game 需配置;青色 = SDK JOY;其余步骤 = Portal / TikTok / 游戏 IPN。

Header Sandbox / Live only switches Portal hosts (dev vs production). TikTok Beans always use the live TikTok path: Open API create trade → TTMinis.game.pay → TikTok webhook → Portal paid → game addGold. Game only calls joySdk.pay(); SDK talks to Portal; Portal talks to TikTok.

Header Sandbox / Live chỉ đổi host Portal (dev vs production). Beans TikTok luôn đi luồng TikTok live: Open API create trade → TTMinis.game.pay → webhook TikTok → Portal paidaddGold game. Game chỉ gọi joySdk.pay(); SDK nói với Portal; Portal nói với TikTok.

页头 Sandbox / Live 仅切换 Portal 域名 (开发 / 生产)。TikTok Beans 始终走正式 TikTok 路径: Open API 创建交易 → TTMinis.game.pay → TikTok webhook → Portal paid → 游戏 addGold。 游戏只调用 joySdk.pay();SDK 与 Portal 通信;Portal 与 TikTok 通信。

Sandbox in this guide = Portal API https://devsdkapi.joyplay.vn + payment https://devnap.joyplay.vn. Live = https://sdkapi.joygames.vn + https://nap.joyplay.vn. Point TikTok’s webhook at the same Portal host you use (https://devsdkapi.joyplay.vn/tiktok/payment/webhook). Sandbox trong guide này = Portal API https://devsdkapi.joyplay.vn + payment https://devnap.joyplay.vn. Live = https://sdkapi.joygames.vn + https://nap.joyplay.vn. Webhook TikTok phải trỏ đúng host Portal đang dùng (https://devsdkapi.joyplay.vn/tiktok/payment/webhook). 本指南中的 Sandbox = Portal API https://devsdkapi.joyplay.vn + 支付 https://devnap.joyplay.vnLive = https://sdkapi.joygames.vn + https://nap.joyplay.vn。 将 TikTok webhook 指向你使用的同一 Portal 域名 (https://devsdkapi.joyplay.vn/tiktok/payment/webhook)。

Product should be active with TikTok Client Key / Secret / Webhook secret configured on Admin so Portal can call TikTok Open API and verify the webhook.

Product nên active và đã cấu hình TikTok Client Key / Secret / Webhook secret trên Admin để Portal gọi TikTok Open API và verify webhook.

Product 应为 active,并在 Admin 配置 TikTok Client Key / Secret / Webhook secret, 以便 Portal 调用 TikTok Open API 并校验 webhook。

Flowchart Sơ đồ流程图

Dev Game
A · Game
Player taps buy → joySdk.pay({ googleProductId, priceBeans, charId, … })
User bấm mua → joySdk.pay({ googleProductId, priceBeans, charId, … })
玩家点击购买 → joySdk.pay({ googleProductId, priceBeans, charId, … })
SDK JOY · Dev Game không cần
B · SDK JOY
POST create order to Portal (not TikTok).
POST tạo đơn lên Portal (không gọi TikTok).
向 Portal(不是 TikTok)POST 创建订单。
POST https://devsdkapi.joyplay.vn/tiktok/orders/create
System · Portal
C · Portal API
Create DB order → call TikTok live Open API create trade → return orderId + tradeOrderId.
Tạo order DB → gọi TikTok Open API live create trade → trả orderId + tradeOrderId.
创建数据库订单 → 调用 TikTok 正式 Open API 创建交易 → 返回 orderId + tradeOrderId
https://open.tiktokapis.com/v2/minis/trade_order/create/
SDK JOY · Dev Game không cần
D · SDK JOY
Open TikTok pay sheet: TTMinis.game.pay({ trade_order_id })
Mở sheet trả Beans: TTMinis.game.pay({ trade_order_id })
打开 TikTok 支付面板:TTMinis.game.pay({ trade_order_id })
System · TikTok
E · TikTok
Player confirms Beans payment inside TikTok (live).
User xác nhận trừ Beans trong TikTok (live).
玩家在 TikTok 内确认 Beans 支付(正式)。
System · TikTok
F · TikTok → Portal
Webhook redeem success to the Portal host you configured (game does not call this).
Webhook redeem thành công về đúng host Portal đã cấu hình (game không gọi).
向你配置的 Portal 域名发送 redeem success webhook(游戏不调用)。
POST https://devsdkapi.joyplay.vn/tiktok/payment/webhook
System · Portal
G · Portal
Verify signature → mark order paid → call game addGold IPN.
Verify chữ ký → đánh dấu paid → gọi IPN addGold của game.
校验签名 → 标记订单 paid → 调用游戏 addGold IPN。
Dev Game · server
H · Game server IPN
Receive addGold → grant currency/items to charId → respond success.
Nhận addGold → cộng vàng/item cho charId → trả success.
接收 addGold → 向 charId 发放货币/道具 → 返回成功。
SDK JOY · Dev Game không cần
I · SDK JOY
Poll until paid (started after pay sheet success).
Poll tới khi paid (bắt đầu sau khi sheet pay OK).
轮询直至已支付(支付面板成功后开始)。
GET /tiktok/orders/:id → status === "paid"
Z · Done
pay() resolves · gold already on character · optionally refresh /tiktok/me.
pay() resolve · vàng đã vào nhân vật · có thể refresh /tiktok/me.
pay() resolve · 角色已到账 · 可选刷新 /tiktok/me

Step by step Từng bước分步说明

  1. Dev Game Login first. joySdk.login() → JWT stored. Đăng nhập trước. joySdk.login() → lưu JWT.先登录。 joySdk.login() → 保存 JWT。
  2. Dev Game Game calls pay. Pass package + character fields. Game gọi pay. Truyền gói + thông tin nhân vật.游戏调用 pay。 传入套餐与角色字段。
  3. SDK JOY SDK JOY → Portal create on the host from the header (dev or prod). SDK JOY → Portal create trên host theo header (dev hoặc prod).SDK → Portal 创建订单,域名随页头(开发或生产)。
  4. System Portal → TikTok live create trade. Real trade_order_id. Portal → TikTok live create trade. Nhận trade_order_id thật.Portal → TikTok 正式创建交易。 真实 trade_order_id
  5. SDK JOY SDK JOY opens TTMinis.game.pay. Native Beans UI. SDK JOY mở TTMinis.game.pay. UI Beans native.SDK JOY 打开 TTMinis.game.pay。 原生 Beans 界面。
  6. System Player pays. TikTok deducts Beans. User trả. TikTok trừ Beans.玩家支付。 TikTok 扣减 Beans。
  7. System TikTok webhook → Portal. Event minis.trade_order.redeem.success. TikTok webhook → Portal. Event minis.trade_order.redeem.success.TikTok webhook → Portal。 事件 minis.trade_order.redeem.success
  8. System Portal setPaid + addGold. Calls game callback URL. Portal setPaid + addGold. Gọi callback URL game.Portal setPaid + addGold。 调用游戏回调 URL。
  9. Dev Game Game IPN sends items. Verify signature (amount), then match googleProductId + amountBean + amount with Dev Game server catalog — only send when all 3 match. IPN game send vật phẩm. Verify signature (theo amount), rồi đối chiếu googleProductId + amountBean + amount với server Dev Game — chỉ send khi đúng cả 3 trường.游戏 IPN 发送道具。校验签名(按 amount),再对照 Dev Game 服务器目录核对 googleProductId + amountBean + amount — 仅当 3 个字段一致时发送。
  10. SDK JOY SDK JOY poll sees paid. pay() returns success. SDK JOY poll thấy paid. pay() trả success.SDK JOY 轮询到 paid。 pay() 返回成功。

addGold IPN — body the Portal sends to your game server addGold IPN — body Portal gửi về game server của bạnaddGold IPN — Portal 发送到你游戏服务器的 body

Dev Game · server must implement Dev Game · server phải implementDev Game · 游戏服务器必须实现

Dev Game: configure callbackUrl on Product, verify signature, match package on game server, send items for charId, return success.
Note: whitelist Portal server IPs so addGold can reach your callback. Review the existing addGold docs carefully.
Dev Game: cấu hình callbackUrl trên Product, verify chữ ký, đối chiếu gói trên game server, send vật phẩm cho charId, trả success.
Lưu ý: cần whitelist IP server Portal để call được addGold. Xem kỹ tài liệu addGold đã gửi.
Dev Game:在 Product 配置 callbackUrl、校验签名、对照游戏服套餐、向 charId 发送道具、返回成功。
注意:需将 Portal 服务器 IP 加入白名单,以便能调用 addGold。请仔细查看已提供的 addGold 文档。

What Portal sends extra for TikTok: type: "tiktok" + amountBean (+ googleProductId). Signature rule unchanged — still signs with amount (VND), not amountBean.

Validate before sending items: on the Dev Game server, check that googleProductId + amountBean + amount match your catalog exactly. Only send items when all 3 fields match.

SKU field: TikTok always uses googleProductId (same field for all devices — do not branch iOS appleProductId vs Android).
Portal gửi thêm cho TikTok: type: "tiktok" + amountBean (+ googleProductId). Rule signature giữ nguyên — vẫn ký theo amount (VND), không dùng amountBean.

Check trước khi send vật phẩm: trên server Dev Game, đối chiếu googleProductId + amountBean + amount với catalog. Chỉ send khi đúng y chang cả 3 trường dữ liệu trên với server Dev Game.

Field SKU: TikTok luôn dùng googleProductId (một field cho mọi thiết bị — không tách iOS appleProductId / Android).
Portal 为 TikTok 额外发送: type: "tiktok" + amountBean(以及 googleProductId)。 签名规则不变 — 仍用 amount(VND)签名,不用 amountBean

发送道具前校验:在 Dev Game 服务器上核对 googleProductId + amountBean + amount 是否与目录完全一致。仅当上述 3 个字段与 Dev Game 服务器一致时才发送道具。

SKU 字段:TikTok 始终使用 googleProductId (所有设备同一字段 — 不要按 iOS appleProductId / Android 分支)。

After Beans pay succeeds, Portal marks the order paid and POSTs this JSON to your product callbackUrl.

Sau khi trả Beans thành công, Portal đánh dấu order paid và POST JSON này về callbackUrl của product.

Beans 支付成功后,Portal 将订单标记为 paid,并向你 product 的 callbackUrl POST 该 JSON。

POST callbackUrl
{
  "action": "addGold",
  "type": "tiktok",
  "platform": "tiktok",
  "userId": 1002752299,
  "amount": 29000,
  "amountBean": 20,
  "serverId": "34",
  "charId": "34000001234",
  "time": 1784438520042,
  "transactionId": "332c9bee-...",
  "orderId": "32ec6b03-...",
  "productId": "com.joyplay.tiennghich.29",
  "googleProductId": "com.joyplay.tiennghich.29",
  "productName": "Tiên Nghịch Mobile",
  "clientID": "b18tiennghich",
  "extraInfo": { "amount": 29000, "goodids": null },
  "signature": "52dfa596..."
}
Field MeaningÝ nghĩa含义
type "tiktok" for TikTok Beans orders (other flows use ticket / iap / sdk). "tiktok" với đơn TikTok Beans (luồng khác là ticket / iap / sdk).TikTok Beans 订单为 "tiktok"(其他流程为 ticket / iap / sdk)。
googleProductId Package SKU. Always use this for TikTok — same on every device (no iOS/Android split). SKU gói. TikTok luôn dùng field này — mọi thiết bị giống nhau (không tách iOS/Android).套餐 SKU。TikTok 始终用此字段 — 所有设备相同(不区分 iOS/Android)。
amount Order revenue in VND (package price / priceSDK). Used in signature. Doanh thu đơn (VND) — giá gói / priceSDK. Dùng trong signature.订单 VND 收入(套餐 price / priceSDK)。用于签名
amountBean Beans the player paid (= package priceBeans). Not in signature — still validate against your catalog. Số Beans user đã trả (= priceBeans). Không nằm trong signature — vẫn phải đối chiếu catalog.玩家支付的 Beans(= priceBeans)。不参与签名 — 仍需对照目录校验。
signature sha1 over action, userId, amount, serverId, charId, time, transactionId + secret. type / amountBean / googleProductId are not signed.
Dev Game: follow the existing addGold docs you already received — implement signature exactly as documented there (same rule as other channels; only amount is signed, not amountBean).
sha1 của action, userId, amount, serverId, charId, time, transactionId + secret. type / amountBean / googleProductId không nằm trong chữ ký.
Dev Game: xem kỹ tài liệu addGold đã gửi trước đó để làm signature cho đúng (cùng rule các kênh khác; chỉ ký theo amount, không ký amountBean).
action, userId, amount, serverId, charId, time, transactionId + secret 的 sha1。type / amountBean / googleProductId 参与签名。
Dev Game:请仔细查看此前已提供的 addGold 文档,按文档正确实现 signature(与其他渠道相同;仅对 amount 签名,不对 amountBean)。

Header Sandbox vs Live (Portal hosts only) Header Sandbox vs Live (chỉ host Portal)页头 Sandbox 与 Live(仅 Portal 域名)

Sandbox Live
Portal API https://devsdkapi.joyplay.vn https://sdkapi.joygames.vn
Payment H5 https://devnap.joyplay.vn https://nap.joyplay.vn
TikTok Beans Same live flow (Open API + webhook)Cùng luồng live (Open API + webhook)同一套正式流程(Open API + webhook)
Webhook URL …devsdkapi…/tiktok/payment/webhook …sdkapi…/tiktok/payment/webhook
Game codeCode game游戏代码 login() + pay() only
Note: Header Sandbox vs Live only changes Portal hosts. Partner games always use real TikTok webhook after Beans pay (see §4). Internal demo helpers are not part of the integration contract. Ghi chú: Header Sandbox vs Live chỉ đổi host Portal. Game partner luôn dùng webhook TikTok thật sau trả Beans (xem §4). Helper demo nội bộ không thuộc hợp đồng tích hợp. 说明: 页头 Sandbox 与 Live 仅切换 Portal 域名。合作方游戏在 Beans 支付后始终使用真实 TikTok webhook (见 §4)。内部 demo 辅助接口不属于集成约定。