# 4399 运营 SDK Harmony Next 客户端接入

本文档面向**鸿蒙 Next 系统（HarmonyOS 5.0.0+）游戏开发**，描述游戏怎样接入“4399游戏平台运营 SDK 鸿蒙 Next 版”。


## 1 隐私政策
如果您是开发者，在为用户提供服务前请阅读 [《4399隐私保护政策》](https://ptlogin.4399.com/resource/protocol.html?type=2&aids=44,10)与[《4399鸿蒙联运SDK个人信息收集清单》](https://ptlogin.4399.com/resource/protocol.html?type=2&aids=43)  
了解 SDK 对个人信息收集范围、处理目的以及权限使用情况。请您向用户提供服务时，告知用户并取得同意。

## 2 关于SDK
4399游戏平台运营 SDK 鸿蒙 Next 版（以下简称 SDK），为接入的游戏提供华为账号一键登录/4399 平台账户/手机号登录、防沉迷、游戏内购等功能。   
SDK 在内部集成了鸿蒙系统提供的，联合登录、防沉迷、角色上报、应用内支付等游戏服务。

### 2.1 SDK内容
SDK 分为客户端与服务端两部分：
- **客户端 har 与接入文档**：`har`包与“4399 运营 SDK Harmony Next 客户端接入”（即本文档）
- **客户端接入示例**：[operate-sample-1.0.0+36.zip](https://sdkftp.4399doc.com/external/harmony/1.0.0/operate-sample-1.0.0+36.zip)
- **服务端接入文档**：参考[4399 运营 SDK Harmony Next 服务端接入](https://sdkftp.4399doc.com/external/harmony/1.0.0/server_guide.html)

### 2.2 运行环境

游戏项目应符合[Stage模型](https://developer.huawei.com/consumer/cn/arkui/arkui-stage)定义的规范，SDK 的开发和运行也是如此。SDK 的最低兼容版本为 `15`，编译目标版本为 `20`，即

```json
{
  "targetSdkVersion": "6.0.0(20)",
  "compatibleSdkVersion": "5.0.3(15)"
}
```

## 3 接入准备
在正式接入 SDK 前，游戏要在运营的协助下，完成华为开发者后台和 4399 开发者后台的信息填写  
完成后，游戏会得到两个参数：  
- **游戏 id**：`game key`，游戏在 4399 平台的唯一标识，初始化的必须参数
- **通信密钥**：游戏服务端与 SDK 服务端加密通信的必须参数

## 4 引入依赖
假定游戏已经创建了鸿蒙 Next 项目，且游戏主模块是 `entry`，且其静态依赖存放目录为 `libs`

- 下载 SDK 的 `har` 包，即静态共享依赖：[operate-1.0.0+36.har](https://sdkftp.4399doc.com/external/harmony/1.0.0/operate-1.0.0+36.har)  
- 将 `har` 文件拷贝到 `entry/libs` 目录下
- 在 `entry/oh-package.json5` 下添加依赖

```json
{
  "name": "entry",
  "dependencies": {
    "operate": "file:./libs/operate-1.0.0+36.har"
  }
}
```

- 调整项目**根目录**下的编译配置 `build-profile.json5` 中的 `"useNormalizedOHMUrl": true`

```json
{
  "products": [
    {
      "name": "default",
      "signingConfig": "default",
      "targetSdkVersion": "6.0.0(20)",
      "compatibleSdkVersion": "5.0.1(13)",
      "runtimeOS": "HarmonyOS",
      "buildOption": {
        "strictMode": {
          "useNormalizedOHMUrl": true
        }
      }
    }
  ]
}
```

- **若是更新 `har`，还需要删除`entry/oh_modules`，重新生成项目**

## 5 接口调用
- 初始化（必接）
- 账号相关
    - 登录（必接）
    - 注册登出回调（必接）
    - 登出
	- 检查登录状态
    - 获取用户信息
    - 上报角色信息（非必接）
- 游戏内购（必接）

### 5.1 初始化（必接）
- **功能简介**： 此接口初始化 SDK 内部状态，是调用其他接口的前提
- **调用时机**： 建议游戏在 `windowStage.loadContent` 回调中进行

```typescript
onWindowStageCreate(windowStage: window.WindowStage): void {
  windowStage.loadContent('pages/Index', (err) => {
    let mainWindow = windowStage.getMainWindowSync();
    let uiContext = mainWindow.getUIContext();

    // 游戏全屏，不显示系统状态栏等
    mainWindow.setWindowSystemBarEnable([]).catch()
    // 游戏方向，GAME_ORIENTATION 是 window.Orientation 中定义的值
    mainWindow.setPreferredOrientation(GAME_ORIENTATION).catch();
    // 初始化 SDK
    // GAME_KEY： 4399 平台的游戏 id，在运营协助完成游戏注册后，提供给游戏
    OperateCenter.init(uiContext, GAME_KEY).then((r: OperateResult<void>) => {
      // 初始化回调
      // OperateResult 字段特别是其中的 code，可查看“客户端接入文档#API 详情”
      // r.success ? "init success" : r.message
    });
  });
}
```

**接口参数**  
主要的参数为游戏 `game key` 或游戏 id

| 参数名       | 类型        |含义          | 说明                    |
| :----------  | :---        |:----         |:----------------------  |
| `uiContext`  | `UIContext` | 页面上下文   | `UIContext` 对象，在`UIAbility` 或页面组件中获取        |  
| `gameKey`    | `number`    | 游戏 id      | 游戏在 4399 平台的唯一标识                              | 

**接口返回**  
SDK 的异步接口都是 `Promise` 化的，接口返回 `OperateResult<T>`，`T` 为泛型字段，具体类型与接口有关  
`OperateResult`中的标准字段 `success, code, message` 举例如下，详细情况参考下文”[API 详情](#6-api-详情)“ 部分

| code        | 含义                               |
|-------------|--------------------------------    |
| `0`         | 成功，通用code                     |
| `1`         | 取消，通用code                     |
| `2`         | 处理中，通用code                   |
| `3`         | 失败，通用code                     |
| `4`         | 超时，通用code                     |
| `5`         | 中止，通用code                     |
| `6`         | 未初始化，通用code                 |
| `7`         | SDK 内部状态异常，通用code         |
| `8`         | 配置不全，通用code                 |
| `9`         | 未知错误，通用code                 |

### 5.2 账号

#### 5.2.1 登录（必接）
- **功能简介**： 正式进入游戏场景前，一般是在游戏启动页面上调用
- **调用时机**： 初始化且回调后

```typescript
// 用户登录
// OperateResult 中的 ‘code, success, message’ 按统一标准定义，具体查看“客户端接入文档#API 详情”
// 若登录成功，将同时返回 User 对象
OperateCenter.login().then((r: OperateResult<User>) => {
  if (r.success) {
    // this.user = r.data;
  }
})
```

**接口返回**，参考下文”[API 详情](#6-api-详情)“ 
</br>`OperateResult`具体 `code` 可查看下文”[API 详情](#6-api-详情)“；登录成功通过 `User` 对象返回用户信息，具体字段描述如下

| 参数名  | 类型     |含义         | 说明                    |
| :-------| :---     |:----        |:----------------------  |
| `uid`   | `string` |官方账号标识 | 数字字符串，唯一，一般就是 4399 账号 |  
| `name`  | `string` |官方账号名称 | 字符串，一般就是 4399 账号     |  
| `state` | `string` |账号登录状态 | 登录状态，用于用户登录状态校验 |  


此时若游戏还需要对登录状态进一步校验，可以使用服务端相关接口，参考：[登录状态接口校验](https://sdkftp.4399doc.com/external/harmony/1.0.0/server_guide.html#登录状态校验)

#### 5.2.2 注册登出回调（必接）
- **功能简介**： 用于监听 SDK 内，由于防沉迷、账号踢出等引起的账号变化
- **调用时机**： 初始化后即可，尽早设置

```typescript
// 注册全局登出回调
// 
// 华为游戏服务通知、防沉迷、账号互顶等都会引发 SDK 内部的登出
// SDK 在此回调接口通知游戏，游戏应更新角色状态，回到游戏登录界面
OperateCenter.addOnLogoutCallback(() => {
  // 游戏应回到自己的登录或者选服页面
});
```

#### 5.2.3 登出
- **功能简介**： 登出账号，SDK 设置服务端无效，清除客户端用户状态
- **调用时机**： 登录后，需要切换账号时  

```typescript
// 用户登出
// OperateResult 中的 ‘code, success, message’ 按统一标准定义，具体查看“客户端接入文档#API 详情”
OperateCenter.logout().then((r: OperateResult<void>) => {
  // 注意：登出接口，有接口本身的回调，不论是否登出成功都返回结果；特别地，登出成功时，还会在全局登出回调中通知，
  //       游戏的登出逻辑，要注意下是否会重复，建议登出接口只提示失败
})
```

#### 5.2.4 检查登录状态
- **功能简介**：检查 SDK 内部是否已有登录状态，一般用在初始化结束后或登录前判断是否已有登录状态
- **调用时机**：初始化后

```typescript
let isLogin = OperateCenter.isLogin()
```

#### 5.2.5 获取用户信息
- **功能简介**：在 SDK 处于登录状态时，可通过该接口获取当前用户的信息
- **调用时机**：初始化后，但应先“检查登录状态”

```typescript
let user: User = OperateCenter.getUser();
```

#### 5.2.6 上报角色信息（非必接）
- **功能简介**： 登录后，上报游戏角色信息到华为游戏平台，如角色名称、区服名称等
- **调用时机**： 游戏角色进入游戏时

```typescript
// 上报角色信息
// OperateResult 中的 ‘code, success, message’ 按统一标准定义，具体查看“客户端接入文档#API 详情”
OperateCenter.reportRole(role).then((r: OperateResult<void>) => {
  
})
```
**接口参数**  
上报需要传入 `Role` 对象，具体字段含义如下，允许部分字段为空，但**不可全部为空**  

| 参数名              | 类型     | 说明                    |
| :-----------------  | :---     |:----------------------  |
| `roleId`            | `string` | 游戏角色ID              |
| `roleName`          | `string` | 角色名称                |
| `serverId`          | `string` | 角色所属区服ID          |
| `serverName`        | `string` | 角色所属区服名称        |

**接口返回**，参考”[API 详情](#6-api-详情)“

### 5.3 游戏内购（必接）
游戏首先要注意，**SDK 当前只支持[消耗型商品](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/iap-purchases)的购买**

在调用客户端接口前，需确认以下流程完成，否则无法测试：

- 定义充值回调地址/退款通知接口，具体实现可稍后完成  
- 完成商品列表、充值回调、退款通知等信息的注册，可在运营协助下进行  
- 实现充值回调接口，接口要遵循：[充值回调接口服务端协议](https://sdkftp.4399doc.com/external/harmony/1.0.0/server_guide.html#充值回调接口协议由游戏厂商提供)  
- 实现退款通知接口，接口遵循：[退款通知接口服务端协议](https://sdkftp.4399doc.com/external/harmony/1.0.0/server_guide.html#退款通知接口协议由游戏厂商提供)
- 完成游戏服务端-客户端订单创建流程
- 完成游戏服务端通知其客户端物品发放流程
- 完成游戏内购退款处理流程


完成上述流程后，调用客户端接口就可以进行发起充值到商品到账的完整测试  
关于充值测试，更多内容参考”[常见问题-怎样进行充值测试](#731-怎样进行充值测试)“

- **功能简介**：游戏调用此接口完成下单、支付、订单查询流程
- **调用时机**：账号登录后，游戏内需要支付的场景

```typescript
// mark 为游戏的订单标识，游戏应通过自己的服务端生成唯一订单标识，此处是模拟生成
// let mark = genFromServerApi()

// 充值金额，如果一次购买多份，游戏要自行计算总价，此处用”单价 x 份数“方式计算
// 示例中通过华为应用内购接口，根据商品 id 查询商品信息，包括单价、币种等，再根据份数计算充值金额
// let money = product.microPrice * quantity / 1000000

// 游戏内购，需要订单信息 Order 对象
OperateCenter.iap({
  productId: product.id, // 商品 id，运营在华为开发者后台提交商品列表后获得
  mark,                  // 游戏订单标识，非空且唯一，支持字母、数字和字符 '-'，'_'，'|', 长度不可超过48个字符
  quantity,              // 购买的消耗性商品的份数
  money,                 // 总价，单位元，并非单价，游戏要自行用单价、份数等计算
  extras: {              // 透传参数，键值对
    "k1": "v1",
    "k2": 1,
    "k3": false
  } as Record<string, ValueType>
} as Order)
  // 充值回调
  // OperateResult 中的 ‘code, success, message’ 按统一标准定义，具体查看“客户端接入文档#API 详情”
  // 若下单成功，会在 Order 对象附上 4399 充值订单号，一起返回给游戏
  .then((r: OperateResult<Order>) => {
    if (r.success) {
      // 充值成功
    } else {
      // 充值失败    
    }
  })
```

**接口参数**  

主要的参数为 `Order` 对象，此对象为游戏订单  

| 参数名      | 类型     |含义             | 说明                                       |
|:------------| :---     |:----            |:-----------------------------------------|
| `productId` | `string` | 华为商品 id     | 商品在华为后台的唯一标识，运营配置后获得                     |  
| `money`     | `number` | 充值金额        | 非负数值，单位‘元’                               |  
| `mark`      | `string` | 游戏订单        | 非空且唯一，游戏对一笔订单的标识<br>支持字母、数字和字符 '-'，'_'，' |' |
| `quantity`  | `number` | 商品份数        | 正整数，消耗型商品的份数                             |  
| `extras`    | `Record` | 透传参数        | 键值对，用于透传游戏附加在订单中的参数                      |  
| `orderId`   | `string` | 4399 充值订单号 | **4399 平台下单成功后，附加在接口返回的 Order 对象中**      |  

**接口返回**，参考下文”[API 详情](#6-api-详情)“

## 6 API 详情
SDK 接口中的 `OperateResult` 对象中的 `code` 的所有情况：

| code | 含义                           |
|------|-------------------             |
| `0`  | 成功，通用code                 |
| `1`  | 取消，通用code                 |
| `2`  | 处理中，通用code               |
| `3`  | 失败，通用code                 |
| `4`  | 超时，通用code                 |
| `5`  | 中止，通用code                 |
| `6`  | 未初始化，通用code             |
| `7`  | SDK 内部状态异常，通用code     |
| `8`  | 配置不全，通用code             |
| `9`  | 未知错误，通用code             |
| `21` | 登录配置获取失败               |
| `22` | 华为联合登录失败               |
| `23` | 华为联合登录结果异常           |
| `24` | 获取官方登录地址失败           |
| `25` | 官方登录后状态异常             |
| `26` | 重复登录                       |
| `31` | 登出失败                       |
| `41` | 当前环境不支持应用内购         |
| `42` | 根据商品 id 找不到对应商品信息 |
| `43` | 根据商品 id 查找信息失败       |
| `44` | 创建/处理华为订单失败          |
| `45` | 获取华为订单状态失败           |

## 7 常见问题

### 7.1 接入问题

#### 7.1.1 SDK 有在线依赖吗
**A**：暂时没有（2025-10-10）

---

#### 7.1.2 接入示例如何运行
**A**：鸿蒙平台应用无法提前配置签名，而签名、应用 id等是关联的，所以要游戏自行配置或替换为真实游戏参数  
具体来说，要替换如下部分:

- 包名： `AppScope/app.json5` 中的 `bundleName`
- 签名：在 ”DevEco Studio - File - Project Structure - Project, Signing Configs“ 中按指引操作
- 应用 id 与 Client id：`entry/module.json5` 中的 `app_id` 与 `client_id`
- 游戏 id: `DemoHelper`中 `GAME_KEY`
- 商品 id：`IapHelper`中的 `iap.QueryProductsParameter` 查询参数中指定商品类型与商品 id

### 7.2 登录与防沉迷问题

#### 7.2.1 华为账号登录与 4399 平台账号登录什么关系
**A**：二者集成在[联合登录](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/gameservice-gameplayer-huawei)。若用户使用华为账号登录，会在后台关联一个 4399 uid，开发者调用 SDK 登录接口后获取的总是 4399 平台 uid

---

#### 7.2.2 游戏需要处理防沉迷吗
**A**：不需要。SDK 集成了华为官方的防沉迷与 4399 平台的防沉迷，在必要的地方进行双重认证，游戏不需要额外处理

### 7.3 充值问题

#### 7.3.1 怎样进行充值测试
**A**：先在沙盒环境，再在正式环境测试。不管哪种测试方式，都要在运营的帮助下进行。

充值测试注意事项

- 已经完成商品列表、充值回调、充值通知等信息的注册
- 已经完成游戏充值准备、客户端接口调用
- 沙盒测试需要注册华为测试账号

---

#### 7.3.2 如何触发退款流程
**A**：满足下面条件
- 未成年账号

---

#### 7.3.3 游戏如何处理退款流程
**A**：可在运营协助下完成