thinkphp微信掃碼支付模式二
阿新 • • 發佈:2019-01-09
1.參考本部落格的博文《thinkphp3.2實現微信JSAPI支付》,在此基礎上實現微信掃碼支付只需做簡單修改;
2.只需改WxPayAction裡面pay函式裡面三個地方:
1)將支付型別JSAPI改成NATIVE:
$this->setParameter("trade_type", "NATIVE");
2)遮蔽掉以下獲取openid相關的程式碼:
$openid = $this->get_openid();
$this->setParameter("openid", $openid);
3)將獲取prepayid的函式改成獲取code_url的函式:
改成:$prepay_id = $this->getPrepayId(); private function getPrepayId() { $response = $this->postXml(); $result = $this->xmlToArray($response); $prepay_id = $result["prepay_id"]; return $prepay_id; }
$code_url = $this->getCodeUrl();
private function getCodeUrl() {
$response = $this->postXml();
$result = $this->xmlToArray($response);
$code_url = $result["code_url"];
return $code_url;
}
3.呼叫我自己寫的生成二維碼的函式生成二維碼:
createQRcode($code_url,0,"","");
整個檔案程式碼如下:
<?php namespace Home\Action; class WxPayAction extends BaseAction { private $wxpayConfig; private $wxpay; private $parameters; private $returnParameters; public function _initialize(){ vendor('Wxpay.jssdk.log_'); $this->wxpayConfig = array('CURL_TIMEOUT' => 30); $this->wxpayConfig['appid'] = C('APP_ID'); // 微信公眾號身份的唯一標識 $this->wxpayConfig['appsecret'] = C('APP_SECRET'); // JSAPI介面中獲取openid $this->wxpayConfig['mchid'] = ""; // 受理商ID $this->wxpayConfig['key'] = ""; // 商戶支付金鑰Key $this->wxpayConfig['notifyurl'] = ""; $this->wxpayConfig['returnurl'] = ""; $this->wxpayConfig['url'] = "https://api.mch.weixin.qq.com/pay/unifiedorder"; } /** * 支付 */ public function pay() { $orderid = time(); $payprice = "0.01"; if(empty($orderid) || empty($payprice)) { // $this->redirect('Mobile/Goods/home'); die('訂單引數不完整!'); } // $openid = $this->get_openid(); // 設定統一支付介面引數 // 設定必填引數 // appid已填,商戶無需重複填寫 // mch_id已填,商戶無需重複填寫 // noncestr已填,商戶無需重複填寫 // spbill_create_ip已填,商戶無需重複填寫 // sign已填,商戶無需重複填寫 // $this->setParameter("openid", $openid); $this->setParameter("body", "百港匯環球購網上商城購物"); // 商品描述 // 自定義訂單號,此處僅作舉例 //$timeStamp = time(); //$out_trade_no = \WxPayConf_pub::$APPID . $timeStamp; $out_trade_no = $orderid; //$out_trade_no = time(); $this->setParameter("out_trade_no", $out_trade_no); // 商戶訂單號 $this->setParameter("total_fee", $payprice * 100); // 總金額 $this->setParameter("notify_url", $this->wxpayConfig['notifyurl']); // 通知地址 $this->setParameter("trade_type", "NATIVE"); // 交易型別 // 非必填引數,商戶可根據實際情況選填 //$unifiedOrder->setParameter("sub_mch_id", "XXXX"); // 子商戶號 //$unifiedOrder->setParameter("device_info", "XXXX"); // 裝置號 //$unifiedOrder->setParameter("attach", "XXXX"); // 附加資料 //$unifiedOrder->setParameter("time_start", "XXXX"); // 交易起始時間 //$unifiedOrder->setParameter("time_expire", "XXXX"); // 交易結束時間 //$unifiedOrder->setParameter("goods_tag", "XXXX"); // 商品標記 //$unifiedOrder->setParameter("openid", "XXXX"); // 使用者標識 //$unifiedOrder->setParameter("product_id", "XXXX"); // 商品ID $code_url = $this->getCodeUrl(); if(empty($code_url)) { die('引數出錯,請重試!'); } createQRcode($code_url,0,"",""); // $this->getParameters($prepay_id); // $this->parameters; // $returnurl = $this->wxpayConfig['returnurl']; // $parameters = json_decode($this->parameters); // $ptimeStamp = $parameters->timeStamp; // $pnonceStr = $parameters->nonceStr; // $ppackage = $parameters->package; // $psignType = $parameters->signType; // $ppaySign = $parameters->paySign; // $signPackage = $this->getSignPackage(); } /** * 伺服器非同步通知頁面路徑 */ public function Paynotify() { $xml = $GLOBALS['HTTP_RAW_POST_DATA']; $data = $this->xmlToArray($xml); if($this->checkSign($data) == FALSE){ $this->setReturnParameter("return_code", "FAIL"); // 返回狀態碼 $this->setReturnParameter("return_msg", "簽名失敗"); // 返回資訊 } else { $this->setReturnParameter("return_code", "SUCCESS"); // 設定返回碼 } $returnXml = $this->returnXml(); // echo $returnXml; if($this->checkSign($data) == TRUE) { if ($data["return_code"] == "FAIL") { // 此處應該更新一下訂單狀態,商戶自行增刪操作 die('【通訊出錯】'.$xml); // $log_->log_result($log_name, "【通訊出錯】:\n" . $xml . "\n"); } elseif ($data["result_code"] == "FAIL"){ // 此處應該更新一下訂單狀態,商戶自行增刪操作 // $log_->log_result($log_name, "【業務出錯】:\n" . $xml . "\n"); die('【業務出錯】'.$xml); } else { // 此處應該更新一下訂單狀態,商戶自行增刪操作 // $log_->log_result($log_name, "【支付成功】:\n" . $orderid . "\n"); $orderNum = $data["out_trade_no"]; $m=M('orders'); $state = $m->where('order_num='.$orderNum)->field('order_state,order_id,order_sum_all')->find(); $orderSum = $state["order_sum_all"]; if($state['order_state']==0){ //儲存訂單狀態 $newState['order_state']=4; $newState['order_pay_way']=3;//微信支付 $newState["order_pay_time"] = date('Y-m-d H:i:s',time()); $res1 = $m->where('order_num='.$orderNum)->save($newState); $child=$m->where('parent_id='.$state['order_id'])->select(); $userInfo = M('user')->where('user_id='.session('user_id'))->find(); $orders_goods=M('orders_goods'); $goods=M('goods'); if($child){ //儲存子訂單的狀態 $newChildState['order_state']=4; $newChildState['order_pay_way']=3;//微信支付 $newChildState["order_pay_time"] = date('Y-m-d H:i:s',time()); $m->where('parent_id='.$state['order_id'])->save($newChildState); //獲取父訂單購買的所有商品 $idList=''; $ids=array(); foreach ($child as $key => $value) { $ids[]=$value['order_id']; } $idList=implode(',',$ids); $goodsList=$orders_goods->where('order_id in ('.$idList.')')->select(); }else{ $goodsList=$orders_goods->where('order_id='.$state['order_id'])->select(); } $allPoints=0; foreach ($goodsList as $key => $value) { $goodsPoints =$goods->where('goods_id='.$value['goods_id'])->getField('goods_points'); $allPoints =$allPoints+ $goodsPoints* $value['goods_buy_num']; D('MyLog')->MyLogWrite("order-goodslist==>".D('MyLog')->myPlay($allPoints)); } D('MyLog')->MyLogWrite("order-goodslist==>".D('MyLog')->myPlay($orders_goods->getLastSql())); // 商品積分增加 $poi=M('points'); $pdata['poi_points']=$allPoints; $pdata['poi_form']='購買商品贈送積分'; $pdata['poi_remain_points']=$userInfo['user_points']+$allPoints; $pdata['poi_get_time']=date('Y-m-d H:i:s',time()); $pdata['user_id']=session('user_id'); $pdata['poi_remark']=0; $rese = $poi->add($pdata); $userData['user_points']=$pdata['poi_remain_points']; $resg = M('user')->where('user_id='.session('user_id'))->save($userData); //消費記錄 $consumeSum=$orderSum; $this->comsumeNotes($consumeSum); session('orderID',null); $this->redirect('Mobile/Orders/orderNoReceive'); }else $this->redirect('Mobile/Orders/orderNoReceive'); } //商戶自行增加處理流程, //例如:更新訂單狀態 //例如:資料庫操作 //例如:推送支付完成資訊 } } /** * 伺服器同步通知頁面路徑 */ public function ReturnNotify() { $this->redirect('Mobile/Orders/orderNoReceive'); } /** * 獲取openid */ private function get_openid() { $openid = $_COOKIE['apiopenid']; if(empty($openid)) { // 通過code獲得openid if (!isset($_GET['code'])) { // 觸發微信返回code碼 $url = $this->createOauthUrlForCode($this->get_url()); Header("Location: " . $url); } else { // 獲取code碼,以獲取openid $code = $_GET['code']; $openid = $this->getOpenId($code); setcookie('apiopenid', $openid, time() + 86400); } } return $openid; } /** * 獲取當前頁面完整URL地址 */ private function get_url() { $sys_protocal = isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == '443' ? 'https://' : 'http://'; $php_self = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME']; $path_info = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; $relate_url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : $php_self.(isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : $path_info); return $sys_protocal . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '') . $relate_url; } /** * 作用:生成可以獲得code的url */ private function createOauthUrlForCode($redirectUrl) { $urlObj["appid"] = $this->wxpayConfig['appid']; $urlObj["redirect_uri"] = "$redirectUrl"; $urlObj["response_type"] = "code"; $urlObj["scope"] = "snsapi_base"; $urlObj["state"] = "STATE"."#wechat_redirect"; $bizString = $this->formatBizQueryParaMap($urlObj, false); return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString; } /** * 作用:通過curl向微信提交code,以獲取openid */ private function getOpenid($code) { $url = $this->createOauthUrlForOpenid($code); //初始化curl $ch = curl_init(); //設定超時 curl_setopt($ch, CURLOPT_TIMEOUT, $this->wxpayConfig['CURL_TIMEOUT']); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE); curl_setopt($ch, CURLOPT_HEADER, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //執行curl,結果以jason形式返回 $res = curl_exec($ch); curl_close($ch); //取出openid $data = json_decode($res,true); $this->openid = $data['openid']; return $this->openid; } /** * 作用:生成可以獲得openid的url */ private function createOauthUrlForOpenid($code) { $urlObj["appid"] = $this->wxpayConfig['appid']; $urlObj["secret"] = $this->wxpayConfig['appsecret']; $urlObj["code"] = $code; $urlObj["grant_type"] = "authorization_code"; $bizString = $this->formatBizQueryParaMap($urlObj, false); return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString; } /** * 作用:格式化引數,簽名過程需要使用 */ private function formatBizQueryParaMap($paraMap, $urlencode) { $buff = ""; ksort($paraMap); foreach ($paraMap as $k => $v) { if($urlencode) { $v = urlencode($v); } //$buff .= strtolower($k) . "=" . $v . "&"; $buff .= $k . "=" . $v . "&"; } $reqPar = ''; if (strlen($buff) > 0) { $reqPar = substr($buff, 0, strlen($buff)-1); } return $reqPar; } /** * 作用:設定請求引數 */ private function setParameter($parameter, $parameterValue) { $this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue); } private function trimString($value) { $ret = null; if (null != $value) { $ret = $value; if (strlen($ret) == 0) { $ret = null; } } return $ret; } /** * 獲取prepay_id */ private function getCodeUrl() { $response = $this->postXml(); $result = $this->xmlToArray($response); $code_url = $result["code_url"]; return $code_url; } /** * 作用:post請求xml */ private function postXml() { $xml = $this->createXml(); $response = $this->postXmlCurl($xml,$this->wxpayConfig['url'],$this->wxpayConfig['CURL_TIMEOUT']); return $response; } /** * 生成介面引數xml */ private function createXml() { try { // 檢測必填引數 if($this->parameters["out_trade_no"] == null) { throw new \Exception("缺少統一支付介面必填引數out_trade_no!"."<br>"); }elseif($this->parameters["body"] == null){ throw new \Exception("缺少統一支付介面必填引數body!"."<br>"); }elseif ($this->parameters["total_fee"] == null ) { throw new \Exception("缺少統一支付介面必填引數total_fee!"."<br>"); }elseif ($this->parameters["notify_url"] == null) { throw new \Exception("缺少統一支付介面必填引數notify_url!"."<br>"); }elseif ($this->parameters["trade_type"] == null) { throw new \Exception("缺少統一支付介面必填引數trade_type!"."<br>"); }elseif ($this->parameters["trade_type"] == "JSAPI" && $this->parameters["openid"] == NULL){ throw new \Exception("統一支付介面中,缺少必填引數openid!trade_type為JSAPI時,openid為必填引數!"."<br>"); } $this->parameters["appid"] = $this->wxpayConfig['appid']; // 公眾賬號ID $this->parameters["mch_id"] = $this->wxpayConfig['mchid']; // 商戶號 $this->parameters["spbill_create_ip"] = $_SERVER['REMOTE_ADDR'];// 終端ip $this->parameters["nonce_str"] = $this->createNoncestr(); // 隨機字串 $this->parameters["sign"] = $this->getSign($this->parameters); // 簽名 return $this->arrayToXml($this->parameters); } catch (\Exception $e) { die($e->errorMessage()); } } /** * 作用:產生隨機字串,不長於32位 */ private function createNoncestr( $length = 32 ) { $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; $str = ""; for ( $i = 0; $i < $length; $i++ ) { $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1); } return $str; } /** * 作用:生成簽名 */ private function getSign($Obj) { foreach ($Obj as $k => $v) { $Parameters[$k] = $v; } //簽名步驟一:按字典序排序引數 ksort($Parameters); $String = $this->formatBizQueryParaMap($Parameters, false); //echo '【string1】'.$String.'</br>'; //簽名步驟二:在string後加入KEY $String = $String."&key=".$this->wxpayConfig['key']; //echo "【string2】".$String."</br>"; //簽名步驟三:MD5加密 $String = md5($String); //echo "【string3】 ".$String."</br>"; //簽名步驟四:所有字元轉為大寫 $result_ = strtoupper($String); //echo "【result】 ".$result_."</br>"; return $result_; } /** * 作用:array轉xml */ private function arrayToXml($arr) { $xml = "<xml>"; foreach ($arr as $key=>$val) { if (is_numeric($val)) { $xml.="<".$key.">".$val."</".$key.">"; } else { $xml.="<".$key."><![CDATA[".$val."]]></".$key.">"; } } $xml.="</xml>"; return $xml; } /** * 作用:以post方式提交xml到對應的介面url */ private function postXmlCurl($xml,$url,$second = 30) { //初始化curl $ch = curl_init(); //設定超時 curl_setopt($ch, CURLOPT_TIMEOUT, $second); //這裡設定代理,如果有的話 //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8'); //curl_setopt($ch,CURLOPT_PROXYPORT, 8080); curl_setopt($ch,CURLOPT_URL, $url); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE); //設定header curl_setopt($ch, CURLOPT_HEADER, FALSE); //要求結果為字串且輸出到螢幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //post提交方式 curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); //執行curl $data = curl_exec($ch); curl_close($ch); //返回結果 if($data) { //curl_close($ch); return $data; } else { $error = curl_errno($ch); echo "curl出錯,錯誤碼:$error"."<br>"; echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>錯誤原因查詢</a></br>"; curl_close($ch); return false; } } /** * 作用:將xml轉為array */ private function xmlToArray($xml) { //將XML轉為array $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $array_data; } /** * 作用:設定jsapi的引數 */ private function getParameters($prepay_id) { $jsApiObj["appId"] = $this->wxpayConfig['appid']; $timeStamp = time(); $jsApiObj["timeStamp"] = "$timeStamp"; $jsApiObj["nonceStr"] = $this->createNoncestr(); $jsApiObj["package"] = "prepay_id=$prepay_id"; $jsApiObj["signType"] = "MD5"; $jsApiObj["paySign"] = $this->getSign($jsApiObj); $this->parameters = json_encode($jsApiObj); } private function checkSign($data) { $tmpData = $data; unset($tmpData['sign']); $sign = $this->getSign($tmpData);//本地簽名 if ($data['sign'] == $sign) { return TRUE; } return FALSE; } /** * 設定返回微信的xml資料 */ private function setReturnParameter($parameter, $parameterValue) { $this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue); } /** * 將xml資料返回微信 */ private function returnXml() { $returnXml = $this->arrayToXml($this->returnParameters); return $returnXml; } /*----以下是JSSDK的檔案----*/ private function getSignPackage() { $jsapiTicket = $this->getJsApiTicket(); // 注意 URL 一定要動態獲取,不能 hardcode. $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://"; $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; $timestamp = time(); $nonceStr = $this->createNonceStr2(); // 這裡引數的順序要按照 key 值 ASCII 碼升序排序 $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url"; $signature = sha1($string); $signPackage = array( "appId" => $this->wxpayConfig['appid'], "nonceStr" => $nonceStr, "timestamp" => $timestamp, "url" => $url, "signature" => $signature, "rawString" => $string ); return $signPackage; } private function createNonceStr2($length = 16) { $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; $str = ""; for ($i = 0; $i < $length; $i++) { $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $str; } private function getJsApiTicket() { // jsapi_ticket 應該全域性儲存與更新,以下程式碼以寫入到檔案中做示例 // $data = json_decode(file_get_contents("jsapi_ticket.json")); $data = json_decode($_COOKIE['jsapi_ticket_json']); if ($data->expire_time < time()) { $accessToken = $this->getAccessToken(); // 如果是企業號用以下 URL 獲取 ticket // $url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=$accessToken"; $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$accessToken"; $res = json_decode($this->httpGet($url)); $ticket = $res->ticket; if ($ticket) { //$data->expire_time = time() + 7000; //$data->jsapi_ticket = $ticket; //$fp = fopen("jsapi_ticket.json", "w"); //fwrite($fp, json_encode($data)); //fclose($fp); $tempArr = array('jsapi_ticket' => $ticket, 'expire_time' => time() + 7000); setcookie('jsapi_ticket_json', json_encode($tempArr), $tempArr['expire_time']); } } else { $ticket = $data->jsapi_ticket; } return $ticket; } private function getAccessToken() { // access_token 應該全域性儲存與更新,以下程式碼以寫入到檔案中做示例 // $data = json_decode(file_get_contents("access_token.json")); $data = json_decode($_COOKIE["access_token_json"]); if ($data->expire_time < time()) { // 如果是企業號用以下URL獲取access_token // $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret"; $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $this->wxpayConfig['appid'] . "&secret=" . $this->wxpayConfig['appsecret']; $res = json_decode($this->httpGet($url)); $access_token = $res->access_token; if ($access_token) { //$data->expire_time = time() + 7000; //$data->access_token = $access_token; //$fp = fopen("access_token.json", "w"); //fwrite($fp, json_encode($data)); //fclose($fp); $tempArr = array('access_token' => $access_token, 'expire_time' => time() + 7000); setcookie('access_token_json', json_encode($tempArr), $tempArr['expire_time']); } } else { $access_token = $data->access_token; } return $access_token; } private function httpGet($url) { $curl = curl_init(); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_TIMEOUT, 500); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($curl, CURLOPT_URL, $url); $res = curl_exec($curl); curl_close($curl); return $res; } //消費記錄 function comsumeNotes($conSum){ $userSum=M('user')->where('user_id='.session('user_id'))->getField('user_sum'); $note['con_type']=-1; $note['con_sum']=$conSum; $note['con_arrive_sum']=$conSum; $note['con_form']="微信支付購買商品"; $note['con_remain_sum']=$userSum; $note['con_operate_time']=date('Y-m-d H:i:s'); $note['user_id']=session('user_id'); $res1=M("depo_cons_notes")->add($note); } }