# （可选）开发消息回执

#### 场景介绍

消息回执是指Push Kit服务端将消息推送到用户终端之后，端侧会给Push服务端反馈送达结果，与此同时，Push服务端会将消息送达状态以回执消息形式发送给您的应用回执服务端，方便您获取消息下达端侧后的状态，定位问题等。

受网络环境以及消息量的影响，消息回执在Push服务端收到端侧响应后发送，会存在一些延迟现象，如果较长时间无法收到回执，可能是设备处于离线状态。

#### 回执服务开发

请参见消息回执API开发您的回执服务器代码，服务接口地址将作为配置回执参数中的回调地址。

#### 开通回执权益

1.  登录AppGallery Connect网站，选择“开发与服务”，在项目列表中选择对应的项目，左侧导航栏选择“项目设置”。
    
2.  在项目列表中找到您的项目，通过“增长 > 推送服务 > 配置”导航到“配置”页签。在该页面可以选择配置项目级回执或者应用级回执，需要注意的是项目级回执消息接收URL地址，对该项目下所有应用生效。如果您同时配置了项目级回执和应用级回执地址，则优先获取应用级回执地址信息。
    
3.  这里以应用级回执举例，选择需要配置回执的应用，点击“开通”应用回执状态。
    
4.  进入回执参数配置，可以选择已有回执或者新建回执。
    

#### 配置回执参数

点击“新建回执”后，需要配置如下参数：

1.  配置消息回执的名称和回调地址。
    
    回调地址配置完成后，Push Kit服务器会校验回执服务器（接收回执消息的应用服务器）提供的证书是否为商用CA签发证书。
    
    *   商用CA提示：
        
    *   自签CA提示：
        
    
    证书过期将导致您无法接收消息回执，请及时更换回执服务器证书。
    
2.  配置回调用户名（可选，下文描述为userName）和回调密钥（可选，下文描述为secret）进行身份验证。回调密钥支持自动生成。
    
    1.  从回执消息的请求Header中获取X-HUAWEI-CALLBACK-ID，举例如下：
        
        ```
        X-HUAWEI-CALLBACK-ID:
        timestamp=1563*****1261;nonce=a07bfa17-6d82-4b53-a9a2-07c*****eef1;value=E4YeO*********************QXF+c=
        ```
        
        其中timestamp为回执消息的时间戳（毫秒级时间戳），nonce为UUID随机数，value为签名信息，签名方法为：Base64(HMAC-SHA256(secret, timestamp+nonce+userName))。
        
    2.  开发者可以根据timestamp、nonce、userName、secret参考示例生成签名，与value的值比较进行签名验证。开发者也可点击网页提供的“生成密钥”自动生成回调密钥。
        
        生成签名示例（以下为java代码）：
        
        ```
        StringBuilder buf = new StringBuilder();
        buf.append(timestamp);
        buf.append(nonce);
        // 在回执配置中的回调用户名
        buf.append(userName);
        // 在回执配置中的回调密钥
        String secret = "your secret";
        String signature = "";
        try {
          Mac mac = Mac.getInstance("HmacSHA256");
          // 老旧版本的回执配置密钥使用secret.getBytes(UTF_8)，新的回执配置密钥使用base64编码
          SecretKeySpec key = new SecretKeySpec(Base64.getDecoder().decode(secret), "HmacSHA256");
          mac.init(key);
          byte[] encodeV = mac.doFinal(buf.toString().getBytes(UTF_8));
          signature = Base64.getEncoder().encodeToString(encodeV);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
          // 打印错误日志
          // ...
        }
        ```
        
3.  配置回执支持版本
    
    V1回执不支持场景化消息发送，请使用V2回执。
    
4.  “测试回执”可以对回执地址进行功能测试，点击“提交”完成回执的创建。
    
    *   华为Push服务器和接收回执消息的应用服务器之间使用HTTPS协议，华为Push服务器会校验应用服务器提供证书的合法性，请使用商用CA签发的HTTPS证书。
        
    *   如果您的回执配置正确，点击“测试回执”后，您的回执服务器将收到由华为Push服务器发送的测试消息，同一回执版本该消息内容固定，仅供测试使用。
        
    
    **示例报文：**
    
    消息到达回执：
    
    除卡片刷新场景外的其他场景化消息均支持消息到达回执。
    
    ```
     {
      "statuses": [
        {
          "biTag": "bi**62",
          "pushType": 0,
          "token": "MsDZmCSyuS+Gd***********ZLs8Es",
          "requestId": "169802**1701",
          "appPackageName": "com.**.**",
          "deliveryStatus": {
            "result": 0,
            "timestamp": 1697741455082
          }
        }
      ]
    }
    ```
    
    卡片刷新回执：
    
    ```
    {
      "statuses": [
        {
          "biTag": "bi**62",
          "pushType": 1,
          "token": "MsDZmCSyuS+Gd***********ZLs8Es",
          "requestId": "169802**1701",
          "appPackageName": "com.**.**",
          "formStatus": {
            "formId": 10086,
            "result": 0,
            "timestamp": 1698027152082
          }
        }
      ]
    }
    ```
    
    您的回执服务器必须返回成功的响应，才能测试通过，再点击“提交”完成回执的创建。
    
    成功响应：
    
    ```
    {
         "code": "0",
         "message": "success"
     }
    ```
    

#### 回执状态码

