其他分享
首页 > 其他分享> > unity接入steam支付

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"+
			"&currency=%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