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


欢迎进入Unity内购系列

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

第二篇:后台对购买结果的验证以及发货(IOS)

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

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

一、基本流程

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

二、AppStore后台设置

1、生成一个共享密钥(选择 “用户和访问”,然后选择 “共享密钥” 子标签页)

2、生成之后的32位密钥字符串需要给php,验证时需要用到。

三、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验证订单真伪:

	public function check_apple_buy_order_exits(){
    
    

		set::newsetlogfile("====_REQUEST".print_r($_REQUEST, true), 'check_apple_buy_order_exits');

		/*1 获取参数*/

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

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

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



		// 客户端传过来的票据信息(未解密)

		$infoStr = IReq::get('receipt_data','post');

		if (empty($infoStr)) Util::jsonError('参数错误!');

		$infoStr = json_decode($infoStr, true);

		$receipt_data 	= $infoStr['Payload'];

		$sandbox 	= IReq::get('sandbox','post');

		$bundleId 	= 'com.hisgl.histongits';//IReq::get('bundleId','post');

		// $transfer 	= IReq::get('transfer','post');

		if(empty($receipt_data) || empty($bundleId)) util::jsonError('缺少查询参数!!');

		$receipt_data = str_replace(" ", '+', $receipt_data);

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

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

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



		/*3 验证票据信息*/

		// set::setlogfile("apple receipt_data:".$receipt_data);

		$result_obj=$this->validata_apple_receipt_data($receipt_data,$sandbox);  // 参数2 false 沙盒测试  ture 正式



        set::newsetlogfile("====result_obj".print_r($result_obj, true), 'check_apple_buy_order_exits');





		/*4 判断*/

		if($result_obj->status!==0) util::jsonError('订单错误!!receipt_data:'.$receipt_data);



		//钻石商品数量

		$zuan_sku=[

			54 => [

				'gold1'=>[60,600,2],
				'gold2'=>[60,600,2]

			],

		];

		$zuan_sku = $zuan_sku[$game_id];

		$bundle_id 	= 	$result_obj->receipt->bundle_id;   					// 应用标识

		$order_id 	=	$result_obj->receipt->in_app[0]->transaction_id; 	// 订单id

   		$productId 	=	$result_obj->receipt->in_app[0]->product_id;    	// 道具id

   		$quantity 	=	$result_obj->receipt->in_app[0]->quantity;       	// 购买数量

   		if( $bundle_id!=$bundleId || !array_key_exists($productId,$zuan_sku) ) util::jsonError('配置错误!!');



        //5 更新订单记录

        $obj = new IModel("player_order");

   		$player_order_info=$obj->getObj('o_no="'.$order_id.'"');

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



   		$ordedr_data=[

			'o_no'				=>	$order_id,													//系统订单号

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

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

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

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

			'o_item_num'		=>	$zuan_sku[$productId][1]*$quantity,							//发放给玩家的数量	=单位数量*购买数量

			'o_money'			=>  $zuan_sku[$productId][0]*$quantity,   						//玩家支付钱数		=单价*购买数量

			'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();



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

		$send_data=[

			'msg'=>'chargeById',

			'userId'=>$player_id,

			'cardType'=>2,  		// 2房卡 4钻石 6金币

			'cardNum'=>$ordedr_data['o_item_num'],

			'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']);

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

			}

		}else{
    
    

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

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

		}

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

	}



    private function validata_apple_receipt_data($receipt_data,$sandbox=false){
    
    



        if($sandbox){
    
    

            $url='https://buy.itunes.apple.com/verifyReceipt';  // 正式接口

        }else{
    
    

            $url='https://sandbox.itunes.apple.com/verifyReceipt';  // 沙盒接口

        }



        // 拼装要发送的数据

        // $data_arr=json_decode($receipt_data,true);

        // $send_data = json_encode([

        // 	// 'receipt-data'	=> $data_arr['Payload'],

        // 	'receipt-data'	=> $receipt_data

        // 	// 'password'		=> $apple_secret

        // ]);

        /*$send_data = $receipt_data;

        $result=get::http_post_jsondata($url,$send_data);*/









        $jsonItem = json_encode(['receipt-data'=>$receipt_data, 'password'=>'6665a76e9fa641c787b437bf5ee4306a']);

        $result=get::http_post_jsondata($url,$jsonItem);



        set::newsetlogfile("==========result===".print_r($result,true),'check_apple_buy_order_exits');





        $msg=[

            '0' 	=>'成功',

            '21000' =>'App Store不能读取你提供的JSON对象',

            '21002' =>'receipt-data域的数据有问题',

            '21003' =>'receipt无法通过验证',

            '21004' =>'提供的shared secret不匹配你账号中的shared secret',

            '21005' =>'receipt服务器当前不可用',

            '21006' =>'receipt合法,但是订阅已过期。服务器接收到这个状态码时,receipt数据仍然会解码并一起发送',

            '21007' =>'receipt是Sandbox receipt,但却发送至生产系统的验证服务',

            '21008' =>'receipt是生产receipt,但却发送至Sandbox环境的验证服务',

        ];



        //set::setlogfile("apple 票据status:".$result->status.'--'.$msg[$result->status]);

        set::newsetlogfile("apple 票据status:".$result->status.'--'.$msg[$result->status], 'check_apple_buy_order_exits');





        if($result->status==21007){
    
    

            if($sandbox){
    
    

                $url='https://sandbox.itunes.apple.com/verifyReceipt';  // 沙盒接口

            }else{
    
    

                $url='https://buy.itunes.apple.com/verifyReceipt';  // 正式接口

            }



            $jsonItem = json_encode(['receipt-data'=>$receipt_data, 'password'=>'33f5a76e9fa641c787b437bf5ee4306a']);

            $result=get::http_post_jsondata($url,$jsonItem);



            $msg=[

                '0' 	=>'成功',

                '21000' =>'App Store不能读取你提供的JSON对象',

                '21002' =>'receipt-data域的数据有问题',

                '21003' =>'receipt无法通过验证',

                '21004' =>'提供的shared secret不匹配你账号中的shared secret',

                '21005' =>'receipt服务器当前不可用',

                '21006' =>'receipt合法,但是订阅已过期。服务器接收到这个状态码时,receipt数据仍然会解码并一起发送',

                '21007' =>'receipt是Sandbox receipt,但却发送至生产系统的验证服务',

                '21008' =>'receipt是生产receipt,但却发送至Sandbox环境的验证服务',

            ];

            //set::setlogfile("apple2 票据status:".$result->status.'--'.$msg[$result->status]);

            set::newsetlogfile("apple2 票据status:".$result->status.'--'.$msg[$result->status], 'check_apple_buy_order_exits');



        }



        return $result;

    }
	

总结

1、内购验证不需要客户端参与
2、提审时可能会遇到审核人员的数据是正式环境,PHP验证订单时如果碰到这种情况,需要先验证正式环境,再验证沙盒环境