thinkphp5實現根據渠道號不同實現安卓和IOS的APP支付和H5支付
阿新 • • 發佈:2018-12-09
<?php namespace app\api\controller; use think\Controller; use app\common\model\ShopInfo as ShopInfoModel; use app\common\model\UserOrderInfo as UserOrderInfoModel; use app\common\model\User as UserModel; use app\common\model\FromInfo as FromInfoModel; class Pay extends Controller { public function _empty() { dump(input('get.'));die; return $this->fetch('/404'); } //下單 public function unifiedorder() { $playerid = input('playerid/d'); //玩家id $shopid = input('shopid/d'); //商品id $fromid = input('fromid/d'); //渠道號 $orderid = date('ymdHis', time()) . mt_rand(1000, 9999); //商戶唯一訂單號 $shopinfo = ShopInfoModel::getOne('shopid, shopname, gamecoins, rmb', ['shopid' => $shopid]); if(empty($shopinfo)) { return json_return(-1, '商品不存在'); } if(!$playerid) { return json_return(-1, '缺少玩家id'); } //訂單資訊 $total_fee = (config('environment') == 'test') ? 1 : $shopinfo->rmb * 100;//; //訂單金額 $order = [ 'appid' => config('system.appid'), 'body' => $shopinfo->shopname, 'mch_id' => config('system.mch_id'), 'nonce_str' => uniqid(), 'notify_url' => config('system.site_url').url('pay/notify'),//接受微信非同步通知地址 'out_trade_no' => $orderid,//商戶唯一訂單號,可包含字母序 'spbill_create_ip'=>request()->ip(),//產生訂單號的伺服器IP 'total_fee' => $total_fee,//訂單金額,單位/分 ]; $frominfo = FromInfoModel::getOne('fromid, paytype', ['fromid' => $fromid]); if(empty($frominfo)) { return json_return(-1, '渠道號不存在'); } $pay_type = $frominfo->paytype; if($fromid < 10000) {//ios switch ($pay_type) { case 1: $order['trade_type'] = 'APP'; $order['appid'] = config('system.ios_appid'); break; case 2: $order['trade_type'] = 'MWEB'; $order['scene_info'] = '{"h5_info": {"type":"IOS","app_name": "'.config('system.ios_appname').'","bundle_id": "'.config('system.ios_packagename').'"}}'; break; case 3: $order['trade_type'] = 'ApplePay'; $orderRes = $this->createOrderInfo($fromid, $orderid, $playerid, $shopinfo, 3); if($orderRes) { $return_data = [ 'trade_type' => 'ApplePay', 'orderid' => $orderid ]; return json_return(0, 'success', $return_data); }else{ return json_return(-1, '儲存訂單資訊失敗'); } break; default: return json_return(-1, '交易型別錯誤'); break; } }else if($fromid > 10000 && $fromid < 100000) { //安卓 switch ($pay_type) { case 1: $order['trade_type'] = 'APP'; $order['appid'] = config('system.android_appid'); break; case 2: $order['trade_type'] = 'MWEB'; $order['scene_info'] = '{"h5_info": {"type":"Android","app_name": "'.config('system.android_appname').'","package_name": "'.config('system.android_packagename').'"}}'; break; default: return json_return(-1, '交易型別錯誤'); break; } }else if($fromid >= 100000) { //other $order['trade_type'] = 'MWEB'; $order['scene_info'] = '{"h5_info": {"type":"IOS","app_name": "'.config('system.ios_appname').'","bundle_id": "'.config('system.ios_packagename').'"}}'; }else{ return json_return(-1, '當前裝置無法支付'); } $response = $this->getWxUnifiedorderResponse($order); //若預下單成功,return_code 和result_code為SUCCESS。 if ( $response['return_code'] ==='SUCCESS' && $response['result_code'] ==='SUCCESS') { //儲存訂單資訊 $res = $this->createOrderInfo($fromid, $orderid, $playerid, $shopinfo); if($res) { //返回trade_type和prepay_id供前端呼叫 $trade_type = $response['trade_type']; $return_data = [ 'trade_type' => $trade_type, 'orderid' => $orderid ]; switch ($trade_type) { case 'APP': $return_data['prepay_order'] = $this->getPrepayOrder($order['appid'], $response['prepay_id']); break; case 'MWEB': $redirect_url = config('system.site_url').'/mweb.php' . '?mweb_url=xxx://xxx'; //後面的mweb_url為瀏覽器拉起應用的連結 $mweb_url = urlencode($response['mweb_url'].'&redirect_url='.$redirect_url); $return_data['mweb_url'] = config('system.site_url').'/mweb.php' . '?mweb_url=' .$mweb_url; //mweb_url為拉起微信支付收銀臺的中間頁面,可通過訪問該url來拉起微信客戶端,完成支付,mweb_url的有效期為5分鐘。 break; default: # code... break; } return json_return(0, 'success', $return_data); }else{ return json_return(-1, '儲存訂單資訊失敗'); } }else{ $errmsg = ($response['return_code'] == 'FAIL') ? $response['return_msg'] : $this->error_code($response['err_code']); return json_return(-1, $errmsg); } } /** * [createOrderInfo 建立訂單資訊] * @param [integer] $orderid [訂單號] * @param [integer] $playerid [玩家id] * @param [obj] $shopinfo [商品資訊] * @param integer $paytype [支付型別] * @return [type] [obj] */ private function createOrderInfo($fromid, $orderid, $playerid, $shopinfo, $paytype = 1) { $userorderinfo = array( 'orderid' => $orderid, 'shopname' => $shopinfo->shopname, 'gamecoins' => $shopinfo->gamecoins, 'rmb' => $shopinfo->rmb, 'orderstatus' => 0, 'fromid' => $fromid, 'paytype' => $paytype, //支付方式1-微信,2-支付寶,3-Apple Pay 'playerid' => $playerid, 'shopid' => $shopinfo->shopid, 'orderdate' => date('Y-m-d H:i:s'), ); return UserOrderInfoModel::create($userorderinfo); } /** * [getWxUnifiedorderResponse 獲得微信下單結果] * @param [array] $orderid [訂單資訊] * @return [array] */ private function getWxUnifiedorderResponse($order) { $sign = $this->getSign($order); $order['sign'] = $sign; //轉換成一維XML格式 $xml = '<xml>'; foreach($order as $k=>$v){ $xml.='<'.$k.'><![CDATA['.$v.']]></'.$k.'>'; } $xml.='</xml>'; //CURL會話 $response = curlHtml('https://api.mch.weixin.qq.com/pay/unifiedorder', $xml); //將xml格式的$response 轉成陣列 $response = json_decode( json_encode( simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA) ), true ); return $response; } //執行第二次簽名,才能返回給客戶端使用 private function getPrepayOrder($appid, $prepayid){ $data["appid"] = $appid; $data["noncestr"] = uniqid(); $data["package"] = "Sign=WXPay"; $data["partnerid"] = config('system.mch_id'); $data["prepayid"] = $prepayid; //微信生成的預支付回話標識,用於後續介面呼叫中使用,該值有效期為2小時 $data["timestamp"] = time(); $data["sign"] = $this->getSign($data); return $data; } /** * 格式化引數格式化成url引數 */ private function ToUrlParams($data) { $buff = ""; foreach ($data as $k => $v) { if($k != "sign" && $v != "" && !is_array($v)){ $buff .= $k . "=" . $v . "&"; } } $buff = trim($buff, "&"); return $buff; } /** * 生成簽名 * @return 簽名,本函式不覆蓋sign成員變數,如要設定簽名需要呼叫SetSign方法賦值 */ public function getSign($data) { //簽名步驟一:按字典序排序引數 if(is_array($data)){ ksort($data); } else{ $data = []; } $string = $this->ToUrlParams($data); //簽名步驟二:在string後加入KEY $string = $string . "&key=".config('system.apikey'); //簽名步驟三:MD5加密 $string = md5($string); //簽名步驟四:所有字元轉為大寫 $result = strtoupper($string); return $result; } //訂單查詢 public function order_query() { $orderid = input('orderid'); require_once "./pay/wxpay/lib/WxPay.Api.php"; $input = new \WxPayOrderQuery(); $input->SetOut_trade_no($orderid); $result = \WxPayApi::orderQuery($input); if ($result['err_code_des'] == "order not exist") { // 訂單不存在 $trade_state = '訂單不存在'; } else { if ($result['trade_state'] == "SUCCESS") { $trade_state = '支付成功'; //支付成功 return json_return(0, $trade_state, $result); } else if ($result['trade_state'] == "REFUND") { $trade_state = '已退款'; //已退款 } else if ($result['trade_state'] == "NOTPAY") { $trade_state = '未支付'; //使用者還沒支付 } else if ($result['trade_state'] == "CLOSED") { $trade_state = '訂單關閉'; //訂單關閉 } else if ($result['trade_state'] == "REVOKED") { $trade_state = '已撤銷'; //已撤銷(刷卡支付) } else if ($result['trade_state'] == "USERPAYING") { $trade_state = '支付中'; //使用者支付中 } else if ($result['trade_state'] == "PAYERROR") { $trade_state = '支付失敗'; //支付失敗(其他原因,例如銀行返回失敗) } } return json_return(-1, $trade_state); } //支付完成後的回撥 public function notify() { $xml = file_get_contents('php://input'); $result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); //為了防止假資料,驗證簽名是否和返回的一樣。 $sign = $this->getSign($result); if ( $sign === $result['sign']) { // 校驗返回的訂單金額是否與商戶側的訂單金額一致。修改訂單表中的支付狀態。 if ($result['result_code'] == "SUCCESS" && $result['return_code'] == "SUCCESS") { //獲取訂單資訊 $orderinfo = UserOrderInfoModel::getOne('orderid, rmb, playerid,shopid,orderstatus', ['orderid' => $result['out_trade_no']]); $total_fee = (config('environment') == 'test') ? 1 : $orderinfo->rmb * 100;//; //訂單金額 if (($total_fee == $result['total_fee']) && ($orderinfo['orderstatus'] == 0)) { $this->payHandler($orderinfo); } } } $return = ['return_code'=>'SUCCESS','return_msg'=>'OK']; $xml = '<xml>'; foreach($return as $k=>$v){ $xml.='<'.$k.'><![CDATA['.$v.']]></'.$k.'>'; } $xml.='</xml>'; echo $xml; } //支付成功後的處理 private function payHandler($orderinfo) { UserOrderInfoModel::transaction(function() use($orderinfo){ //開啟事務 //更新訂單等操作 }); } /** * 錯誤程式碼 * @param $code 伺服器輸出的錯誤程式碼 * return string */ private function error_code($code) { $errList = array( 'NOAUTH' => '商戶未開通此介面許可權', 'NOTENOUGH' => '使用者帳號餘額不足', 'ORDERNOTEXIST' => '訂單號不存在', 'ORDERPAID' => '商戶訂單已支付,無需重複操作', 'ORDERCLOSED' => '當前訂單已關閉,無法支付', 'SYSTEMERROR' => '系統錯誤!系統超時', 'APPID_NOT_EXIST' => '引數中缺少APPID', 'MCHID_NOT_EXIST' => '引數中缺少MCHID', 'APPID_MCHID_NOT_MATCH' => 'appid和mch_id不匹配', 'LACK_PARAMS' => '缺少必要的請求引數', 'OUT_TRADE_NO_USED' => '同一筆交易不能多次提交', 'SIGNERROR' => '引數簽名結果不正確', 'XML_FORMAT_ERROR' => 'XML格式錯誤', 'REQUIRE_POST_METHOD' => '未使用post傳遞引數 ', 'POST_DATA_EMPTY' => 'post資料不能為空', 'NOT_UTF8' => '未使用指定編碼格式', ); if (array_key_exists($code, $errList)) { return $errList[$code]; }else{ return $code; } } }