这一篇文章主要记录了C#服务端唤起steam交易授权页面时遇到的问题。
问题主要在于steam文档太难读了。
这一段是重点:
第一步:获取客户端请求生成订单时,要先请求GetUserInfo接口
请求地址:https://partner.steam-api.com/ISteamMicroTxn/GetUserInfo/v2/?key={steam_key}&appid={steam_appid}&steamid={steamID}
接口是GET请求方式,直接贴代码,其中steamID是客户端调用SteamUser.GetSteamID()获取的,返回值是一个UInt64类型,直接tostring()传给服务端就行:
private string GetSteamUserInfo(string steamID)
{
string ret = "";
if (!string.IsNullOrEmpty(steamID))
{
try
{
string country = "";
string url_userinfo = "";
if (isSandBox == "0")
{
//沙盒环境
url_userinfo = $"https://partner.steam-api.com/ISteamMicroTxnSandBox/GetUserInfo/v2/?key={steam_key}&appid={steam_appid}&steamid={steamID}";
}
else
{
url_userinfo = $"https://partner.steam-api.com/ISteamMicroTxn/GetUserInfo/v2/?key={steam_key}&appid={steam_appid}&steamid={steamID}";
}
string res = GetHttpRequest(url_userinfo);//就是封装的Get请求方式,函数代码就不贴了
if (string.IsNullOrEmpty(res))
{
return ret;
}
var jsonResult = JsonConvert.DeserializeObject<JObject>(res);
var result = JsonConvert.DeserializeObject<JObject>(jsonResult["response"].ToString());
if (result["result"].ToString() == "OK")
{
var resParam = JsonConvert.DeserializeObject<Dictionary<string, string>>(result["params"].ToString());
country = resParam["country"].ToString();
ret = resParam["currency"].ToString();
}
}
catch (Exception ex)
{
LogHelper.Error(ex.ToString());
}
}
return ret;
}
第二步:在获取steam接口返回的currency之后,就可以请求拉起steam支付授权页面了,这里要注意,客户端通过Steam接口SteamApps.GetCurrentGameLanguage()获取到的,这里要转化一下:
public static string TransferLanguage(string str)
{
string res = "";
switch (str)
{
case "arabic":
res = "ar";
break;
case "bulgarian":
res = "bg";
break;
case "schinese":
res = "zh-CN";
break;
case "tchinese":
res = "zh-TW";
break;
case "czech":
res = "cs";
break;
case "danish":
res = "da";
break;
case "dutch":
res = "nl";
break;
case "english":
res = "en";
break;
case "finnish":
res = "fi";
break;
case "french":
res = "fr";
break;
case "german":
res = "de";
break;
case "greek":
res = "el";
break;
case "hungarian":
res = "hu";
break;
case "italian":
res = "it";
break;
case "japanese":
res = "ja";
break;
case "koreana":
res = "ko";
break;
case "norwegian":
res = "no";
break;
case "polish":
res = "pl";
break;
case "portuguese":
res = "pt";
break;
case "brazilian":
res = "pt-BR";
break;
case "romanian":
res = "ro";
break;
case "russian":
res = "ru";
break;
case "spanish":
res = "es";
break;
case "latam":
res = "es-419";
break;
case "swedish":
res = "sv";
break;
case "thai":
res = "th";
break;
case "turkish":
res = "tr";
break;
case "ukrainian":
res = "uk";
break;
case "vietnamese":
res = "vn";
break;
}
return res;
}
然后,请求steam,这里steam会自己在你的客户端拉起steam的支付授权界面,贴代码:
请求地址:https://partner.steam-api.com/ISteamMicroTxn/InitTxn/v3/
private async Task<bool> CallSteamPay(string steamID, string cpOrderID, string language, int ammount, string itemID, string productName, string currency)
{
bool ret = false;
if (string.IsNullOrEmpty(steamID) || string.IsNullOrEmpty(cpOrderID) || string.IsNullOrEmpty(language) || string.IsNullOrEmpty(itemID)
|| string.IsNullOrEmpty(productName) || string.IsNullOrEmpty(currency) || ammount <= 0)
{
return ret;
}
try
{
var urlParams = new Dictionary<string, string>()
{
{ "key",steam_key},//Steamworks Web API 发行商验证密钥。
{ "orderid",cpOrderID},// 订单的 64 位唯一 ID。
{ "steamid",steamID},//进行购买的用户的 Steam ID。
{ "appid",steam_appid},// 此交易所针对的游戏的 app ID。
{ "itemcount","1"},//购物车中的物品数量。
{ "language",language},//物品描述的 ISO 639-1 语言代码。
{ "currency",currency},//ISO 4217 货币代码。 参见支持币种,了解每个币种的正确格式。
{ "itemid[0]",itemID},// 物品的第三方 ID。
{ "qty[0]","1"},//物品的数量。
{ "amount[0]",ammount.ToString()},//目前要收取的物品的总费用(以分计)。
{ "description[0]",productName}//物品的描述。 最大长度为 128 字符。
};
string url_initTxn = "";
if (isSandBox == "0")
{
//沙盒环境
url_initTxn = "https://partner.steam-api.com/ISteamMicroTxnSandBox/InitTxn/v3/";
}
else
{
url_initTxn = "https://partner.steam-api.com/ISteamMicroTxn/InitTxn/v3/";
}
var res = await GetHttpRequestToPostFormData(url_initTxn, urlParams);
if (!string.IsNullOrEmpty(res))
{
var callResult = JsonConvert.DeserializeObject<JObject>(res);
var result = JsonConvert.DeserializeObject<JObject>(callResult["response"].ToString());
if (result["result"].ToString() == "OK")
{
ret = true;
}
}
}
catch (Exception ex)
{
LogHelper.Error(ex.ToString());
}
return ret;
}
这个接口在我尝试了好几种方法后,始终提示400 BadRequest,当我从HttpWebRequest更换为HttpClient请求方式后,返回了Required param‘orderid’ is missing,我就知道是我ContentType定义错了:
public static async Task<string> GetHttpRequestToPostFormData(string url, Dictionary<string, string> postData)
{
if (string.IsNullOrEmpty(url))
return string.Empty;
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Connection.Add("keep-alive");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
HttpContent cont = new FormUrlEncodedContent(postData);
cont.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
string result= string.Empty;
try
{
var response = await httpClient.PostAsync(url, cont);
if(response.StatusCode == HttpStatusCode.OK)
{
result = await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
result = ex.ToString();
}
return result;
}
第三步:用户在支付授权成功授权后,steam会通过客户端回调方法通知,之后客户端请求服务端发放道具,服务端在收到成功授权后,调用ISteamMicroTxnSandBox/FinalizeTxn接口,查看订单状态,之后就可以发道具了:
请求地址:https://partner.steam-api.com/ISteamMicroTxn/FinalizeTxn/v2/
[HttpPost]
public async Task<string> Finalize()
{
var ret = new { code = 1, msg = "", data = "" };
try
{
var orderID = GetFormValue(Request.Form, "cpOrderID");
var sign = GetFormValue(Request.Form, "sign");
StringBuilder sb = new StringBuilder();
sb.Append("orderID=" + orderID);
sb.Append(shaKey);
//针对客户端的请求需要做md5加密
var ticket = MD5DecryptLong(sb.ToString()).ToLower();
if (ticket.Equals(sign))
{
var urlParams = new Dictionary<string, string>()
{
{ "key",steam_key},
{ "orderid",orderID},
{ "appid",steam_appid}
};
string finalUrl;
if (isSandBox == "0")
{
finalUrl = "https://partner.steam-api.com/ISteamMicroTxnSandBox/FinalizeTxn/v2/";
}
else
{
finalUrl = "https://partner.steam-api.com/ISteamMicroTxn/FinalizeTxn/v2/";
}
string res = await GetHttpRequestToPostFormData(finalUrl, urlParams);
if (!string.IsNullOrEmpty(res))
{
var jsonResult = JsonConvert.DeserializeObject<JObject>(res);
var result = JsonConvert.DeserializeObject<JObject>(jsonResult["response"].ToString());
if (result["result"].ToString() == "OK")
{
//
//在这里可以添加你发放道具的代码了
//
ret = new { code = 0, msg = msg, data = "" };
}
else
{
ret = new { code = 2, msg = "Not Pay", data = "" };
}
}
else
{
ret = new { code = 1, msg = "Query Error", data = "" };
}
}
else
{
ret = new { code = 5, msg = "Sign Error", data = "" };
}
}
catch (Exception ex)
{
LogHelper.Error(ex.ToString());
ret = new { code = 500, msg = "Server Error", data = "" };
}
return JsonConvert.SerializeObject(ret);
}
到这里,整个内购充值流程就结束了。
总结了一下在接入过程中遇到的几种问题:
1、拉起支付页面必须要客户端打包上传到steam Workers后台,然后通过steam下载后运行才能唤起支付页面。
2、ISteamMicroTxnSandBox/FinalizeTxn接口返回User ‘xxxxxxxx’ is not logger in,一般是你请求参数中appid和web api key错误导致的,一定检查一下appid和客户端打包的appid是否一致!
3、唤起的支付授权界面会出现页面加载错误,或者样式啥的没加载出来,这个一般是网络问题,可以开vpn,或者下载一个网易UU加速器,里面有免费的Steam社区加速。
记录一下自己的学习记录。
接入steam,还是要把文档多读几遍!!!!