米洛SDK 服务器对接文档

本文档用于游戏服务器端对接米洛SDK平台,包含充值回调接口、签名验证、登录二次验证等完整服务端接入指南。

接口说明

合作商需要开发一个接收充值结果的系统,提供该系统的 URL 给米洛SDK,由米洛SDK发起请求,合作商响应请求。请求协议采用 HTTP POST

1.1.1 通知充值结果

玩家在游戏中使用米洛SDK 进行充值,平台在处理完充值流程之后,主动请求通知游戏方,游戏方返回接收成功(接收成功判断请参考合作商响应请求),平台会启动系统重发机制(一共发送五次,如果五次没有响应将由工作人员补单)。

注意

只有充值成功才会回调通知结果,充值失败的不会通知。

合作商接收到请求后,需要对所有 String 类型的参数值做 urldecode 处理,urldecode 编码统一为 UTF-8,请求参数具体字段说明如下:

参数名 类型 长度 参数说明 签名顺序
orderid String 35 平台订单号 1
cporderid String 35 游戏订单号 2
username String 30 平台登录账号 3
gameid int 11 游戏 ID 4
roleid String 30 游戏角色 ID 5
serverid int 11 服务器 ID 6
amount int 11 成功充值金额,单位(元) 7
paytime int 11 玩家充值时间,时间戳形式,如 1394087000 8
extrasparams String - 商户拓展参数 9
sign String 32 参数签名(用于验签对比) 10

1.1.2 签名说明

合作商接收到平台的请求后,需要获取相关参数并做验签处理,验签规则如下:以请求参数字段说明中的签名顺序签名,规则为 key=value 的形式,参数之间以"&"符号相连,组成一串字符串。目前包共 10个签名字段。

签名算法为 MD5,统一使用 32 位 UTF-8 加密算法,编码后转为小写与参数中的 sign 进行对比,如果相同-则签名通过,否则-失败。

签名字符串示例

重要说明

下文中的key为服务端的paykey 与登录使用的appkey不同

签名示例
sign = MD5("orderid=100000&cporderid=100000&username=zhangsan&gameid=6&roleid=zhangsanfeng&serverid=1&amount=1&paytime=20130101125612&extrasparams=test&key=123123123213")

注意

  • 验签参数值为 urlencode 后的内容
  • 如果参数没有数据值,请以"key="的形式进行签名,例如:paytime=&extrasparams=自定义
  • 要注意字段的大小写
  • key 由米洛SDK提供,登录、充值使用同一个 key

PHP验签示例

PHP
<?php
// 获取参数
$orderid = $_POST['orderid'];
$cporderid = $_POST['cporderid'];
$username = $_POST['username'];
$gameid = $_POST['gameid'];
$roleid = $_POST['roleid'];
$serverid = $_POST['serverid'];
$amount = $_POST['amount'];
$paytime = $_POST['paytime'];
$extrasparams = $_POST['extrasparams'];
$sign = $_POST['sign'];

// 你的paykey
$key = 'YOUR_PAY_KEY';

// 组装签名字符串
$signStr = "orderid={$orderid}&cporderid={$cporderid}&username={$username}&gameid={$gameid}&roleid={$roleid}&serverid={$serverid}&amount={$amount}&paytime={$paytime}&extrasparams={$extrasparams}&key={$key}";

// 计算签名
$mySign = strtolower(md5($signStr));

// 验证签名
if ($mySign === $sign) {
    echo 'success';
    // 处理充值逻辑
} else {
    echo 'errorSign';
}
?>

Java验签示例

Java
import java.security.MessageDigest;

public class SignUtil {
    public static boolean verifySign(String orderid, String cporderid, String username, 
            String gameid, String roleid, String serverid, String amount, 
            String paytime, String extrasparams, String sign, String key) {
        try {
            // 组装签名字符串
            String signStr = "orderid=" + orderid + 
                           "&cporderid=" + cporderid + 
                           "&username=" + username + 
                           "&gameid=" + gameid + 
                           "&roleid=" + roleid + 
                           "&serverid=" + serverid + 
                           "&amount=" + amount + 
                           "&paytime=" + paytime + 
                           "&extrasparams=" + extrasparams + 
                           "&key=" + key;
            
            // MD5加密
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(signStr.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte b : array) {
                sb.append(String.format("%02x", b));
            }
            String mySign = sb.toString().toLowerCase();
            
            // 验证签名
            return mySign.equals(sign);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

1.1.3 合作商响应请求

合作商接收到平台发出的请求后,以纯字符串的形式返回下列代码:

响应示例
out.print("success");
代码 代码描述
success 接收成功
errorSign 签名错误
error 未知错误

重要

如果合作商未返回"success",平台会启用系统重发机制

1.1.4 温馨提示

建议

  1. 避免重复发放
    由于平台同步系统暂时并不能绝对保证一个订单只同步一次(订单本身是唯一的),因此建议合作商在发放游戏虚拟货币的时候做一次订单的判断,避免同一订单多次发放。
  2. 处理订单状态变更
    总结运营阶段的经验,平台侧发现支付合作商有时候一个订单会同步两次状态,第一次为失败,第二次成功(实际为成功)。因此建议合作商接收同步的订单时处理一下此类情况,例如:系统考虑支持同一个订单,第一次同步失败状态,第二次同步成功状态,系统照常发放游戏币。

1.2 服务器端的登陆二次验证

请求接口

API Endpoint
https://unify.wancms.com/sdkstandard/login/verification

请求参数

参数名 说明 来源
username 用户名 登录接口返回
logintime 登录时间 登录接口返回
token 登录令牌 登录接口返回
gameid 游戏ID 配置的游戏id

请求方式

  • 方法:GET / POST
  • 格式:key/value 提交

请求示例

HTTP GET
https://unify.wancms.com/sdkstandard/login/verification?username=wan_xttxtt01&gameid=1&token=448b9c8b521194949e8b6b6ec736781e&logintime=1672797023

返回格式

字段 说明
a 状态码:成功=1,失败=-1
b 提示信息

返回示例

成功响应

JSON
{
    "a": 1,
    "b": "成功"
}

失败响应

JSON
{
    "a": -1,
    "b": "验证失败"
}

PHP调用示例

PHP
<?php
$username = 'wan_xttxtt01';
$gameid = 1;
$token = '448b9c8b521194949e8b6b6ec736781e';
$logintime = 1672797023;

$url = "https://unify.wancms.com/sdkstandard/login/verification";
$params = [
    'username' => $username,
    'gameid' => $gameid,
    'token' => $token,
    'logintime' => $logintime
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . '?' . http_build_query($params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
if ($result['a'] == 1) {
    echo "验证成功";
} else {
    echo "验证失败:" . $result['b'];
}
?>

Java调用示例

Java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.json.JSONObject;

public class LoginVerification {
    public static boolean verifyLogin(String username, int gameid, 
                                     String token, long logintime) {
        try {
            String baseUrl = "https://unify.wancms.com/sdkstandard/login/verification";
            String params = String.format("?username=%s&gameid=%d&token=%s&logintime=%d",
                username, gameid, token, logintime);
            
            URL url = new URL(baseUrl + params);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);
            
            BufferedReader in = new BufferedReader(
                new InputStreamReader(conn.getInputStream()));
            String inputLine;
            StringBuilder response = new StringBuilder();
            
            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();
            
            JSONObject result = new JSONObject(response.toString());
            return result.getInt("a") == 1;
            
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}