Sdk-h5-server
目录
前言
- 服务端接口调用,构造请求数据并签名必须在商户服务端完成,商户的应用密钥绝对不能保存在商户 APP 客户端中,也不能从服务端下发
- 请求参数必须是 表单POST提交过来 , Content-Type: application/x-www-form-urlencoded
签名算法
所有接口都需要做参数签名,防止伪造请求
签名算法为:
1). 参数排序:将所有参数名按字典顺序进行排序[sign字段不参与签名] 2). 参与签名的数据不要做 URL Encoding,一律以 UTF-8 编码参与签名。 3). 参数拼接:将所有参数按 k1=v1&k2=v2&k3=v3...格式进行拼接。 4). 将第3步所得得字符串 &APP_SECRET [每个游戏分配得密钥],生成待签名字符串。 5). 第4步所得的字符串md5 就是最终的sign了
参考PHP示例:
<?php
public function notify() {
$cp_data_arr = $_POST; //接收陌陌传递过来的数据详情
$app_secret = '280ffa37af884aa3abbacb7c01ad16e4’; //陌陌提供的 app_secret
//根据签名算法拼凑 待签名字符串
ksort($cp_data_arr);
$res = ;
foreach($cp_data_arr as $key=>$value) {
if($key == 'sign') {
continue;
}
$res .= $key .'='. $value.'&';
}
$sign = md5($res.$app_secret); //待验签字符串
错误码
接口响应ec大于0为错误
| 错误码 | 错误描述 |
| 21001 | 接口不存在 |
| 21002 | appid没有传 |
| 21003 | userid没传或者是错误的 |
| 21004 | 缺失必传参数 |
| 21005 | 参数错误,值不合法 |
| 21006 | 签名(sign)校验失败 |
| 21007 | vtoken已过期 |
| 21008 | vtoken错误 |
| 21009 | 用户未授权登录 |
| 21010 | 消息盒子推送--未配置权限 |
| 21011 | 消息盒子推送失败-用户未开启 |
| 21012 | 道具消耗-道具不存在 |
| 21015 | 道具消耗-服务不可用 |
接口
登录检验
1). 接口功能
验证用户是否登录。
2). 接口名
https://game.immomo.com/v2/srv/login/check
3). 调用时机
客户端调用SDK登录后,服务器确认此次是否是真实登录
4). 提交参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| appid | string | Y | 应用ID |
| sign | string | Y | 参数签名 |
| vtoken | string | Y | 由客户端提交的验证参数 |
| userid | string | Y | 用户ID |
注:vtoken对应MMSDK.getUserInfo的vtoken字段,24小时失效,不是token(永久有效)
5). 返回值:(JSON)
{
"ec": 0,
"em": "success",
"timesec": 1578132537,
"data": {
"userid": "UmFXSDh1VVRFcGFpbzNBdG1HNzU5dz09",
"name": "吴道林",
"sign": "一天一苹果,疾病远离我",
"vip": 0,
"reg_time": 1357445132,
"constellation": "摩羯座",
"birthday": "1987-01-15",
"age": 32,
"sex": "M",
"avatar": "https://img.momocdn.com/album/45/AE/45AE5B32-42BB-F81A-409A-777956E45ADA20171204_S.jpg",
"photo": [
"45AE5B32-42BB-F81A-409A-777956E45ADA20171204"
],
"city": "隐身",
"cityid": 110100,
"big_r": "2"
}
}
消息盒子推送
接口地址: https://game.immomo.com/v2/srv/message/box
参数:
- appid 应用id
- userid 用户id, 加密后的momoid
- content 发送内容
- sign 签名
返回内容
{
"ec": 0,
"em": "success",
"timesec": 1578132537,
"data": {
"result" => true;
}
}
数据同步
接口地址: https://game-api.immomo.com/3/server/prop/sync-game-data
参数:
- appid 应用id
- userid 用户id, 加密后的momoid
- type start 游戏开始,score 上报游戏总的分, outcome 游戏胜负结果
- time 秒级时间戳
- sign 签名
- score (int) type = score 时传递
- outcome type = outcome 时传递 1.胜利 0.失败
返回内容
{
"ec": 0,
"em": "success",
"result": {
"ec": 200,
"em": "success",
"data": [],
}
}
游戏室-任务完成上报
陌陌-游戏室-任务中心,约定好的任务完成后触发通知操作
接口地址: https://game.immomo.com/v2/srv/sync/task
参数:
- appid 应用id
- userid 用户id, 加密后的momoid
- task_id 任务id
- sign 签名
返回内容
{
"ec": 0,
"em": "success",
"result": {
"ec": 200,
"em": "success",
"data": [],
}
}
游戏内下线通知陌陌(必须)
游戏内下线通知陌陌,用于中宣部防沉迷行为上报
接口地址: https://game.immomo.com/v2/srv/sync/offline
参数:
- appid 应用id
- userid 用户id, 加密后的momoid
- sign 签名
返回内容
{
"ec": 0,
"em": "success",
"result": {
"ec": 200,
"em": "success",
"data": [],
}
}
转盘抽奖创建订单
接口地址: https://game.immomo.com/v2/srv/draw/create
参数:
- appid 应用id
- userid 用户id, 加密后的momoid
- order_id 订单ID (只能包含数字和字母 32位)
- type 抽奖类型 1、单抽 10、10抽 100、百抽
- awards 抽奖获得的奖品信息 json数组 [{'prop_id' : '111',num:2, begin_consume_time(道具开始消耗时间戳毫秒): 1593500012000}]
- extra 扩展参数.json格式
- create_time 毫秒时间戳
- sign 签名
返回内容
{
"ec": 0,
"em": "success",
"timesec": 1578996514,
"data": {
"order_id": "202006301523423xxxxxxxxx",
}
}
转盘付费道具消耗通知
通过转盘付费抽奖获取的道具,消耗的时候通知
接口地址: https://game.immomo.com/v2/srv/draw/usedNotify
参数:
- appid 应用id
- userid 用户id, 加密后的momoid
- product_id 消耗道具ID
- product_num 消耗道具数量
- time 毫秒时间戳
- sign 签名
返回内容
{
"ec": 0,
"em": "success",
"timesec": 1592986638,
"data": {
"consumeOrderId": "20200624161718284039623000001"//陌陌道具系统订单ID
}
}
支付通知
1).(仔细看这里 ):
支付成功时陌陌向游戏服务器发起http请求。 游戏方务必进行RSA签名校验,防止通知内容伪造; 同时必须对通知数据中的app_trade_no , total_fee, product_id, appid 进行验证, 如果任意一个验证不通过,则表明是异常通知,务必忽略; 过滤重复的通知 校验通过之后务必返回给陌陌 success 字符串,否则陌陌认为游戏方没有成功接受支付通知,会重试15次,时间分别是0s, 10s, 25s, 40s ..... 2h17m15s;
2). 参数列表
| 参数名 | 类型 | 必选 | 说明 |
| appid | string | Y | 应用id |
| momoid | string | Y | 和sdk返回userid一致,即加密后的momoid |
| trade_no | string | Y | Momo返回的订单号 |
| app_trade_no | string | Y | 游戏商的订单号,(长度不超过64字节 ) |
| sign | string | Y | 签名(已废弃) |
| product_id | string | Y | 商品id |
| currency_type | int | Y | 货币类型 0-人民币 |
| total_fee | double | Y | 价格(单位:元) |
| trade_time | string | Y | 交易时间 |
| is_test_order | string | Y | 是否是测试订单 1-测试订单 0-普通订单 |
| channel_type | int | Y | 支付渠道 3-陌陌币 8-陌陌钱包 |
| encrypted | string | Y | 加密结果[具体加密算法如下] |
| encrypt_type | string | Y | RSA |
以上参数设置以Form格式提交(POST方式给CP通知,需要对参数进行URLDecode处理) CP 返回示例: 成功返回success7个标准字样。 失败返回示例[JSON]:
{
ec: 非0错误码,
em: 错误描述, //错误描述尽可能简短清晰,便于后期快速查询问题
}
签名之前,请游戏方先向陌陌索要校验RSA签名所需要的公钥。
签名算法为:
1). 参数排序:将所有参数名按字母顺序进行排序,没有值的参数不要参与签名。[sign,encrypted,encrypt_type不参与签名]
注:按照字母顺序进行排序的时候,是按照字符在ASCII码中的顺序进行的从小到大的排序。第一个字符如果一样的话,
就按照第二个字符在ASCII码中的顺序继续进行排序。如此类推。
2). 参与签名的数据不要做 URL Encoding,一律以 UTF-8 编码参与签名。
3). 参数拼接:将所有参数按 k1=v1&k2=v2&k3=v3...格式进行拼接。
4). 将第3步所得得字符串 &APP_SECRET [每个游戏分配得密钥],生成待签名字符串。
RSA签名
当获得到通知返回的待签名字符串后,把待签名字符串,陌陌提供的公钥,陌陌通知返回参数中的参数encrypted的值[base64_decode处理下] 三者一同放入RSA的签名函数中进行非对称的签名运算,来判断签名是否校验通过。
签名备注: 陌陌在对字符串进行加密的时候使用的是 SHA1withRSA (OPENSSL_ALGO_SHA1)
参考示例:
1、JAVA示例:
JAVA版本文件:Pay-java-demo.zip
2、PHP示例:
<?php
public function notify() {
$cp_data_arr = $_REQUEST; //接收陌陌传递过来的数据详情
$app_secret = '280ffa37af884aa3abbacb7c01ad16e4’; //陌陌提供的 app_secret
//陌陌传递数据通过RSA签名方式
if (isset($cp_data_arr['encrypt_type']) && $cp_data_arr['encrypt_type'] === 'RSA') {
$appid = $cp_data_arr['appid'];
//根据签名算法拼凑 待签名字符串
ksort($cp_data_arr);
$res = ;
foreach($cp_data_arr as $key=>$value) {
if($value === || $key == 'encrypted' || $key == 'encrypt_type' || $key == 'sign') {
continue;
}
$res .= $key .'='. $value.'&';
}
$data = $res.$app_secret; //待验签字符串
$public_key = ; //陌陌提供的 公钥
$pu_key = openssl_pkey_get_public($public_key);
$unsign_msg = base64_decode($cp_data_arr['encrypted']);
$res = openssl_verify($data, $unsign_msg, $pu_key);
//error_log("verify result is " . $res);
if ($res) {
//执行 发货操作 并 返回陌陌所需要的 success 字样
echo 'success';
} else {
echo 'falied';
}
}
}
3、Python示例:
!/usr/bin/env python
import base64
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Hash import SHA
if __name__ == '__main__':
# 应用密钥
app_secret =
# 支付签名检验用的公钥
pub_key =
# http request 参数
params = {'appid':'appid', 'momoid':'VEgwQng3emRNK2c4Wjd0cW5mcHRUZz09', 'trade_no':'20151026143931553920061', 'app_trade_no':'79396e329eaf4e8b94f27c41cfc7b944-6377453-405-14', 'product_id':'com.wemomo.game.buyu.8', 'currency_type':'0', 'total_fee':'15', 'trade_time':'2022-09-01 09:47:01.0', 'channel_type':'5', 'sign':'06c56a89949d617def52f371c357b6db', 'encrypted':'JYoeEzoh6ucdslnYVeqFFBi4NZZRc3vsjjIIH63THISkxGzXkdElCjum7zgWFL9IdacPoESRabG3v9QJARi7vC7byURM3hB9dVFFMFpBWym+LWyW4vgPUQTVfc06JHUAGLKxLMEgq2HKniJPp+5JYw8E+WWB\/TvxuQbOJn6L1sc=', 'encrypt_type':'RSA'}
# 对http request参数按key排序(升序)并连接起来
items = params.items()
items.sort()
data =
for key, value in items:
if key == 'encrypted' or key == 'encrypt_type' or key == 'sign' or value == :
continue
data = data + key + '=' + value + '&'
data = data + app_secret
print data
# 校验签名,其中签名需要base64 decode
rsa_key = RSA.importKey(pub_key)
signature_scheme = PKCS1_v1_5.new(rsa_key)
digest = SHA.new(data.encode('utf-8'))
signature = base64.b64decode(params['encrypted'])
result = signature_scheme.verify(digest, signature)
if result:
print '签名校验通过。'
else:
print '签名校验失败。'
4、C++示例
说明:需要依赖第三方库,地址:http://www.cryptopp.com/
礼包发放通知
1).(仔细看这里 ):
陌陌-游戏室-礼包中心,用户点击兑换后通知。 游戏方务必进行RSA签名校验,防止通知内容伪造; 根据trade_no做幂等性校验
2). 参数列表 post提交, Content-Type: application/x-www-form-urlencoded
| 参数名 | 类型 | 必选 | 说明 |
| appid | string | Y | 应用id |
| userid | string | Y | 和sdk返回userid一致,即加密后的momoid |
| trade_no | string | Y | 用户兑换礼包的订单号 |
| gift_bag_id | string | Y | 游戏商的礼包ID,(长度不超过64字节 ) |
| trade_time | string | Y | 交易时间戳 |
| sign | string | Y | 签名 等价于支付通知的 `encrypted`, 相同的加密算法 |
3). 返回值
返回json格式字符串 {"ec":200, "em":"success"};
ec 200表示成功,201 表示用户不存在,202:其他失败情况
4). 加密参考示例
PHP示例:
<?php
public function notify() {
$cp_data_arr = $_POST; //接收陌陌传递过来的数据详情
$app_secret = '280ffa37af884aa3abbacb7c01ad16e4’; //陌陌提供的 app_secret
$public_key = '’; //陌陌提供的 RSA 公钥
//拼凑 数据
ksort($cp_data_arr);
$res = ;
foreach($cp_data_arr as $key=>$value) {
if($key == 'sign') {
continue;
}
$res .= $key .'='. $value.'&';
}
$data = md5($res.$app_secret); //待验签数据
$pu_key = openssl_pkey_get_public($public_key);
$unsign_msg = base64_decode($cp_data_arr['sign']);
$res = openssl_verify($data, $unsign_msg, $pu_key);
//error_log("verify result is " . $res);
if ($res) {
//执行 发货操作 并 返回陌陌所需要的 success 字样
echo json_encode(['ec' => 200, 'em' => 'success']);
} else {
echo json_encode(['ec' => 202, 'em' => '签名错误']);;
}
exit();
转盘抽奖扣费成功通知
1).
扣费成功时陌陌向游戏服务器发起http请求。 游戏方务必进行RSA签名校验,防止通知内容伪造; 过滤重复的通知。根据order_id做幂等 校验通过之后务必返回给陌陌 success 字符串,否则陌陌认为游戏方没有成功接受支付通知,会重试15次,时间分别是0s, 10s, 25s, 40s ..... 2h17m15s;
2). 参数列表
| 参数名 | 类型 | 必选 | 说明 |
| appid | string | Y | 应用id |
| userid | string | Y | 加密后的momoid |
| order_id | string | Y | Momo返回的订单号 |
| sign | string | Y | 签名 等价于支付通知的 `encrypted`, 相同的加密算法 |
| is_test | string | Y | 是否是测试订单 1-测试订单 0-普通订单 |
以上参数设置以Form格式提交(POST方式给CP通知,需要对参数进行URLDecode处理) CP 返回示例: 成功返回success7个标准字样。 失败返回示例[JSON]:
{
ec: 非0错误码,
em: 错误描述, //错误描述尽可能简短清晰,便于后期快速查询问题
}
