TP5 -- 微信支付整合(APP,JSAPI)
原本项目上有一个提现功能,因原客户商户号和流水原因一直没做。
最近因为刚刚符合条件的原因,今天才开始做。
顺便浏览下博客,发现原来写的微信支付有些那个啥了。
因为最近项目前后端分离居多,而且项目也多JSAPI、APP、NATIVE等多种一起居多
所以就将原来的微信支付 重新整合了下
与原来的相比,整合过后的更适用于前后端分离来做
如果没有前后端分离,还可以借鉴原来的公众号支付:tp5 -- 微信公众号支付
不过最好还是自己调整下,毕竟那个代码比较久了
好了,话不多说,还是直接上代码吧
因为此支付是自己封装,同样还是在 extend/ 文件下 的Wxpay.php
代码如下:
<?php namespace pay; class Wxpay{ private $config =[ "appid" => "", // 开放平台或商户平台APPID "mch_id" => "", // 商户平台 商户号 "key" => "", // 商户平台 秘钥KEY "TOKEN" => "", // 此参数非必传 有的前端在jsapi 支付时会要求返回signature 参数 此参数即为此准备 ]; public function index($param,$openid="") { $order = [ 'out_trade_no' => $param['out_trade_no'],// 订单号 'total_fee' => intval($param['total_fee']*100),// 订单金额 以(分)为单位 'body' => $param['body'],// 商品描述 'notify_url' => $param['notify_url'], //回调地址 'spbill_create_ip' => $param['spbill_create_ip'], //对应IP 'trade_type' => $param['trade_type'] //对应支付类型 ]; #当支付类型为JAPI时 openid 必传 if($param['trade_type'] == "JSAPI") { $order['openid'] = $openid; } #统一下单 获取prepay_id $unified_order=$this->unifiedOrder($order); #获取当前时间戳 $time = time(); #JSAPI if($param['trade_type'] == 'JSAPI') { #组合jssdk需要用到的数据 $data = [ 'appId' => $this->config['appid'], //appid 'timeStamp' => strval($time), //时间戳 'nonceStr' =>$unified_order['nonce_str'],// 随机字符串 'package' => 'prepay_id='.$unified_order['prepay_id'],// 预支付交易会话标识 'signType' => 'MD5' //加密方式 ]; // 生成签名 $data['paySign']=$this->makeSign($data); // #有的可能会有需求signature 此参数的在此加密一下即可 // $token = $this->config['token']; // $tmpArr = array($token, strval($time),$unified_order['nonce_str']); // sort($tmpArr, SORT_STRING); // $tmpStr = implode( $tmpArr ); // $tmpStr = sha1($tmpStr); // $data['signature'] = $tmpStr; } #APP elseif($param['trade_type'] == 'APP') { $data = [ 'appid' => $this->config['appid'], 'partnerid' => $this->config['mch_id'], 'prepayid' => $unified_order['prepay_id'], 'package' => 'Sign=WXPay', 'noncestr' => $unified_order['nonce_str'],// 随机字符串 'timestamp' => strval($time), //时间戳 ]; //生成签名 $data['sign'] = $this->makeSign($data); } return $data; } /** * 统一下单 * @param array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型:JSAPI,NATIVE,APP) */ public function unifiedOrder($order) { $config =[ 'appid' => $this->config['appid'], //appid 'mch_id' => $this->config['mch_id'], //商户号ID 'nonce_str' => $this->getNonceStr() ]; # 合并配置数据和订单数据 $data=array_merge($order,$config); # 生成签名 $sign=$this->makeSign($data); $data['sign']=$sign; #转换成xml $xml=$this->toXml($data); $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; //接收xml数据的文件 $header[] = "Content-type: text/xml"; //定义content-type为xml,注意是数组 $ch = curl_init ($url); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 兼容本地没有指定curl.cainfo路径的错误 curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); $response = curl_exec($ch); if(curl_errno($ch)){ # 显示报错信息;终止继续执行 die(curl_error($ch)); } curl_close($ch); #转换成数组 $result=$this->toArray($response); #显示错误信息 if ($result['return_code']=='FAIL') { die($result['return_msg']); } $result['sign']=$sign; $result['nonce_str']=$this->getNonceStr(); return $result; } /** * 生成签名 * @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值 */ public function makeSign($data) { # 去空 $data=array_filter($data); #签名步骤一:按字典序排序参数 ksort($data); #将数组转成url形式 $string_a=http_build_query($data); $string_a=urldecode($string_a); #签名步骤二:在string后加入KEY $string_sign_temp=$string_a."&key=".$this->config['key']; #签名步骤三:MD5加密 $sign = md5($string_sign_temp); # 签名步骤四:所有字符转为大写 $result=strtoupper($sign); return $result; } /** * 将xml转为array * @param string $xml xml字符串 * @return array 转换得到的数组 */ public function toArray($xml){ #禁止引用外部xml实体 libxml_disable_entity_loader(true); $result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $result; } /** * * 产生随机字符串,不长于32位 * @param int $length * @return 产生的随机字符串 */ 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字符 * @throws WxPayException **/ public function toXml($data) { if(!is_array($data) || count($data) <= 0) { throw new WxPayException("数组数据异常!"); } $xml = "<xml>"; foreach ($data as $key=>$val){ if (is_numeric($val)){ $xml.="<".$key.">".$val."</".$key.">"; }else{ $xml.="<".$key."><![CDATA[".$val."]]></".$key.">"; } } $xml.="</xml>"; return $xml; } /** * 验证 * @return array 返回数组格式的notify数据 */ public function notify() { // 获取xml $xml=file_get_contents('php://input', 'r'); # 转成php数组 $data=$this->toArray($xml); # 保存原sign $data_sign=$data['sign']; # sign不参与签名 unset($data['sign']); $sign=$this->makeSign($data); # 判断签名是否正确 判断支付状态 if ($sign===$data_sign && $data['return_code']=='SUCCESS' && $data['result_code']=='SUCCESS') { $result=$data; }else{ $result=false; } # 返回状态给微信服务器 if ($result) { $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'; }else{ $str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>'; } return $result; } } ?>
下面就是调用方法超级简单:
<?php namespace app\index\controller; header("Content-Type: text/html;charset=utf-8"); use think\Controller; use pay\Wxpay; class Buy extends Controller { #新的微信支付类调用 public function newpay() { $pay_sn = date('YmdHis').rand(1000,9999); $total_fee = '0.01'; $body = "商品描述"; $spbill_create_ip = '192.168.0.1'; $notify_url = "你的回调地址"; #根据不同类型回调地址不同 $trade_type = 'APP'; #JSAPI--JSAPI支付(或小程序支付)、 #NATIVE--Native支付、 #APP--app支付, #MWEB--H5支付, #不同trade_type决定了调起支付的方式,请根据支付产品正确上传 #新的需要参数为六个 # out_trade_no 商户订单号 # total_fee 订单总额 # body 商品描述 # spbill_create_ip 终端IP # notify_url 回调通知地址 # trade_type 交易类型 $wxpay = new Wxpay(); $date = [ 'out_trade_no' => $pay_sn, 'total_fee' => $total_fee, 'body' => $body, 'spbill_create_ip' => getIp(), 'notify_url' => $notify_url, 'trade_type' => $trade_type ]; #根据 trade_type 类型不同,是否传递openid #APP 类型支付调用 $res = $wxpay->index($date); #JSAPI 类型支付调用 $res = $wxpay->index($date,$openid); #获得后将对应内容返回前端即可 return $res; } } ?>
最后就是回调咯:
<?php namespace app\index\controller; header("Content-Type: text/html;charset=utf-8"); use think\Controller; use pay\Wxpay; class Pays extends Controller { public function notify() { $wxpay = new Wxpay(); $result = $wxpay->notify(); #根据拿到的数据 来进行自己的数据逻辑 if($result) { $out_trade_no = $result['out_trade_no']; echo "success";exit; } echo "error"; } } ?>
以上就是本次整合的微信支付咯
感谢各位大大的观看。
2020年4月17日