# 资源兑换
部分游戏(如小美果园)会提供资源兑换的能力,厂商通过接口调用可以实现给目标用户发送奖励。
使用此功能之前需要与对应资源提供方达成协议,分配对应资源的库存。
可兑换资源列表:
资源id 业务归属 业务归属缩写 上架资源 资源缩写 单位 备注 1011001 小美果园 tree 水滴 water 克 1011002 小美果园 tree 小袋化肥 smallFertilize 袋 1011003 小美果园 tree 大袋化肥 bigFertilize 袋 90011001 酒店 hotel 5元酒店无门槛券 hotelTicket-5-0 张 90011002 酒店 hotel 10元酒店无门槛券 hotelTicket-10-0 张 90011003 酒店 hotel 30元酒店无门槛券 hotelTicket-30-0 张 90011004 酒店 hotel 100元酒店无门槛券 hotelTicket-100-0 张 90011005 酒店 hotel 500元酒店无门槛券 hotelTicket-500-0 张 90011006 酒店 hotel 1500元酒店无门槛券 hotelTicket-1500-0 张 90011007 酒店 hotel 【三亚大奖】1500元酒店无门槛券 sanyaBigPrize-hotelTicket-1500-0 张 90021001 酒店 hotel 【三亚大奖】1500元机票无门槛券 sanyaBigPrize-airTicket-1500-0 张 待定 金服 financial 立减金 bonus 分(货币) 待定 团好货 groupGoodGoods 团好货券 ticket-?-?(待定) 张 待定 团好货 groupGoodGoods 团好货券 ticket-?-?(待定) 张 待定 团好货 groupGoodGoods 团好货券 ticket-?-?(待定) 张 待定 团好货 groupGoodGoods 团好货券 ticket-?-?(待定) 张 待定 团好货 groupGoodGoods 团好货券 ticket-?-?(待定) 张 待定 微信余额 financial 微信余额 分 最小金额30分,提现结果异步通知,见文档提现回调通知 待定 美团余额 financial 美团余额 分 同步返回提现结果 - 接口参数传缩写
可查询资源列表:
游戏名 游戏缩写 上架资源 资源缩写 单位 备注 小美果园 tree 水滴 water 克 小美果园 tree 小袋化肥 smallFertilize 袋 小美果园 tree 大袋化肥 bigFertilize 袋
# 兑换
URL: https://mgc.meituan.com/mgc/gateway/api/v3/resource/exchange
Method: POST
Content-Type: application/json
入参
参数名 类型 默认值 是否必填 说明 clientId String 是 应用ID 可从开放平台查看 ts long 是 发送请求的时间戳 1970-01-01 00:00:00.000到当前时刻的毫秒数 sign String 是 参数签名,见附录说明 nonce String 是 随机字符串 signType String 是 签名算法 固定为: SHA1 encryptType String 是 加密算法 固定为: AES-256-GCM bizContent String 是 业务参数 需要加密 见下表 bizContent内容
参数名 类型 默认值 是否必填 说明 mgcId String 是 美团侧玩家角色号 resourceId String 是 资源id (参考:「可兑换资源列表」中的id) targetGameName String 否 目标游戏名称(兑换资源列表中的游戏缩写) resourceType String 否 目标资源类型(兑换资源列表中的资源缩写) resourceNum long 是 目标资源数量 sn String 是 流水号,幂等使用 保证唯一 出参
{ "code": 0, "msg": "ok", "data": { "sn": "sn-number" } }code !=0 时为失败,详见异常码
异常码(仅供参考)
异常码 异常说明 730001 没有权限兑换 730002 资源类型不合法 730003 兑换用户不存在 730004 订单重复 730005 余额不足 730006 数量不合法 730007 兑换失败 513005 兑换失败
# 查询剩余资源
URL: https://mgc.meituan.com/mgc/gateway/api/v3/resource/query
Method: POST
Content-Type: application/json
入参
参数名 类型 默认值 是否必填 说明 clientId String 是 应用ID 可从开放平台查看 ts long 是 发送请求的时间戳 1970-01-01 0时 到当前时刻的毫秒数 sign String 是 参数签名,见附录说明 nonce String 是 随机字符串 signType String 是 签名算法 固定为: SHA1 encryptType String 是 加密算法 固定为: AES-256-GCM resourceType String 是 目标资源类型(兑换资源列表中的资源缩写) bizContent String 是 业务参数 需要加密 见下表 bizContent内容
参数名 类型 默认值 是否必填 说明 resourceType String 是 目标资源类型(兑换资源列表中的资源缩写) targetGameName String 是 目标游戏名称(兑换资源列表中的游戏缩写) 出参
参数名 类型 默认值 是否必填 说明 code int 是 异常码 msg String 是 异常描述 resourceNum long 是 剩余资源数量 { "code": 0, "msg": "ok", "data": { "targetGameName": "tree", "resourceType": "water", "resourceNum": 99590 } }code !=0 时为失败,详见异常码。
异常码
异常码 异常说明 730001 没有权限兑换 730002 资源类型不合法
# 附录
- Sign签名的计算算法为
String data = "" //bizContent中的参数按照参数名字典排序,以&符连接,例如:mgcId=12342&resourceId=34532&sn=jlu9jdf data += String.format("&uri=%s", URLEncoder.encode("/api/v3/resource/exchange")); // 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; }