📖 File Reader
<?php
namespace app\common;
use app\BaseController;
use app\api\common\Common_config;
use think\facade\Db;
use think\facade\View;
/*微信支付处理*/
class Common_weixinpay extends BaseController
{
protected $app_id = '';//app_id
protected $app_secret = '';//secret
protected $mch_id = '';//商户号
protected $mishi = '';//商户秘钥
//商户证书地址
protected $cert_path = '/www/wwwroot/yishangjiaoyu.gkktc.cn/apiclient_cert.pem';
protected $key_path = '/www/wwwroot/yishangjiaoyu.gkktc.cn/apiclient_key.pem';
public function initialize()
{
$config = Common_config::config("'app_id','app_secret','mch_id','mishi'");
$this->app_id = $config["app_id"];
$this->app_secret = $config["app_secret"];
$this->mch_id = $config["mch_id"];
$this->mishi = $config["mishi"];
}
/**
* 微信支付(app)
* @param $user_miyao 会员秘钥
* @param $order_sn 订单编号
* @param $goods_name 商品名称
* @param $price 订单总价格
*/
public function pay_app($user_miyao,$order_sn,$goods_name,$price)
{
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
$data['appid'] = $this->app_id;
$data['mch_id'] = $this->mch_id;
$data['trade_type'] = 'APP';
$data['out_trade_no'] = $order_sn;
$data['nonce_str'] = $this->getNonceStr();
$data['body'] = $goods_name;
$data['total_fee'] = ($price*100);
$data['spbill_create_ip'] = $this->get_client_ip();
$data['notify_url'] = 'http://'.$_SERVER["SERVER_NAME"].'/api/Order/notify_url/user_miyao/'.$user_miyao.'/order_sn/'.$order_sn;
$data['sign'] = $this->MakeSign($data);
$xml =$this->ToXml($data);
$curl = curl_init(); // 启动一个CURL会话
curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
//设置header
curl_setopt($curl, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_POST, TRUE); // 发送一个常规的Post请求
curl_setopt($curl, CURLOPT_POSTFIELDS, $xml); // Post提交的数据包
curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
$tmpInfo = curl_exec($curl); // 执行操作
curl_close($curl); // 关闭CURL会话
$arr = $this->FromXml($tmpInfo);
//输出参数列表
$prePayParams = [
'appid'=>$this->app_id,
'noncestr'=>$arr["nonce_str"],
'package'=>'Sign=WXPay',
'partnerid'=>$this->getNonceStr(),
'prepayid'=>$arr["prepay_id"],
'timestamp'=>time(),
];
$prePayParams['sign'] = $this->MakeSign($prePayParams);
return $this->succeed_json("ok",$prePayParams,201);
}
/**
* 微信支付
* @param $user_miyao 会员秘钥
* @param $order_sn 订单编号
* @param $goods_name 商品名称
* @param $price 订单总价格
* @param $open_id open_id
* @param int $order_type 订单类型:0正常订单 1vip办理订单
*/
public function pay($user_miyao,$order_sn,$goods_name,$price,$open_id,$order_type=0)
{
//$price = '0.01';
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
$data['appid'] = $this->app_id;
$data['mch_id'] = $this->mch_id;
$data['openid'] = $open_id;
$data['trade_type'] = 'JSAPI';
$data['out_trade_no'] = $order_sn;
$data['nonce_str'] = $this->getNonceStr();
$data['body'] = $goods_name;
$data['total_fee'] = ($price*100);
$data['spbill_create_ip'] = $this->get_client_ip();
$data['notify_url'] = 'http://'.$_SERVER["SERVER_NAME"].'/api/Order/notify_url/user_miyao/'.$user_miyao.'/order_sn/'.$order_sn.'/order_type/'.$order_type;
$data['sign'] = $this->MakeSign($data);
$xml =$this->ToXml($data);
$curl = curl_init(); // 启动一个CURL会话
curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
//设置header
curl_setopt($curl, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_POST, TRUE); // 发送一个常规的Post请求
curl_setopt($curl, CURLOPT_POSTFIELDS, $xml); // Post提交的数据包
curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
$tmpInfo = curl_exec($curl); // 执行操作
curl_close($curl); // 关闭CURL会话
$arr = $this->FromXml($tmpInfo);
if ($arr["result_code"] == 'FAIL'){
return $this->error_json($arr["err_code_des"]);
}
//输出参数列表
$prePayParams = [
'appId'=>$this->app_id,
'timeStamp'=>"'".time()."'",
'nonceStr'=>$this->getNonceStr(),
'package'=>"prepay_id=" . $arr['prepay_id'],
'signType'=>'MD5',
];
$prePayParams['paySign'] = $this->MakeSign($prePayParams);
return $this->succeed_json("ok",$prePayParams,201);
}
/**
* 付款码支付(用于收银台付款)
* @param array $order_info 订单信息
* @param int $user_miyao 会员秘钥
* @param int $auth_code 付款码编号
*/
public function payment_code($order_info=[],$user_miyao='',$auth_code='')
{
$url = "https://api.mch.weixin.qq.com/pay/micropay";
$data['appid'] = $this->app_id;
$data['mch_id'] = $this->mch_id;
$data['out_trade_no'] = $order_info["order_sn"];
$data['nonce_str'] = $this->getNonceStr();
$data['body'] = '收银台收款'.date("Y-m-d H:i:s",time());
$data['total_fee'] = ($order_info["price_pay"]*100);
$data['spbill_create_ip'] = $this->get_client_ip();
$data['auth_code']= ($auth_code);//付款码编号
$data['sign'] = $this->MakeSign($data);
$xml =$this->ToXml($data);
$curl = curl_init(); // 启动一个CURL会话
curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
//设置header
curl_setopt($curl, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_POST, TRUE); // 发送一个常规的Post请求
curl_setopt($curl, CURLOPT_POSTFIELDS, $xml); // Post提交的数据包
curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
$tmpInfo = curl_exec($curl); // 执行操作
curl_close($curl); // 关闭CURL会话
$arr = $this->FromXml($tmpInfo);
return $arr;
}
/**
* 检测订单是否支付成功
* @param array $order_info 订单信息
*/
public function pay_detection($order_info=[])
{
$order_sn = $order_info["order_sn"];
$url = "https://api2.mch.weixin.qq.com/pay/orderquery";
$data['appid'] = $this->app_id;
$data['mch_id'] = $this->mch_id; //商户号
$data['out_trade_no'] = $order_sn;
$data['nonce_str'] = $this->getNonceStr();
$data['sign'] = $this->MakeSign($data);
$xml =$this->ToXml($data);
$curl = curl_init(); // 启动一个CURL会话
curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
//设置header
curl_setopt($curl, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_POST, TRUE); // 发送一个常规的Post请求
curl_setopt($curl, CURLOPT_POSTFIELDS, $xml); // Post提交的数据包
curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
$tmpInfo = curl_exec($curl); // 执行操作
curl_close($curl); // 关闭CURL会话
$arr = $this->FromXml($tmpInfo);
if ($arr["return_code"] == 'SUCCESS' && $arr["result_code"] == 'SUCCESS' && $arr["trade_state"] == 'SUCCESS'){
}
return 'ok';
}
/**
* 微信退款
* @param $money 订单金额
* @param $ordersn 商户订单号
* @param $order_sn 商户退款单号
* @param $momo 退款金额
*/
public function payment_refund($money=0, $ordersn=0, $order_sn=0, $momo=0)
{
header('Content-Type: text/html; charset=utf-8');
$param = array(
'appid' => $this->app_id,
'mch_id' => $this->mch_id,
'nonce_str' => $this->getNonceStr(),
'out_trade_no' => $ordersn,//商户订单号
'out_refund_no' => $order_sn,//商户退款单号
'total_fee' => $money * 100,//订单金额
'refund_fee' => $momo * 100,//退款金额
);
$param['sign'] = $this->MakeSign($param);
$xmldata = $this->ToXml($param);
$url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
$ch = curl_init();
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLCERT,$this->cert_path);
curl_setopt($ch, CURLOPT_SSLKEY, $this->key_path);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xmldata);
$tmpInfo = curl_exec($ch);
curl_close($ch);
$arr = $this->FromXml($tmpInfo);
return $arr;
}
/**
* uniapp h5 获取openid
* @param string $code
* @return mixed
*/
public function GetOpenid_uniapp($code='')
{
$data = $this->getOpenidFromMp($code);
return $data;
}
/*获取OpenID*/
public function GetOpenid()
{
//通过code获得openid
if (!isset($_GET['code'])){
//触发微信返回code码
$baseUrl = urlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].$_SERVER['REQUEST_URI']);
$url = $this->__CreateOauthUrlForCode($baseUrl);
Header("Location: $url");
exit();
} else {
//获取code码,以获取openid
$code = $_GET['code'];
$data = $this->getOpenidFromMp($code);
return $data;
}
}
//构造获取code的url连接
private function __CreateOauthUrlForCode($redirectUrl)
{
$urlObj["appid"] = $this->app_id;
$urlObj["redirect_uri"] = "$redirectUrl";
$urlObj["response_type"] = "code";
$urlObj["scope"] = "snsapi_userinfo";//snsapi_userinfo需要授权才能获取到用户信息 snsapi_base不需要授权只获取openid
$urlObj["state"] = "STATE"."#wechat_redirect";
$bizString = $this->ToUrlParams($urlObj);
return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString;
}
//构造获取open和access_toke的url地址
private function __CreateOauthUrlForOpenid($code)
{
$urlObj["appid"] = $this->app_id;
$urlObj["secret"] = $this->app_secret;
$urlObj["code"] = $code;
$urlObj["grant_type"] = "authorization_code";
$bizString = $this->ToUrlParams($urlObj);
return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString;
}
//通过code从工作平台获取openid机器access_token
public function GetOpenidFromMp($code)
{
$url = $this->__CreateOauthUrlForOpenid($code);
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, 120);
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,结果以jason形式返回
$res = curl_exec($ch);
curl_close($ch);
//取出openid
$data = json_decode($res,true);
//$openid = $data['openid'];
return $data;
}
//将xml转为array
public function FromXml($xml) {
//将XML转为array
libxml_disable_entity_loader(true);
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
}
// 输出xml字符
public function ToXml($arr) {
$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;
}
//格式化参数格式化成url参数
protected function ToUrlParams($arr) {
$buff = "";
foreach ($arr as $k => $v) {
if ($k != "sign" && $v != "" && !is_array($v)) {
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
//生成签名
protected function MakeSign($arr) {
foreach ($arr as $k => $v){
$arr[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($arr);
$string = $this->ToUrlParams($arr);
//签名步骤二:在string后加入KEY
$string = $string . "&key=" . $this->mishi;
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
//32位随机码
public static 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;
}
//获得当前ip
private function get_client_ip() {
if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
$ip = getenv('HTTP_CLIENT_IP');
} elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
$ip = getenv('HTTP_X_FORWARDED_FOR');
} elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
$ip = getenv('REMOTE_ADDR');
} elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
$ip = $_SERVER['REMOTE_ADDR'];
}
return preg_match ( '/[\d\.]{7,15}/', $ip, $matches ) ? $matches [0] : '';
}
/**
* CURL请求
* @param $url 请求url地址
* @param $method 请求方法 get post
* @param null $postfields post数据数组
* @param array $headers 请求header信息
* @param bool|false $debug 调试开启 默认false
* @return mixed
*/
function httpRequest($url, $method, $postfields = null, $headers = array(), $debug = false) {
$method = strtoupper($method);
$ci = curl_init();
/* Curl settings */
curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($ci, CURLOPT_HTTPHEADER, array ('Client_Ip: '.mt_rand(0, 255).'.'.mt_rand(0, 255).'.'.mt_rand(0, 255).'.'.mt_rand(0, 255)));//优点:伪造成本低,通杀90%系统
curl_setopt($ci, CURLOPT_HTTPHEADER, array ('X-Forwarded-For: '.mt_rand(0, 255).'.'.mt_rand(0, 255).'.'.mt_rand(0, 255).'.'.mt_rand(0, 255)));//优点:伪造成本低,通杀90%系统
curl_setopt($ci, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0");
curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 60); /* 在发起连接前等待的时间,如果设置为0,则无限等待 */
curl_setopt($ci, CURLOPT_TIMEOUT, 7); /* 设置cURL允许执行的最长秒数 */
curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);
switch ($method) {
case "POST":
curl_setopt($ci, CURLOPT_POST, true);
if (!empty($postfields)) {
$tmpdatastr = is_array($postfields) ? http_build_query($postfields) : $postfields;
curl_setopt($ci, CURLOPT_POSTFIELDS, $tmpdatastr);
}
break;
default:
curl_setopt($ci, CURLOPT_CUSTOMREQUEST, $method); /* //设置请求方式 */
break;
}
$ssl = preg_match('/^https:\/\//i',$url) ? TRUE : FALSE;
curl_setopt($ci, CURLOPT_URL, $url);
if($ssl){
curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, FALSE); // 不从证书中检查SSL加密算法是否存在
}
//curl_setopt($ci, CURLOPT_HEADER, true); /*启用时会将头文件的信息作为数据流输出*/
//curl_setopt($ci, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ci, CURLOPT_MAXREDIRS, 2);/*指定最多的HTTP重定向的数量,这个选项是和CURLOPT_FOLLOWLOCATION一起使用的*/
curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ci, CURLINFO_HEADER_OUT, true);
/*curl_setopt($ci, CURLOPT_COOKIE, $Cookiestr); * *COOKIE带过去** */
$response = curl_exec($ci);
$requestinfo = curl_getinfo($ci);
$http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
if ($debug) {
echo "=====post data======\r\n";
var_dump($postfields);
echo "=====info===== \r\n";
print_r($requestinfo);
echo "=====response=====\r\n";
print_r($response);
}
curl_close($ci);
return $response;
}
}