diff --git a/.gitignore b/.gitignore index ae66225..c6dbc7e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,8 +17,6 @@ yarn-error.log public/.user.ini 404.html - /.well-known -/app/Libs/WxPay.class.php /cert diff --git a/app/Http/Controllers/Admin/StatisticsController.php b/app/Http/Controllers/Admin/StatisticsController.php index 47e031c..7de7fc3 100755 --- a/app/Http/Controllers/Admin/StatisticsController.php +++ b/app/Http/Controllers/Admin/StatisticsController.php @@ -10,12 +10,10 @@ namespace App\Http\Controllers\Admin; use App\Customer; use App\Libs\AlipayF2F; -use App\Libs\WxMicroPay; use App\Models\AdminAreaLink; use App\Models\Area; use App\Models\Balance; use App\Models\Bed; -use App\Models\Factor; use App\Models\FactorItems; use App\Models\OrderItems; use App\Models\Orders; diff --git a/app/Http/Controllers/Customer/OrdersController.php b/app/Http/Controllers/Customer/OrdersController.php index f068b0c..a837e52 100644 --- a/app/Http/Controllers/Customer/OrdersController.php +++ b/app/Http/Controllers/Customer/OrdersController.php @@ -569,7 +569,7 @@ class OrdersController extends CommonController $currentBalance = $customer->balance; // 4. 如果支付总额等于用户余额,进行退款处理 - if ($totalPaid > 0 && $totalPaid == $currentBalance) { + if ($totalPaid > 0 && float_equals($totalPaid, $currentBalance)) { foreach ($recharges as $recharge) { // 更新用户余额 $currentBalance -= $recharge->money; diff --git a/app/Libs/WxPay.class.php b/app/Libs/WxPay.class.php new file mode 100755 index 0000000..6abce62 --- /dev/null +++ b/app/Libs/WxPay.class.php @@ -0,0 +1,558 @@ +first(); + } else { + $project = \App\Models\Project::with("wechatpayAccount")->find($project_id); + $wechatpayAccount = $project->wechatpayAccount; + } + + self::$APPID = env("WEIXINPAY_APPID"); + self::$APPSECRET = env("WEIXINPAY_APPSECRET"); + self::$MCHID = $wechatpayAccount->mchid; + self::$KEY = $wechatpayAccount->key; + self::$SSLCERT_PATH = env("WEIXINPAY_CERT_PUBLIC_PATH") . $wechatpayAccount->mchid . "/apiclient_cert.pem"; + self::$SSLKEY_PATH = env("WEIXINPAY_CERT_PUBLIC_PATH") . $wechatpayAccount->mchid . "/apiclient_key.pem"; + } +} + +class SDKRuntimeException extends Exception +{ + public function errorMessage() + { + return $this->getMessage(); + } + +} + +class CommonUtilPub +{ + public $project_id; + public $merchant_id; + + function __construct($project_id, $merchant_id = null) + { + $this->project_id = $project_id; + $this->merchant_id = $merchant_id; + } + + function trimString($value) + { + $ret = null; + if (null != $value) { + $ret = $value; + if (strlen($ret) == 0) { + $ret = null; + } + } + return $ret; + } + + /** + * 作用:产生随机字符串,不长于32位 + */ + public function createNoncestr($length = 32) + { + $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + $str = ""; + for ($i = 0; $i < $length; $i++) { + $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); + } + return $str; + } + + /** + * 作用:格式化参数,签名过程需要使用 + */ + function formatBizQueryParaMap($paraMap, $urlencode) + { + $buff = ""; + ksort($paraMap); + foreach ($paraMap as $k => $v) { + if ($urlencode) { + $v = urlencode($v); + } + //$buff .= strtolower($k) . "=" . $v . "&"; + $buff .= $k . "=" . $v . "&"; + } + $reqPar = ""; + if (strlen($buff) > 0) { + $reqPar = substr($buff, 0, strlen($buff) - 1); + } + return $reqPar; + } + + /** + * 作用:生成签名 + */ + public function getSign($Obj) + { + foreach ($Obj as $k => $v) { + $Parameters[$k] = $v; + } + //签名步骤一:按字典序排序参数 + ksort($Parameters); + $String = $this->formatBizQueryParaMap($Parameters, false); + //echo '【string1】'.$String.'
'; + //签名步骤二:在string后加入KEY + $KEY = (new WxPayConfPub($this->project_id, $this->merchant_id))::$KEY; + $String = $String . "&key=" . $KEY; + //echo "【string2】".$String."
"; + //签名步骤三:MD5加密 + $String = md5($String); + //echo "【string3】 ".$String."
"; + //签名步骤四:所有字符转为大写 + $result_ = strtoupper($String); + //echo "【result】 ".$result_."
"; + return $result_; + } + + /** + * 作用:array转xml + */ + function arrayToXml($arr) + { + $xml = ""; + foreach ($arr as $key => $val) { + if (is_numeric($val)) { + $xml .= "<" . $key . ">" . $val . ""; + + } else + $xml .= "<" . $key . ">"; + } + $xml .= ""; + return $xml; + } + + /** + * 作用:将xml转为array + */ + public function xmlToArray($xml) + { + //将XML转为array + $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); + return $array_data; + } + + /** + * 作用:以post方式提交xml到对应的接口url + */ + public function postXmlCurl($xml, $url, $second = 30) + { + //初始化curl + $ch = curl_init(); + //设置超时 + curl_setopt($ch, CURLOPT_TIMEOUT, $second); + //这里设置代理,如果有的话 + //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8'); + //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); + //设置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); + echo "curl出错,错误码:$error" . "
"; + echo "错误原因查询
"; + curl_close($ch); + return false; + } + } + + /** + * 作用:使用证书,以post方式提交xml到对应的接口url + */ + function postXmlSSLCurl($xml, $url, $second = 30) + { + $ch = curl_init(); + $WxPayConfPub = new WxPayConfPub($this->project_id, $this->merchant_id); + //超时时间 + curl_setopt($ch, CURLOPT_TIMEOUT, $second); + //这里设置代理,如果有的话 + //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8'); + //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); + //设置header + curl_setopt($ch, CURLOPT_HEADER, FALSE); + //要求结果为字符串且输出到屏幕上 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); + //设置证书 + //使用证书:cert 与 key 分别属于两个.pem文件 + //默认格式为PEM,可以注释 + curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM'); + curl_setopt($ch, CURLOPT_SSLCERT, $WxPayConfPub::$SSLCERT_PATH); + //默认格式为PEM,可以注释 + curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM'); + curl_setopt($ch, CURLOPT_SSLKEY, $WxPayConfPub::$SSLKEY_PATH); + //post提交方式 + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); + $data = curl_exec($ch); + //返回结果 + if ($data) { + curl_close($ch); + return $data; + } else { + $error = curl_errno($ch); + echo "curl出错,错误码:$error" . "
"; + echo "错误原因查询
"; + curl_close($ch); + return false; + } + } + + /** + * 作用:打印数组 + */ + function printErr($wording = '', $err = '') + { + print_r('
');
+        echo $wording . "
"; + var_dump($err); + print_r('
'); + } +} + +/** + * 请求型接口的基类 + */ +class WxpayClientPub extends CommonUtilPub +{ + var $parameters;//请求参数,类型为关联数组 + public $response;//微信返回的响应 + public $result;//返回参数,类型为关联数组 + var $url;//接口链接 + var $curl_timeout;//curl超时时间 + + /** + * 作用:设置请求参数 + */ + function setParameter($parameter, $parameterValue) + { + $this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue); + } + + /** + * 作用:设置标配的请求参数,生成签名,生成接口参数xml + */ + function createXml() + { + $WxPayConfPub = new WxPayConfPub($this->project_id, $this->merchant_id); + $this->parameters["appid"] = $WxPayConfPub::$APPID;//公众账号ID + $this->parameters["mch_id"] = $WxPayConfPub::$MCHID;//商户号 + $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串 + $this->parameters["sign"] = $this->getSign($this->parameters);//签名 + return $this->arrayToXml($this->parameters); + } + + /** + * 作用:post请求xml + */ + function postXml() + { + $xml = $this->createXml(); + $this->response = $this->postXmlCurl($xml, $this->url, $this->curl_timeout); + return $this->response; + } + + /** + * 作用:使用证书post请求xml + */ + function postXmlSSL() + { + $xml = $this->createXml(); + $this->response = $this->postXmlSSLCurl($xml, $this->url, $this->curl_timeout); + return $this->response; + } + + /** + * 作用:获取结果,默认不使用证书 + */ + function getResult() + { + $this->postXml(); + $this->result = $this->xmlToArray($this->response); + return $this->result; + } +} + + +/** + * 统一支付接口类 + */ +class UnifiedOrderPub extends WxpayClientPub +{ + public $project_id; + public $merchant_id; + + function __construct($project_id, $merchant_id = null) + { + $this->project_id = $project_id; + $this->merchant_id = $merchant_id; + //设置接口链接 + $this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; + //设置curl超时时间 + $this->curl_timeout = WxPayConfPub::CURL_TIMEOUT; + + parent::__construct($project_id, $merchant_id); + } + + /** + * 生成接口参数xml + */ + function createXml() + { + $WxPayConfPub = new WxPayConfPub($this->project_id, $this->merchant_id); + try { + //检测必填参数 + if ($this->parameters["out_trade_no"] == null) { + throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!" . "
"); + } elseif ($this->parameters["body"] == null) { + throw new SDKRuntimeException("缺少统一支付接口必填参数body!" . "
"); + } elseif ($this->parameters["total_fee"] == null) { + throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!" . "
"); + } elseif ($this->parameters["notify_url"] == null) { + throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!" . "
"); + } elseif ($this->parameters["trade_type"] == null) { + throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!" . "
"); + } elseif ($this->parameters["trade_type"] == "JSAPI" && + $this->parameters["openid"] == NULL) { + throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!" . "
"); + } + $this->parameters["appid"] = $WxPayConfPub::$APPID;//公众账号ID + $this->parameters["mch_id"] = $WxPayConfPub::$MCHID;//商户号 + $this->parameters["spbill_create_ip"] = $_SERVER['REMOTE_ADDR'];//终端ip + $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串 + $this->parameters["sign"] = $this->getSign($this->parameters);//签名 + return $this->arrayToXml($this->parameters); + } catch (SDKRuntimeException $e) { + die($e->errorMessage()); + } + } + + /** + * 获取prepay_id + */ + function getPrepayId() + { + $this->postXml(); + $this->result = $this->xmlToArray($this->response); + return isset($this->result["prepay_id"]) ? $this->result["prepay_id"] : null; + } +} + +/** + * JSAPI支付——H5网页端调起支付接口 + */ +class JsApiPub extends CommonUtilPub +{ + var $code;//code码,用以获取openid + var $openid;//用户的openid + var $parameters;//jsapi参数,格式为json + var $prepay_id;//使用统一支付接口得到的预支付id + var $curl_timeout;//curl超时时间 + + function __construct($project_id, $merchant_id = null) + { + //设置curl超时时间 + $this->curl_timeout = WxPayConfPub::CURL_TIMEOUT; + parent::__construct($project_id, $merchant_id); + } + + /** + * 作用:生成可以获得code的url + */ + function createOauthUrlForCode($redirectUrl) + { + $WxPayConfPub = new WxPayConfPub($this->project_id, $this->merchant_id); + $urlObj["appid"] = $WxPayConfPub::$APPID; + $urlObj["redirect_uri"] = "$redirectUrl"; + $urlObj["response_type"] = "code"; + $urlObj["scope"] = "snsapi_base"; + $urlObj["state"] = "STATE" . "#wechat_redirect"; + $bizString = $this->formatBizQueryParaMap($urlObj, false); + return "https://open.weixin.qq.com/connect/oauth2/authorize?" . $bizString; + } + + /** + * 作用:生成可以获得openid的url + */ + function createOauthUrlForOpenid() + { + $WxPayConfPub = new WxPayConfPub($this->project_id, $this->merchant_id); + $urlObj["appid"] = $WxPayConfPub::$APPID; + $urlObj["secret"] = $WxPayConfPub::$APPSECRET; + $urlObj["code"] = $this->code; + $urlObj["grant_type"] = "authorization_code"; + $bizString = $this->formatBizQueryParaMap($urlObj, false); + return "https://api.weixin.qq.com/sns/oauth2/access_token?" . $bizString; + } + + + /** + * 作用:通过curl向微信提交code,以获取openid + */ + function getOpenid() + { + $url = $this->createOauthUrlForOpenid(); + //初始化curl + $ch = curl_init(); + //设置超时 + curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout); + curl_setopt($ch, CURLOPT_URL, $url); + 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); + //运行curl,结果以json形式返回 + $res = curl_exec($ch); + curl_close($ch); + //取出openid + $data = json_decode($res, true); + $this->openid = $data['openid']; + return $this->openid; + } + + /** + * 作用:设置prepay_id + */ + function setPrepayId($prepayId) + { + $this->prepay_id = $prepayId; + } + + /** + * 作用:设置code + */ + function setCode($code_) + { + $this->code = $code_; + } + + /** + * 作用:设置jsapi的参数 + */ + public function getParameters() + { + $WxPayConfPub = new WxPayConfPub($this->project_id, $this->merchant_id); + $jsApiObj["appId"] = $WxPayConfPub::$APPID; + $timeStamp = time(); + $jsApiObj["timeStamp"] = "$timeStamp"; + $jsApiObj["nonceStr"] = $this->createNoncestr(); + $jsApiObj["package"] = "prepay_id=$this->prepay_id"; + $jsApiObj["signType"] = "MD5"; + $jsApiObj["paySign"] = $this->getSign($jsApiObj); + $this->parameters = json_encode($jsApiObj); + + return $this->parameters; + } +} + +/** + * 响应型接口基类 + */ +class WxpayServerPub extends CommonUtilPub +{ + public $data;//接收到的数据,类型为关联数组 + var $returnParameters;//返回参数,类型为关联数组 + + /** + * 将微信的请求xml转换成关联数组,以方便数据处理 + */ + function saveData($xml) + { + $this->data = $this->xmlToArray($xml); + } + + function checkSign() + { + $tmpData = $this->data; + unset($tmpData['sign']); + $sign = $this->getSign($tmpData);//本地签名 + if ($this->data['sign'] == $sign) { + return TRUE; + } + return FALSE; + } + + /** + * 获取微信的请求数据 + */ + function getData() + { + return $this->data; + } + + /** + * 设置返回微信的xml数据 + */ + function setReturnParameter($parameter, $parameterValue) + { + $this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue); + } + + /** + * 生成接口参数xml + */ + function createXml() + { + return $this->arrayToXml($this->returnParameters); + } + + /** + * 将xml数据返回微信 + */ + function returnXml() + { + $returnXml = $this->createXml(); + return $returnXml; + } +} + + +/** + * 通用通知接口 + */ +class NotifyPub extends WxpayServerPub +{ + +} + +?> diff --git a/app/Libs/WxPayCommon.class.php b/app/Libs/WxPayCommon.class.php index c302d50..5705512 100644 --- a/app/Libs/WxPayCommon.class.php +++ b/app/Libs/WxPayCommon.class.php @@ -15,14 +15,20 @@ class WxPayCommon public $parameters = []; public $query_url = "https://api.mch.weixin.qq.com/pay/orderquery"; - public function __construct($project_id) + public function __construct($project_id, $merchant_id = null) { - $project = \App\Models\Project::with("wechatpayAccount")->find($project_id); + if ($merchant_id) { + $wechatpayAccount = \App\Models\WechatpayAccount::where("mchid", $merchant_id)->first(); + } else { + $project = \App\Models\Project::with("wechatpayAccount")->find($project_id); + $wechatpayAccount = $project->wechatpayAccount; + } + $this->app_id = env("WEIXINPAY_APPID"); - $this->merchant_id = $project->wechatpayAccount->mchid; - $this->key = $project->wechatpayAccount->key; - $this->ssl_cert_path = env("WEIXINPAY_CERT_PUBLIC_PATH") . $project->mchid . "/apiclient_cert.pem"; - $this->ssl_key_path = env("WEIXINPAY_CERT_PUBLIC_PATH") . $project->mchid . "/apiclient_key.pem"; + $this->merchant_id = $wechatpayAccount->mchid; + $this->key = $wechatpayAccount->key; + $this->ssl_cert_path = env("WEIXINPAY_CERT_PUBLIC_PATH") . $wechatpayAccount->mchid . "/apiclient_cert.pem"; + $this->ssl_key_path = env("WEIXINPAY_CERT_PUBLIC_PATH") . $wechatpayAccount->mchid . "/apiclient_key.pem"; } function trimString($value) @@ -206,10 +212,10 @@ class WxPayCommon //使用证书:cert 与 key 分别属于两个.pem文件 //默认格式为PEM,可以注释 curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM'); - curl_setopt($ch, CURLOPT_SSLCERT, WxPayConfPub::SSLCERT_PATH); + curl_setopt($ch, CURLOPT_SSLCERT, $this->ssl_cert_path); //默认格式为PEM,可以注释 curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM'); - curl_setopt($ch, CURLOPT_SSLKEY, WxPayConfPub::SSLKEY_PATH); + curl_setopt($ch, CURLOPT_SSLKEY, $this->ssl_key_path); //post提交方式 curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); diff --git a/app/Models/Refund.php b/app/Models/Refund.php index 72fde25..0dbf904 100755 --- a/app/Models/Refund.php +++ b/app/Models/Refund.php @@ -97,8 +97,8 @@ class Refund extends SoftDeletesModel { $order = $this->order; $url = "https://api.mch.weixin.qq.com/secapi/pay/refund"; - $CommonUtilPub = new \CommonUtilPub($order->project_id); - $WxPayConfPub = new \WxPayConfPub($order->project_id); + $CommonUtilPub = new \CommonUtilPub($order->project_id, $this->merchant_id); + $WxPayConfPub = new \WxPayConfPub($order->project_id, $this->merchant_id); $inputObj = []; $inputObj["transaction_id"] = $this->relatedRecharge->payment_serial; $inputObj["out_refund_no"] = $this->serial;