微信开拓文档:pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=2_1
APP支付流程
从上面的图片中,可以看出来,须要把稳的流程是一共是3部分;
第一部分:调用下单API,返回预支付订单,署名之后再返复书息
第二部分:异步关照
第三部分:末了的判断支付结果
最须要把稳的便是第一部分:调用下单API,返回预支付订单,署名之后再返复书息
这里封装好的一个支付类文件,多余的东西都去除掉了,并且把配置参数放到了这个支付类中,只须要修正Weixinpayandroid方法内的几个参数就可以直接复制利用:
class Wxpayandroid{ //参数配置 public $config = array( 'appid' => \公众\"大众, /微信开放平台上的运用id/ 'mch_id' => \公众\公众, /微信申请成功之后邮件中的商户id/ 'api_key' => \"大众\"大众, /在微信商户平台上自己设定的api密钥 32位/ ); //做事器异步关照页面路径(必填) public $notify_url = ''; //商户订单号(必填,商户网站订单系统中唯一订单号) public $out_trade_no = ''; //商品描述(必填,不填则为商品名称) public $body = ''; //付款金额(必填) public $total_fee = 0; //自定义超时(选填,支持dhmc) public $time_expire = ''; private $WxPayHelper; public function Weixinpayandroid($total_fee,$tade_no) { $this->total_fee = intval($total_fee 100);//订单的金额 1元 $this->out_trade_no = $tade_no;// date('YmdHis') . substr(time(), - 5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));//订单号 $this->body = 'wxpay';//支付描述信息 $this->time_expire = date('YmdHis', time() + 86400);//订单支付的过期韶光(eg:一天过期) $this->notify_url = \"大众http://www.ceshi.com/notifyandroid\"大众;//异步关照URL(变动支付状态) //数据以JSON的形式返回给APP $app_response = $this->doPay(); if (isset($app_response['return_code']) && $app_response['return_code'] == 'FAIL') { $errorCode = 100; $errorMsg = $app_response['return_msg']; $this->echoResult($errorCode, $errorMsg); } else { $errorCode = 0; $errorMsg = 'success'; $responseData = array( 'notify_url' => $this->notify_url, 'app_response' => $app_response, ); $this->echoResult($errorCode, $errorMsg, $responseData); } } //接口输出 function echoResult($errorCode = 0, $errorMsg = 'success', $responseData = array()) { $arr = array( 'errorCode' => $errorCode, 'errorMsg' => $errorMsg, 'responseData' => $responseData, ); exit(json_encode($arr)); //exit可以正常发送给APP json数据 // return json_encode($arr); //在TP5中return这个json数据,APP吸收到的是null,无法正常吊起微信支付 } function getVerifySign($data, $key) { $String = $this->formatParameters($data, false); //署名步骤二:在string后加入KEY $String = $String . \"大众&key=\"大众 . $key; //署名步骤三:MD5加密 $String = md5($String); //署名步骤四:所有字符转为大写 $result = strtoupper($String); return $result; } function formatParameters($paraMap, $urlencode) { $buff = \"大众\公众; ksort($paraMap); foreach ($paraMap as $k => $v) { if($k==\"大众sign\公众){ continue; } if ($urlencode) { $v = urlencode($v); } $buff .= $k . \"大众=\"大众 . $v . \"大众&\"大众; } $reqPar; if (strlen($buff) > 0) { $reqPar = substr($buff, 0, strlen($buff) - 1); } return $reqPar; } / 得到署名 @param object $obj @param string $api_key @return string / function getSign($obj, $api_key) { foreach ($obj as $k => $v) { $Parameters[strtolower($k)] = $v; } //署名步骤一:按字典序排序参数 ksort($Parameters); $String = $this->formatBizQueryParaMap($Parameters, false); //署名步骤二:在string后加入KEY $String = $String.\公众&key=\"大众.$api_key; //署名步骤三:MD5加密 $result = strtoupper(md5($String)); return $result; } / 获取指定长度的随机字符串 @param int $length @return Ambigous <NULL, string> / function getRandChar($length){ $str = null; $strPol = \"大众ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz\公众; $max = strlen($strPol)-1; for($i=0;$i<$length;$i++){ $str.=$strPol[rand(0,$max)];//rand($min,$max)天生介于min和max两个数之间的一个随机整数 } return $str; } / 数组转xml @param array $arr @return string / 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 @param string $xml 须要post的xml数据 @param string $url url @param bool $useCert 是否须要证书,默认不须要 @param int $second url实行超时时间,默认30s @throws WxPayException / function postXmlCurl($xml, $url, $second=30, $useCert=false, $sslcert_path='', $sslkey_path='') { $ch = curl_init(); //设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, $second); curl_setopt($ch,CURLOPT_URL, $url); //设置header curl_setopt($ch, CURLOPT_HEADER, FALSE); //哀求结果为字符串且输出到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE); if($useCert == true){ curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验 //设置证书 //利用证书:cert 与 key 分别属于两个.pem文件 curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLCERT, $sslcert_path); curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLKEY, $sslkey_path); } //post提交办法 curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); //运行curl $data = curl_exec($ch); //返回结果 if($data){ curl_close($ch); return $data; } else { $error = curl_errno($ch); curl_close($ch); return false; } } / 获取当前做事器的IP @return Ambigous <string, unknown> / function get_client_ip() { if (isset($_SERVER['REMOTE_ADDR'])) { $cip = $_SERVER['REMOTE_ADDR']; } elseif (getenv(\"大众REMOTE_ADDR\"大众)) { $cip = getenv(\"大众REMOTE_ADDR\"大众); } elseif (getenv(\"大众HTTP_CLIENT_IP\"大众)) { $cip = getenv(\公众HTTP_CLIENT_IP\"大众); } else { $cip = \"大众127.0.0.1\"大众; } return $cip; } / 将数组转成uri字符串 @param array $paraMap @param bool $urlencode @return string / function formatBizQueryParaMap($paraMap, $urlencode) { $buff = \公众\"大众; ksort($paraMap); foreach ($paraMap as $k => $v) { if($urlencode) { $v = urlencode($v); } $buff .= strtolower($k) . \公众=\"大众 . $v . \"大众&\公众; } $reqPar; if (strlen($buff) > 0) { $reqPar = substr($buff, 0, strlen($buff)-1); } return $reqPar; } / XML转数组 @param unknown $xml @return mixed / function xmlToArray($xml) { //将XML转为array $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $array_data; } public function chkParam() { //用户网站订单号 if (empty($this->out_trade_no)) { die('out_trade_no error'); } //商品描述 if (empty($this->body)) { die('body error'); } if (empty($this->time_expire)){ die('time_expire error'); } //检测支付金额 if (empty($this->total_fee) || !is_numeric($this->total_fee)) { die('total_fee error'); } //异步关照URL if (empty($this->notify_url)) { die('notify_url error'); } if (!preg_match(\"大众#^http:\/\/#i\"大众, $this->notify_url)) { $this->notify_url = \公众http://\公众 . $_SERVER['HTTP_HOST'] . $this->notify_url; } return true; } / 天生支付(返回给APP) @return boolean|mixed / public function doPay() { //检测布局参数 $this->chkParam(); return $this->createAppPara(); } / APP统一下单 / private function createAppPara() { $url = \"大众api.mch.weixin.qq.com/pay/unifiedorder\公众; $data[\公众appid\公众] = $this->config['appid'];//微信开放平台审核通过的运用APPID $data[\"大众body\"大众] = $this->body;//商品或支付单简要描述 $data[\公众mch_id\"大众] = $this->config['mch_id'];//商户号 $data[\"大众nonce_str\"大众] = $this->getRandChar(32);//随机字符串 $data[\公众notify_url\"大众] = $this->notify_url;//关照地址 $data[\公众out_trade_no\公众] = $this->out_trade_no;//商户订单号 $data[\"大众spbill_create_ip\公众] = $this->get_client_ip();//终端IP $data[\"大众total_fee\"大众] = $this->total_fee;//总金额 $data[\"大众time_expire\"大众] = $this->time_expire;//交易结束韶光 $data[\"大众trade_type\"大众] = \公众APP\"大众;//交易类型 $data[\公众sign\公众] = $this->getSign($data, $this->config['api_key']);//署名 $xml = $this->arrayToXml($data); $response = $this->postXmlCurl($xml, $url); //将微信返回的结果xml转成数组 $responseArr = $this->xmlToArray($response); if(isset($responseArr[\"大众return_code\"大众]) && $responseArr[\"大众return_code\"大众]=='SUCCESS'){ return $this->getOrder($responseArr['prepay_id']); } return $responseArr; } / 实行第二次署名,才能返回给客户端利用 @param int $prepayId:预支付交易会话标识 @return array / public function getOrder($prepayId) { $data[\公众appid\公众] = $this->config['appid']; $data[\"大众noncestr\"大众] = $this->getRandChar(32); $data[\"大众package\公众] = \"大众Sign=WXPay\"大众; $data[\公众partnerid\"大众] = $this->config['mch_id']; $data[\"大众prepayid\"大众] = $prepayId; $data[\"大众timestamp\"大众] = time(); $data[\"大众sign\"大众] = $this->getSign($data, $this->config['api_key']); $data[\"大众packagestr\"大众] = \公众Sign=WXPay\"大众; return $data; } / 异步关照信息验证 @return boolean|mixed / public function verifyNotify() { $xml = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : ''; if(!$xml){ return false; } $wx_back = $this->xmlToArray($xml); if(empty($wx_back)){ return false; } $checkSign = $this->getVerifySign($wx_back, $this->config['api_key']); if($checkSign=$wx_back['sign']){ return $wx_back; }else{ return false; } }}
2.创建掌握器定义统一下单接口和支付后的异步回调接口:
//异步关照接口 public function notifyandroid() { $wxpayandroid = new \Wxpayandroid; //实例化微信支付类 $verify_result = $wxpayandroid->verifyNotify(); if ($verify_result['return_code']=='SUCCESS' && $verify_result['result_code']=='SUCCESS') { //商户订单号 $out_trade_no = $verify_result['out_trade_no']; //交易号 $trade_no = $verify_result['transaction_id']; //交易状态 $trade_status = $verify_result['result_code']; //支付金额 $total_fee = $verify_result['total_fee']/100; //支付过期韶光 $pay_date = $verify_result['time_end']; $order = new Order(); $ret = $order->getOrderN2($out_trade_no); //获取订单信息 $total_amount=$ret['money']; if ($total_amount==$total_fee) { // 验证成功 修正数据库的订单状态等 $result['out_trade_no']为订单号 //此处写自己的逻辑代码 } exit('<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'); }else{ exit('<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[ERROR]]></return_msg></xml>'); } } //调用统一下单接口天生预支付订单并把数据返回给APP public function wxpayandroid(Request $request) { $param = $request->param(); //吸收值 $tade_no = $param['orderCode']; $order = new Order(); //实例化订单 $ret = $order->getOrderN2($tade_no); //查询订单信息 $total_fee = $ret['money']; //订单总金额 $wxpayandroid = new \Wxpayandroid; //实例化微信支付类 $res = $wxpayandroid->Weixinpayandroid($total_fee,$tade_no); //调用weixinpay方法 }
封装一个支付类文件,并把配置参数放到支付类内,再定义掌握器创建两个方法,这样三步走都在了,不是很大略呀