MSSDK 服务端接入指南
<p>[TOC]</p>
<h2>变更记录</h2>
<table>
<thead>
<tr>
<th>日期</th>
<th>修订版本</th>
<th>修改描述</th>
<th>修改人</th>
</tr>
</thead>
<tbody>
<tr>
<td>2019.06.30</td>
<td>V1.0</td>
<td>创建</td>
<td>bruce.lan</td>
</tr>
</tbody>
</table>
<h2>重要事项</h2>
<p>1、在接入SDK之前,需要在我们后台申请账号并且创建应用参数,具体可以参考 <a href="https://www.showdoc.cc/mssdk?page_id=2692703335764519">技术中台服务接入指南</a> 文档。</p>
<p>2、该文档是为了方便开发者对接乐逗SDK服务端而编写,目的是为了登录和支付安全。</p>
<p>3、在接入完客户端SDK后,接入该文档遇到任何问题请 <a href="https://www.showdoc.cc/mssdk?page_id=2615409307929897">联系我们</a>。</p>
<h2>1、文档简介</h2>
<p>本文档描述开发者接入 <code>乐逗SDK服务器</code> 的过程。本文档面向的读者是 Android、iOS、Unity SDK 接入乐逗客户端SDK的服务端开发者。</p>
<h2>2、服务端回调指南</h2>
<h3>2.1、登录有效性验证 (必接)</h3>
<h4>2.1.1、登录回调流程</h4>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/fd6a6fc95978265a2deed4418fa97143?showdoc=.jpg" alt="" /></p>
<p>客户端登录成功后,需要游戏服务器向SDK服务器调用验证接口,传递 <code>openid</code>、<code>appkey</code>、<code>sessionId</code> 及 <code>Signature</code> 给SDK服务器。</p>
<p>主要的目的:</p>
<ul>
<li>为了校验 <code>sessionId</code> 是否过期。</li>
<li>防止钓鱼恶意程序。</li>
</ul>
<h4>2.1.2、登录验证接口</h4>
<ul>
<li>调用接口: <code>https://pub-gw.uu.cc/public-gateway/ms-public-oauth2/sdk_/oauth/checkSession</code></li>
<li>请求方式: POST</li>
<li>请求头参数,如下表所示:</li>
</ul>
<table>
<thead>
<tr>
<th>参数名</th>
<th>中文描述</th>
<th>类型(精度)</th>
<th>是否必填</th>
<th>参与签名</th>
</tr>
</thead>
<tbody>
<tr>
<td>Content-Type</td>
<td>填固定值为:application/json</td>
<td>String</td>
<td>必填</td>
<td>否</td>
</tr>
<tr>
<td>User-Agent</td>
<td>填固定值为:platform:CP;channel:CP;appVersion:1.0.0;package:com.cp.sdk;<br>sdkVersion:1.0.0;sdkName:MSSDK;networkType:WiFi;<br>deviceBrand:common;deviceId:00000000;localTime:2019-01-01 00:00:00</td>
<td>String</td>
<td>必填</td>
<td>否</td>
</tr>
<tr>
<td>Accept-Language</td>
<td>固定值为:zh_CN</td>
<td>String</td>
<td>必填</td>
<td>否</td>
</tr>
<tr>
<td>AppKey</td>
<td>给游戏分配的 appKey</td>
<td>String</td>
<td>必填</td>
<td>是</td>
</tr>
<tr>
<td>Nonce</td>
<td>随机字符串UUID,与 Timestamp 联合起来,用于防止重放攻击。<br>10分钟内的请求Nonce 不能有重复。</td>
<td>String</td>
<td>必填</td>
<td>是</td>
</tr>
<tr>
<td>Signature</td>
<td>请求签名,用来验证此次请求的合法性,需要用户根据实际的输入参数计算得出见【签名说明】</td>
<td>String</td>
<td>必填</td>
<td>否</td>
</tr>
<tr>
<td>Timestamp</td>
<td>当前 UNIX 时间戳,可记录发起 API 请求的时间。精确到毫秒</td>
<td>String</td>
<td>必填</td>
<td>是</td>
</tr>
</tbody>
</table>
<ul>
<li>请求体参数,如下表:</li>
</ul>
<table>
<thead>
<tr>
<th>参数名</th>
<th>中文描述</th>
<th>类型(精度)</th>
<th>是否必填</th>
<th>参与签名</th>
</tr>
</thead>
<tbody>
<tr>
<td>appkey</td>
<td>游戏 appkey</td>
<td>String</td>
<td>必填</td>
<td>是</td>
</tr>
<tr>
<td>openId</td>
<td>乐逗用户的唯一标识</td>
<td>String</td>
<td>必填</td>
<td>是</td>
</tr>
<tr>
<td>sessionId</td>
<td>会话id</td>
<td>String</td>
<td>必填</td>
<td>是</td>
</tr>
</tbody>
</table>
<ul>
<li>示例</li>
</ul>
<pre><code class="language-json">{
"sessionId":"c0a2296d9195572df7275f297b0ef525",
"openId":"a1eedfe67d69234a663e93aa5035ee7aa2628a8278ba0cb2da0a4438db2a14ec",
" appkey ":" 10001_LsP2XAYmBF6jHXTPOMZO "
}</code></pre>
<p><strong>注:</strong></p>
<ul>
<li><code>openId</code>, <code>sessionId</code> 的值,是在客户端登录成功的回调接口中获取到的, 具体获取方式参考客户端接入文档 ,并告知游戏服务器。</li>
<li><code>sessionId</code> 的有效期为10分钟,且校验成功过后不能再进行校验。</li>
<li>
<p>参数名请严格按照大小写。</p>
</li>
<li>返回结果 </li>
</ul>
<p>校验成功,返回当前会话的 <code>openId</code>, <code>playerId</code>, <code>sessionId</code>.</p>
<pre><code class="language-json">{
"code": 0,
"desc": "成功",
"result": {
"encrypt": "NONE",
"data": {
"openId": "d70b36b916ae734ec8a3965f70bf0ea6",
"sessionId": "54aa52c74911d0d1450d4be6076d0242",
"playerId": 3800793368
}
}
}</code></pre>
<ul>
<li>错误码</li>
</ul>
<table>
<thead>
<tr>
<th>错误代码</th>
<th>错误原因</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>成功</td>
</tr>
<tr>
<td>10010002</td>
<td>签名错误</td>
</tr>
<tr>
<td>10010001</td>
<td>appkey不正确</td>
</tr>
<tr>
<td>1011118</td>
<td>sessionId不存在</td>
</tr>
<tr>
<td>1011117</td>
<td>sessionId无效</td>
</tr>
</tbody>
</table>
<p><strong>签名说明:</strong></p>
<blockquote>
<p>签名示例代码可以参考 <a href="https://www.showdoc.cc/mssdk?page_id=2992099572666798">签名示例代码</a> 文档。</p>
</blockquote>
<p>1、对所有请求key按参数名做字典序升序排列。(所谓字典序升序排列,直观上就如同在字典中排列单词一样排序,按照字母表或数字表里递增顺序的排列次序,即先考虑第一个“字母”,在相同的情况下考虑第二个“字母”,依此类推。)
参数排序后拼接字符串,将把上一步排序好的请求参数格式化成“参数名称”=“参数值”的形式如:</p>
<pre><code>Key1=value1& Key2=value2& Key3=value3&…………</code></pre>
<p>2、拼接最终签名字符串,将秘钥 <code>appSecrect</code> 拼接在字符串前后</p>
<pre><code>appSecrect& Key1=value1& Key2=value2& Key3=value3&………& appSecrect</code></pre>
<p>注意:对于 post 请求,key 固定为 “requestBody”,value 为请求的整个 body。</p>
<p>例如:post的body</p>
<pre><code>{
"openId":"8ba49d502895d521e7c29885597218d7",
"sessionId":"2fe410d9fc9f708f77000eab113aaa0a",
"appkey":"LsP2XAYmBF6jHXTPOMZO"
}</code></pre>
<p>参数字典升排序字符串前后加上 <code>appSecrect</code>(JSxPpoOzc9de9gC2wiSt)</p>
<pre><code>JSxPpoOzc9de9gC2wiSt&AppKey=LsP2XAYmBF6jHXTPOMZO&Nonce=123456&Timestamp=201910101&requestBody={"openId":"8ba49d502895d521e7c29885597218d7","sessionId":"2fe410d9fc9f708f77000eab113aaa0a","appkey":"LsP2XAYmBF6jHXTPOMZO"}&JSxPpoOzc9de9gC2wiSt</code></pre>
<p>3、md5生成签名串(32位小写)</p>
<p>md5(appSecrect& Key1=value1& Key2=value2& Key3=value3&………& appSecrect)</p>
<pre><code>ee427fc6c0afad74c6116aad13be0b68</code></pre>
<p><strong>注意事项</strong>:</p>
<ul>
<li>appkey,appSecrect 是给乐逗给游戏分配的秘钥对。</li>
<li>微服务业务控制台地址:<a href="http://biz.cloud.idreamsky.com">http://biz.cloud.idreamsky.com</a> 创建应用就能得到相应的 <code>appkey</code>,<code>appSecrect</code>.</li>
</ul>
<h3>2.2 订单支付状态通知 (必接)</h3>
<h4>2.2.1 订单支付状态通知流程</h4>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/730f912618c8d9aeabda6eeb517ac586?showdoc=.jpg" alt="" /></p>
<p>充值接口需要 <code>服务器对服务器</code> 调用方式。
主要目的是乐逗服务器 把订单的状态通知到游戏服务器。
通知游戏服务器包含:订单号,openId,充值金额,实际到账金额, attach(透传参数)。
游戏服务器收到通知后返回正确的数据,并把相应的道具发放给玩家。</p>
<p><strong>注意:</strong></p>
<ul>
<li>游戏需要先在乐逗微服务控制台上配置支付回调地址。 </li>
<li>微服务业务控制台地址:<a href="http://biz.cloud.idreamsky.com">http://biz.cloud.idreamsky.com</a> 进入相应应用下的支付模块下配置。</li>
</ul>
<h4>2.2.2 支付回调地址</h4>
<p>游戏方提供一个回调接口,在玩家支付完后乐逗会调用该接口,通知游戏服务发放道具。</p>
<ul>
<li>方式:POST</li>
<li>请求头:</li>
</ul>
<table>
<thead>
<tr>
<th>参数名</th>
<th>中文描述</th>
<th>类型</th>
<th>是否必填</th>
</tr>
</thead>
<tbody>
<tr>
<td>Nonce</td>
<td>随机串</td>
<td>String</td>
<td>是</td>
</tr>
<tr>
<td>Timestamp</td>
<td>时间戳(毫秒)</td>
<td>String</td>
<td>是</td>
</tr>
<tr>
<td>Signature</td>
<td>签名</td>
<td>String</td>
<td>是</td>
</tr>
</tbody>
</table>
<ul>
<li>body参数:</li>
</ul>
<table>
<thead>
<tr>
<th>参数名</th>
<th>中文描述</th>
<th>类型</th>
<th>是否必填</th>
</tr>
</thead>
<tbody>
<tr>
<td>appId</td>
<td>商户在乐逗开放平台申请的APPID</td>
<td>String</td>
<td>必填</td>
</tr>
<tr>
<td>resultCode</td>
<td>业务结果 SUCCESS/FAIL</td>
<td>String</td>
<td>必填</td>
</tr>
<tr>
<td>totalAmount</td>
<td>订单总金额(元)</td>
<td>BigDecimal</td>
<td>必填</td>
</tr>
<tr>
<td>currency</td>
<td>币种(默认CNY=人民币)</td>
<td>String</td>
<td>必填</td>
</tr>
<tr>
<td>payAmount</td>
<td>支付金额(元)</td>
<td>BigDecimal</td>
<td>必填</td>
</tr>
<tr>
<td>payCurrency</td>
<td>支付币种(默认CNY=人民币)</td>
<td>String</td>
<td>必填</td>
</tr>
<tr>
<td>payTime</td>
<td>支付时间(格式:yyyy-MM-dd HH:mm:ss)</td>
<td>String</td>
<td>必填</td>
</tr>
<tr>
<td>payOrderNo</td>
<td>乐逗支付订单号</td>
<td>String</td>
<td>必填</td>
</tr>
<tr>
<td>outTradeNo</td>
<td>商户订单号</td>
<td>String</td>
<td>必填</td>
</tr>
<tr>
<td>openId</td>
<td>用户的乐逗的唯一标识</td>
<td>String</td>
<td>必填(内容可为空)</td>
</tr>
<tr>
<td>playerId</td>
<td>CP传入的玩家编号</td>
<td>String</td>
<td>必填</td>
</tr>
<tr>
<td>attach</td>
<td>商户透传字段</td>
<td>String</td>
<td>必填(内容可为空)</td>
</tr>
</tbody>
</table>
<p><strong>注意:</strong>
1、支付成功通知</p>
<pre><code class="language-json">{
"appId":"XXXX",
"outTradeNo":"xxxxxx",
"resultCode":"SUCCESS",
"totalAmount":0.01,
"currency":"CNY",
"payOrderNo":"xxxxxxxxxxxx",
"payAmount":0.01,
"payCurrency":"CNY",
"payTime":"2019-08-21 18:34:00",
"playerId":"XXXXXX",
"openId":"", --可为空
"attach":"" --可为空
}</code></pre>
<p>2、支付失败通知,仅返回如下字段(appid,resultCode,outTradeNo):</p>
<pre><code class="language-json">{
"appId":"xxx",
"resultCode":"FAIL",
"outTradeNo":"xxxx"
}</code></pre>
<p>以上参数是以POST方式通过json数据提交过去的,签名规则如下:
1、 将请求头除Signature的所有参数及请求体采用key=value方式,注意:请求体为requestBody={...}
2、 将所有的key按字典升序排序,排序好的格式为key1=value1&key2=value2&…
3、 将排好序的字符串前后拼接appSecret(由乐逗提供)
4、 第二步得到的源串进行签名,得到32位小写MD5签名串
md5(appSecrect&Key=value&requestBody={...}&appSecrect)</p>
<p><strong>签名示例</strong></p>
<blockquote>
<p>签名示例代码可以参考 <a href="https://www.showdoc.cc/mssdk?page_id=2992099572666798">签名示例代码</a> 文档。</p>
</blockquote>
<p>请求头
Nonce : 606130559785107456
Timestamp : 1565166201849
Signature : 9373edc5a62a64386ee4076d2e66dba4</p>
<p>1、请求体body(JSON格式)</p>
<pre><code>{
"appId":"10001",
"attach":"253be7f2-941b-47fb-b45b-385dfdbad7ec",
"currency":"CNY",
"openId":"04fe86f72b9bfcc02f7e849047e05b86",
"outTradeNo":"123456",
"payAmount":0.01,
"payCurrency":"CNY",
"payOrderNo":"DEV100011906281135450001",
"payTime":"2019-06-28 11:36:29",
"playerId":"3800790662",
"resultCode":"SUCCESS",
"totalAmount":0.01
}</code></pre>
<p>2、参数字典升排序字符串前后加上appSecrect(JSxPpoOzc9de9gC2wiSt)</p>
<pre><code>JSxPpoOzc9de9gC2wiSt &Nonce=606130559785107456&Timestamp=1565166201849&requestBody={"appId":"10001", "attach":"253be7f2-941b-47fb-b45b-385dfdbad7ec", "currency":"CNY", "openId":"04fe86f72b9bfcc02f7e849047e05b86", "outTradeNo":"123456","payAmount":0.01,"payCurrency":"CNY", "payOrderNo":"DEV100011906281135450001", "payTime":"2019-06-28 11:36:29", "playerId":"3800790662", "resultCode":"SUCCESS", "totalAmount":0.01}&JSxPpoOzc9de9gC2wiSt</code></pre>
<p>3、对数据进行MD5加密后得到<strong>32位小写加密串</strong>:</p>
<pre><code>9373edc5a62a64386ee4076d2e66dba4</code></pre>
<p>游戏服务器返回结果(json格式):</p>
<pre><code class="language-json">{
"returnCode":"xxx",
"returnMsg":"xxx"
}</code></pre>
<p>参数说明:
returnCode:<code>SUCCESS</code> 或者 <code>FAIL</code>
returnMsg:信息说明</p>
<p>返回码说明:
<code>SUCCESS</code>:表示游戏服务器已成功处理该订单逻辑了, 乐逗服务器不再发起通知。
<code>FAIL</code>: 表示游戏服务器这次逻辑处理失败了, 还需要收到乐逗服务器下次通知再次处理,或者无返回(超时),乐逗服务器会再做补单重发,总共发起八次通知,通知频率为:
<code>5s</code>,<code>15s</code>,<code>1分钟</code>,<code>5分钟</code>,<code>10分钟</code>,<code>20分钟</code>,<code>30分钟</code>,<code>1小时</code>。
八次之后将不再通知。</p>
<p><strong>注意事项:</strong></p>
<p>1、参与签名的参数严格使用请求表格的类型,请不要随意转换类型;
2、请求头除<strong>Signature不参与签名</strong>,其他参数全部参与签名;
3、签名值与参数先后顺序和大小写相关;
4、当 <code>resultCode</code> 为 <code>SUCCESS</code> 时才表示成功,其他值均为失败,失败时游戏端不要发货,乐逗服务器会再次发起通知;</p>