欢迎进入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验证订单时如果碰到这种情况,需要先验证正式环境,再验证沙盒环境