Unity接入IAP内购(Android,IOS)最新流程,第三篇 后台对购买结果的验证以及发货(Android)


欢迎进入Unity内购系列

你好! 这将是一个系列的文章
第一篇 介绍客户端里支付的调起以及购买。
第二篇 介绍后台对购买结果的验证以及发货(IOS)。
第三篇 介绍后台对购买结果的验证以及发货(Android)。
第四篇 介绍后台对内购退单问题的处理(IOS欺诈检测以及欺诈信息反馈)。

第三篇 后台对购买结果的验证以及发货(Android)

本篇介绍PHP后台对购买结果的验证以及发货(Android)。
这是纯后端内容,不限于Unity项目,其他项目同样可用。

重要提示:
1、本人不会PHP,这是同事提供的代码,是项目实战代码,代码里有些遗漏的还请自行添加,万一有无法解决的可以留言,我再请教同事。
2、代码中涉及到sql和db相关的代码为数据库操作,需要根据自己的项目实现

一、基本流程

1、玩家在客户端内发起购买,并成功付款
2、Google返回购买凭证给客户端
3、客户端将购买凭证发送给服务器,由服务器向Google验证真伪
4、服务器根据验证结果给玩法发放相关虚拟商品

二、GooglePlay后台设置

1、在Google开发者后台中启用Google Play Android Developer项目
2、进入GoogleAPI控制台,创建一个Web 类型的客户端 ID,具体步骤可以参考我的另一篇文章《Unity接入Google登录超详细流程》
3、打开浏览器打开这个地址:

https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri=http://localhost:8080&client_id=123122121

