跳到主要内容

Steam Unity SDK 对接文档

面向 Unity 游戏研发:接入 Steam SDK,完成登录、内购、事件上报、用户信息等能力。

版本

当前版本及SDK下载

客户端 Unity

最新版本下载: 1.0.2unitypackage

历史版本

1. 接入前确认

1.1 必需信息

游戏侧用途说明提供者
SDK对接包游戏方接入时需要,下载后导入游戏项目见【当前版本及SDK下载】SDK研发,见说明
正式 API 地址游戏方接入时需要,SDK正式接口地址https://api-steam-sdk.miaorui1.com/api/v1SDK研发,见说明
Steam AppId游戏方接入时需要,游戏在Steam后台的ID类似于 5052040 数字Steam 后台
Web API密钥SDK技术需要,服务端校验需配置在【用户与权限-管理组-对应游戏用户组】右侧创建Steam 后台
内购 product_id 列表SDK接入时需要,购买时需传入内购商品字段:商品id(product_id)、商品名、商品描述、人民币价格(分)游戏方
发货回调 URLSDK接入时需要,服务端支付发货接口游戏研发按文档接入后并提供游戏方
发货密钥 DELIVERY_SECRETSDK接入时需要,服务端支付发货密钥游戏研发按文档接入后并提供SDK研发

2. Unity工程操作

2.1 导入 SDK

步骤操作作用
1在游戏工程 Assets → Import Package → Custom Package 导入MJGameSDK_v1.x.x.unitypackage将 SDK 代码、Steamworks.NET、Editor 脚本导入游戏

SDK 已内置 Steamworks.NET(v20.2.0),一般无需再单独下载。

2.2 创建并填写 MJGameConfig

