unity接入steam支付
作者:互联网
接到策划需求,需要将移动端开发的游戏移植到steam平台。奈何策划水平有点次,信誓旦旦的说什么steam支付是使用dlc支付的,使用什么dlc币(窝里个大草),马的,不懂就别瞎咧咧,净给老子挖坑。
查了一圈资料,在steam后台配置若干steam相关dlc相关,就是没有相关与dlc相关支付接口,这时候才意识到公司策划真不靠谱,还老是逼逼开发,搞得一肚子气,马上就摔键盘走人,但是这才是给我挖的第一个坑。
踩了坑之后发现,dlc配置完成之后,是在steam商店里面起作用的,不是在游戏商店起作用,和游戏内购完全没有关系,steam里面的内购又叫做小额支付,相关文档在这里。
在接入小额支付时候,需要一个关键参数key,文档解释key是Steamworks Web API 发行商验证密钥。对的,你没猜错,这是他们给我挖的第二个坑。当时注明要发行商密钥,可是偏偏他们给的还是用户密钥,接口调试不通,让他们确认的时候,还是一口咬定给我的是发行商密钥,mmp想打人。
好的,抱怨完了,接下来我们开始接入sdk,steam的文档很怪,明明都是中文可就是看不懂,网上翻了一遍又一遍资料终于给摸索出来了。
服务端接入
下面是相关服务器接入相关代码(go开发的,大家可进行相关参考,感觉坑的是请求Header参数构造以及下订单时参数的构造,这些在steam文档里面是没有的)
const (
SANDBOX_URL = "https://partner.steam-api.com/ISteamMicroTxnSandbox/"
PRODUCT_URL = "https://partner.steam-api.com/ISteamMicroTxn/"
KEY = "--------------------MY KEY----------------------" //这里替换成自己的发行商密钥
)
type userInfoResp struct {
Response respInfo `json:"response"`
}
type userInfoParams struct {
State string `json:"state"`
Country string `json:"country"`
Currency string `json:"currency"`
Status string `json:"status"`
}
type respInfo struct {
Result string `json:"result"`
Params userInfoParams `json:"params"`
}
type steamOrderInfo struct {
Response steamOrderResponse `json:"response"`
}
type steamOrderParams struct {
Orderid string `json:"orderid"`
Transid string `json:"transid"`
}
type steamOrderResponse struct {
Result string `json:"result"`
Params steamOrderParams `json:"params"`
ErrorObj ErrorObj `json:"error"`
}
type ErrorObj struct {
ErrorCode string `json:"errorcode"`
ErrorDesc string `json:"errordesc"`
}
func getUrl(isSandbox bool) string {
if isSandbox {
return SANDBOX_URL
} else {
return PRODUCT_URL
}
}
func steamPostReq(httpUrl string, data io.Reader) (*http.Response, error) {
req, err := http.NewRequest("POST", httpUrl, data)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")
req.Header.Set("Accept-Language", "en_US")
client := &http.Client{}
resp, err := client.Do(req)
return resp, err
}
// 获取用户所需的相关国家地区等信息
func GetUserInfo(write http.ResponseWriter, request *http.Request) {
body := utils.GetRequestMap(request, "getUserInfo")
appid := body["appid"].(string)
steamid := body["steamid"].(string)
isSandbox := body["isSandbox"].(bool)
log.Info("Steam UserInfo Body %v", body)
userInfo := getSteamUserInfo(appid, steamid, isSandbox)
log.Info("Steam UserInfo UserInfo %v", userInfo)
utils.ResultSuccess(write, userInfo)
}
func getSteamUserInfo(appid, steamid string, isSandbox bool) *userInfoParams {
url := getUrl(isSandbox) + "GetUserInfo/v2/?key=%s&appid=%s&steamid=%s"
url = fmt.Sprintf(url, KEY, appid, steamid)
client := &http.Client{}
resp, err := client.Get(url)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Info("GetUserInfo err:%v", err)
return nil
}
info := new(userInfoResp)
err = json.Unmarshal(body, &info)
if err != nil {
log.Info("Get UserInfo Unmarshal err:", err)
return nil
}
return &info.Response.Params
}
// InitTxn 用户请求下订单
func InitTxn(write http.ResponseWriter, request *http.Request) {
txnInfo := utils.GetRequestMap(request, "initTxn")
steamId, _ := txnInfo["steamid"].(string)
appid, _ := txnInfo["appid"].(string)
language := txnInfo["language"].(string)
currency := txnInfo["currency"].(string)
itemId, _ := txnInfo["itemid"].(string)
amount, _ := txnInfo["amount"].(string)
userId, _ := utils.CovToLong(txnInfo["userId"])
description := txnInfo["description"].(string)
userIdStr := strconv.FormatInt(userId, 10)
orderId := userIdStr[len(userIdStr)-6:] + strconv.FormatInt(time.Now().Unix(), 10)
isSandbox := txnInfo["isSandbox"].(bool)
httpUrl := getUrl(isSandbox) + "InitTxn/v3/"
dataStr := fmt.Sprintf(
"key=%v"+
"&orderid=%v"+
"&steamid=%v"+
"&appid=%v"+
"&itemcount=%v"+
"&language=%v"+
"¤cy=%v"+
"&itemid[0]=%v"+
"&qty[0]=%v"+
"&amount[0]=%v"+
"&description[0]=%v"+
"&usersession=%v",
KEY, orderId, steamId, appid, 1, language, currency, itemId, "1", amount, description, "client")
data := strings.NewReader(dataStr)
resp, err := steamPostReq(httpUrl, data)
defer resp.Body.Close()
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Info("Steam Purchase Fail:%v", err.Error())
}
steamOrderInfo := new(steamOrderInfo)
err = json.Unmarshal(content, &steamOrderInfo)
log.Info("Steam Purchase back:%v", string(content))
utils.ResultSuccess(write, steamOrderInfo)
}
// FinalizeTxn 验证订单
func FinalizeTxn(write http.ResponseWriter, request *http.Request) {
txnInfo := utils.GetRequestMap(request, "finalizeTxn")
isSandbox := txnInfo["isSandbox"].(bool)
orderid := txnInfo["orderid"].(string)
appid := txnInfo["appid"].(string)
httpUrl := getUrl(isSandbox) + "FinalizeTxn/v2/"
dataStr := fmt.Sprintf("key=%v"+
"&orderid=%v"+
"&appid=%v", KEY, orderid, appid)
data := strings.NewReader(dataStr)
resp, err := steamPostReq(httpUrl, data)
defer resp.Body.Close()
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Info("Steam Purchase Confirm back Fail:%v", err.Error())
}
log.Info("Steam Purchase Confirm back:%v", string(content))
steamOrderInfo := new(steamOrderInfo)
err = json.Unmarshal(content, &steamOrderInfo)
if err != nil {
log.Error("Steam Purchase Confirm back response error :%v", err)
return
}
//TODO 执行相关入库操作
// ...
utils.ResultSuccess(write, steamOrderInfo)
}
客户端的接入
客户端执行步骤Window>PackageManager>点击+>Add package from git URL,然后填入相关地址Add即可(这个地址可能版本有变动,具体的以文档为准)https://github.com/rlabrecque/Steamworks.NET/releases
在sdk引入项目后,你会发现,在项目的Assets目录的同级目录下,会生成一个steam_appid.txt文件,里面只存了一个appid,这个appid是一个默认值480,当你的app申请下来时,替换成自己的appid即可。需要注意的是项目平台需要切到PC平台,然后下载steam客户端,登录账号,才能正常进入编辑器,此时你打开steam会发现游戏正在运行,那就是指的你的unity编辑器。
开发过程中移动端和pc端会经常切换,而steam提供的宏定义DISABLESTEAMWORKS是关闭steam功能的,这个比较怪,也即是说默认情况下steam平台功能是开启的,再来回切平台时候要注意点。
在客户端点击支付之前,需要访问服务器接口GetUserInfo,获取接下来下订单所需的参数(country: ISO 3166-1-alpha-2 国家代码、currency:价格的 ISO 4217 货币代码。)这个推荐游戏启动时候就调用并存在本地。
接下来就是下订单所需的另外的参数
language
public string GetCurrentGameLanguage()
{
return SteamApps.GetCurrentGameLanguage();
}
通过C#调用,但是返回过来的不是文档上要求的 ISO 639-1 语言代码。为此续写一个转换方法,然后将转换之后的数值传递给服务器即可。
local languages = {
["arabic"] = "ar",
["bulgarian"] = "bg",
["schinese"] = "zh-CN",
["tchinese"] = "zh-TW",
["czech"] = "cs",
["danish"] = "da",
["dutch"] = "nl",
["english"] = "en",
["finnish"] = "fi",
["french"] = "fr",
["german"] = "de",
["greek"] = "el",
["hungarian"] = "hu",
["italian"] = "it",
["japanese"] = "ja",
["koreana"] = "ko",
["norwegian"] = "no",
["polish"] = "pl",
["portuguese"] = "pt",
["brazilian"] = "pt-BR",
["romanian"] = "ro",
["russian"] = "ru",
["spanish"] = "es",
["latam"] = "es-419",
["swedish"] = "sv",
["thai"] = "th",
["turkish"] = "tr",
["ukrainian"] = "uk",
["vietnamese"] = "vn",
}
function SteamUtil.GetWebAPI(key)
return languages[key]
end
和其他的支付不同(如谷歌支付,苹果支付),其他的支付平台能够提供在不同地区商品的价格以及货币符号类型,在steam平台需要自己获取到地区然后根据自己的配置,将价格传递给服务器。
好的,现在如果你准备好了各种参数,然后打包,上传至steam,再在steam平台下载游戏,如果一切正常的话,调用服务器的下订单接口,成功的话会出来一个steam支付弹窗,如下:(打码主要是怕公司策划看到,毕竟程序的地位最低,测试都能给我们骂哭)
如果此时关闭支付弹窗,或者点击支付完成,在C#端会收到一个回调(首先回调要正确加入)
private Action<uint, ulong> OnPurchaseCallback;
private void OnEnable()
{
m_MicroTxnAuthorizationResponse =
Steamworks.Callback<MicroTxnAuthorizationResponse_t>.Create(OnMicroTxnAuthorizationResponse);
}
private void OnMicroTxnAuthorizationResponse(MicroTxnAuthorizationResponse_t pCallback)
{
Debug.Log("[" + MicroTxnAuthorizationResponse_t.k_iCallback + " - MicroTxnAuthorizationResponse] - " +
pCallback.m_unAppID + " -- " + pCallback.m_ulOrderID + " -- " + pCallback.m_bAuthorized);
OnPurchaseCallback?.Invoke(pCallback.m_unAppID, pCallback.m_ulOrderID);
}
然后在回调中调用服务器的支付验证接口,验证正常的话就下发奖励。
至此,steam支付接入完成,再次咒骂一下垃圾策划!!!
标签:string,err,接入,json,unity,appid,steam,txnInfo 来源: https://www.cnblogs.com/Yellow0-0River/p/16266459.html