steam游戏内置商店购买

文档地址:https://partner.steamgames.com/doc/features/microtransactions/implementation

1.服务器需要订单信息:
①用户信息

'steamid' => '',
'language' => '客户端游戏语言代码 – ISO 639-1 语言代码,客户端游戏正在使用的语言',
'currency' => 'SO 4217 货币代码,对客户端扣款所使用的币种'

②订单信息

'itemcount' => '购买总数(int)',
'orderid' => '由您为此次购买指定的唯一的 64 位数字。 此数字也是此次交易的关键, 在 Steam 系统中引用该交易时使用(int)',
'itemid[0]' => '商品id(int)',
'qty[0]' =>'商品数量(int)',
'amount[0]' => '商品总价值(单位:分)(int)',
'description[0]' => '商品描述',

③游戏信息

'key' => '网页 API 密钥',
'appid' => '您的 Steam 游戏的唯一标识符'

注意事项:
currency字段由服务器获取,orderid由服务器生成外,其他信息均由游戏服务器提供。
language虽由客户端提供,但客户端接口返回的language并不是iso 639-1标准的,转换如下:(键为客户端使用语言,值为web服务器使用语言)

private function languages($language)
    {
        $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',
            'swedish' => 'sv',
            'thai' => 'th',
            'turkish' => 'tr',
            'ukrainian' => 'uk',
        ];

        return $languages[$language];
    }
正式地址:
$baseUri = 'https://partner.steam-api.com/ISteamMicroTxn/';
测试地址:
$basrUriSandBox = 'https://partner.steam-api.com/ISteamMicroTxnSandbox/';

curl https

function curl_get_https($url){
    $curl = curl_init(); // 启动一个CURL会话
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_HEADER, 0);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, true);  // 从证书中检查SSL加密算法是否存在
    $tmpInfo = curl_exec($curl);     //返回api的json对象
    //关闭URL请求
    curl_close($curl);
    return $tmpInfo;    //返回json对象
}

function curl_post_https($url,$data){ // 模拟提交数据函数
    $curl = curl_init(); // 启动一个CURL会话
    curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); // 从证书中检查SSL加密算法是否存在
    curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
    curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
    curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
    curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包
    curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
    curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
    $tmpInfo = curl_exec($curl); // 执行操作
    if (curl_errno($curl)) {
        echo 'Errno'.curl_error($curl);//捕抓异常
    }
    curl_close($curl); // 关闭CURL会话
    return $tmpInfo; // 返回数据,json格式
}

①获取用户currency: https://partner.steamgames.com/doc/webapi/ISteamMicroTxn#GetUserInfo

private function getSteamUserInfo($steamId)
    {
        //GET https://partner.steam-api.com/ISteamMicroTxn/GetUserInfo/v2/
        $steamInfoUrl = $this->baseUri . 'GetUserInfo/v2/?key=%s&steamid=%s';
        $url = sprintf($steamInfoUrl, $this->apiKey, $steamId);

        $response = curl_post_https($url);
        $json = json_decode($response, true);

        if ($json['response']['result'] == 'Failure') {
            \Log::info('steam_user_info', $json);
            throw new \Exception('Steam Response Error');
        }

        return $json['response']['params'];
    }

②向steam发起购买: https://partner.steamgames.com/doc/webapi/ISteamMicroTxn#InitTxn

public function initOrder(Request $request)
    {
        $attributes = $request->only([
            'steamid', 'appid', 'language', 'itemid'
        ]);
       
        $userInfo = $this->getSteamUserInfo($attributes['steamid']);
        $attributes['currency'] = $userInfo['currency'];
        
         //生成唯一订单号
        $order = SteamOrders::query()->orderByDesc('created_at')->first(['orderid']);
        $attributes['orderid'] = $order ? $order->orderid + 1 : 10000000;

        $goods = $this->goods($attributes['itemid']);
         //订单信息
        $data = [
            'key' => $this->apiKey,
            'steamid' => $attributes['steamid'],
            'appid' => $attributes['appid'],
            'language' => $this->languages($attributes['language']),
            'itemcount' => 1,
            'orderid' => (int)$attributes['orderid'],
            'currency' => $attributes['currency'],
            'itemid[0]' => $goods['itemid'],
            'qty[0]' => 1,//(int)$attributes['qty'],
            'amount[0]' => $goods['amount'],
            'description[0]' => $goods['description'],
        ];

        //POST https://partner.steam-api.com/ISteamMicroTxn/InitTxn/v3/
        $initTxnUrl = $this->baseUri . 'InitTxn/v3/';
        $response = $this->curlPostHttps($initTxnUrl, $data);       
        $result = json_decode($response, true);
        $result = $result['response'];

        if ($result['result'] == 'Failure') {
            \Log::info('steam_init_order', $result);

            return ['code' => 500, 'message' => 'Steam Response Error'];
        }

        $attributes['amount'] = $goods['amount'];
        $attributes['description'] = $goods['description'];
        SteamOrders::create($attributes);

        return [
            'code' => 0,
            'data' => [
                'orderid' => $attributes['orderid'],
                'appid' => $attributes['appid']
            ]
        ];
    }

③完成订单:https://partner.steamgames.com/doc/webapi/ISteamMicroTxn#FinalizeTxn

public function finishOrder(Request $request)
    {
        $userId = '';

        $attributes = $request->only(['appid', 'orderid']);
        $attributes['key'] = $this->apiKey;

        //POST https://partner.steam-api.com/ISteamMicroTxn/FinalizeTxn/v2/
        $finishTxnUrl = $this->baseUri . 'FinalizeTxn/v2/';
        //$response = $this->client->post($initTxnUrl, ['form_params' => $attributes, 'verify' => false]);
        $response = $this->curlPostHttps($finishTxnUrl, $attributes);
        $result = json_decode($response, true);
        $result = $result['response'];

        if ($result['result'] == 'Failure') {
            \Log::info('steam_finish_order', $result);

            return ['code' => 500, 'message' => 'Steam Response Error'];
        }

        $order = SteamOrders::query()->where('orderid', $attributes['orderid'])->first();
        $itemId = $order->itemid;

        $result = $this->sendUserGoods($userId, $itemId);
        $status = 2;
        if ($result) {
            $status = 3;
        }

        $order->user_id = $userId;
        $order->transid = $result['params']['transid'];
        $order->pay_state = $status;
        $order->save();

        return ['code' => 0];
    }

后记:数据库设计

Schema::create('steam_orders', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->nullable();
            $table->integer('orderid');
            $table->integer('transid')->nullable()->comment('steam交易id');
            $table->integer('steamid');
            $table->string('currency')->comment('货币类型');
            $table->string('appid');
            $table->string('language');
            $table->string('itemid', 50)->comment('商品id');
            $table->tinyInteger('qty')->default(1)->comment('购买数量');
            $table->bigInteger('amount')->default(0)->comment('总价:分');
            $table->string('description', 255)->comment('商品描述');
            $table->tinyInteger('pay_state')->default(1)->comment('交易状态:1未支付,2支付完成,3已发货');
            $table->timestamps();
        });
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容