通过回执状态码定位问题之前，请优先检查消息推送接口URL（]**/messages:send）是否正确：

*   请使用v3版本的推送接口URL，不要使用v1或v2版本的推送接口URL，详情请参见场景化消息中的请求URL版本问题。
    
*   请检查推送接口地址中的projectId，确保与您当前应用所属的项目保持一致，若不一致请更新推送接口URL中的projectId，并重新生成鉴权令牌，应用重新获取Push Token，再进行消息推送。
    

您可以基于接收到的消息回执码进行数据统计和分析，回执状态码如下表所示：

| 回执状态码 | 状态码描述 | 原因及处理 |
| --- | --- | --- |
| 0 | 成功送达 | 不涉及。 |
| 2 | Token无效，应用卸载 | 成功发送到设备后发现应用不存在，通常表示应用已卸载。 |
| 5 | Token无效，Token不匹配 | 终端收到应用的Push消息，但Push消息带的Token与本地应用的Token不一致。请排查以下几种原因： · 终端用户清除了本地应用数据。 · 终端用户通过卸载再安装的方式更新了本地应用。 · 您在应用中调用pushService.deleteToken()方法删除了本地应用的Token。 · 您在应用中调用了deleteAAID()，该方法删除AAID的同时也会删除本地应用的Token。 · 终端设备恢复出厂设置后终端用户重新进入预安装应用或者重新安装并进入应用。 终端收到应用的Push消息，但检查本地数据存储中并没有应用的Token。请排查以下原因： · 应用未激活。 若您的问题仍无法解决，请通过在线提单提交问题。 |
| 6 | 通知消息不展示 | 请排查以下三种原因： · 用户关闭了设备上的系统通知总开关。 · 用户关闭了本应用的通知渠道开关。 · 用户开启了未成年模式。 |
| 10 | 非活跃设备 | 设备为非活跃设备（终端设备未接入网络达30天），消息不进行下发。 |
| 14 | 其它错误 | 系统内部网络异常。 |
| 15 | 离线用户消息管控 | 1. 设置了离线用户消息覆盖（服务端API中的collapseKey）功能，消息被覆盖掉了，未下发到设备。 2. 离线消息最多缓存120条，超过后旧消息被新消息覆盖。 |
| 22 | 目标用户不匹配 | 下发消息时Push Token归属的用户与当前终端设备上的本地用户不匹配。请排查Push Token是否是当前本地用户下申请的。 说明： 如您在进入隐私空间前的本地用户为用户A，点击进入隐私空间后，系统会将本地用户切换为用户B，如果此刻您使用归属用户A下的Push Token推送消息，则会返回该回执状态码。 |
| 27 | 应用进程不在，后台消息被缓存 | 在终端设备上目标应用进程不存在导致后台消息被缓存。 |
| 31 | 目标应用中不存在指向的页面 | 请参见点击消息动作检查应用skills标签配置和消息请求体中的clickAction字段。 |
| 51 | 终端设备处于开机未解锁状态 | 用户重启终端设备后，点亮屏幕未解锁。 |
| 102 | 消息频控丢弃 | 系统会根据使用场景和流量进行管控，不合理的使用场景系统会进行频控。 |
| 144 | profileId不存在 | 发送下行消息时请检查场景化消息payload中的profileId字段。 |
| 151 | 推送消息未展示或卡片formId不存在 | 1. 请检查卡片是否已经添加到终端设备桌面。 2. 请检查卡片刷新消息中指定的formId是否存在。 |
| 155 | 实况窗通知更新失败 | 请检查您的通知是否未创建或已经过期。 |
| 156 | 实况窗通知消息乱序更新 | 实况窗通知消息携带version小于当前版本，更新失败。 |
| 158 | 创建的实况窗已存在 | 在设备中已存在通过Live View Kit创建的activityId相同的实况窗。 |
| 188 | 拉起应用失败 | 请稍后重试。 |
| 191 | 角标更新失败 | 系统内部设置角标异常。 |
| 256 | 消息频次限制 | 频次限制请参考消息发送频次限制。 |
| 259 | 消息被拒绝 | 1. 发送消息中存在违规内容。 2. 存在不安全的url。 请更换消息体内容或url。 |
| 261 | 卡片不存在 | 卡片刷新消息被Push服务端管控不下发（管控周期30天），建议做过滤处理减少无效推送。原因：不存在卡片刷新消息中指定formId的卡片。 建议填写已分配过的formId，避免后续formId分配后，对应的卡片刷新消息被管控导致管控周期内无法下发。 |
| 262 | 卡片刷新次数达上限 | 卡片刷新消息被Push服务端管控不下发（管控周期1天），建议做过滤处理减少无效推送。原因：终端设备上对应的卡片刷新次数达到上限（未被Push服务端频次限制）。 |
| 264 | 消息未订阅 | 发送的订阅消息，但实际未订阅。 |
| 265 | 实况窗通知更新被管控 | 发送的activityId对应的实况窗通知不存在，限制发送该activityId的实况窗通知消息24小时。 |
| 268 | 未携带status字段 | 通过Push Kit创建的实况窗，在对该实况窗更新时未携带status字段。 |
| 269 | 重复创建实况窗 | 通过Push Kit创建的实况窗重复（activityId相同）。 |

您需要对上述状态中的2、5、6、10做过滤处理，减少对这些用户的无效推送。