快应用消息开放 API 接口文档(服务端)

    更新时间:

    公共

    推送超量说明

    服务消息:

    • 针对用户授权的一个模板消息,模板消息推送数量没有限制,只要用户允许授权即可推送,在消息有效期内只能发送一条(发送条数 1.1 版本会调整,针对个别行业开放授权一次推送多条。但不影响 1.0 版本的开发,只需后台配置即可)。
    • 单次可授权最多 3 条模板。

    订阅消息:

    • 针对用户授权的一个模板消息,模板消息的订阅数量没有上限,且 CP 可配置推送消息的人群,在用户订阅下即可推送,单个用户 24 小时内不同模板消息发送总数量一天最多 5 条。
    • 单次可授权最多 3 条模板。

    vivo 服务器地址

    tips:由于测试环境整个链路打通对CP来说比较繁琐,所以建议CP接入时直接对接线上环境

    V订阅测试环境域名:jovi-msgcenter-test.vivo.com.cn

    V订阅线上环境域名:jovi-msgcenter.vivo.com.cn

    公共传入参数

    HTTP Header 中( 推送鉴权接口除外 )。

    属性名字 类型 是否必填 Y/N 描述
    access-token String Y 当鉴权成功时会返回该字段,推送消息时需要提供 access-token,有效期默认为 30 天,过期后无法使用,请调用授权接口重新获取;

    全局公共返回码详解

    对接完成上线前请一定在群里面跟我们开发确认几个线上参数:client_id、client_secret、在 rpk 的 manifest.json 文件钟添加的参数 appId 和 appKey。

    result desc
    0 请求成功
    7 accessToken 校验错误
    10000 业务错误,具体请参考返回的 msg 信息
    10001 消息已经过期
    10010 资源已达上限,稍后重试
    10020 用户的该模板消息发送次数超过限制
    10030 推送速度过快,请稍后再试
    10040 未处理异常,请联系开发人员
    10050 推送内容审核流量过大,稍后重试
    10051 推送内容审核异常
    10052 推送内容含有敏感信息
    10060 重复订阅
    10070 模板信息校验错误,请核对
    10080 用户未订阅
    10090 模板关键词匹配错误,具体请参考返回的 msg 信息
    10100 发生熔断
    10110 发生限流
    10130 vivo 账号登录态校验失败
    10140 模板不存在
    20000 参数错误,具体请参考返回的 msg 信息

    接口定义

    鉴权

    获取 accesstoken 接口

    接口说明

    要想调用V订阅相关接口,任何接入方都要有个鉴权操作。其他接口 header 中必须携带该接口返回的参数 accessToken 。

    注意:需要对 url 参数进行转义处理

    限制:一天限制调用不超过 1000 次。

    注意:GET请求需要对url参数进行转义处理,POST不需要 传入的参数 client_secret 需要 client_secret = URLEncoder.encode(client_secret,"utf-8") 一次

    接口定义

    接口url

    /openapi/oauth/token
    协议
    HTTPS
    请求方法
    GET 或者 POST
    调用方向
    开发者服务器 -> vivo 开放平台
    接口备注
    POST 情况下 Content-Type 为:application/x-www-form-urlencoded
    请求头
    字段名
    字段类型
    是否必须
    字段描述
    输入参数
    字段名
    字段类型
    是否必须
    字段描述
    grant_type
    String
    Y
    固定值:client_credentials
    client_id
    String
    Y
    vivo 开放平台上创建一个服务系统自动分配的 serviceId
    client_secret
    String
    Y
    自动分配的 serviceId 对应的 serviceSecret
    响应头
    字段名
    字段类型
    是否必须
    字段描述
    输出参数

    (Json)

    字段名
    字段类型
    是否必须
    字段描述
    access_token
    String
    Y
    获取的 Access Token,预留足 250 个字符
    expires_in
    int
    Y
    Access Token 的有效期,以秒为单位
    请求示例
    Request:
    GET /openapi/oauth/token?
    grant_type=client_credentials&client_id=TEST_CLIENT_ID&client_secret=TEST_SECRET
    HTTP/1.1
    Host: jovi-msgcenter.vivo.com.cn
    Accept: */*
    Cache-Control: no-cache
    Connection: keep-alive
    cache-control: no-cache


    Response:
    HTTP/1.1 200 OK
    Cache-Control: no-store
    Pragma: no-cache
    Content-Type: application/json;charset=UTF-8
    Transfer-Encoding: chunked
    Proxy-Connection: Keep-alive
    {
    "access_token": "ACCESS_TOKEN",
    "expires_in": 2592000

    }

    模板消息推送

    关键字长度限制;

    参数类型 参数示例 类型
    事物 姓名、地址等 30 字以内(60 个字节) thing
    数字 32 位数字以内 number
    字母 32 个字母以内 character
    符号 5 位符号以内 symbol
    字符串 订单号、内容备注等(100 位以内) string
    时间 2020 年 12 月 11 日 16:35(40 位以内) time
    金额 CNY99.9/99.9 元(10 位数以内) name
    电话 +86-0766-66888866(17 位以内) phone
    车牌 粤 Z8Z888 挂(8 位以内) licenseplate
    状态 5 个汉字以内 status
    快应用消息文档 - 快应用消息开放 API 接口文档(服务端)

    一次性服务消息推送接口

    接口说明

    服务消息仅用于向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等。不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息。

    接口定义

    接口url

    /openapi/templete/service/send
    协议名称
    HTTPS
    请求方法
    POST
    消息方向
    开发者服务器 -> vovi 开放平台
    接口备注
    Content-Type 为:application/json;charset=UTF-8
    请求头
    字段名
    字段类型
    是否必须
    字段描述
    access-token
    String
    Y 通过鉴权接口获取到的 AccessToken
    输入参数
    字段名
    字段类型
    是否必须
    字段描述
    scene
    String
    Y
    场景标识,在订阅消息的时候 CP 传递的内容(64位以内)
    userId
    String
    Y
    CP 维护的用户的标识,和订阅时传递的 userId 对应(64位以内)
    clientId
    String
    Y
    快应用 Id
    templateId
    String
    Y
    模板 id,开发者在开放平台的服务消息中创建的模板 id
    skipType
    integer
    Y
    点击跳转类型 1:跳转快应用页面/APP页面
    skipUrl
    String
    Y
    跳转的 url,例如跳转快应用:hap://app/com.example.quickapp/page?key=value
    noticeDigest
    String
    N
    自定义通知内容(最多 60 字)
    data
    Object
    Y
    模板填充的数据,根据消息模板的关键词进行填充
    color
    String
    Y
    文字颜色,默认黑色
    响应头
    字段名
    字段类型
    是否必须
    字段描述
    输出参数

    (Json)

    字段名
    字段类型
    是否必须
    字段描述
    code
    int
    Y
    业务状态码,注意和 http 协议的 Response 状态码区分开来。具体取值含义参考附录
    请求示例

    Request:
    POST /openapi/templete/service/send
    HTTP/1.1access-token: ACCESS-TOKEN-VALUE
    Content-Type: application/json;charset=UTF-8
    Accept: /
    Cache-Control: no-cache
    Host: jovi-msgcenter.vivo.com.cn
    accept-encoding: gzip, deflate
    content-length: 43
    Connection: keep-alive
    {
    "scene": "123",
    "userId":"fsdf",
    "clientId":"12324",
    "templateId": "fsdfdfggdfgfgffgd",
    "skipType": 1,
    "skipUrl": "https://www.baidu.com",
    "data": {

    "string1": {
    "value": "巧克力",
    "color": "#123435"
    },
    "string2": {
    "value": "39.8 元",
    "color": "#123435"
    },
    "date1": {
    "value": "2020 年 12 月 25 日",
    "color": "#123435"
    }
    }
    }

    Response:
    HTTP/1.1 200 OK
    Content-Type: application/json;charset=UTF-8
    Transfer-Encoding: chunked
    Proxy-Connection: Keep-alive

    {"code":0}

    内容消息推送接口

    接口说明

    内容消息用于向用户发送已经订阅的频道通知;

    接口定义

    接口名称

    /openapi/templete/subscribe/send
    协议名称
    HTTPS
    请求方法
    POST
    消息方向
    开发者服务器 -> vovi 开放平台
    接口备注
    Content-Type 为:application/json;charset=UTF-8
    请求头
    字段名
    字段类型
    是否必须
    字段描述
    access-token
    String
    Y 通过鉴权接口获取到的 AccessToken
    输入参数
    字段名
    字段类型
    是否必须
    字段描述
    scene
    String
    Y
    场景标识,在订阅消息的时候 CP 传递的内容(64位以内)
    clientId
    String
    Y
    快应用 Id
    userId
    array
    Y
    userId 数组,CP 维护的用户的标识,和订阅时传递的 userId 对应,一次最多 500 个
    templateId
    String
    Y
    模板 id,开发者在开放平台的服务消息中创建的模板 id
    skipType
    integer
    Y
    点击跳转类型 1:跳转快应用页面/APP页面
    skipUrl
    String
    Y
    跳转的 url,例如跳转快应用:hap://app/com.example.quickapp/page?key=value
    noticeDigest
    String
    N
    自定义通知内容(最多 60 字)
    data
    Object
    Y
    模板填充的数据,根据消息模板的关键词进行填充
    color
    String
    Y
    文字颜色,默认黑色
    响应头
    字段名
    字段类型
    是否必须
    字段描述
    输出参数

    (Json)

    字段名
    字段类型
    是否必须
    字段描述
    code
    int
    Y
    业务状态码,注意和 http 协议的 Response 状态码区分开来。具体取值含义参考附录
    请求示例

    Request:
    POST /openapi/templete/subscribe/send HTTP/1.1
    access-token: ACCESS-TOKEN-VALUE
    Content-Type: application/json;charset=UTF-8
    Accept: /
    Cache-Control: no-cacheH
    ost: jovi-msgcenter.vivo.com.cn
    accept-encoding: gzip, deflate
    content-length: 43
    Connection: keep-alive
    {
    "scene": "123",
    "userId":
    ["fsdf","fsdffd"],
    "clientId":"123545",
    "templateId": "fsdfdfggdfgfgffgd",
    "skipType": 1,
    "skipUrl": "https://www.baidu.com",
    "data": {
    "string1": {
    "value": "巧克力",
    "color": "#123435"
    },
    "string2": {
    "value": "39.8 元",
    "color": "#123435"
    },
    "date1": {
    "value": "2020 年 12 月 25 日",
    "color": "#123435"
    }
    }
    }

    Response:
    HTTP/1.1 200 OK
    Content-Type: application/json;
    charset=UTF-8
    Transfer-Encoding: chunked
    Proxy-Connection: Keep-alive

    {"code":0}

    长期服务消息推送接口

    接口说明

    长期服务消息用于发送需要持续推送服务状态的订单通知,如机票值机、延误、起飞等。不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息。

    接口定义

    接口url

    /openapi/templete/longService/send
    协议名称
    HTTPS
    请求方法
    POST
    消息方向
    开发者服务器 -> vovi 开放平台
    接口备注
    Content-Type 为:application/json;charset=UTF-8
    请求头
    字段名
    字段类型
    是否必须
    字段描述
    access-token
    String
    Y 通过鉴权接口获取到的 AccessToken
    输入参数
    字段名
    字段类型
    是否必须
    字段描述
    scene
    String
    Y
    场景标识,在订阅消息的时候 CP 传递的内容(64位以内)
    userId
    String
    Y
    CP 维护的用户的标识,和订阅时传递的 userId 对应(64位以内)
    clientId
    String
    Y
    快应用 Id
    templateId
    String
    Y
    模板 id,开发者在开放平台的服务消息中创建的模板 id
    skipType
    integer
    Y
    点击跳转类型 1:跳转快应用页面/APP页面
    skipUrl
    String
    Y
    跳转的 url,例如跳转快应用:hap://app/com.example.quickapp/page?key=value
    noticeDigest
    String
    N
    自定义通知内容(最多 60 字)
    data
    Object
    Y
    模板填充的数据,根据消息模板的关键词进行填充
    color
    String
    Y
    文字颜色,默认黑色
    响应头
    字段名
    字段类型
    是否必须
    字段描述
    输出参数

    (Json)

    字段名
    字段类型
    是否必须
    字段描述
    code
    int
    Y
    业务状态码,注意和 http 协议的 Response 状态码区分开来。具体取值含义参考附录
    请求示例

    Request:
    POST /openapi/templete/longService/send HTTP/1.1
    access-token: ACCESS-TOKEN-VALUE
    Content-Type: application/json;
    charset=UTF-8
    Accept: */*Cache-Control: no-cache
    Host: jovi-msgcenter.vivo.com.cn
    accept-encoding: gzip, deflate
    content-length: 43
    Connection: keep-alive
    {
    "scene": "123",
    "userId":"fsdf",
    "clientId":"12324",
    "templateId": "fsdfdfggdfgfgffgd",
    "skipType": 1,
    "skipUrl": "https://www.baidu.com",
    "data": {
    "string1": {
    "value": "巧克力",
    "color": "#123435"
    },
    "string2": {
    "value": "39.8 元",
    "color": "#123435"
    },
    "date1": {
    "value": "2020 年 12 月 25 日",
    "color": "#123435"
    }
    }
    }

    Response:
    HTTP/1.1 200 OK
    Content-Type: application/json;
    charset=UTF-8
    Transfer-Encoding: chunked
    Proxy-Connection: Keep-alive

    {"code":0}

    事件回传配置

    订阅解订阅事件回传

    接口说明

    用户触发订阅或者解订阅,服务器处理业务完成之后,能够将处理结果回传给开发者;

    开发者需要在服务消息管理后台回传配置里配置

    服务器地址:vivo 回传数据的服务器地址,HTTPS 访问

    密钥:secretKey 数据签名算法需要的密钥;vivo 服务器会根据这个密钥对数据进行签名,开发者服务器根据密钥对签名进行校验;

    ps:建议长度为 256 位的 AES 密钥

    注意:这里会一次回传多条数据,但是只会使用第一条数据进行签名校验,也就是 CP 服务器收到回传之后也只需要使用第一条数据进行验签即可。

    加密算法

    相关代码

    dto 为 list 报文第一个元素 ,调用 getCpSign 方法即可

    public static String getCpSign(Map<String, Object> dto, Long timestamp, String scrit) {
    StringBuilder sb = new StringBuilder();
        sb.append(converDto(dto)).append("&").append(scrit);
        String str1 = sb.toString();
        String str2 = getSHA256Str(str1);
        String toSignStr = getStringToSign(timestamp.toString(), str2);
        return sha256_HMAC(toSignStr, scrit);
    }
    
    private static String converDto(Map<String, Object> dto){
    StringBuilder sb = new StringBuilder(dto.get("event").toString());
    Collection array = (Collection)dto.get("templateIds");
    for(Object tmpId:array){
        sb.append(tmpId);
    }
    
    return sb.append(dto.get("userId"))
        .append(dto.get("scene")).toString();
    }
    
    /***
     * 利用Apache的工具类实现SHA-256加密 并转小写
     * @param str 加密后的报文
     * @return
     */
    
    private static String getSHA256Str(String str) {
        MessageDigest messageDigest;
        String encdeStr = "";
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            byte[] hash = messageDigest.digest(str.getBytes("UTF-8"));
            encdeStr = Hex.encodeHexString(hash);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return encdeStr.toLowerCase();
    }
    
    private static String getStringToSign(String timestamp, String sHA256Str) {
        return new StringBuilder(timestamp).append(sHA256Str).toString();
    }
    
    /**
     * sha256_HMAC加密
     * @param message 消息
     * @param secret  秘钥
     * @return 加密后字符串
     */
    private static String sha256_HMAC(String message, String secret) {
        String hash = "";
        try {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] bytes = sha256_HMAC.doFinal(message.getBytes());
            hash = byteArrayToHexString(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return hash;
    }
    
    /**
     * 将加密后的字节数组转换成字符串
     * @param b 字节数组
     * @return 字符串
     */
    
    private static String byteArrayToHexString(byte[] b) {
        StringBuilder hs = new StringBuilder();
        String stmp;
        for (int n = 0; b != null && n < b.length; n++) {
            stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1) {
                hs.append('0');
            }
            hs.append(stmp);
        }
        return hs.toString().toLowerCase();
    

    测试数据

    dto:{"event": "sub","scene": "123","userId":"fsdf","templateIds": ["fsdfdfggdfgfgffgd"]}
    timestamp:1615449854093
    scrit:XrwuQQsIdn0CJ/QYW176BMtshpEaRrLvJB0R/mtmLNc=
    
    生成的
    sign:f7056be6b1c7d5792da5719bc7312a1d1e98d9efa61728c4f4eca0478d2d2a49
    
    按照提供的 dto,timestamp,scrit 生成的 sign 和上面的保持一致,说明签名正确。
    

    接口定义

    协议名称
    HTTPS
    请求方法
    POST
    消息方向
    开发者服务器 -> vovi 开放平台
    接口备注
    Content-Type 为:application/json;charset=UTF-8
    请求头
    字段名
    字段类型
    是否必须
    字段描述
    timestamp
    long
    Y 时间戳
    sign
    String
    Y 签名字符串
    输入参数
    字段名
    字段类型
    是否必须
    字段描述
    Array
    event
    String
    Y
    事件标识 sub:订阅事件 unSub:解订阅事件
    scene
    String
    Y
    场景标识,在订阅消息的时候 CP 传递的内容
    userId
    String
    Y
    CP 维护的用户的标识,和订阅时传递的 userId 对应
    templateIds
    Array
    Y
    模板 id,开发者在开放平台的服务消息中创建的模板 id
    响应头
    字段名
    字段类型
    是否必须
    字段描述
    输出参数

    (Json)

    字段名
    字段类型
    是否必须
    字段描述
    code
    int
    Y
    业务状态码,注意和 http 协议的 Response 状态码区分开来。具体取值含义参考附录
    请求示例

    Request:
    POST HTTP/1.1
    timestamp: 1611129775000
    sign:gfdgffdr4gtdgdfgdfh45
    Content-Type: application/json;
    charset=UTF-8
    Accept: /
    Cache-Control: no-cache
    Host: jovi-msgcenter.vivo.com.cn
    accept-encoding: gzip, deflate
    content-length: 43
    Connection: keep-alive
    [{
    "event": "sub",
    "scene": "123",
    "userId":"fsdf",
    "templateIds": ["fsdfdfggdfgfgffgd"]
    },
    {
    "event": "unSub",
    "scene": "1235",
    "userId":"fsdfdf",
    "templateIds": ["fsdfdfggdfgfsdfgffgd"]
    }]

    Response:
    HTTP/1.1 200 OK
    Content-Type: application/json;
    charset=UTF-8
    Transfer-Encoding: chunked
    Proxy-Connection: Keep-alive

    {"code":0}

    注意

    对接完成上线前请一定在群里面跟我们开发确认几个线上参数:client_id、client_secret、在 rpk 的 manifest.json 文件钟添加的参数 appId 和 appKey。

    Q&A

    Q:订阅失败?

    A:请确认下订阅参数同一个应用同一个模板 ID 下"userId"+"scene"参数唯一。


    Q:发送消息时返回{"code":10040,"msg":"系统异常"}?

    A:

    1. 如果是订阅消息请检查发送参数"userId"是否为数组格式,因为订阅消息可以一次发送给多个用户,所以设计格式为数组;
    2. 关键字参数为 key-value 格式:"data": { "thing1": { "value": "我", "color": "#123435" }, "thing2": { "value": "王快马", "color": "#123435" } }。

    Q:发送消息返回{"code":10090,"msg":"关键词匹配错误"}?

    A:请仔细对照在"快应用消息管理后台"配置的模板关键词的属性,例如:

    交易状态 { { status1 } }

    电话号码 { { number1 } }

    充值金额 { { amount1 } }

    充值类型 { { thing1 } }


    Q:发送消息返回{"code":10020,"msg":"超过调用频率限制"}?

    A:一次性服务消息只能发送一条消息,订阅消息一天之内同一个模板同一个用户只能发送 3 条消息。


    Q:订阅成功或者取消订阅之后没有收到事件回调?

    A:请检查在"快应用消息管理后台"配置的回调地址是否正确,除首次添加立即生效外,后续修改都会在 6 小时之内才能生效。


    Q:消息发送成功,消息中心也有消息,但是手机没有弹窗通知?

    A:请检查手机的快应用通知开关是否开启悬浮弹窗选项,快应用引擎版本需要大于等于 1091,智慧引擎版本需要大于等于 4.6.6.0,并确认 vivo 账户的登录状态是否已经失效,如失效需要重新登录。

    另外请检查在 rpk 的 manifest.json 文件钟添加的参数 appId 和 appKey 是否正确。