其中redirect_uri换成自己第二部生成的客户端 ID时填入的网址(可以随便写,但是如果是真实的地址,他会很快跳转,导致拿不到想要的东西,所以一般情况填写http://localhost:8080),client_id换成自己第二部生成的客户端 ID
访问这个地址后会叫你登录google账号,就选择开发者这个账号登录
4、登录成功后,会在浏览器的地址栏中包含code,只要拿出code的值即可:

Code=4/bAEGOfkpDnG5hhtv8E7FSkKPp-oFVQpTPeg8l_jWjKQd5BaOviZLyimEywJR9ptEoFtRqb95sZh4yXfRLI81BbM

5、获取到code后发送post请求,以下POST请求实例以postman工具来进行操作
(1)、请求头 Content-Type = application/x-www-form-urlencoded
(2)、请求地址 https://accounts.google.com/o/oauth2/token
(3)、参数

grant_type=authorization_code(为固定值)
code=4步中获取到的code值,
client_id=客户端ID,(第二步生成的客户端ID)
client_secret=客户端密钥,(第二步生成的客户端ID,里面对应的密钥)
redirect_url=重定向链接(http://localhost:8080

6、请求结果有一个refresh_token,这个是永久的,而且只会返回一次,这个refresh_token需要保存给php,后续步骤需要用到。

以下3个步骤由php代码动态实现,不需要手动操作了
以下3个步骤由php代码动态实现,不需要手动操作了
以下3个步骤由php代码动态实现,不需要手动操作了
7、POST请求:携带refresh_token可获取access_token
(1)、请求头 Content-Type = application/x-www-form-urlencoded
(2)、请求地址 https://accounts.google.com/o/oauth2/token
(3)、参数

grant_type=refresh_token为固定值,
client_id=客户端ID,(第二步生成的客户端ID下载下来直接交给php)
client_secret=客户端密钥,(第二步生成的客户端ID下载下来直接交给php)
refresh_token=5步中获取到的refresh_token值,

8、验证订单:GET请求(不再是POST)

GET地址:

https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{purchaseToken}?access_token=access_token

相关参数:

packageName=需要查询的应用ID(包名、gradle中的applicationId )
productId=开发者后台中创建好的商品ID
purchaseToken=客户端传过来的purchaseToken
access_token=7步中获得的access_token

三、PHP代码实现

unity客户端需要传给php的数据:

	public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    {
        //支付成功返回给客户端的数据
        var product = args.purchasedProduct;

        Debug.Log("Purchase Complete - Product: " + product.definition.id);
        string receipt = args.purchasedProduct.receipt;
        //receipt 这个要发送给php
        /*
		此处为自己客户端跟php通信的代码
		*/
        
        return PurchaseProcessingResult.Complete;
    }

php验证订单真伪:

	/********************google内购start**********************/



	/**

	 * 1.通过内购账户获得 code  code 只能用一次

	 * 2.通过 code 获得 refresh_token          【刷新令牌时间一般比较长】

	 * 3.通过 refresh_token 获得 access_token  【一般3600s】

	 * 4.通过 access_token 访问 google api

	 *

	 * 参考:

	 * https://developers.google.com/android-publisher/authorization

	 * https://blog.csdn.net/alex_my/article/details/82984706#2__8

	 *

	 * [check_google_buy_order_exits 检查 google 内欧订单 成功就发送发游戏服务器 加某些东西]

	 * @param  [int] $game_id   [游戏 ID]

	 * @param  [int] $player_id [玩家 ID]

	 *

	 * @param  [string] $packageName [出售inapp产品的应用程序的软件包名称(例如,“ com.some.thing”)]

	 * @param  [string] $productId [Inapp产品SKU(例如,“ com.some.thing.inapp1”)。]   产品id对应的钻石数=[1=>66,2=>268,3=>828,4=>1436,5=>3436,6=>7416]

	 * @param  [string] $token    [购买inapp产品时,提供给用户设备的令牌。]

	 * @return []            ['status'=>$code,'message'=>$msg,]

	 */

	public function check_google_buy_order_exits(){

		// ini_set("display_errors","on");

		// IException::setDebugMode(true);

		// error_reporting(E_ALL | E_STRICT);

		/*1 获取参数*/

		$game_id 			= IFilter::act(IReq::get('game_id','post'),'int');

		$child_game_id 			= IFilter::act(IReq::get('child_game_id','post'),'int');//子包id

		$player_id 			= IFilter::act(IReq::get('player_id','post'),'int');

		if(empty($game_id) || empty($player_id)) util::jsonError('缺少玩家参数!!');

		empty($child_game_id) && $child_game_id = $game_id;

		// packageName / purchases /

		// products / productId /

		// tokens / 令牌

		$packageName 	= IFilter::act(IReq::get('packageName','post'),'string');

		$productId 		= IFilter::act(IReq::get('productId','post'),'string');

		$token 			= IFilter::act(IReq::get('token','post'),'string');

        $order_no 	    = IFilter::act(IReq::get('order_no','post'),'string');

		if(empty($packageName) || empty($productId) || empty($token)) util::jsonError('缺少查询参数!!');



		/*2 检查玩家真实性**************************************************/

		$player_info=info::getPlayerInfo($player_id,$game_id);

		// if(empty($player_info)) util::jsonError('玩家不存在!!');



		/*2 查找订单*******************************************************/

		// 2.1 获取 access_token

		$access_token=$this->get_google_pay_access_token($child_game_id);

		$url='https://www.googleapis.com/androidpublisher/v3/applications/'.$packageName.'/purchases/products/'.$productId.'/tokens/'.$token.'?access_token='.$access_token;

	        set::newsetlogfile("====".$url, 'check_google_buy_order_exits');



		$result=get::httpRequest($url,'','get');



		set::setlogfile($player_id.'谷歌内购支付'.$result);





		$zuan_sku=[

		        53 => [

                    'gold1'=>[0.99,60,4],//每日首充送8,二次无赠送

                    'gold2'=>[4.99,300,4],

				],



			];

		$zuan_sku = $zuan_sku[$child_game_id];





        $result_arr=json_decode($result,TRUE);   //数组



        // 错误 400 订单无效  403 项目未关联

        if(!empty($result_arr['error']) || empty($access_token)){

            $obj = new IModel("player_order");




            /*3.2 成功后修改数据库*/

            $ordedr_data=[

                'o_no'				=>	$order_no?$order_no:0,			//系统订单号

                'o_game_id'			=>	$game_id,						//游戏id

                'o_pid'				=>	$player_info['p_id'],						//玩家表id

                // 'o_from_os'			=>	1, 										//1.android 2.ios

                'o_item_id'			=>	4,								//道具表id

                'o_item_num'		=>	$zuan_sku[$productId][1],			//道具数量

                'o_money'			=>  $zuan_sku[$productId][0],

                'o_status'			=>	0,     							//订单状态 0.未完成 1.已完成

                'o_pay_status'		=>	0, 								//支付状态 0.未支付 1.已支付

                'o_dopay_pid'		=>	$player_id,  					//实际进行支付的玩家id

                'o_order_type'		=>	0, 								//订单类型0直接付 1代付

                'o_date'			=>	time(),       					//创建日期

                'o_isChecked'		=>	0,   							//是否回执 0.否 1.是

            ];

            $obj->setData($ordedr_data);

            $obj->add();

            util::jsonError('错误'.$result_arr['error']['code'].':'.$result_arr['error']['message']);

        }



   		// 3、订单的购买状态 0已购买 1取消 2待定

   		switch ($result_arr['purchaseState']) {

   			case '0':

   				$obj = new IModel("player_order");

   				/*3.1数据库中存在充值订单*/

   				$player_order_info=$obj->getObj('o_no="'.$result_arr['orderId'].'"');

   				if(!empty($player_order_info)) util::jsonError('此充值订单已完成');

   				//当充值商品为第一档时查询订单判断当天是否充值过

   				$first_sku = current(array_keys($zuan_sku));

   				if($productId==$first_sku){

   					$firstOrderCurrDay = $obj->query('o_pid='.$player_info['p_id'].' and o_date>='.strtotime(date('Y-m-d 00:00:00')),'o_id','o_id asc',1);

	   				if(empty($firstOrderCurrDay)){

	   					$zuan_sku[$first_sku][1] += 8 ;

	   				}

   				}

   				/*3.2 成功后修改数据库*/

   				$ordedr_data=[

					'o_no'				=>	$result_arr['orderId'],			//系统订单号

					'o_game_id'			=>	$game_id,						//游戏id

					'o_pid'				=>	$player_info['p_id'],						//玩家表id

					// 'o_from_os'			=>	1, 										//1.android 2.ios

					'o_item_id'			=>	4,								//道具表id

					'o_item_num'		=>	$zuan_sku[$productId][1],			//道具数量

					'o_money'			=>  $zuan_sku[$productId][0],

					'o_status'			=>	1,     							//订单状态 0.未完成 1.已完成

					'o_pay_status'		=>	1, 								//支付状态 0.未支付 1.已支付

					'o_dopay_pid'		=>	$player_id,  					//实际进行支付的玩家id

					'o_order_type'		=>	0, 								//订单类型0直接付 1代付

					'o_date'			=>	time(),       					//创建日期

					'o_isChecked'		=>	0,   							//是否回执 0.否 1.是

					// 'o_apple_receipt'=>''  									//苹果内验证数据

				];

		        $obj->setData($ordedr_data);

		        $add_id=$obj->add();

   				break;

   			case '1': util::jsonError('订单购买状态:取消');

   				# code...

   				break;

   			case '2': util::jsonError('订单购买状态:待定');

   				# code...

   				break;

   		}



		/*4 发送游戏服务器*/



		// $params='msg=set'.'&openId='.$player_id.'&cardType='.$password_new1.'&cardNum='.;

		$send_data=[

			'msg'=>'chargeById',

			'userId'=>$player_id,

			'cardType'=> $zuan_sku[$productId][2],  		// 2房卡 4钻石 6金币

			'cardNum'=>$zuan_sku[$productId][1],

			'operType'=>2,   		//为什么有这一笔,enum  充值 赠送 都是 2

		];

		$params =http_build_query($send_data ,'','&');



		$result=set::setMsgToGameServer($game_id,1,$params,1);

		if($result){

			if($result['errorCode']>0){

				set::setlogfile("发送钻石失败,与游戏服务器通信失败:".$result['errorCode'].$result['errorMsg']);

				// util::jsonError('发送钻石失败,与游戏服务器通信失败:'.$result['errorCode'].$result['errorMsg']);

			}

		}else{

			set::setlogfile("发送钻石失败,与游戏服务器通信失败:未获取返回值".$result['errorCode']);

			// util::jsonError('发送钻石失败,与游戏服务器通信失败:未获取返回值'.$result['errorCode']);

		}

		util::jsonSuccess(['number' => $ordedr_data['o_item_num']]);

	}



	/**

	 * [get_google_pay_access_token 获取 access_token]

	 * @param  [string] $grant_type    [参数类型]

	 * @param  [string] $client_id     [客户端ID]

	 * @param  [string] $client_secret [客户端秘钥]

	 * @param  [string] $refresh_token [刷新token] 用户获取access_token

	 * @return [string]                []

	 */

	private function get_google_pay_access_token($game_id){



		// 判断 access_token 是否过期

		$access_token=Common::rget('access_token');

		if(!empty($access_token)){

			return $access_token;

		}

		// 1.获取 token  通过刷新token 获取 访问 token

		$config_arr=$this->get_google_pay_josn_config($game_id);  //获取基本数据

		// 2.拼装 paeams and url

		$send_params=[

			'grant_type'	=>'refresh_token',

			'client_id'		=>$config_arr['web']['client_id'],

			'client_secret'	=>$config_arr['web']['client_secret'],

			//'refresh_token'	=>'1//06otA7GjQrGkZCgYIARAAGAYSNwF-L9IrYZR_WaEEvsCFDZZZ-5d4qlfovUSvllLxyxz1hlHJVVP4zCfGbOXaIqxcepaG2ojSiRo'

			'refresh_token' => $config_arr['web']['refresh_token'],

		];

        set::newsetlogfile("====".print_r($send_params,true), 'check_google_buy_order_exits');



		$url='https://accounts.google.com/o/oauth2/token';

		$send_params_str=http_build_query($send_params, '', '&');



		// 获取数据

		$response_data=get::http_post_data($url,$send_params_str);   //对象 no array



        set::newsetlogfile("==access_token-response_data==".print_r($response_data,true), 'check_google_buy_order_exits');



		// 存库  redis

        if(!empty($response_data)){

            Common::rset_ex('access_token',$response_data->access_token,$response_data->expires_in);

        }



		return !empty($response_data)?$response_data->access_token:'';

	}





	/**

	 * [get_google_pay_josn_config 获取参数信息 ]

	 * @return [type] [数组 或 null]

	 */

	private function get_google_pay_josn_config($game_id){

		$file_path= IWeb::$app->getBasePath().'plugins/google_pay/client_'.$game_id.'.json.txt';

		if(file_exists($file_path)){

			//将整个文件内容读入到一个字符串中

			$arr= json_decode(file_get_contents($file_path),true);

			// var_dump(file_get_contents($file_path));

			// var_dump($arr);

			return $arr;

		}else{

			return;

		}

	}
	

总结

1、GooglePlay关联到GoogleApi项目(Google后台操作)
2、创建GoogleApi访问的客户端ID(Google后台操作)
3、获取refresh_token(Google后台操作)
4、获取access_token(PHP代码实现)
5、验证订单并发货(PHP代码实现)

  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity IAP中,恢复购买流程需要以下几个步骤: 1. 在代码中实现恢复购买功能。在Unity中,你可以使用IAPManager类在代码中实现恢复购买功能。以下是一个示例: ```csharp using UnityEngine; using UnityEngine.Purchasing; public class IAPManager : MonoBehaviour, IStoreListener { private IStoreController m_Controller; void Start() { var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance()); builder.AddProduct("TestProduct", ProductType.Consumable); UnityPurchasing.Initialize(this, builder); } public void OnInitialized(IStoreController controller, IExtensionProvider extensions) { m_Controller = controller; } public void OnInitializeFailed(InitializationFailureReason error) { Debug.Log("IAP initialization failed: " + error); } public void OnPurchaseFailed(Product item, PurchaseFailureReason error) { Debug.Log("IAP purchase failed: " + error); } public void OnPurchaseComplete(Product item, PurchaseEventArgs args) { Debug.Log("IAP purchase complete: " + item.definition.id); } public void Purchase(string productId) { if (m_Controller != null) { var product = m_Controller.products.WithID(productId); if (product != null && product.availableToPurchase) { m_Controller.InitiatePurchase(product); } else { Debug.Log("IAP product not available: " + productId); } } } public void RestorePurchases() { if (m_Controller != null) { m_Controller.RestoreTransactions(); } } } ``` 在上面的代码中,我们添加了一个RestorePurchases方法,该方法调用m_Controller.RestoreTransactions()方法来恢复购买。 2. 在iOS项目中配置恢复购买功能。要在iOS上启用恢复购买功能,你需要在Apple开发者中心中创建一个新的IAP项目,并将其添加到Xcode项目中。在Xcode中,你需要设置应用程序的IAP权限,并添加IAP产品的标识符。 3. 在Unity中测试恢复购买功能。在Unity中,你可以使用IAPManager中的RestorePurchases方法测试恢复购买功能。在调用RestorePurchases方法之前,请确保你已经在Unity IAP控制台中创建了一个有效的产品,并在iOS项目中正确配置了IAP。 以上就是Unity IAP中恢复购买流程的实现步骤。请注意,在实际发布应用程序之前,请确保你已经按照苹果的要求正确实现了恢复购买功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值