HwuXiaofei.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. <?php
  2. /**
  3. * H5支付控制器 [为避免ios审查的敏感词,使用拼音命名控制器]
  4. *
  5. */
  6. namespace app\api\controller\v1;
  7. use app\api\controller\Api;
  8. use app\common\model\Game;
  9. use app\common\model\PayType;
  10. use app\common\model\Payrule;
  11. use app\common\model\Members;
  12. use app\common\model\ChannelFrozen;
  13. use app\common\model\Cpurl;
  14. use app\common\model\Pay as PayModel;
  15. use app\common\model\MemberCoin;
  16. use app\common\model\PayCpinfo;
  17. use app\common\model\MemberChannelGame;
  18. use app\common\logic\PayCallback;
  19. use app\common\logic\Pay as PayLogic;
  20. use app\common\library\WeixinPay;
  21. use app\common\model\App;
  22. use think\Db;
  23. use think\Config;
  24. use think\Cache;
  25. use think\Env;
  26. class HwuXiaofei extends Api {
  27. protected $nowTime;
  28. protected $payRequestLimit = 15; //重复下单的限制时间
  29. protected $redis; //redis的句柄对象
  30. protected $game_id;
  31. protected $username;
  32. protected $userid;
  33. protected $sub_username; //子账号名
  34. public function _initialize()
  35. {
  36. }
  37. private function _ini_config()
  38. {
  39. $token = $_REQUEST['token'];
  40. if (empty($token) || !Cache::store('default')->get($token)) {
  41. $this->error('请先登录!');
  42. }
  43. list($userid, $username, $gameid,$sub_username,$token_random) = explode('|', auth_code($token, "DECODE", Env::get('auth_key')));
  44. $redisTokenRandom = Cache::store('default')->get('token|sdk|'.$userid.'|'.$gameid);
  45. //redis中玩家登录安全控制的随机码不存在时
  46. if ( !$redisTokenRandom ) {
  47. Cache::store('default')->rm($token);
  48. $this->error('token随机码不存在,请重新登录');
  49. }
  50. //两个token随机码不同时
  51. elseif($redisTokenRandom!=$token_random){
  52. Cache::store('default')->rm($token);
  53. $this->error('token随机码错误,请重新登录');
  54. }
  55. $this->userid = $userid;
  56. $this->username = $username;
  57. $this->game_id = $gameid;
  58. $this->sub_username = $sub_username;
  59. $this->nowTime = NOW_TIMESTAMP;
  60. $this->redis = \think\Cache::store('default')->handler();
  61. }
  62. /**
  63. * 选择支付方式
  64. */
  65. public function index() {
  66. $this->_ini_config();
  67. $gameModel = new Game;
  68. $payRuleModel = new Payrule;
  69. $payTypeModel = new PayType;
  70. $sdkModel = model('common/AndroidSdk');
  71. $version = input('version'); //sdk版本号 [v3.0.2 参数]
  72. $data = input();
  73. $data['productname']= input('productname','','urldecode');
  74. $data['rolename'] = input('rolename','','urldecode');
  75. $data['serverid'] = input('serverid','','urldecode');
  76. $data['gameid'] = $this->game_id;
  77. $data['userid'] = $this->userid;
  78. $validResult = $this->validate($data, 'Pay.index');
  79. if (true !== $validResult) {
  80. $this->error($validResult);
  81. }
  82. elseif(!($gameInfo = $gameModel->field('id,android_sdk_id,name')->where(['id' => $this->game_id,'isdelete'=>0])->find())){
  83. $this->error('传入游戏参数错误');
  84. }
  85. //游戏禁止的支付方式
  86. $denyPayType = $payRuleModel->where("game_id=".$this->game_id)->column('deny_paytype');
  87. if(!empty($version)){
  88. $android_sdk_id = $sdkModel->where(['version'=>$version])->value('id');
  89. if(empty($android_sdk_id)){
  90. $this->error('sdk版本信息不存在');
  91. }
  92. $arrPayType = $payTypeModel->field('paytype,payname')->where('paytype',['not in',$denyPayType],['like','%h5%'])->where(['status'=>1,'used_sdk_id'=>['like','%,'.$android_sdk_id.',%']])->select();
  93. }
  94. else{
  95. $arrPayType = $payTypeModel->field('paytype,payname')->where('paytype',['not in',$denyPayType],['like','%h5%'])->where(['status'=>1,'used_sdk_id'=>['like','%,'.$gameInfo['android_sdk_id'].',%']])->select();
  96. }
  97. if(empty($arrPayType)){
  98. $this->error('该游戏的h5支付方式还未配置,请到后台配置');
  99. }
  100. $this->assign('arrPayType',$arrPayType);
  101. return $this->fetch();
  102. }
  103. /**
  104. * 支付处理
  105. */
  106. public function pay()
  107. {
  108. die();
  109. $this->_ini_config();
  110. $gameid = $this->game_id; //游戏ID
  111. $member_id = $this->userid; //用户ID
  112. $appid = input('appid'); //APPID
  113. $serverid = input('serverid'); //服务器ID
  114. $servername = input('servername'); //区服名称
  115. $amount = input('amount'); //充值金额
  116. $roleid = input('roleid'); //角色ID
  117. $attach = input('attach'); //上一级的扩展参数,多数是订单编号[麻花网络自己聚合自己时,会加个aoyou###前缀]
  118. $mc_id = input('mc_id',0); //平台币劵ID
  119. $pay_channel_id = input('channel_id',0); //当前SDK的渠道
  120. $imeil = input('imeil'); //手机imeil码
  121. $productname= input('productname'); //消费的商品名称
  122. $paytype = input('paytype'); //支付类型
  123. $rolename = input('rolename',''); //角色名
  124. $rolelevel = input('rolelevel',''); //角色角色等级
  125. $orderid = makeOrderid();
  126. $pay_status = 0; //cy_pay表的支付状态
  127. $cp_payflag = 0; //cy_paycpinfo表的支付状态
  128. $memberModel = new Members;
  129. $frozenModel = new ChannelFrozen;
  130. $cpurlModel = new Cpurl;
  131. $gameModel = new Game;
  132. $payModel = new PayModel;
  133. $coinModel = new MemberCoin;
  134. $payCpinfoModel = new PayCpinfo;
  135. $memberChannelGameModel = new MemberChannelGame;
  136. $appModel = new App;
  137. $payLogic = new PayLogic();
  138. $checkResult = $this->validate(['gameid' =>$gameid,
  139. 'userid' =>$member_id,
  140. 'appid' =>$appid,
  141. 'serverid' =>$serverid,
  142. 'servername' =>$servername,
  143. 'amount' =>$amount,
  144. 'roleid' =>$roleid,
  145. 'attach' =>$attach,
  146. 'mc_id' =>$mc_id,
  147. 'channel_id' =>$pay_channel_id,
  148. 'productname' =>$productname,
  149. 'paytype' =>$paytype,
  150. 'imeil' =>$imeil
  151. ], 'Pay.add');
  152. if (true !== $checkResult) {
  153. $this->error($checkResult);
  154. }
  155. if (empty($mc_id) && (substr($paytype,0,4)=='mix-' || $paytype=='ptb')) {
  156. $this->error('请选择平台币使用劵');
  157. }
  158. //玩家在这款游戏的归属渠道
  159. $channel_id = $memberChannelGameModel->where(['member_id'=>$member_id,'game_id'=>$gameid])->value('channel_id');
  160. if (empty($channel_id)) {
  161. $this->error('无关联的归属渠道信息');
  162. }
  163. //当前游戏的渠道是否禁止消费(非归属渠道)
  164. if (isFrozenOption($pay_channel_id,$gameid,'consume')) {
  165. $this->error('您所在渠道已经被禁止在此游戏消费');
  166. }
  167. //充值回调地址
  168. $cpurl = $cpurlModel->where(['appid'=>$appid])->value('url');
  169. if(empty($cpurl)){
  170. $this->error('没有回调地址,请通知我方配置');
  171. }
  172. $appInfo = $appModel->field('id,appkey,client_appkey,gameid')->where(['id'=>$appid])->find();
  173. if(empty($appInfo)){
  174. $this->error('密钥信息不存在');
  175. }
  176. //游戏信息
  177. $gameInfo = $gameModel->field('order_recheck')->where(['id' => $gameid])->find();
  178. if(empty($gameInfo)){
  179. $this->error('游戏id参数错误');
  180. }
  181. //有使用平台币劵时
  182. if(!empty($mc_id)){
  183. $coinInfo = $coinModel->field('mc_id,coin_sn,mc_coin_residue,history_id,mc_coin_amount,mc_coin_number')
  184. ->where(['mc_id'=>$mc_id,'mc_userid'=>$member_id,'mc_gameid'=>$gameid,'mc_coin_residue'=>['>',0]])
  185. ->where('(mc_expire_time=0 or mc_expire_time>'.$this->nowTime.')')
  186. ->find();
  187. if(empty($coinInfo)){
  188. $this->error('有效的平台币劵不存在');
  189. }
  190. $arrPayAmount = $payLogic->calPayAmount($amount,$coinInfo['mc_coin_residue'],($coinInfo['mc_coin_amount']/$coinInfo['mc_coin_number']));
  191. }
  192. else{
  193. $arrPayAmount = $payLogic->calPayAmount($amount);
  194. }
  195. //实际支付金额为0时
  196. if (!($arrPayAmount['real_amount']>0)) {
  197. $paytype = 'ptb'; //全部用平台币支付
  198. $pay_status = 1; //支付成功
  199. $cp_payflag = 1;
  200. //付款时间
  201. $payData['pay_time'] = $this->nowTime;
  202. }
  203. //有实际支付金额并且支付方式是平台币时
  204. elseif($arrPayAmount['real_amount']>0 && $paytype=='ptb'){
  205. $this->error('平台币劵金额不足,请使用混合支付');
  206. }
  207. //指定时间内,禁止重复下单
  208. if(!requestDuplicateCheck('pay_duplicate_'.$member_id.'_'.$gameid,$this->payRequestLimit)){
  209. $this->error('充值请求过多,请于'.$this->payRequestLimit.'s以后,再次进行充值操作');
  210. }
  211. if($payModel->where(['attach'=>$attach,'gameid'=>$gameid])->find()){
  212. $this->redis->del('pay_duplicate_'.$member_id.'_'.$gameid);
  213. $this->error('游戏合作方的订单参数不能重复');
  214. }
  215. $payData['orderid'] = $orderid;
  216. $payData['use_coin_sn'] = !empty($mc_id) ? $coinInfo['coin_sn'] : ''; //平台币劵订单号
  217. $payData['amount'] = $amount;
  218. $payData['real_amount'] = $arrPayAmount['real_amount'];
  219. $payData['real_ptb'] = $arrPayAmount['real_ptb'];
  220. $payData['real_ptb_amount'] = $arrPayAmount['real_ptb_amount'];
  221. $payData['userid'] = $member_id;
  222. $payData['username'] = $this->username;
  223. $payData['roleid'] = $roleid;
  224. $payData['rolename'] = $rolename;
  225. $payData['rolelevel'] = $rolelevel;
  226. $payData['paytype'] = $paytype;
  227. $payData['productname'] = $productname;
  228. $payData['serverid'] = $serverid;
  229. $payData['gameid'] = $gameid;
  230. $payData['status'] = $pay_status;
  231. $payData['ip'] = request()->ip();
  232. $payData['imeil'] = $imeil;
  233. $payData['create_time'] = $this->nowTime;
  234. $payData['channel_id'] = $channel_id;
  235. $payData['recheck_status'] = $gameInfo['order_recheck'];
  236. $payData['attach'] = $attach;
  237. // 启动事务
  238. Db::startTrans();
  239. try{
  240. $result = ''; //返回值
  241. $payModel->insert($payData);
  242. //如果是自己聚合自己时,支付回调地址固定
  243. if(mb_substr($attach,0,8,'UTF-8')=='xkhyn###'){
  244. $cpurl = Config::get('self_complex_callback_url');
  245. $attach = str_replace("xkhyn###","",$attach); //聚合的订单编号,去掉[aoyou###]前缀
  246. }
  247. //给Cp的用户名用子账号名
  248. $paycp_username = (!empty($this->sub_username) ? $this->sub_username : $this->username);
  249. $str = "orderid=".urlencode($orderid)."&username=".urlencode($paycp_username)."&gameid=".$gameid."&roleid=".urlencode($roleid)."&serverid=".urlencode($serverid)."&paytype=".urlencode($paytype)."&amount=".$amount."&paytime=".$this->nowTime."&attach=".urlencode($attach);
  250. $param = $str."&appkey=".urlencode($appInfo['appkey']);
  251. $md5params = md5($param);
  252. $params = $str . "&sign=".urlencode($md5params);
  253. $paycpData['orderid'] = $orderid;
  254. $paycpData['fcallbackurl'] = $cpurl;
  255. $paycpData['params'] = $params;
  256. $paycpData['create_time'] = $this->nowTime;
  257. $paycpData['payflag'] = $cp_payflag;
  258. //写入cy_paycpinfo表数据
  259. $payCpinfoModel->insert($paycpData);
  260. //如果使用了平台币
  261. if($arrPayAmount['real_ptb']>0){
  262. $payLogic->saveCoinData($coinInfo,$payData);
  263. }
  264. //第三方支付用的商品简单描述 (去除&+等特殊字符)
  265. $payBody = preg_replace("/[\&\+]+/", '', $productname);
  266. //全部平台币支付,并且支付成功时
  267. if($pay_status){
  268. //更新member表的total_pay_amount
  269. $memberModel->where(['id'=>$member_id])->update(['total_pay_amount'=>Db::raw('total_pay_amount+'.$amount)]);
  270. }
  271. $this->redis->del('pay_duplicate_'.$member_id.'_'.$gameid);
  272. // 提交事务
  273. Db::commit();
  274. } catch (\Exception $e) {
  275. // 回滚事务
  276. Db::rollback();
  277. $this->error('订单生成失败'.$e->getMessage());
  278. }
  279. //全部平台币支付,并且支付成功时
  280. if($pay_status){
  281. // 通知CP,支付成功
  282. (new PayCallback())->callBackToCp($orderid);
  283. $payLogic->payWarning($member_id, $channel_id, $gameid, $amount);
  284. }
  285. //支付方式是支付宝或者支付宝混合支付时
  286. if($paytype=='zfb' || $paytype=='mix-zfb'){
  287. //支付宝支付参数
  288. $alipayParam = $this->getAlipayParam($orderid,$arrPayAmount['real_amount'],$payBody);
  289. $result = ['alipay_param'=>$alipayParam];
  290. }
  291. //支付方式是微信官方h5支付或者微信官方h5混合支付时
  292. elseif($paytype=='wxpay-h5' || $paytype=='mix-wxpay-h5')
  293. {
  294. $wxpayh5_param = $this->getWxpayH5Param($orderid,$arrPayAmount['real_amount'],$payBody);
  295. if(is_array($wxpayh5_param)){
  296. $this->redirect($wxpayh5_param['mweb_url'].'&redirect_url='.url('weixinFinish'));
  297. }
  298. else{
  299. $this->error($wxpayh5_param);
  300. }
  301. }
  302. //更新游戏区服信息
  303. $this->saveGameServer($member_id,$gameid,$imeil,'pay');
  304. }
  305. /**
  306. * 微信官方h5支付
  307. *
  308. * @param string $orderid 订单号
  309. * @param float $real_amount 支付金额
  310. * @param string $body 商品名称
  311. *
  312. * @return array 支付参数数组
  313. */
  314. private function getWxpayH5Param($orderid,$real_amount,$body)
  315. {
  316. $pay_return = false;
  317. $wxPay = new WeixinPay;
  318. $params['appid'] = Config::get('wxpay-h5')['appid'];
  319. $params['mch_id'] = Config::get('wxpay-h5')['mch_id'];
  320. $params['nonce_str'] = random(10);
  321. $params['body'] = $body;
  322. $params['out_trade_no'] = $orderid;
  323. $params['total_fee'] = $real_amount*100; // 支付金额,以分为单位
  324. $params['spbill_create_ip'] = $this->request->ip();
  325. $params['notify_url'] = Config::get('wxpay-h5')['notify_url'];
  326. $params['trade_type'] = 'MWEB';
  327. $params['scene_info'] = '{"h5_info":{"type":"Wap","wap_url":"http://m.weilongwl.com","wap_name":"麻花网络游戏"}}';
  328. //获取签名数据
  329. $params['sign'] = $wxPay->MakeSign($params,Config::get('wxpay-h5')['key']);
  330. $xml = dataToXml($params);
  331. $response = $wxPay->postXmlCurl($xml);
  332. if (!$response) {
  333. return '微信支付请求失败';
  334. }
  335. $result = $wxPay->xmlToArray($response);
  336. if( array_key_exists("return_code", $result) && $result["return_code"] == "SUCCESS" && array_key_exists("result_code", $result) && $result["result_code"] == "SUCCESS")
  337. {
  338. $pay_return['prepay_id'] = $result['prepay_id'];
  339. $pay_return['trade_type'] = $result['trade_type'];
  340. $pay_return['mweb_url'] = $result['mweb_url'];
  341. $pay_return['referer'] = Config::get('wxpay-h5')['referer'];
  342. }
  343. else{
  344. return ' 错误代码:'.$result["err_code"].' 错误代码描述:'.$result["err_code_des"];
  345. }
  346. return $pay_return;
  347. }
  348. /**
  349. * 微信支付完成后的跳转页
  350. *
  351. */
  352. public function weixinFinish()
  353. {
  354. return $this->fetch('weixin_finish');
  355. }
  356. }