首页 » 微信公众平台开发:从零基础到ThinkPHP5高性能框架实践 » 微信公众平台开发:从零基础到ThinkPHP5高性能框架实践全文在线阅读

《微信公众平台开发:从零基础到ThinkPHP5高性能框架实践》17.1.4 微信支付基础类

关灯直达底部

统一下单接口的地址如下。


https:// api.mch.weixin.qq.com/pay/unifiedorder  

向统一下单接口,POST数据示例如下。


<xml>    <appid>wx2421b1c4370ec43b</appid>    <attach>支付测试</attach>    <body>JSAPI支付测试</body>    <mch_id>10000100</mch_id>    <detail><![CDATA[{ "goods_detail":[ { "goods_id":"iphone6s_16G", "wxpay_goods_id":    "1001", "goods_name":"iPhone6s 16G", "quantity":1, "price":528800, "goods_category":    "123456", "body":"苹果手机" }, { "goods_id":"iphone6s_32G", "wxpay_goods_id":    "1002", "goods_name":"iPhone6s 32G", "quantity":1, "price":608800, "goods_category"    :"123789", "body":"苹果手机" } ] }]]></detail>    <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>    <notify_url>http:// wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php</notify_url>    <openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>    <out_trade_no>1415659990</out_trade_no>    <spbill_create_ip>14.23.150.211</spbill_create_ip>    <total_fee>1</total_fee>    <trade_type>JSAPI</trade_type>    <sign>0CB01533B8C1EF103065174F50BCA001</sign></xml>  

上述数据的参数说明如表17-2所示。

表17-2 统一下单接口的参数说明

正确创建时,返回的数据示例如下。


<xml>    <return_code><![CDATA[SUCCESS]]></return_code>    <return_msg><![CDATA[OK]]></return_msg>    <appid><![CDATA[wx2421b1c4370ec43b]]></appid>    <mch_id><![CDATA[10000100]]></mch_id>    <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>    <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>    <result_code><![CDATA[SUCCESS]]></result_code>    <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>    <trade_type><![CDATA[JSAPI]]></trade_type></xml>  

上述数据的参数说明如表17-3所示。

表17-3 统一下单接口返回参数说明

下面将列举微信支付基础类的实现,这些类包含产生随机字符串、格式化参数、生成签名、array转XML、以POST方式提交XML到对应的接口等功能,是后面实现各种支付场景的前提。

