微信企业支付 API 类库(php)
做到微信支付与提现这一块,因为用了 easywechat 踩了很多坑,故在这里分享一个绝对能用的微信企业支付 demo
<?php namespace App\wxpayv3; class WxPayPostal{ private $appId = null; private $mchId = null; private $certPath = null; private $keyPath = null; private $key = null; private $url = null; /* * 以下各个参数需要根据自己的坏境与参数进行配置,2 个证书的路径必须为绝对地址 */ public function __construct(){ $this->appId = env("WXPAY_APPID"); $this->mchId = env("WXPAY_MCHID"); $this->key = env("WXPAY_KEY"); $this->certPath = env("WXPAY_CERTPATH"); $this->keyPath = env("WXPAY_KEYPATH"); $this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; } /** * 转换为 post 数据 * */ private function formatBizQueryParaMap($paraMap,$urlencode){ $buff = ""; ksort($paraMap); foreach ($paraMap as $k => $v) { if($urlencode) { $v = urlencode($v); } $buff .= $k . "=" . $v . "&"; } $reqPar = null; if (strlen($buff) > 0) { $reqPar = substr($buff, 0, strlen($buff)-1); } return $reqPar; } private function getSign($Obj){ foreach ($Obj as $k => $v) { $Parameters[$k] = $v; } //签名步骤一:按字典序排序参数 ksort($Parameters); $String = $this->formatBizQueryParaMap($Parameters, false); //echo '【string1】'.$String.'</br>'; //签名步骤二:在 string 后加入 KEY $String = $String."&key={$this->key}"; //echo "【string2】".$String."</br>"; //签名步骤三:MD5 加密 $String = md5($String); //echo "【string3】 ".$String."</br>"; //签名步骤四:所有字符转为大写 $result_ = strtoupper($String); //echo "【result】 ".$result_."</br>"; return $result_; } protected function curl_post_ssl($url, $vars, $second=30,$aHeader=array()){ $ch = curl_init(); //超时时间 curl_setopt($ch,CURLOPT_TIMEOUT,$second); curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1); //这里设置代理,如果有的话 //curl_setopt($ch,CURLOPT_PROXY, '10.206.30.98'); //curl_setopt($ch,CURLOPT_PROXYPORT, 8080); curl_setopt($ch,CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false); //以下两种方式需选择一种 //第一种方法,cert 与 key 分别属于两个.pem 文件 //默认格式为 PEM,可以注释 curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLCERT,$this->certPath); //默认格式为 PEM,可以注释 //curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLKEY,$this->keyPath); //第二种方式,两个文件合成一个.pem 文件 //curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem'); if( count($aHeader) >= 1 ){ curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader); } curl_setopt($ch,CURLOPT_POST, 1); curl_setopt($ch,CURLOPT_POSTFIELDS,$vars); $data = curl_exec($ch); if($data){ curl_close($ch); return $data; } else { $error = curl_errno($ch); echo "call faild, errorCode:$error\n"; curl_close($ch); return false; } } //生成随机字符串 protected function getShuffle($length = 16){ $str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; return substr(str_shuffle($str),0,$length); } //获取 server 端 ip protected function getServerIp(){ return $_SERVER["SERVER_ADDR"]; } //发起支付 /** * $data=[ * "money"=>100, * "name"=>"张三", * "check"=>1 or 2 or 3, * "desc"=>"支付", * "openid"=>"asdkfjalkdjfa", * "order_sn"=>"121313131" * ] */ public function postal($arr){ $data["amount"] = $arr["money"]; $data["openid"] = $arr["openid"]; $data["re_user_name"] = $arr["name"]; $data["desc"] = $arr["desc"]; $data["spbill_create_ip"] = $this->getServerIp(); $data["mch_appid"] = $this->appId; $data["mchid"] = $this->mchId; $data["nonce_str"] = $this->getShuffle(16); $data["openid"] = $arr["openid"]; switch ($arr["check"]){ case 1: $data["check_name"] = "NO_CHECK"; break; case 2: $data["check_name"] = "FORCE_CHECK"; break; default: $data["check_name"] = "OPTION_CHECK"; break; } $data["partner_trade_no"] = $arr["order_sn"]; $sign = $this->getSign($data); $data["sign"] = $sign; //拼接 xml $xml = "<xml> <mch_appid>%s</mch_appid> <mchid>%s</mchid> <nonce_str>%s</nonce_str> <partner_trade_no>%s</partner_trade_no> <openid>%s</openid> <check_name>%s</check_name> <re_user_name>%s</re_user_name> <amount>%s</amount> <desc>%s</desc> <spbill_create_ip>%s</spbill_create_ip> <sign>%s</sign> </xml>"; $post = sprintf($xml,$data["mch_appid"], $data["mchid"], $data["nonce_str"], $data["partner_trade_no"], $data["openid"], $data["check_name"], $data["re_user_name"], $data["amount"], $data["desc"], $data["spbill_create_ip"], $data["sign"] ); $result = $this->curl_post_ssl($this->url,$post); $result = $this->readXml($result); $res = ["error"=>1,"msg"=>""]; if($result->return_code == "SUCCESS"){ if($result->result_code == "SUCCESS"){ $res = ["error"=>0,"msg"=>"返现成功!"]; }else if($result->result_code == "FAIL"){ $res = ["error"=>2,"msg"=>$result->err_code_des]; } }else if($result->return_code == "FAIL"){ $res = ["error"=>1,"msg"=>""]; switch ($result->err_code){ case "NOAUTH": $res["msg"] = "没有权限"; break; case "AMOUNT_LIMIT": $res["msg"] = "付款金额不能小于最低限额"; break; case "PARAM_ERROR": $res["msg"] = "参数错误"; break; case "OPENID_ERROR": $res["msg"] = "Openid 错误"; break; case "NOTENOUGH": $res["msg"] = "余额不足"; break; case "SYSTEMERROR": $res["msg"] = "系统繁忙,请稍后再试。"; break; case "NAME_MISMATCH": $res["msg"] = "姓名校验出错"; break; case "SIGN_ERROR": $res["msg"] = "签名错误"; break; case "XML_ERROR": $res["msg"] = "Post 内容出错"; break; case "FATAL_ERROR": $res["msg"] = "两次请求参数不一致"; break; case "CA_ERROR": $res["msg"] = "证书出错"; break; } } return $res; } protected function readXml($xmlData){ libxml_disable_entity_loader(true); $req = simplexml_load_string($xmlData, 'SimpleXMLElement', LIBXML_NOCDATA); return $req; } }