微信支付之jsapi支付和h5支付
支付类:
<?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); } }