微信支付接口基类的实现代码如下。


  1 /**  2  * 所有接口的基类  3  */  4 class Common_util_pub  5 {  6     function __construct {  7     }  8   9     function trimString($value) 10     { 11         $ret = null; 12         if (null != $value)  13         { 14             $ret = $value; 15             if (strlen($ret) == 0)  16             { 17                 $ret = null; 18             } 19         } 20         return $ret; 21     } 22      23     /** 24      *     作用:产生随机字符串,不长于32位 25      */ 26     public function createNoncestr( $length = 32 )  27     { 28         $chars = "abcdefghijklmnopqrstuvwxyz0123456789";   29         $str =""; 30         for ( $i = 0; $i < $length; $i++ )  {   31             $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);   32         }   33         return $str; 34     } 35      36     /** 37      *     作用:格式化参数,签名过程需要使用 38      */ 39     function formatBizQueryParaMap($paraMap, $urlencode) 40     { 41         $buff = ""; 42         ksort($paraMap); 43         foreach ($paraMap as $k => $v) 44         { 45             if($urlencode) 46             { 47                $v = urlencode($v); 48             } 49             // $buff .= strtolower($k) . "=" . $v . "&"; 50             $buff .= $k . "=" . $v . "&"; 51         } 52         $reqPar; 53         if (strlen($buff) > 0)  54         { 55             $reqPar = substr($buff, 0, strlen($buff)-1); 56         } 57         return $reqPar; 58     } 59      60     /** 61      *     作用:生成签名 62      */ 63     public function getSign($Obj) 64     { 65         foreach ($Obj as $k => $v) 66         { 67             $Parameters[$k] = $v; 68         } 69         // 签名步骤一:按字典序排序参数 70         ksort($Parameters); 71         $String = $this->formatBizQueryParaMap($Parameters, false); 72         // 签名步骤二:在string后加入key 73         $String = $String."&key=".WxPayConf_pub::KEY; 74         // 签名步骤三:MD5加密 75         $String = md5($String); 76         // 签名步骤四:所有字符转为大写 77         $result_ = strtoupper($String); 78         return $result_; 79     } 80      81     /** 82      *     作用:array转XML 83      */ 84     function arrayToXml($arr) 85     { 86         $xml = "<xml>"; 87         foreach ($arr as $key=>$val) 88         { 89              if (is_numeric($val)) 90              { 91                  $xml.="<".$key.">".$val."</".$key.">";  92  93              } 94              else 95                  $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";   96         } 97         $xml.="</xml>"; 98         return $xml;  99     }100     101     /**102      *     作用:将XML转为array103      */104     public function xmlToArray($xml)105     {        106         // 将XML转为array107         $array_data = json_decode(json_encode(simplexml_load_string($xml, 'Simple-            XMLElement', LIBXML_NOCDATA)), true);108         return $array_data;109     }110 111     /**112      *     作用:以POST方式提交XML到对应的接口URL113      */114     public function postXmlCurl($xml,$url,$second=30)115     {        116         // 初始化curl        117            $ch = curl_init;118         // 设置超时119         curl_setopt($ch, CURLOPT_TIMEOUT, $second);120         curl_setopt($ch,CURLOPT_URL, $url);121         curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);122         curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);123         // 设置header124         curl_setopt($ch, CURLOPT_HEADER, FALSE);125         // 要求结果为字符串且输出到屏幕上126         curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);127         // POST提交方式128         curl_setopt($ch, CURLOPT_POST, TRUE);129         curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);130         // 运行curl131         $data = curl_exec($ch);132         // 返回结果133         if($data)134         {135             curl_close($ch);136             return $data;137         }138         else 139         { 140             $error = curl_errno($ch);141             echo "curl出错,错误码:$error"."<br>"; 142             echo "<a href='http:// curl.haxx.se/libcurl/c/libcurl-errors.html'>                错误原因查询</a></br>";143             curl_close($ch);144             return false;145         }146     }147 148     /**149      *     作用:使用证书,以POST方式提交XML到对应的接口URL150      */151     function postXmlSSLCurl($xml,$url,$second=30)152     {153         $ch = curl_init;154         // 超时时间155         curl_setopt($ch,CURLOPT_TIMEOUT,$second);156         // 这里设置代理,如果有的话157         // curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');158         // curl_setopt($ch,CURLOPT_PROXYPORT, 8080);159         curl_setopt($ch,CURLOPT_URL, $url);160         curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);161         curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);162         // 设置header163         curl_setopt($ch,CURLOPT_HEADER,FALSE);164         // 要求结果为字符串且输出到屏幕上165         curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);166         // 设置证书167         // 使用证书:cert 与 key 分别属于两个.pem文件168         // 默认格式为PEM,可以注释169         curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');170         curl_setopt($ch,CURLOPT_SSLCERT, WxPayConf_pub::SSLCERT_PATH);171         // 默认格式为PEM,可以注释172         curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');173         curl_setopt($ch,CURLOPT_SSLKEY, WxPayConf_pub::SSLKEY_PATH);174         // POST提交方式175         curl_setopt($ch,CURLOPT_POST, true);176         curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);177         $data = curl_exec($ch);178         // 返回结果179         if($data){180             curl_close($ch);181             return $data;182         }183         else { 184             $error = curl_errno($ch);185             echo "curl出错,错误码:$error"."<br>"; 186             echo "<a href='http:// curl.haxx.se/libcurl/c/libcurl-errors.html'>        错误原因查询</a></br>";187             curl_close($ch);188             return false;189         }190     }191     192     /**193      *     作用:打印数组194      */195     function printErr($wording='',$err='')196     {197         print_r('<pre>');198         echo $wording."</br>";199         var_dump($err);200         print_r('</pre>');201     }202 } 

请求型接口主要是设置请求参数,生成接口参数XML。


 1 /** 2  * 请求型接口的基类 3  */ 4 class Wxpay_client_pub extends Common_util_pub 5 { 6     var $parameters;                                        // 请求参数,类型为关联数组 7     public $response;                                       // 微信返回的响应 8     public $result;                                         // 返回参数,类型为关联数组 9     var $url;                                               // 接口链接10     var $curl_timeout;                                      // curl超时时间11 12     /**13      *     作用:设置请求参数14      */15     function setParameter($parameter, $parameterValue)16     {17         $this->parameters[$this->trimString($parameter)] = $this->trimString       ($parameterValue);18     }19 20     /**21      *     作用:设置标配的请求参数,生成签名,生成接口参数XML22      */23     function createXml24     {25         $this->parameters["appid"] = WxPayConf_pub::APPID;          // 公众账号ID26         $this->parameters["mch_id"] = WxPayConf_pub::MCHID;         // 商户号27         $this->parameters["nonce_str"] = $this->createNoncestr; // 随机字符串28         $this->parameters["sign"] = $this->getSign($this->parameters);// 29         $abc =$this->arrayToXml($this->parameters);;30         return  $abc;31     }32 33     /**34      *     作用:POST请求XML35      */36     function postXml37     {38         $xml = $this->createXml;39         $this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);40         return $this->response;41     }42 43     /**44      *     作用:使用证书POST请求XML45      */46     function postXmlSSL47     {48         $xml = $this->createXml;49         $this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);50         return $this->response;51     }52 53     /**54      *     作用:获取结果,默认不使用证书55      */56     function getResult57     {58         $this->postXml;59         $this->result = $this->xmlToArray($this->response);60         return $this->result;61     }62 }  

响应型接口基类的成员函数包括XML转成关联数组、校验签名、生成返回的XML参数。


 1 /** 2  * 响应型接口基类 3  */ 4 class Wxpay_server_pub extends Common_util_pub 5 { 6     public $data;                                // 接收到的数据,类型为关联数组 7     var $returnParameters;                        // 返回参数,类型为关联数组 8  9     /**10      * 将微信的请求XML转换成关联数组,以方便数据处理11      */12     function saveData($xml)13     {14         $this->data = $this->xmlToArray($xml);15     }16 17     function checkSign18     {19         $tmpData = $this->data;20         unset($tmpData['sign']);21         $sign = $this->getSign($tmpData);        // 本地签名22         if ($this->data['sign'] == $sign) {23             return TRUE;24         }25         return FALSE;26     }27 28     /**29      * 获取微信的请求数据30      */31     function getData32     {33         return $this->data;34     }35 36     /**37      * 设置返回微信的XML数据38      */39     function setReturnParameter($parameter, $parameterValue)40     {41         $this->returnParameters[$this->trimString($parameter)] = $this->trimString       ($parameterValue);42     }43 44     /**45      * 生成接口参数XML46      */47     function createXml48     {49         return $this->arrayToXml($this->returnParameters);50     }51 52     /**53      * 将XML数据返回微信54      */55     function returnXml56     {57         $returnXml = $this->createXml;58         return $returnXml;59     }60 }  

在上述接口的基础上,统一支付接口主要设置接口链接、检测参数,以及生成prepay_id。


 1 /** 2  * 统一支付接口类 3  */ 4 class UnifiedOrder_pub extends Wxpay_client_pub 5 { 6     function __construct 7     { 8         // 设置接口链接 9         $this->url = "https:// api.mch.weixin.qq.com/pay/unifiedorder";10         // 设置curl超时时间11         $this->curl_timeout = WxPayConf_pub::CURL_TIMEOUT;12     }13 14     /**15      * 生成接口参数XML16      */17     function createXml18     {19         try20         {21             // 检测必填参数22             if($this->parameters["out_trade_no"] == null)23             {24                 throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!".                   "<br>");25             }elseif($this->parameters["body"] == null){26                 throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");27             }elseif ($this->parameters["total_fee"] == null ) {28                 throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!".                   "<br>");29             }elseif ($this->parameters["notify_url"] == null) {30                 throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!".                   "<br>");31             }elseif ($this->parameters["trade_type"] == null) {32                 throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!                   "."<br>");33             }elseif ($this->parameters["trade_type"] == "JSAPI" && $this->parameters                   ["openid"] == NULL){35                 throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!                   trade_type为JSAPI时,openid为必填参数!"."<br>");36             }37             $this->parameters["appid"] = WxPayConf_pub::APPID;// 公众账号ID38             $this->parameters["mch_id"] = WxPayConf_pub::MCHID;// 商户号39             $this->parameters["spbill_create_ip"] = $_SERVER['REMOTE_ADDR'];// 终端IP40             $this->parameters["nonce_str"] = $this->createNoncestr;// 随机字符串41             $this->parameters["sign"] = $this->getSign($this->parameters);// 签名42             $xyz =$this->arrayToXml($this->parameters);43             return  $xyz;44         }catch (SDKRuntimeException $e)45         {46             die($e->errorMessage);47         }48     }49 50     /**51      * 获取prepay_id52      */53     function getPrepayId54     {55         $this->postXml;56         $this->result = $this->xmlToArray($this->response);57         $prepay_id = $this->result["prepay_id"];58         return $prepay_id;59     }60 } 

短链接接口主要是将微信支付的长链接转换为短链接。


 1 /** 2  * 短链接转换接口 3  */ 4 class ShortUrl_pub extends Wxpay_client_pub 5 { 6     function __construct 7     { 8         // 设置接口链接 9         $this->url = "https:// api.mch.weixin.qq.com/tools/shorturl";10         // 设置curl超时时间11         $this->curl_timeout = WxPayConf_pub::CURL_TIMEOUT;12     }13 14     /**15      * 生成接口参数XML16      */17     function createXml18     {19         try20         {21             if($this->parameters["long_url"] == null )22             {23                 throw new SDKRuntimeException("短链接转换接口中,缺少必填参数long_url!".                   "<br>");24             }25                $this->parameters["appid"] = WxPayConf_pub::APPID;// 公众账号ID26                $this->parameters["mch_id"] = WxPayConf_pub::MCHID;// 商户号27             $this->parameters["nonce_str"] = $this->createNoncestr;// 随机字符串28             $this->parameters["sign"] = $this->getSign($this->parameters);// 签名29             return  $this->arrayToXml($this->parameters);30         }catch (SDKRuntimeException $e)31         {32             die($e->errorMessage);33         }34     }35 36     /**37      * 获取prepay_id38      */39     function getShortUrl40     {41         $this->postXml;42         $prepay_id = $this->result["short_url"];43         return $prepay_id;44     }45 }