快应用消息开放 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 |
一次性服务消息推送接口
接口说明
服务消息仅用于向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等。不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息。
接口定义
接口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: |
内容消息推送接口
接口说明
内容消息用于向用户发送已经订阅的频道通知;
接口定义
接口名称 |
/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: |
长期服务消息推送接口
接口说明
长期服务消息用于发送需要持续推送服务状态的订单通知,如机票值机、延误、起飞等。不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息。
接口定义
接口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: |
事件回传配置
订阅解订阅事件回传
接口说明
用户触发订阅或者解订阅,服务器处理业务完成之后,能够将处理结果回传给开发者;
开发者需要在服务消息管理后台回传配置里配置
服务器地址: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();