动态素材更新
简单来说:将需要更新的内容表单,和需要的入参,推送到小程序开放平台的指定接口。我们会在负一屏请求开放平台的时候,提供相应的更新内容。
一、环境选择
环境选择 | 访问前缀 | 备注 |
---|---|---|
生产环境 | https://api.dlight-app.com/miniapp/launchplatformdeveloper/openapi | |
印度环境 | https://api-ind.dlight-app.com/miniapp/launchplatformdeveloper/openapi | 如果需要推送给印度服务,需要调用该域名 |
二、接入方案
1. 接入前准备
第三方线下申请接入小程序卡片后,小程序会注册卡片更新表单,然后第三方获取卡片对应的更新表单 id(bizId),密钥,以及可以更新的 keys
第三方可以选择同步或者异步获取返回信息,
同步:直接调用写入接口等到返回
异步:第三方提供回调接口,详情见 3.6 章
2. OpenApi 接口
2.1 写入接口
请求 URL:/cardform/cmd/sendCardForm
请求方式:post
请求 Header 参数
参数名称 | 参数类型 | 是否必要 | 参数描述 |
---|---|---|---|
Accept-Timezone | String | 是 | 时区 |
OpenApi 字段
参数 | 数据类型 | 说明 | 必须参数 | 来源 | 示例 |
---|---|---|---|---|---|
data | CardFormInfoRequestDTO | 素材更新参数 | true | 开发者 | 见 CardFormInfoRequestDTO 参数 |
bizId | String | 卡片表单 id | true | 咨询小程序产运团队 | 1000675095676309504 |
miniappId | String | 卡片对应的小程序 id | true | 咨询小程序产运团队 | 3000317415459377152 |
operatorId | String | 操作人 id | false | 咨询小程序产运团队 | qewrqewrq-qweqwrqw |
sign | String | 签名字段 | true | 开发者 | FA8762C9BB9BE1B162D0386F1AB3 |
timeStamp | String | 时间戳 | false | 开发者 | 1690180572312 |
requestId | String | 请求 id | true | 开发者 | qqrea |
CardFormInfoRequestDTO 参数
参数 | 数据类型 | 说明 | 必须参数 | 来源 | 示例 |
---|---|---|---|---|---|
text | String(json) | 表单信息(注:如果是 deeplink 参数,需要按照 clickUrl 规则 强校验,否则不予通过) | true | 开发者 | {\"title\":\"logo\", \"name\":\name\"} |
cardFormReceiverInfo | CardFormReceiverInfoDTO | 表单信息得接收人 | true | 开发者 | 见 CardFormReceiverInfoDTO 参数 |
CardFormReceiverInfoDTO 参数
参数 | 数据类型 | 说明 | 必须参数 | 来源 | 示例 |
---|---|---|---|---|---|
uniqueIds | List<String> | uniqueId 数组,(一次性最多传 50 个),且该字段不为空时,不处理标签信息 | false | 获取方式dlt.getUniqueId | ["003c92f3245bddbe736eefd66a7b 55c3b9cb04f446c4d3a13b1ec0dc4ae99"] |
keyword | String | 关键字:如果卡片时全搜场景,不能为空 | false | 咨询小程序产运团队 | phone |
labels | List<LabelDTO> | 接收人得标签数组 | false | 咨询小程序产运团队 | 见 LabelDTO 参数 |
LabelDTO 参数
参数 | 数据类型 | 说明 | 必须参数 | 来源 | 示例 |
---|---|---|---|---|---|
labelName | String | 标签名称 | false | 咨询小程序产运团队 | country |
labelValue | String | 标签值 | false | 咨询小程序产运团队 | 406 |
2.2 查询接口
2.2.1 与客户端接口区别
涉及标签时
客户端:若素材的某个标签为空,客户端该标签传入任何值都匹配
该接口:查询传入的标签值必须与素材的标签值完全匹配
请求 URL:/cardform/cmd/getCardForm
请求方式:post
请求 Header 参数:
参数名称 | 参数类型 | 是否必要 | 参数描述 |
---|---|---|---|
Accept-Timezone | String | 是 | 时区 |
2.3 OpenApi 字段
参数 | 数据类型 | 说明 | 必须参数 | 来源 | 示例 |
---|---|---|---|---|---|
data | GetCardFormReceiverInfoDTO | 素材查询参数 | false | 开发者 | 见 GetCardFormReceiverInfoDTO 参数 |
bizId | String | 卡片表单 id | true | 咨询小程序产运团队 | 1000675095676309504 |
miniappId | String | 卡片对应的小程序 id | true | 咨询小程序产运团队 | 3000317415459377152 |
operatorId | String | 操作人 id,第三方自行定义 | false | 开发者 | 11 |
sign | String | 签名字段 | true | 开发者 | FA8762C9BB9BE1B162D0386F1AB3BFE D72A6255E985379B017D68DB4080D5 |
timeStamp | String | 时间戳 | false | 开发者 | 1690180572312 |
requestId | String | 请求 id | true | 开发者 | qqrea |
GetCardFormReceiverInfoDTO 参数
参数 | 数据类型 | 说明 | 必须参数 | 来源 | 示例 |
---|---|---|---|---|---|
uniqueId | String | uniqueId | false | 开发者更新时传入 | 003c92f3245bddbe736eefd66a7b 55c3b9cb04f446c4d3a13b1ec0dc4ae99 |
keyword | String | 关键字 | false | 开发者更新时传入 | Phone |
labels | LabelDTO | 标签 | false | 开发者更新时传入 | 见 LabelDTO 参数 |
LabelDTO 参数
参数 | 数据类型 | 说明 | 必须参数 | 来源 | 示例 |
---|---|---|---|---|---|
labelName | String | 标签名称 | false | 咨询小程序产运团队 | country |
labelValue | String | 标签值 | false | 咨询小程序产运团队 | 406 |
3. OpenApi 签名
3.1 报文签名说明
使用 RSA 来验证数据传输过程中的数据真实性和完整性,需要对数据进行数字签名,在接收签名数据之后进行签名校验。 要求对整段报文做签名值,然后赋值给 sign(签名值)字段,签名时只对 data 一个字段进行签名
3.2 签名方式
使用的签名算法为 SHA256withRSA,返回报文先验签再反序列化为对象
3.3 签名报文组装
将请求报文中 data 字段值按照名称的 ASCII 码从小到大进行排序以&连接,如果名称的首字母相同,则比较第二个字母,以此类推拼接成 key=value&key=value 的字符串,然后进行签名,并且赋值给 sign。
3.4 签名数据示例
@Slf4j
public class EncryptSha256Util {
private EncryptSha256Util() {
throw new IllegalStateException("EncryptSha256Util class");
}
/**
* sha256加密
*
* @param str 要加密的字符串
* @return 加密后的字符串
*/
public static String getSha256Str(String str) {
MessageDigest messageDigest;
String encodeStr = "";
try {
messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
encodeStr = byte2Hex(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
log.error("进行sha256加密失败!massage:{}", e.getMessage());
}
return encodeStr;
}
/**
* sha256加密 将byte转为16进制
*
* @param bytes 字节码
* @return 加密后的字符串
*/
private static String byte2Hex(byte[] bytes) {
StringBuilder stringBuilder = new StringBuilder();
String tempStr;
for (byte aByte : bytes) {
tempStr = Integer.toHexString(aByte & 0xFF);
if (tempStr.length() == 1) {
//得到第一位的进行补0操作
stringBuilder.append("0");
}
stringBuilder.append(tempStr);
}
return stringBuilder.toString();
}
public static void main(String[] args) {
getCardFormParm();
SetCardFormParm();
}
//查询接口参数
private static void getCardFormParm() {
CardRequestDTO<GetCardFormReceiverInfoDTO>res = new CardRequestDTO<>();
String timeStamp = System.currentTimeMillis()+"";
String secretAccessKey = "你的 AccessKeySecret";
String miniappId = "1000083312531542016";
String bizId = "101691407379048";
String requestId = "fdsagfsdgfsdagsdg";
String operatorId = "11";
GetCardFormReceiverInfoDTO dto = new GetCardFormReceiverInfoDTO();
dto.setUniqueId("04820f410572fee703273addc166a41aeead24de447d8abd8e08eacd44e3d901");
dto.setKeyword("iphone");
List<LabelDTO> labels = new ArrayList<>();
LabelDTO dto1 = new LabelDTO();
dto1.setLabelName("country");
dto1.setLabelValue("406");
LabelDTO dto2 = new LabelDTO();
dto2.setLabelName("brand");
dto2.setLabelValue("cy");
LabelDTO dto3 = new LabelDTO();
dto3.setLabelName("model");
dto3.setLabelValue("acth");
LabelDTO dto4 = new LabelDTO();
dto4.setLabelName("model111");
dto4.setLabelValue("mode");
labels.add(dto1);
labels.add(dto2);
labels.add(dto3);
labels.add(dto4);
dto.setLabels(labels);
String requestStr = "requestId=" + requestId + "&bizId=" + bizId +"&miniappId=" + miniappId + "&operatorId=" + operatorId + "&data=" + JSONObject.toJSONString(dto) + "&timeStamp=" + timeStamp;
String secretAccessKeySign = "&secretAccessKey=" + secretAccessKey;
requestStr = requestStr + secretAccessKeySign;
String sign = EncryptSha256Util.getSha256Str(requestStr).toUpperCase();
res.setData(dto);
res.setBizId(bizId);
res.setRequestId(requestId);
res.setMiniappId(miniappId);
res.setOperatorId(operatorId);
res.setTimeStamp(timeStamp);
res.setSign(sign);
System.out.println(JSONObject.toJSONString(res));
}
//更新接口参数
private static void SetCardFormParm() {
CardRequestDTO<CardFormInfoRequestDTO>res = new CardRequestDTO<>();
String timeStamp = System.currentTimeMillis()+"";
String secretAccessKey = "你的 AccessKeySecret";
String miniappId = "1000083312531542016";
String bizId = "101691407379048";
String requestId = "fdsagfsdgfsdagsdg";
String operatorId = "11";
CardFormInfoRequestDTO dto = new CardFormInfoRequestDTO();
JSONObject textJson = new JSONObject();
textJson.put("imageUrl", "title_uniqueid_555_gaid");
textJson.put("ClickUrl", "https://app-oss.byte-app.com/demoapp/Sryb8HnKqK.jpg");
dto.setText(textJson.toJSONString());
CardFormReceiverInfoDTO info = new CardFormReceiverInfoDTO();
List<String> uniqueIds = new ArrayList<>();
uniqueIds.add("4dc737855eb61401d6e426f61799a0156831356b0fc51848557034b20a7c3546");
uniqueIds.add("85e267220b86c309a85b4e86142366af813304e581014258dd9a0585cd898069");
uniqueIds.add("c81962159ee8417b8e113abb4ce4670f98c3941c97d86c13f1e521def2c62af0");
uniqueIds.add("04820f410572fee703273addc166a41aeead24de447d8abd8e08eacd44e3d901");
info.setUniqueIds(uniqueIds);
info.setKeyword("iphone");
List<LabelDTO> labels = new ArrayList<>();
LabelDTO dto1 = new LabelDTO();
dto1.setLabelName("country");
dto1.setLabelValue("406");
LabelDTO dto2 = new LabelDTO();
dto2.setLabelName("brand");
dto2.setLabelValue("cy");
LabelDTO dto3 = new LabelDTO();
dto3.setLabelName("model");
dto3.setLabelValue("acth");
labels.add(dto1);
labels.add(dto2);
labels.add(dto3);
info.setLabels(labels);
dto.setCardFormReceiverInfo(info);
String requestStr = "requestId=" + requestId + "&bizId=" + bizId +"&miniappId=" + miniappId + "&operatorId=" + operatorId + "&data=" + JSONObject.toJSONString(dto) + "&timeStamp=" + timeStamp;
String secretAccessKeySign = "&secretAccessKey=" + secretAccessKey;
requestStr = requestStr + secretAccessKeySign;
String sign = EncryptSha256Util.getSha256Str(requestStr).toUpperCase();
res.setData(dto);
res.setBizId(bizId);
res.setRequestId(requestId);
res.setMiniappId(miniappId);
res.setOperatorId(operatorId);
res.setTimeStamp(timeStamp);
res.setSign(sign);
System.out.println(JSONObject.toJSONString(res));
}
}
4. 数据规范
4.1 请求报文规范
应答报文分为两层:
- 外层 公共参数
- 内层 【data】 业务参数
内容体 data 在 code 等于 0 时返回
{
"data": {
"cardFormReceiverInfo": {
"labels": [
{ "labelName": "country", "labelValue": "ch" },
{ "labelName": "brand", "labelValue": "brand1" },
{ "labelName": "model", "labelValue": "model2" },
{ "labelName": "model111", "labelValue": "model1" },
{ "labelName": "serviceProvider", "labelValue": "serviceProvider1" }
],
"uniqueIds": [
"003c92f3245bddbe736eefd66a7b55c3b9cb04f446c4d3a13b1ec0dc4ae99a01"
]
},
"text": "{\"titleName\":\"logo222\",\"backLogo\":\"name222\",\"checkurl\":\"title222\",\"titleValue\":\"http://hhh.html\"}"
},
"formId": "101690449368932",
"miniappId": "1000083312531542016",
"operatorId": "11",
"sign": "F87E21E1ECDB776001134AA94C89A6F13D70DFF6F687B5749AE845B6A476E229",
"timeStamp": "1690458554576"
}
返回结果
返回参数 | 返回参数类型 | 描述 | |
---|---|---|---|
code | Integer | 错误状态码 | |
message | String | 返回消息 | |
data | CardFormInfoVo | 见 CardFormInfoVo 参数 |
CardFormInfoVo 参数
返回参数 | 返回参数类型 | 描述 |
---|---|---|
successUniqueId | list<String> | 当一次更新多个 uniqueid 的信息时,返回成功的 uniqueid |
errorUniqueId | list<String> | 当一次更新多个 uniqueid 的信息时,返回失败的 uniqueid |
示例:
{
"code": 0,
"message": null,
"data": {
"successUniqueId": ["gsdgsdfgsdfss"],
"errorUniqueId": ["gsdgsdfgsdf"]
},
"sign": "CDD73496515A7F9B66E620CB39470F560A93730E5B4F4C24474E342C4F1B7945"
}
状态码:
状态码 | 描述 |
---|---|
10004 | Data JSON 解析异常 |
10005 | 签名异常 |
10007 | 权限异常 |
20001 | 该表单 id 不存在 |
20002 | 该表单 id 和小程序 id 不匹配 |
20006 | 更新表单失败 |
20007 | DATA 表单数据不能为空且不能缺失 |
20008 | 一次发送用户 id 超过限制 |
20009 | 传入的 key 对应的 value 不能为空 |
30001 | 用户 uniqueId 不存在 |
00002 | 请求过快限流了 |
4.2 回调接口
请求 URL:开发者提供
请求方式:post
输入参数:
参数 | 数据类型 | 备注 |
---|---|---|
requestId | string | 写入时的请求 id |
returInfo | returInfo | 详见 3.5 返回结果 |
4.3 deeplink
素材传入的 DeepLink 链接是否正确,格式如下:
包壳小程序形式:launcherdlt://miniapp?appId={小程序 id}&h5Path=xxx&query=yyy
- 跳首页,不带额外参数:h5Path=&query=
- 跳具体 path,不带额外参数: h5Path=xxx&query=;xxx 代表页面路径
- 跳具体 path,需要携带额外参数:h5Path=xxx&query=yyy;xxx 代表页面路径,yyy 是携带的参数,xxx,yyy 需要 encode
- 示例:https://www.xxx.com/example?param=1
- 对 /example 做 encode 得 %2Fexample
- 对 param=1 做 encode param%3D1xxx
- 更新素材接口传值 h5Path=%2Fexample&query=param%3D1
常规小程序形式:launcherdlt://miniapp?appId={小程序 id}&page=xxx&query=yyy
- 跳首页,不带额外参数:page=&query=
- 跳具体 path,不带额外参数: page=xxx&query=;xxx 代表页面路径
- 跳具体 path,需要携带额外参数:page=xxx&query=yyy;xxx 代表页面路径,yyy 是携带的参数,yyy 需要 encode
- 示例:launcherdlt://miniapp?appId={小程序 id}&page=pages/index/index, 参数为 param=1
- 对 param=1 做 encode param%3D1
- 更新素材接口传值 page=pages/index/index&query=param%3D1
多小程序类型卡片:该卡片绑定一个默认小程序 defaultMiniappId,但是该卡片还可以跳转至其他小程序
- 若跳转到默认小程序,根据 a,b 规则进行链接参数生成
- 若跳转到非默认小程序 jumpMiniappId,在 a,b 规则的基础上生成链接后,在链接前面拼接参数 appId={jumpMiniappId}&,示例如下
- 跳转小程序 :300000(h5 小程序)
- 跳转链接:https://www.xxx.com/example?param=1
- 因为是 h5 小程序,按照 a 规则 生成 :h5Path=%2Fexample&query=param%3D1
- 最终链接参数 appId=300000&h5Path=%2Fexample&query=param%3D1