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()。
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。红色标记的章节。
tt.login, /tiktok/sync, create order, TTMinis.game.pay, poll. Dev Game does not call these APIs by hand.
SDK JOY sẽ làm — tt.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。
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 游戏。
<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>
Download the SDK into your game project, then import it as an ES module
(mode: 'minigame').
Tải SDK vào project game, rồi import ES module
(mode: 'minigame').
将 SDK 下载到游戏工程,再以 ES module 导入(mode: 'minigame')。
Download joygames-tiktok-sdk.js Tải joygames-tiktok-sdk.js下载 joygames-tiktok-sdk.js
-
Click
joygames-tiktok-sdk.js
(CDN for the host selected in the header), or copy from
tiktok-app/sdk/. -
Bấm
joygames-tiktok-sdk.js
(CDN theo host trên header), hoặc copy từ
tiktok-app/sdk/. - 点击
joygames-tiktok-sdk.js
(CDN 随页头所选域名变化),或从
tiktok-app/sdk/复制。 - Save e.g.
libs/joygames-tiktok-sdk.js. - Lưu ví dụ
libs/joygames-tiktok-sdk.js. - 例如保存为
libs/joygames-tiktok-sdk.js。
import JoyGameSDK from './libs/joygames-tiktok-sdk.js';
const joySdk = new JoyGameSDK({
productCode: 'YOUR_PRODUCT_CODE',
mode: 'minigame',
env: 'live',
apiEndpoint: 'https://devsdkapi.joyplay.vn',
paymentPortalUrl: 'https://devnap.joyplay.vn'
});
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。
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 流程相同且均为正式。
H5 / webview games still call the same POST /tiktok/sync.
In production, identity must come from a real TikTok auth code (or open the game inside TikTok).
Do not send a client-made tiktokUserId against deployed Portal APIs.
Game H5 / webview vẫn gọi cùng POST /tiktok/sync.
Production: identity phải từ code TikTok thật (hoặc chạy trong TikTok).
Không gửi tiktokUserId tự tạo lên API Portal deploy.
H5 / Webview 游戏仍调用同一 POST /tiktok/sync。
正式环境身份必须来自真实 TikTok 授权 code(或在 TikTok 内打开游戏)。
不要向已部署的 Portal API 发送客户端自造的 tiktokUserId。
joySdk.login()joySdk.login()joySdk.login()code (or Native host)code TikTok thật (hoặc host Native)code(或原生宿主)POST /tiktok/sync → jscode2sessionPOST /tiktok/sync → jscode2sessionPOST /tiktok/sync → jscode2sessiontokens.accessTokentokens.accessTokentokens.accessTokenjoySdk.login().then((user) => {
console.log('user', user.id);
// joySdk.accessToken is set automatically
});
SDK JOY: obtain TikTok auth code (when available) → POST /tiktok/sync → store JWT on the SDK instance. Do not call /tiktok/sync yourself.
SDK JOY: lấy code TikTok (khi có) → POST /tiktok/sync → lưu JWT trên SDK. Game không tự gọi /tiktok/sync.SDK JOY:获取 TikTok 授权 code(如可用)→ POST /tiktok/sync → 在 SDK 实例上保存 JWT。游戏不要自己调用 /tiktok/sync。
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.login → code → 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。
joySdk.login()joySdk.login()joySdk.login()tt.login → codett.login → codett.login → codePOST /tiktok/sync + codePOST /tiktok/sync + codePOST /tiktok/sync + codejscode2session → openidjscode2session → openidjscode2session → openidjoySdk.login()
.then((user) => console.log('Portal user id:', user.id))
.catch((err) => console.error(err.message));
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。
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)。
Opens the Portal payment page /game-payment
(mode: 'webview' or paymentMethod: 'portal').
Player pays with MoMo/QR/etc. — not TikTok Beans.
Query price must be Beans (same as Admin priceBeans); Portal looks up the package by
googleProductId + Beans, then charges VND from the package price.
Mở trang thanh toán Portal /game-payment
(mode: 'webview' hoặc paymentMethod: 'portal').
User trả bằng MoMo/QR/… — không dùng TikTok Beans.
Query price phải là số Beans (cùng Admin priceBeans); Portal tìm gói theo
googleProductId + Beans, rồi charge VND theo giá gói.
打开 Portal 支付页 /game-payment
(mode: 'webview' 或 paymentMethod: 'portal')。
玩家使用 MoMo/二维码等支付 — 不是 TikTok Beans。
Query price 必须是 Beans(与 Admin priceBeans 一致);Portal 按
googleProductId + Beans 查找套餐,再按套餐价格收取 VND。
login()login()login()/game-payment/game-payment/game-paymentURL URLURL
https://devnap.joyplay.vn/game-payment
?productCode=b18tiennghich
&userId=1002752295
&charId=34000001234
&charName=DoNhac+Thuy5883
&serverId=34
&serverName=S34+-+Chu+Vu+Thai
&packageName=Goi+29k
&price=20
&googleProductId=com.joyplay.tiennghich.29
&platform=tiktok
&accessToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
| Param | RequiredBắt buộc必填 | SourceNguồn来源 |
|---|---|---|
productCode |
yes | SDK productCode |
accessToken |
yes | tokens.accessToken |
charId |
yes | pay() options |
googleProductId |
yes | SKU gói |
price |
yes | Beans (= priceBeans) |
userId |
— | Portal user id |
charName, serverId, serverName, packageName |
— | pay() options |
joySdk.pay({
paymentMethod: 'portal',
serverId: '34',
serverName: 'S34 - Chu Vũ Thái',
charId: '34000001234',
charName: 'ĐồNhạc Thủy5883',
price: 20, // Beans (= priceBeans)
priceBeans: 20, // optional alias
packageName: 'Gói 29k',
googleProductId: 'com.joyplay.tiennghich.29'
});
devsdkapi.joyplay.vn.
Payment page: devnap.joyplay.vn/game-payment.
This path does not call Beans / /tiktok/orders/create.
Host login/API: devsdkapi.joyplay.vn.
Trang thanh toán: devnap.joyplay.vn/game-payment.
Luồng này không gọi Beans / /tiktok/orders/create.
登录/API 域名:devsdkapi.joyplay.vn。
支付页:devnap.joyplay.vn/game-payment。
此路径不会调用 Beans / /tiktok/orders/create。
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 流程相同)。
POST /tiktok/orders/createPOST /tiktok/orders/createPOST /tiktok/orders/createtradeOrderIdtradeOrderIdtradeOrderIdTTMinis.game.payTTMinis.game.payTTMinis.game.payGET /tiktok/orders/:idGET /tiktok/orders/:idGET /tiktok/orders/:id
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 /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)响应(成功)
{
"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。
|
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'
});
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 之后)时发放游戏币。
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 轮询此接口直到
status 为 paid(并检查 addGoldStatus)。
:id = create 返回的 orderId。需要鉴权。
{
"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: placed → paid (or failed).
Đơn: placed → paid (hoặc failed).订单:placed → paid(或失败)。
|
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。
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/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 查询用户。
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 paid → addGold 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 通信。
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.vn。
Live = 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ơ đồ流程图
joySdk.pay({ googleProductId, priceBeans, charId, … })joySdk.pay({ googleProductId, priceBeans, charId, … })joySdk.pay({ googleProductId, priceBeans, charId, … })POST https://devsdkapi.joyplay.vn/tiktok/orders/create
orderId + tradeOrderId.orderId + tradeOrderId.orderId + tradeOrderId。https://open.tiktokapis.com/v2/minis/trade_order/create/
TTMinis.game.pay({ trade_order_id })TTMinis.game.pay({ trade_order_id })TTMinis.game.pay({ trade_order_id })POST https://devsdkapi.joyplay.vn/tiktok/payment/webhook
paid → call game addGold IPN.paid → gọi IPN addGold của game.paid → 调用游戏 addGold IPN。charId → respond success.charId → trả success.charId 发放货币/道具 → 返回成功。GET /tiktok/orders/:id → status === "paid"
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分步说明
-
Dev Game
Login first.
joySdk.login()→ JWT stored. Đăng nhập trước.joySdk.login()→ lưu JWT.先登录。joySdk.login()→ 保存 JWT。 - 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。 传入套餐与角色字段。
- 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 创建订单,域名随页头(开发或生产)。
-
System
Portal → TikTok live create trade. Real
trade_order_id. Portal → TikTok live create trade. Nhậntrade_order_idthật.Portal → TikTok 正式创建交易。 真实trade_order_id。 - 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 界面。
- System Player pays. TikTok deducts Beans. User trả. TikTok trừ Beans.玩家支付。 TikTok 扣减 Beans。
-
System
TikTok webhook → Portal. Event
minis.trade_order.redeem.success. TikTok webhook → Portal. Eventminis.trade_order.redeem.success.TikTok webhook → Portal。 事件minis.trade_order.redeem.success。 - System Portal setPaid + addGold. Calls game callback URL. Portal setPaid + addGold. Gọi callback URL game.Portal setPaid + addGold。 调用游戏回调 URL。
-
Dev Game
Game IPN sends items. Verify signature (
amount), then matchgoogleProductId+amountBean+amountwith Dev Game server catalog — only send when all 3 match. IPN game send vật phẩm. Verify signature (theoamount), rồi đối chiếugoogleProductId+amountBean+amountvới server Dev Game — chỉ send khi đúng cả 3 trường.游戏 IPN 发送道具。校验签名(按amount),再对照 Dev Game 服务器目录核对googleProductId+amountBean+amount— 仅当 3 个字段一致时发送。 -
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: 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 文档。
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。
{
"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 |
|