步骤操作作用
1菜单 MJGameSDK > Create Default Config生成 Assets/MJGameSDK/Resources/MJGameConfig.asset
2在 Inspector 填写字段(见下表)MJGameConfig.LoadDefault() 从 Resources 加载,决定连哪套环境、是否 Mock
字段含义正式
appIdSteam AppId,同时作为 X-Steam-App-Id正式 Steam AppId
useMockSteamtrue 时不调真 Steam,用 Mock ticketfalse
serverBaseUrlSDK 服务端 API 根地址(须含 /api/v1正式 API 地址
requestTimeoutHTTP 超时(秒)30
privacyPolicyUrl目前用不到,可不动正式 URL
logLevelConsole / 文件日志级别Info / Debug
enableFileLog是否写入本地 mjgame_sdk.log建议 true

2.3 配置 steam_appid.txt

步骤操作作用
1确认 Assets/StreamingAssets/steam_appid.txt 内容为一行 Steam AppId编辑器 / 本地调试时 Steamworks 识别 App
2构建后 exe 同目录会自动复制该文件(MJGameCopySteamAppId本地双击 exe 调试可用;Steam 库启动的正式包通常不依赖此文件

2.4 设置编译符号

步骤操作作用
1导入含 Steamworks.NET 的工程后,Editor 会自动添加符号;也可手动 MJGameSDK > Setup Steamworks.NET Defines定义 STEAMWORKS_NET + STEAMWORKS_WIN(Win)或 STEAMWORKS_LIN_OSX(Mac 编辑器)
2若未定义 STEAMWORKS_NET真 Steam 路径会静默回退 Mock 票,正式联调会失败

2.5 场景挂载 Runner

步骤操作作用
1在首个常驻场景(如 Loading)创建空物体
2挂载 MJGameSDKRunnerDontDestroyOnLoad;每帧 SteamAPI.RunCallbacks;承载 SDK 协程
备选Initialize(..., runner: this) 传入任意常驻 MonoBehaviour未挂 Runner 时 SDK 也会自动创建,但显式挂载更可控

2.6 在启动代码中调用 Initialize

在游戏启动入口(Loading)调用一次 MJGameSDK.Initialize,成功后再登录 / 上报。

2.7 构建与 Steam 启动(正式联调)

步骤操作作用
1Build Settings → Windows x64正式包目标平台
2将产物放到 Steam 安装目录,或通过 SteamPipe 上传Steam 才能以「库启动」方式拉起游戏
3Steam 设置中开启游戏内 Overlay;库内点「开始游戏」Overlay 是微交易授权必要条件
4进游戏 Shift+Tab 确认 Overlay;日志应有 overlayEnabled=True避免内购卡在 2005 超时

3. 推荐接入流程

启动游戏
→ Initialize SDK
→ ReportGameActivate(启动激活,无需登录)
→ LoginWithSteam (静默登录Steam账号)
→ ReportLoadingUI(加载界面,上报日志)
→ ReportLoginUI(登录界面展示时,上报日志)
→ [游戏自管:选服、选角]
→ ReportRoleCreate(首次创建角色,上报日志)
→ ReportRoleLogin(每次进入角色,上报日志)
→ ReportLoadingGame(进服加载,上报日志)
→ ReportEnterGame(进入游戏,上报日志)
→ [内购 / GetOrders / 用户信息]

3.1 启动 → 登录 → 进角全流程

启动到进角时序

涉及:游戏客户端、游戏服务端、SDK、Steam 客户端、SDK 服务端、Steam 服务端。

顺序与 §3 一致:InitializeReportGameActivateLoginWithSteamReportLoadingUI / ReportLoginUI → 选服选角 → ReportRoleCreate / ReportRoleLoginReportLoadingGameReportEnterGame

3.2 内购(含 Overlay、验单、游戏服发货)

内购发货时序

发货由 SDK 服务端回调游戏服务端;客户端 Purchase 成功仅表示订单 completed


4. SDK API 接入

通用约定:

  • 失败回调统一为 Action<MJGameResult>,字段见 §4.10
  • 标注「需登录」的接口须先 LoginWithSteam 成功。
  • 除特别说明外,成功回调在主线程 / 协程回调链上触发,可直接刷新 UI。

4.1 初始化 MJGameSDK.Initialize

MJGameSDK.Initialize(MJGameConfig config, Action<MJGameResult> onComplete, MonoBehaviour runner = null);
参数类型必填含义
configMJGameConfig建议环境与 Steam 配置;nullLoadDefault()
onCompleteAction<MJGameResult>初始化完成回调
runnerMonoBehaviour承载协程的 Runner;默认用场景中的 MJGameSDKRunner

onCompleteMJGameResult

字段含义
Code == 0 / IsSuccessSteam(或 Mock)初始化成功,可继续登录
Code != 0失败,见错误码(常见 2000/2001)
Message可读说明

调用时机:游戏启动、进入业务前调用 一次。已初始化时再次调用会直接 Success("SDK already initialized")

var config = MJGameConfig.LoadDefault();
MJGameSDK.Initialize(config, result =>
{
if (!result.IsSuccess)
{
Debug.LogError($"SDK 初始化失败: [{result.Code}] {result.Message}");
return;
}
// 推荐:Activate → 静默 LoginWithSteam → LoadingUI / LoginUI → 选服选角 → 创角/登角 → LoadingGame / EnterGame
}, runner: this);

相关:

成员含义
MJGameSDK.IsInitialized是否已初始化成功

4.2 登录 Auth.LoginWithSteam

MJGameSDK.Auth.LoginWithSteam(Action<MJGameUserInfo> onSuccess, Action<MJGameResult> onError);
参数类型含义
onSuccessAction<MJGameUserInfo>验票成功,Token / request_key 已写入 SDK
onErrorAction<MJGameResult>Steam 出票失败或服务端验票失败等

onSuccessMJGameUserInfo

字段类型含义
idint平台用户 ID
app_idint平台 apps 表主键
steam_app_idulongSteam AppId
steam_idstring64 位 SteamID,Steam用户id
nicknamestringSteam 昵称(PersonaName)
avatar_urlstring头像 URL(可能为空)

其它

成员含义
Auth.IsLoggedIn是否有用户且本地有 access token
Auth.CurrentUser当前用户;未登录为 null
Auth.Logout()仅清本地 Token / 用户,调服务端

调用时机:初始化成功后、进入需登录业务前。正式模式需 Steam 客户端已登录且对本 App 有权限。


4.3 事件上报 Analytics.*

激活 / UI 阶段的设备字段由 SDK 内部自动采集device_id、机型、OS、版本等),游戏不用拼。创角/登角/进服阶段由游戏传入服/角色信息。

4.3.1 ReportGameActivate

MJGameSDK.Analytics.ReportGameActivate(Action onSuccess = null, Action<MJGameResult> onError = null);
参数含义
onSuccess上报成功(无业务载荷);可省略
onError失败(如 5001 缺 device_id);可省略

无需传入 MJGameActivateInfo;SDK 内部调用与 CollectActivateInfo() 相同的采集逻辑。

时机Initialize 成功后立刻调用;无需登录(推荐排在 LoginWithSteam 之前)。

MJGameSDK.Analytics.ReportGameActivate(
() => Debug.Log("激活上报成功"),
err => Debug.LogWarning($"激活上报失败: {err.Message}"));

4.3.2 ReportLoadingUI / ReportLoginUI

MJGameSDK.Analytics.ReportLoadingUI(Action onSuccess = null, Action<MJGameResult> onError = null);
MJGameSDK.Analytics.ReportLoginUI(Action onSuccess = null, Action<MJGameResult> onError = null);
方法推荐时机需登录
ReportLoadingUI静默登录成功后,进入/展示加载界面时否(推荐流程在登录后)
ReportLoginUI展示登录角色相关界面时(推荐紧接 LoadingUI)否(同上)

内部自动带设备信息;回调可空(失败仅打日志时可省略 onError)。

4.3.3 ReportLoadingGame / ReportEnterGame

MJGameSDK.Analytics.ReportLoadingGame(MJGameRoleInfo roleInfo, Action onSuccess = null, Action<MJGameResult> onError = null);
MJGameSDK.Analytics.ReportEnterGame(MJGameRoleInfo roleInfo, Action onSuccess = null, Action<MJGameResult> onError = null);
参数含义
roleInfo建议传入当前服/角色;可 null(仅设备信息)
onSuccess / onError可选

需登录。推荐时机:ReportRoleCreate / ReportRoleLogin 之后,进服加载中 / 真正进入游戏世界时。

4.3.4 ReportRoleCreate / ReportRoleLogin

MJGameSDK.Analytics.ReportRoleCreate(MJGameRoleInfo roleInfo, Action onSuccess, Action<MJGameResult> onError);
MJGameSDK.Analytics.ReportRoleLogin(MJGameRoleInfo roleInfo, Action onSuccess, Action<MJGameResult> onError);
参数含义
roleInfo必填null → 5002
onSuccess上报成功
onError失败

MJGameRoleInfo

字段建议含义
server_id强建议区服 ID
server_name建议区服名
role_id强建议角色 ID
role_name建议角色名
role_level可选等级(未传服务端可按 0)
role_class可选职业/兵种等
方法时机需登录
ReportRoleCreate该角色第一次创建;推荐在选角后、LoadingGame 之前
ReportRoleLogin每次进入该角色;推荐紧接 RoleCreate(若有),再 LoadingGame
var role = new MJGameRoleInfo
{
server_id = "1",
server_name = "华东一区",
role_id = "101",
role_name = "剑士·阿尔法",
role_level = 1,
role_class = "Warrior"
};
MJGameSDK.Analytics.ReportRoleCreate(role, () => { }, err => Debug.LogError(err.Message));
MJGameSDK.Analytics.ReportRoleLogin(role, () => { }, err => Debug.LogError(err.Message));

4.4 商品列表 IAP.GetProducts (可选)

MJGameSDK.IAP.GetProducts(Action<MJGameProduct[]> onSuccess, Action<MJGameResult> onError);
参数含义
onSuccess商品数组(可能为空数组)
onError网络/鉴权失败等

MJGameProduct

字段含义
product_id商品 ID,购买时必须传这个;须与 游戏、平台配置需一致
name显示名
description描述
price_cents价格(分)
currency币种,如 USD / CNY

需登录。时机:打开商城 UI 时拉取。


4.5 购买 IAP.Purchase

MJGameSDK.IAP.Purchase(string productId, Action<MJGameOrder> onSuccess, Action<MJGameResult> onError);
MJGameSDK.IAP.Purchase(string productId, MJGamePurchaseContext context, Action<MJGameOrder> onSuccess, Action<MJGameResult> onError);
参数含义
productId平台商品 ID(同 MJGameProduct.product_id
context服/角色/自定义字段,原样进入发货回调
onSuccess仅当订单 status == "completed"(Steam 验单 + 游戏服发货成功)
onError拒付、超时、验单失败、发货未完成(3008)等

MJGamePurchaseContext

字段含义
server_id / server_name发货目标区服
role_id / role_name发货目标角色
extra游戏自定义,推荐传入游戏订单判定标识,最长 255,原样回传游戏发货接口

onSuccessMJGameOrder

字段含义
id平台订单表主键
order_id平台订单号(Steam orderid,幂等键)
product_id商品 ID
status成功回调时恒为 completed
amount_cents金额(分)
currency币种
created_at创建时间

内部步骤(游戏无需逐步调用)

  1. POST /iap/purchase/start → SDK 服务端 InitTxn
  2. 等待 Steam Overlay 授权(默认 120s)
  3. POST /iap/purchase/confirm → FinalizeTxn → 回调游戏服发货
  4. completed 触发 onSuccess

需登录。正式模式须 Steam 库启动且 Overlay 可用。

var ctx = new MJGamePurchaseContext
{
server_id = "1",
server_name = "华东一区",
role_id = "101",
role_name = "剑士·阿尔法",
extra = "game_order_id=xxx"
};
MJGameSDK.IAP.Purchase("1001", ctx, order =>
{
Debug.Log($"购买成功 order_id={order.order_id}");
// 道具以游戏服发货为准;此处可刷新商城 UI / 提示成功
}, err => Debug.LogError($"[{err.Code}] {err.Message}"));

4.6 订单列表 IAP.GetOrders

MJGameSDK.IAP.GetOrders(Action<MJGameOrder[]> onSuccess, Action<MJGameResult> onError);
参数含义
onSuccess当前用户在本 App 下 最近 15 天 的订单列表(可能为空数组),按创建时间倒序
onError未登录、网络错误等

onSuccess 中每个 MJGameOrder

字段含义
order_id平台订单号
product_id商品 ID
status见下表
amount_cents / currency金额与币种
created_at创建时间

status 含义

status含义客户端建议
pending已创单,待 Overlay 授权或验单可提示「支付中」;勿当已发货
steam_verifiedSteam 已扣款验单,发货中或待补单不要客户端再发货;等补单或查游戏服
completed游戏服发货成功Purchase 成功一致
cancelled已取消(拒付/超时取消等)展示已取消

用途与边界

适合不适合
商城「购买记录」展示作为发货依据(发货只认游戏服回调)
客服 / 联调核对订单状态替代 Purchase 成功回调做入账
客户端异常退出后,查看是否有 steam_verified / completed在客户端根据查单结果再调一次发货

需登录

MJGameSDK.IAP.GetOrders(orders =>
{
foreach (var o in orders)
Debug.Log($"{o.order_id} {o.product_id} {o.status}");
}, err => Debug.LogError($"[{err.Code}] {err.Message}"));

发货以游戏服务端收到的平台通知为准。 GetOrders 是查询辅助,不是发货 API。详见 §5


4.7 用户信息 User.GetProfile

MJGameSDK.User.GetProfile(Action<MJGameUserProfile> onSuccess, Action<MJGameResult> onError);

MJGameUserProfile

字段含义
id平台用户 ID
steam_idSteamID
nickname / avatar_url昵称 / 头像
privacy_accepted是否已记录同意隐私协议,暂时无作用
last_login_at最近登录时间
total_orders订单数量(平台统计)

需登录


4.9 静态入口一览

方法需登录说明
MJGameSDK.Initialize初始化
MJGameSDK.Shutdown重置(测试)
Auth.LoginWithSteamSteam 登录
Auth.Logout本地登出
Analytics.ReportGameActivate启动激活
Analytics.ReportLoadingUI / ReportLoginUIUI 阶段
Analytics.ReportLoadingGame / ReportEnterGame游戏阶段
Analytics.ReportRoleCreate / ReportRoleLogin创角 / 登角
IAP.GetProducts商品列表
IAP.Purchase完整购买
IAP.GetOrders订单历史(查询辅助)
User.GetProfile用户资料

4.10 通用类型

MJGameResult(失败 / 初始化回调)

字段含义
Code0 成功;其它见 §6
Message说明文案
IsSuccessCode == 0

5. 内购与发货约定

5.1 权威数据源

事项权威方
是否扣款成功Steam + SDK 服务端 Finalize
是否给玩家发道具游戏服务端(收到平台回调并幂等处理后)
客户端 Purchase success仅表示平台侧已 completed(验单+发货回调成功)
GetOrders平台订单状态快照,不能替代游戏服发货逻辑

客户端 不要Purchase 成功或 GetOrders 看到 completed 后再自己调游戏服发货;避免重复发货。游戏服必须以 order_id 幂等。

5.2 游戏发货接口(游戏服实现)

游戏按以下规则实现,实现后将URL发给SDK技术。 平台在 FinalizeTxn 成功后,向游戏提供的 URL POST JSON

请求体字段

字段说明
user_id平台用户 ID
steam_idSteam ID
product_id商品 ID
order_id平台订单号(幂等键
amount_cents金额(分)
currency币种
extra客户端 MJGamePurchaseContext.extra
role_id / server_id角色 / 区服
created_at订单创建时间(ISO8601)
req_timeUnix 秒;建议允许 ±5 分钟
sign见下

签名

sign = md5(user_id + "#" + steam_id + "#" + order_id + "#" + product_id + "#" + req_time + "#" + delivery_secret)

成功响应:HTTP 200 且 {"code":0,"message":"OK"}。平台仅认 code 整型 0

如果游戏返回不成功,平台会在1小时内进行多次补单;仍失败则 delivery_abandoned

游戏服原生 PHP 验签示例

<?php
header('Content-Type: application/json; charset=utf-8');
$deliverySecret = 'YOUR_DELIVERY_SECRET';
$payload = json_decode(file_get_contents('php://input'), true);
$userId = (string) ($payload['user_id'] ?? '');
$steamId = (string) ($payload['steam_id'] ?? '');
$orderId = (string) ($payload['order_id'] ?? '');
$productId = (string) ($payload['product_id'] ?? '');
$reqTime = (int) ($payload['req_time'] ?? 0);
$sign = (string) ($payload['sign'] ?? '');
if (abs(time() - $reqTime) > 300) {
echo json_encode(['code' => 4003, 'message' => 'req_time expired']);
exit;
}
$expected = md5($userId . '#' . $steamId . '#' . $orderId . '#' . $productId . '#' . $reqTime . '#' . $deliverySecret);
if ($expected !== $sign) {
echo json_encode(['code' => 4004, 'message' => 'Invalid sign']);
exit;
}
// TODO: 按 order_id 幂等发货
echo json_encode(['code' => 0, 'message' => 'OK']);

6. 错误处理

void OnErr(MJGameResult err)
{
Debug.LogError($"[{err.Code}] {err.Message}");
}
码段含义
0成功
1xxx客户端网络 / 解析
2xxxSteam / 签名相关
3xxx内购业务
5xxx上报参数
Code说明
1001网络错误
2000Steam 未初始化
2001SteamAPI.Init / 验票失败
2002Web API Ticket 获取失败
2004用户 Overlay 拒付
2005Overlay 授权超时(120s)
2010–2014请求签名相关(缺头、时钟、nonce、key 过期、验签失败)
3001商品不存在
3002验单失败
3003InitTxn / start 失败
3008游戏发货未完成(Steam 已验单,平台补单)
5001device_id
5002角色信息为空

10. 日志与联调

10.1 日志与联调

位置说明
Unity Console前缀 [MJGameSDK][Http][Steam][IAP]
本地文件{persistentDataPath}/mjgame_sdk.log

对接问题请提供:AppId、serverBaseUrl、复现步骤、客户端 mjgame_sdk.log、失败时的 X-Request-Id