# PUSH 推送
- 场景:Push指运营人员对用户移动设备进行的主动消息推送,主要可分为功能推送和营销推送,是促进用户活跃的重要手段。 美团Push中心作为一个成熟的基建工具,为美团内部运营人员提供了基础。它将运营消息下发至美团App,用户可在通知栏看到相应内容。
- 功能:为各个三方游戏厂商提供主动向美团App推送消息的能力,提供运营渠道。
# 接口
# 发送push
URL: https://mgc.meituan.com/mgc/gateway/api/v3/push/pushMsg
- 如无特殊说明,可按 v3 接入(部分历史游戏按 v1、v2 进行接入。不同 api 版本传参不同)
Method: POST
Content-Type: application/json
入参(默认v3版本)
| 参数名 | 类型 | 默认值 | 是否必填 | 说明 |
|---|---|---|---|---|
| clientId | String | 是 | 应用ID 可从开放平台查看 | |
| ts | long | 是 | 发送请求的时间戳ms 1970-01-01 0时 到当前时刻的毫秒数 | |
| sign | String | 是 | 参数签名,见附录说明 | |
| nonce | String | 是 | 随机字符串 | |
| signType | String | 是 | 签名算法 固定为: SHA1 | |
| encryptType | String | 是 | 加密算法 固定为: AES-256-GCM | |
| bizContent | String | 是 | 业务参数 需要加密 见下表 |
- bizContent内容
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| pushId | long | 是 | pushid,提需求申请 |
| mgcIds | jsonstring | 是 | 需要推送数据的MgcIds json类型为 List< Long> |
| type | int | 无需求不需关注!push参数类型,与mgcIdWithParamsWX和mgcIdWithParamsMT搭配使用 | |
| mgcIdWithParamsWX | jsonstring | 无需求不需关注!微信push参数 json类型为 Map<String, Map<String, Map<String, String>>> mgcId -> lineph -> (paraName, paraValue) | |
| mgcIdWithParamsMT | jsonstring | 无需求不需关注!美团push参数 json类型为 Map<String, Map<String, String>> mgcId -> (paraName, paraValue) |
- 出参
| 参数名 | 类型 | 默认值 | 是否必填 | 说明 |
|---|---|---|---|---|
| code | int | 是 | 异常码 | |
| msg | String | 是 | 异常描述 |
- code 为0表示请求正常
# 附录
- push/pushMsg v1 和 v2 传参 补充说明
- 新游戏接入默认走v3,无需关注此部分
| 参数 | 参数类型 | 是否必需 | 说明 |
|---|---|---|---|
| clientId | string | 是 | 第三方的应用id |
| ts | long | 是 | 时间戳(单位:ms) |
| nonce | string | 是 | 随机数(uuid生成,去掉“-”) |
| sign | string | 是 | 签名值,附录 |
| pushId | long | 是 | pushid |
| mgcIds | jsonstring | 是 | 需要推送数据的MgcIds json类型为 List< Long> |
| type | int | push参数类型,与mgcIdWithParamsWX和mgcIdWithParamsMT搭配使用 | |
| mgcIdWithParamsWX | jsonstring | 微信push参数 json类型为 Map<String, Map<String, Map<String, String>>> mgcId -> lineph -> (paraName, paraValue) | |
| mgcIdWithParamsMT | jsonstring | 美团push参数 json类型为 Map<String, Map<String, String>> mgcId -> (paraName, paraValue) |
- Sign签名的计算算法为
String data = "mgcIds=%5B123%5D&pushId=456" //bizContent参数按照参数名字典排序且数值用URLEncoder.encode,以&符连接。假定bizContent初始内容为 {"pushId":"456","mgcIds":"[123]"} data += String.format("&uri=%s", URLEncoder.encode("/api/v3/push/pushMsg")); // path随着访问的url改变 data += "&method=POST"; data += String.format("&secret=%s", appSecret); //开发者可以在开发者后台查询得到开放平台为其分配的appId和appSecret // 以上生成data数据的顺序不可改变 String secretKey = AESUtil.createKey(appId + "&" + appSecret); String signature = SHAUtil.encryptSHA1Str(data + secretKey); - AESUtil工具类
import java.security.MessageDigest; import javax.crypto.SecretKey; public static String createKey(String password) { try { byte[] keyBytes = password.getBytes(StandardCharsets.UTF_8); MessageDigest sha = MessageDigest.getInstance("SHA-1"); keyBytes = sha.digest(keyBytes); keyBytes = Arrays.copyOf(keyBytes, 32); SecretKey secretKey = new SecretKeySpec(keyBytes, "AES"); return Base64.getEncoder().encodeToString(secretKey.getEncoded()); } catch (Exception e) { throw new RuntimeException(e); } } - SHAUtil工具类
import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import org.apache.commons.codec.digest.DigestUtils; public static String encryptSHA1Str(String text) { byte[] bytes = encryptSHA(text, "SHA-1"); return byte2hex(bytes); } private static byte[] encryptSHA(String text, String algorithm) { MessageDigest md; try { md = MessageDigest.getInstance(algorithm); } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException(e); } byte[] infoBytes = text.getBytes(StandardCharsets.UTF_8); md.update(infoBytes); return md.digest(); } private static String byte2hex(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(b & 0xFF); if (hex.length() == 1) { stringBuilder.append("0"); } stringBuilder.append(hex); } return stringBuilder.toString(); } - bizContent内容的加密使用AES_256_GCM算法加密,加密工具类如下
以下代码为AES_256_GCM的加密java示例代码,仅供参考 import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.Arrays; import java.util.Base64; private static final String ALGORITHM = "AES"; private static final String ALGORITHM_PADDING = "AES/GCM/NoPadding"; private static final int AES_KEY_LENGTH_BIT = 256; public static final int GCM_NONCE_LENGTH_BIT = 128; public static final int GCM_TAG_LENGTH_BIT = 128; public static String encryptWithAESGCM256(String key, byte[] content) { try { byte[] keyBytes = Base64.getDecoder().decode(key.getBytes(StandardCharsets.UTF_8)); SecretKeySpec secretKey = new SecretKeySpec(keyBytes, ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM_PADDING, "SunJCE"); SecureRandom random = SecureRandom.getInstance("NativePRNGNonBlocking"); final byte[] nonceBytes = new byte[GCM_NONCE_LENGTH_BIT / 8]; random.nextBytes(nonceBytes); GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH_BIT, nonceBytes); cipher.init(Cipher.ENCRYPT_MODE, secretKey, spec); byte[] contentBytes = cipher.doFinal(content); byte[] finalBytes = new byte[nonceBytes.length + contentBytes.length]; System.arraycopy(nonceBytes, 0, finalBytes, 0, nonceBytes.length); System.arraycopy(contentBytes, 0, finalBytes, nonceBytes.length, contentBytes.length); String result = new String(Base64.getUrlEncoder().encode(finalBytes), StandardCharsets.UTF_8); return result; } catch (Exception e) { logger.error("encrypt error", e); } return null; }