关于微信,支付宝支付都做过了,但是很少有时间去写个博客,笔记啥的。话不多少,直接上代码吧!
此版本基于tp5(thinkphp5)的一个简易的微信支付类,目前可以正常支付哦,退款没有做,哈哈~~ 如下:
Pay.php(/baby/extend/wx/Pay.php)
<?php namespace wx; class Pay { /** * pay config * @var array */ private $_payCfg = array( 'appId' => 'your appId', 'appSecret' => 'your appSecret', 'mchId' => 'your mchId',#商户号 'mchSecret' => 'your mchSecret',#商户号secret ); /** * redirect_url * @var */ private $_redirectUrl; public function __construct($redirectUrl = NULL) { if (!empty($redirectUrl)) { $this->_redirectUrl = $redirectUrl; } } /** * 通过redirectUri获取授权信息 * @return mixed */ public function getAuthInfo() { //通过appId获取code $redirectUri = urlencode($this->_redirectUrl); $codeUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . $this->_payCfg['appId'] . '&redirect_uri=' . $redirectUri . '&response_type=code&scope=snsapi_base&state=YQJ#wechat_redirect'; if (empty($_REQUEST['code'])) { header('Location:' . $codeUrl); exit; } //通过code换取网页授权信息 $curl = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $this->_payCfg['appId'] . '&secret=' . $this->_payCfg['appSecret'] . '&code=' . $_REQUEST['code'] . '&grant_type=authorization_code'; $res = $this->_curlGetReq($curl); $authInfo = ''; if ($res['tag']) { if (is_object($res['msg'])) { $formatRes = (array)$res['msg']; $authInfo = array( 'accessToken' => !empty($formatRes['access_token']) ? $formatRes['access_token'] : '', 'expiresIn' => !empty($formatRes['expires_in']) ? $formatRes['expires_in'] : '', 'refreshToken' => !empty($formatRes['refresh_token']) ? $formatRes['refresh_token'] : '', 'openId' => !empty($formatRes['openid']) ? $formatRes['openid'] : '', 'scope' => !empty($formatRes['snsapi_base']) ? $formatRes['snsapi_base'] : '', ); } } return $authInfo; } /** * 下单支付 * @param $param * @return array|string */ public function toPay($param) { $body = empty($param['body']) ? 'xoxoxo' : $param['body']; $orderSn = empty($param['orderSn']) ? $this->generateOrderNum() : $param['orderSn']; $totalFee = empty($param['fee']) ? 0.01 : $param['fee']; $openId = empty($param['openId']) ? '' : $param['openId']; //统一下单参数构造 $unifiedOrder = array( 'appid' => $this->_payCfg['appId'], 'mch_id' => $this->_payCfg['mchId'], 'nonce_str' => $this->getNonceStr(), 'body' => $body, 'out_trade_no' => $orderSn, 'total_fee' => $totalFee*100, 'spbill_create_ip' => $this->getClientIp(), 'notify_url' => 'https://www.baidu.com',//todo 你的支付回调url 'trade_type' => 'JSAPI', 'openid' => $openId ); $unifiedOrder['sign'] = $this->makeSign($unifiedOrder); //请求数据,统一下单 $xmlData = $this->toXml($unifiedOrder); $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; $res = $this->postXmlCurl($url, $xmlData); if (!$res) { return array('status' => 0, 'msg' => "Can't connect the server"); } $content = $this->toArr($res); if (!empty($content) && is_array($content)) { if (!empty($content['result_code'])) { if ($content['result_code'] == 'FAIL') { return array('status' => 0, 'msg' => $content['err_code'] . ':' . $content['err_code_des']); } } if (!empty($content['return_code'])) { if ($content['return_code'] == 'FAIL') { return array('status' => 0, 'msg' => $content['return_msg']); } } $time = time(); settype($time, "string"); $resData = array( 'appId' => strval($content['appid']), 'nonceStr' => strval($content['nonce_str']), 'package' => 'prepay_id=' . strval($content['prepay_id']), 'signType' => 'MD5', 'timeStamp' => $time ); $resData['paySign'] = $this->makeSign($resData); } return json_encode($resData); } /** * 产生随机字符串,不长于32位 * @param int $length * @return string */ 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 $unifiedOrder * @return string */ public function makeSign($unifiedOrder) { //签名步骤一:按字典序排序参数 ksort($unifiedOrder); $string = $this->toUrlParams($unifiedOrder); //签名步骤二:在string后加入KEY $string = $string . "&key=" . $this->_payCfg['mchSecret']; //签名步骤三:MD5加密 $string = md5($string); //签名步骤四:所有字符转为大写 $result = strtoupper($string); return $result; } /** * 格式化参数格式化成url参数 * @param $unifiedOrder * @return string * @internal param $unifiedOrder */ public function toUrlParams($unifiedOrder) { $buff = ""; foreach ($unifiedOrder as $k => $v) { if ($k != "sign" && $v != "" && !is_array($v)) { $buff .= $k . "=" . $v . "&"; } } $buff = trim($buff, "&"); return $buff; } /** * 输出xml字符 * @param $unifiedOrder * @return string */ public function toXml($unifiedOrder) { if (!is_array($unifiedOrder) || count($unifiedOrder) <= 0) exit('数组异常'); $xml = "<xml>"; foreach ($unifiedOrder as $key => $val) { if (is_numeric($val)) { $xml .= "<" . $key . ">" . $val . "</" . $key . ">"; } else { $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">"; } } $xml .= "</xml>"; return $xml; } /** * 将xml转为array * @param $xml * @return mixed */ public function toArr($xml) { //将XML转为array,禁止引用外部xml实体 libxml_disable_entity_loader(true); return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); } /** * curl get request * @param $url * @return array */ public function _curlGetReq($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_URL, $url); 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); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $res = curl_exec($ch); $error = curl_error($ch); curl_close($ch); if ($res === false) { return array('tag' => false, 'msg' => $error); } return array( 'tag' => true, 'msg' => json_decode($res) ); } /** * 以post方式提交xml到对应的接口url * @param $xml * @param $url * @return mixed */ public function postXmlCurl($url, $xml) { $ch = curl_init(); //设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, 10); //如果有配置代理这里就设置代理 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); //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); exit("curl出错,错误码:$error"); } } /** * 获取ip * @return bool */ public function getClientIp() { $ip = false; if (!empty($_SERVER["HTTP_CLIENT_IP"]) && '127.0.0.1' != $_SERVER["HTTP_CLIENT_IP"]) { $ip = $_SERVER["HTTP_CLIENT_IP"]; } if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ips = explode(", ", $_SERVER['HTTP_X_FORWARDED_FOR']); if ($ip) { array_unshift($ips, $ip); $ip = FALSE; } for ($i = 0; $i < count($ips); $i++) { //echo $ips[$i].'<br>'; if (!preg_match('/^(10|172\.16|192\.168)\./', $ips[$i])) { $ip = $ips[$i]; break; } } } //@author jzzmly 2015-8-11 验证从HTTP变量里拿到的ip信息,防止伪造非法字符攻击 return (empty($ip) || preg_match('/[^0-9^\.]+/', $ip)) ? $_SERVER['REMOTE_ADDR'] : $ip; //return ($ip ? $ip : $_SERVER['REMOTE_ADDR']); } /** * 生成16位订单号 * @return string */ public function generateOrderNum() { return $order_number = date('Ymd') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8); } }简单的支付类已经OK了,现在差调用层了,代码如下:
Test.php(/baby/application/index/controller/Test.php)
<?php namespace app\index\controller; use think\Controller; use think\Request; class Test extends Controller { public function index(){ $redirectUri = 'https://www.baidu.com/test.html'; $payObj = new \wx\Pay($redirectUri); $authInfo = $payObj->getAuthInfo(); if(!empty($authInfo['openId'])){ $payInfo = array( 'body'=>'xoxoxoxo', 'fee'=>0.01, 'openId'=>$authInfo['openId'], ); $payRes = $payObj->toPay($payInfo); $this->assign('payRes',$payRes); } return $this->fetch('index/test'); } //异步通知没有做,后续补充~ public function notify(){ exit('回调'); } }调用层的代码也已经OK了,最后就差模板了,如下:
test.html(/baby/application/index/view/index/test.html)
<html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>微信JSAPI支付</title> <script type="text/javascript"> function jsApiCall() { WeixinJSBridge.invoke( 'getBrandWCPayRequest',{$payRes}, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok"){ alert("支付成功!"); window.location.href="http://www.baidu.com"; }else if(res.err_msg == "get_brand_wcpay_request:cancel"){ alert("用户取消支付!"); }else{ alert("支付失败!"); } } ); } function callpay() { if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', jsApiCall, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', jsApiCall); document.attachEvent('onWeixinJSBridgeReady', jsApiCall); } }else{ jsApiCall(); } } </script> </head> <body> <br/> <font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px">1分</span>钱</b></font><br/><br/> <font color="#9ACD32"><b><span style="color:#f00;font-size:50px;margin-left:40%;">1分</span>钱也是爱</b></font><br/><br/> <div align="center"> <button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer; color:white; font-size:16px;" type="button" onclick="callpay()" >剁手吧</button> </div> </body> </html>