支付类:
<?php /** * Desc:微信支付jsapi和h5支付 * Created by PhpStorm. * User: HOUDJ * Date: 2020/6/17 * Time: 16:36 */ namespace app\common\bussiness; class WechatPay { const UNURL = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; const SEORDERURL = 'https://api.mch.weixin.qq.com/pay/orderquery'; //生成签名 public function getSign($arr){ //去除空值 $arr = array_filter($arr); if(isset($arr['sign'])){ unset($arr['sign']); } //按照键名字典排序 ksort($arr); //生成url格式的字符串 $str = $this->arrToUrl($arr) . '&key=' . config('app.wechat.api_key'); return strtoupper(md5($str)); } //获取带签名的数组 public function setSign($arr){ $arr['sign'] = $this->getSign($arr);; return $arr; } public function arrToUrl($arr){ return urldecode(http_build_query($arr)); } //验证签名 public function chekSign($arr){ $sign = $this->getSign($arr); if($sign == $arr['sign']){ return true; }else{ return false; } } /** * Notes:调用统一下单api * User: HOUDJ * Date: 2020/7/2 */ public function unifiedOrder($data,$type=false){ /** * 1.构建原始数据 * 2.加入签名 * 3.将数据转换为XML * 4.发送XML格式的数据到接口地址 */ $params = [ 'appid'=> config('app.wechat.appid'), 'mch_id'=> config('app.wechat.mch_id'), 'nonce_str'=>md5(time()), 'body'=>$data['body'], 'attach'=>$data['attach'], 'out_trade_no'=>$data['out_trade_no'], 'total_fee'=> floatval($data['order_price']*100), 'spbill_create_ip'=>$_SERVER['REMOTE_ADDR'], 'notify_url'=> config('app.wechat.notify'), 'trade_type'=>'JSAPI', ]; if($type == 'h5'){ $params['trade_type'] = 'MWEB'; }else{ $params['openid'] = $data['open_id']; } $params = $this->setSign($params); $xmldata = $this->ArrToXml($params); $resdata = $this->postXml(self::UNURL, $xmldata); $arr = $this->XmlToArr($resdata); return $arr; } /** * Notes:调用查询订单接口 * User: HOUDJ * Date: 2020/7/2 */ public function queryOrder($trade_no){ //构建数据 $params = [ 'appid'=> config('app.wechat.appid'), 'mch_id'=> config('app.wechat.mch_id'), 'out_trade_no' => $trade_no, 'nonce_str'=>md5(time()), 'sign_type' => 'MD5' ]; $params = $this->setSign($params); $xmldata = $this->ArrToXml($params); $resdata = $this->postXml(self::SEORDERURL, $xmldata); $arr = $this->XmlToArr($resdata); return $arr; } /** * Notes:获取prepayid * User: HOUDJ * Date: 2020/7/2 */ public function getPrepayId($oid){ $arr = $this->unifiedOrder($oid); return $arr['prepay_id']; } /** * Notes:获取公众号支付所需要的json数据 * User: HOUDJ * Date: 2020/7/2 */ public function getJsParams($prepay_id){ $params = [ 'appId' => config('app.wechat.appid'), 'timeStamp' =>time(), 'nonceStr' => md5(time()), 'package' =>'prepay_id=' . $prepay_id, 'signType' =>'MD5', // 'paySign' => $this->getSign($params) ]; $params['paySign'] = $this->getSign($params); return json_encode($params); } /** * Notes:数组转xml * User: HOUDJ * Date: 2020/7/2 */ public function ArrToXml($arr) { if(!is_array($arr) || count($arr) == 0) return ''; $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; } /** * Notes:Xml转数组 * User: HOUDJ * Date: 2020/7/2 */ public function XmlToArr($xml) { if($xml == '') return ''; libxml_disable_entity_loader(true); $arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $arr; } /** * Notes:发送 * User: HOUDJ * Date: 2020/7/2 */ public function postXml($url,$postfields){ $ch = curl_init(); $params[CURLOPT_URL] = $url; //请求url地址 $params[CURLOPT_HEADER] = false; //是否返回响应头信息 $params[CURLOPT_RETURNTRANSFER] = true; //是否将结果返回 $params[CURLOPT_FOLLOWLOCATION] = true; //是否重定向 $params[CURLOPT_POST] = true; $params[CURLOPT_POSTFIELDS] = $postfields; $params[CURLOPT_SSL_VERIFYPEER] = false; $params[CURLOPT_SSL_VERIFYHOST] = false; curl_setopt_array($ch, $params); //传入curl参数 $content = curl_exec($ch); //执行 curl_close($ch); //关闭连接 return $content; } /** * Notes:获取post过来的数据 * User: HOUDJ * Date: 2020/7/3 */ public function getPost(){ $xml = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : file_get_contents("php://input"); return $xml; } }
支付:
判断当前支付环境是h5还是为微信内支付,前台通过user_anv传递环境
public function createOrder(){ $project_id=$this->request->param('project_id',0,'intval'); $integral=$this->request->param('integral',0,'intval'); if(!$project_id){ return returnMsg(config('status.error'),'','参数错误'); } $orders=new OrdersModel(); $info=$orders->createOrder($this->userId,$project_id,$integral);//生成订单 if($info['code']){//订单生成成功 处理支付 //如果当前是微信环境并且用户openid不为空 调取jsapi支付 否则h5支付 $data['body']=$info['data']['body']; $data['out_trade_no']=$info['data']['order_sn']; $data['order_price']=$info['data']['pay_price']; $data['open_id']=$this->open_id; $data['attach']='课程购买'; $pay=new WechatPay(); if($this->user_env && $this->open_id){ $prepay_id = $pay->getPrepayId($data); $code_str = $pay->getJsParams($prepay_id); $info['data']['pay_type']='jsapi'; $info['data']['pay']=$code_str; }else{ $code_str = $pay->unifiedOrder($data,'h5'); $info['data']['pay_type']='h5'; $info['data']['pay']=$code_str['mweb_url']; } return returnMsg($info['code'],$info['data'],$info['msg']); }else{ return returnMsg($info['code'],$info['data'],$info['msg']); } }
jsapi支付返回
{ "code": 1, "data": { "stu_id": 15, "order_sn": "DD1593756697752151350", "order_price": "0.01", "pay_price": "0.01", "cost_integral": 0, "body": "学员注册", "pay_type": "jsapi", "pay": "{\"appId\":\"wxbf20372629712be4\",\"timeStamp\":1593756697,\"nonceStr\":\"23709aa6c748eab1f7abe3afbd1c6d7f\",\"package\":\"prepay_id=wx0314113788641179329909651256281400\",\"signType\":\"MD5\",\"paySign\":\"D4E388855A2E8AFA6C8736C98F0B74DD\"}" }, "msg": "订单生成成功" }
H5支付返回
{ "code": 1, "data": { "stu_id": 15, "order_sn": "DD1593756510004805466", "order_price": "0.01", "pay_price": "0.01", "cost_integral": 0, "body": "学员注册", "pay_type": "h5", "pay": "https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx0314083017318922e8e1ab591995287800&package=1813868424" }, "msg": "订单生成成功" }
支付回调:
<?php /** * Created by PhpStorm. * User: HOUDJ * Date: 2020/7/2 * Time: 16:16 */ namespace app\index\controller; use app\BaseController; use app\common\bussiness\WechatPay; use app\index\model\Orders; use app\index\model\PayLogs; use app\index\model\StudentStudyRules; use app\index\model\Students; class Payment extends BaseController { /** * Notes:支付回调 * User: HOUDJ * Date: 2020/7/3 */ public function notify(){ $pay=new WechatPay(); $xmlData = $pay->getPost(); $arr = $pay->XmlToArr($xmlData); if($pay->chekSign($arr)){ if($arr['return_code'] == 'SUCCESS' && $arr['result_code'] == 'SUCCESS') { $this->pay_action($arr, $arr['out_trade_no']); }else{ logs(date('Y-m-d H:i:s').'业务结果错误:'.json_encode($arr),'pay_notify_fail'); } //通知微信 $returnParams = [ 'return_code' => 'SUCCESS', 'return_msg' => 'OK' ]; echo $pay->ArrToXml($returnParams); }else{ logs(date('Y-m-d H:i:s').'签名错误:'.json_encode($arr),'pay_notify_fail'); //通知微信 $returnParams = [ 'return_code' => 'FAIL', 'return_msg' => '签名错误' ]; echo $pay->ArrToXml($returnParams); } } /** * Notes:h5查询是否支付成功 * User: HOUDJ * Date: 2020/7/3 */ public function h5_notify(){ $order_sn=$this->request->param('order_sn'); $pay=new WechatPay(); $arr=$pay->queryOrder($order_sn); if($arr['return_code'] == 'SUCCESS' && $arr['result_code'] == 'SUCCESS') { $this->pay_action($arr,$order_sn); return returnMsg(config('status.success'),'','支付成功'); }else{ logs(date('Y-m-d H:i:s').'业务结果错误:'.json_encode($arr),'pay_notify_fail'); return returnMsg(config('status.error'),'','支付失败'); } } /** * Notes:处理支付成功的逻辑 * User: HOUDJ * Date: 2020/7/3 */ public function pay_action($arr,$order_sn=''){ logs(date('Y-m-d H:i:s').json_encode($arr), 'pay_notify_success'); //将金额分转化成交易金额 $total_fee = number_format($arr['total_fee']/100,2,'.',''); //改变订单状态 $orderInfo= Orders::where('order_sn', '=', $order_sn)->find(); $map[] = ['id', '=', $orderInfo['id']]; Orders::updateOrderStatus($map, ['order_status' => 1, 'pay_time' => time(), 'pay_price' => $total_fee]); //如果是购买的课程 将课程的效期显示 如果购买的会员注册 则将会员状态改变 if($arr['attach']=='课程购买') { StudentStudyRules::where('order_id', '=', $orderInfo['id'])->update(['status' => 1]); }else{ Students::where('id','=',$orderInfo['stu_id'])->update(['status'=>2,'frozen'=>0]); } PayLogs::logs($orderInfo,$arr,$total_fee); } }