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支付接入完成,再次咒骂一下垃圾策划!!!
相关知识
unity接入steam支付
steam小额支付对接心得由于最近在steam渠道的客户端要推出商品购买功能,而steam官方要求使用steam小额支付
开放能力 / PC接入指南 / PC小游戏接入指南
游戏内购服务功能介绍
Unity游戏发行服务
城市型恋爱冒险游戏《循环支付》繁体中文版公开
《潜水员戴夫》unity报错解决方法
微信小游戏内购米大师支付,不同金额创单问题处理
Unity
近期发布微信小游戏的流程与心得:引擎选择、资源分包、软著申请、SDK接入及审核流程
推荐资讯
- 1老六爱找茬美女的烦恼怎么过- 4999
- 2博德之门3黄金雏龙法杖怎么得 4867
- 3《大侠立志传》剿灭摸金门任务 4312
- 4代号破晓官方正版角色介绍 4023
- 5赛马娘锻炼到底的伙伴支援卡事 3802
- 6闪烁之光11月兑换码大全20 3774
- 7原神原海异种刷怪路线-原神原 3547
- 8爆梗找茬王厕所特工怎么通关- 3542
- 9《我的世界》领地删除指令是什 3440
- 10原神开局星落湖怎么出去 原神 3426