做过微信支付的小伙伴都知道,可以直接使用微信公众平台提供的支付demo,但是你会发现有很多没用的文件,而且demo写得异常复杂,文件太多,需要花比较多的时间去熟悉和调试。
这个对于新手小伙伴来说还是有一定的难度,在开发的过程中会遇到很多的坑,当初我第一次实现微信支付也是同样的啦!人生总会有很对第一次的不是,包括...嘿嘿,别想太多啦!
sdk对于有代码洁癖的资深php开发小伙伴来说就简直不能忍了,凭着高超的记忆怎么也要自己来封装一个吧,装逼必备啊有木有!
自己封装也是有很多好处的,能够更完美的嵌入到自身的框架内,能够更加方便的维护和修改,你看几十上百个文件和看一个文件实现同样的功能,那个更加容易些,答案显而易见了!
下面我们来研究一波:
看下请求的参数主要就是这个【签名和随机字符串】,其他字段的数据都能够很容易的得到,而这个签名和随机字符串需要做一个处理;
签名如何处理?我们写一个函数来搞定他,代码如下
1 2 3 4 5 6 | //data就是所有必填参数值,key就是你的支付秘钥(商户中心设置那个32位那个) function sign( array $data , $key ) { ksort( $data ); $sign = strtoupper (md5(urldecode(http_build_query( $data )). '&key=' . $key )); return $sign ; } |
随机字符串如何处理?我们也写一个函数来搞定他
1 2 3 4 5 6 7 8 | public function getNonceStr( $length = 32){ $chars = "abcdefghijklmnopqrstuvwxyz0123456789" ; $str = "" ; for ( $i = 0; $i < $length ; $i ++ ) { $str .= substr ( $chars , mt_rand(0, strlen ( $chars )-1), 1); } return $str ; } |
第二:微信支付请求数据类型位xml,因此我们还需要一个将数组转为xml的函数,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /** * @param $values * @return string * @throws \think\Exception * 数组转xml */ function ToXml( $values ){ if (! is_array ( $values ) || count ( $values ) <= 0) { throw new \think\Exception( '数组数据异常' ); } $xml = "<xml>" ; foreach ( $values as $key => $val ) { if ( is_numeric ( $val )){ $xml .= "<" . $key . ">" . $val . "</" . $key . ">" ; } else { $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">" ; } } $xml .= "</xml>" ; return $xml ; } |
第三:组装完毕参数之后需要将参数发送到微信支付接口,因此需要一个curl请求函数代码如下(需要自己将参数配置到配置文件中,Config::get()为获取配置文件的微信支付配置信息)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | protected static function postXmlCurl( $xml , $url , $useCert = false, $second = 30){ $ch = curl_init(); //设置超时 curl_setopt( $ch , CURLOPT_TIMEOUT, $second ); //如果有配置代理这里就设置代理 if (Config::get( 'wx.CURL_PROXY_HOST' ) != "0.0.0.0" && Config::get( 'wx.CURL_PROXY_PORT' ) != 0){ curl_setopt( $ch ,CURLOPT_PROXY, Config::get( 'wx.CURL_PROXY_HOST' )); curl_setopt( $ch ,CURLOPT_PROXYPORT, Config::get( 'wx.CURL_PROXY_PORT' )); } curl_setopt( $ch ,CURLOPT_URL, $url ); curl_setopt( $ch ,CURLOPT_SSL_VERIFYPEER,TRUE); curl_setopt( $ch ,CURLOPT_SSL_VERIFYHOST,2); //严格校验 //设置header curl_setopt( $ch , CURLOPT_HEADER, FALSE); //要求结果为字符串且输出到屏幕上 curl_setopt( $ch , CURLOPT_RETURNTRANSFER, TRUE); if ( $useCert == true){ //设置证书 //使用证书:cert 与 key 分别属于两个.pem文件 curl_setopt( $ch ,CURLOPT_SSLCERTTYPE, 'PEM' ); curl_setopt( $ch ,CURLOPT_SSLCERT, Config::get( 'wx.SSLCERT_PATH' )); curl_setopt( $ch ,CURLOPT_SSLKEYTYPE, 'PEM' ); curl_setopt( $ch ,CURLOPT_SSLKEY, Config::get( 'wx.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 ); returnFailMsg( '10018' , "curl出错,错误码:$error" ); } } |
微信支付需要的几个函数我们就写完了,剩下的就是如何传递参数和调用了。
最后大概是这样,需要自己调调了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | class Wechat { public $values = []; /** * @return \think\response\Json * 支付 */ public function wxPay(){ //通用参数 $this ->values[ 'appid' ] = Config::get( 'wx.APPID' ); $this ->values[ 'mch_id' ] = Config::get( 'wx.MCHID' ); $this ->values[ 'nonce_str' ] = $this ->getNonceStr(); $this ->values[ 'goods_tag' ] = 'test' ; $this ->values[ 'out_trade_no' ] = time().rand(1000,9999); //平台订单号 $this ->values[ 'total_fee' ] = 1 * 100; //单位为分*100为元 $this ->values[ 'time_start' ] = date ( "YmdHis" ); $this ->values[ 'time_expire' ] = date ( "YmdHis" , time() + 600); $this ->values[ 'notify_url' ] = Config::get( 'wx.NOTIFY' ); if ( 'JSAPI' === $this ->values[ 'trade_type' ]){ if ( empty ( $this ->values[ 'openid' ]) || !isset( $this ->values[ 'openid' ])) die ( 'jsAPI支付openid为必传项' ) } if ( 'NATIVE' === $this ->values[ 'trade_type' ]){ $this ->values[ 'spbill_create_ip' ] = $_SERVER [ 'REMOTE_ADDR' ]; //获取机器的IP if ( empty ( $this ->values[ 'product_id' ] || !isset( $this ->values[ 'product_id' ]))){ die ( 'NATIVE支付商品id为必传项' ) } } $sign = $this ->sign( $this ->values,Config::get( 'wx.KEY' )); //签名 $this ->values[ 'sign' ] = $sign ; $xml = $this ->ToXml( $this ->values); //转xml对象 $response = $this ->postXmlCurl( $xml , Config::get( 'wx.PAYURL' ), false, 6); //发起post请求 $this ->values = $this ->FromXml( $response ); if ( $this ->sign( $this ->values,Config::get( 'wx.KEY' )) !== $this ->values[ 'sign' ]){ die ( '签名错误' ) } var_dump( $this ->values); } public function sign( $data , $key ) { //sign不参与签名 if ( $data [ 'sign' ]){ unset( $data [ 'sign' ]); } ksort( $data ); $sign = strtoupper (md5(urldecode(http_build_query( $data )). '&key=' . $key )); return $sign ; } /** * @param $values * @return string * @throws \think\Exception * 数组转xml */ function ToXml( $values ){ if (! is_array ( $values ) || count ( $values ) <= 0) { throw new \think\Exception( '数组数据异常' ); } $xml = "<xml>" ; foreach ( $values as $key => $val ) { if ( is_numeric ( $val )){ $xml .= "<" . $key . ">" . $val . "</" . $key . ">" ; } else { $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">" ; } } $xml .= "</xml>" ; return $xml ; } public function getNonceStr( $length = 32){ $chars = "abcdefghijklmnopqrstuvwxyz0123456789" ; $str = "" ; for ( $i = 0; $i < $length ; $i ++ ) { $str .= substr ( $chars , mt_rand(0, strlen ( $chars )-1), 1); } return $str ; } /** * @param $xml * @param $url * @param bool $useCert * @param int $second * @return mixed * post方式请求 */ protected static function postXmlCurl( $xml , $url , $useCert = false, $second = 30){ $ch = curl_init(); //设置超时 curl_setopt( $ch , CURLOPT_TIMEOUT, $second ); //如果有配置代理这里就设置代理 if (Config::get( 'wx.CURL_PROXY_HOST' ) != "0.0.0.0" && Config::get( 'wx.CURL_PROXY_PORT' ) != 0){ curl_setopt( $ch ,CURLOPT_PROXY, Config::get( 'wx.CURL_PROXY_HOST' )); curl_setopt( $ch ,CURLOPT_PROXYPORT, Config::get( 'wx.CURL_PROXY_PORT' )); } curl_setopt( $ch ,CURLOPT_URL, $url ); curl_setopt( $ch ,CURLOPT_SSL_VERIFYPEER,TRUE); curl_setopt( $ch ,CURLOPT_SSL_VERIFYHOST,2); //严格校验 //设置header curl_setopt( $ch , CURLOPT_HEADER, FALSE); //要求结果为字符串且输出到屏幕上 curl_setopt( $ch , CURLOPT_RETURNTRANSFER, TRUE); if ( $useCert == true){ //设置证书 //使用证书:cert 与 key 分别属于两个.pem文件 curl_setopt( $ch ,CURLOPT_SSLCERTTYPE, 'PEM' ); curl_setopt( $ch ,CURLOPT_SSLCERT, Config::get( 'wx.SSLCERT_PATH' )); curl_setopt( $ch ,CURLOPT_SSLKEYTYPE, 'PEM' ); curl_setopt( $ch ,CURLOPT_SSLKEY, Config::get( 'wx.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 ); returnFailMsg( '10018' , "curl出错,错误码:$error" ); } } /** * 回调 */ public function notify(){ //接受回调参数 $xml = file_get_contents ( 'php://input' ); }
|
友情提示:垃圾评论一律封号...