thinkphp微信支付和微信退款

以下代码修改完自己的
appid
商户号
商户密钥
即可进行运行测试

微信支付

	//微信支付
	public function index(){
		//接收用户下单信息
		$data = [];
		$data['sorts'] = input('sorts');//套餐分类
		$data['sciencename']=input('sciencename');//景区名称
		$data['price']=input('price');//订单价格
		$data['create_time']=time();//下单时间
		$data['phone']=input('phone');//用户手机号
		$data['userid']=input('userid');//用户id
		$data['menuid']=input('menuid');//套餐id
		$data['scienceid'] = input('scienceid');//景区ID
		$data['num'] = input('num');//景区ID
		
		if(input('type')){
			$data['type']=input('type');//1为音频套餐
		}
		
		$user = Db::name('user_info')->where(array('id' => $data['userid']))->field('openid')->find();//获取当前用户openID
       
		
		//发起微信支付,调用统一下单支付接口
		$fee = $data['price'];//支付金额
		$appid =        '用到的appid';//appid.如果是公众号 就是公众号的appid
		$body =         $data['sorts'];
		$mch_id =       '用到的商户号';  //商户号
		$nonce_str =    $this->nonce_str();//随机字符串
		$notify_url =   'https://ht.hongtuzhijian.top/api/order/notifyurl'; //回调的url
		$openid =       $user['openid'];
		$time = time();
		$year = date('Y',$time);
		$rand = rand(000000000,999999999);
		$out_trade_no = $year.$rand;//订单号
		
		//$out_trade_no = $this->order_number($openid);//商户订单号
		$spbill_create_ip = $_SERVER["REMOTE_ADDR"];//服务器的ip;
		$total_fee =    $fee*100;// 微信支付单位是分,所以这里需要*100
		$trade_type = 'JSAPI';//交易类型 默认
		
		$data['order_no']=$out_trade_no;
		//echo json_encode($data);exit;
		$order = Db::name('user_order')->insert($data);
		
	 
		//这里是按照顺序的 因为下面的签名是按照顺序 排序错误 肯定出错
		$post['appid'] = $appid;
		$post['body'] = $body;
		$post['mch_id'] = $mch_id;
		$post['nonce_str'] = $nonce_str;//随机字符串
		$post['notify_url'] = $notify_url;
		$post['openid'] = $openid;
		$post['out_trade_no'] = $out_trade_no;
		$post['spbill_create_ip'] = $spbill_create_ip;//用户终端的ip
		$post['total_fee'] = $total_fee;//总金额 
		$post['trade_type'] = $trade_type;
		
		$sign = $this->sign($post);//签名
		$post_xml = '<xml>
			   <appid>'.$appid.'</appid>
			   <body>'.$body.'</body>
			   <mch_id>'.$mch_id.'</mch_id>
			   <nonce_str>'.$nonce_str.'</nonce_str>
			   <notify_url>'.$notify_url.'</notify_url>
			   <openid>'.$openid.'</openid>
			   <out_trade_no>'.$out_trade_no.'</out_trade_no>
			   <spbill_create_ip>'.$spbill_create_ip.'</spbill_create_ip>
			   <total_fee>'.$total_fee.'</total_fee>
			   <trade_type>'.$trade_type.'</trade_type>
			   <sign>'.$sign.'</sign>
			</xml> ';
	 
	 
		//print_r($post_xml);die;
		//统一接口prepay_id
		$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';//调用统一下单
		$xml = $this->http_request($url,$post_xml);
	 
		$array = $this->xml($xml);//全要大写
	//echo json_encode($array);exit;
		//print_r($array);
		unset($data);
		//统一下单请求成功回调前端支付参数
		if($array['RETURN_CODE'] == 'SUCCESS' && $array['RESULT_CODE'] == 'SUCCESS'){
			$time = time();
			$tmp=[];//临时数组用于签名
			$tmp['appId'] = $appid;
			$tmp['nonceStr'] = $nonce_str;
			$tmp['package'] = 'prepay_id='.$array['PREPAY_ID'];
			$tmp['signType'] = 'MD5';
			$tmp['timeStamp'] = $time;
	 
	 
			$data['state'] = 200;
			$data['timeStamp'] = $time;//时间戳
			$data['nonceStr'] = $nonce_str;//随机字符串
			$data['signType'] = 'MD5';//签名算法,暂支持 MD5
			$data['package'] = 'prepay_id='.$array['PREPAY_ID'];//统一下单接口返回的 prepay_id 参数值
			$data['paySign'] = $this->sign($tmp);//签名
			$data['out_trade_no'] = $out_trade_no;
	 
	 
		}else{
			$data['state'] = 0;
			$data['text'] = "错误";
			$data['RETURN_CODE'] = $array['RETURN_CODE'];
			$data['RETURN_MSG'] = $array['RETURN_MSG'];
		}
	 
	 
		echo json_encode($data);
	}
	 
	 
	 
	 
	//随机32位字符串
	private function nonce_str(){
		$result = '';
		$str = 'QWERTYUIOPASDFGHJKLZXVBNMqwertyuioplkjhgfdsamnbvcxz';
		for ($i=0;$i<32;$i++){
			$result .= $str[rand(0,48)];
		}
		return $result;
	}
	 
	 
	 
	//生成订单号
	private function order_number($openid){
		//date('Ymd',time()).time().rand(10,99);//18位
		return md5($openid.time().rand(10,99));//32位
	}
	 
	 
	 
	//签名 $data要先排好顺序
	private function sign($data){
		$stringA = '';
		foreach ($data as $key=>$value){
			if(!$value) continue;
			if($stringA) $stringA .= '&'.$key."=".$value;
			else $stringA = $key."=".$value;
		}
		$wx_key = '用到的key';//申请支付后有给予一个商户账号和密码,登陆后自己设置的key
		$stringSignTemp = $stringA.'&key='.$wx_key;
		return strtoupper(md5($stringSignTemp));
	}
	 
	 
	//curl请求
	public function http_request($url,$data = null,$headers=array())
	{
		$curl = curl_init();
		if( count($headers) >= 1 ){
			curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
		}
		curl_setopt($curl, CURLOPT_URL, $url);
	 
	 
		curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
		curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
	 
	 
		if (!empty($data)){
			curl_setopt($curl, CURLOPT_POST, 1);
			curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
		}
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
		$output = curl_exec($curl);
		curl_close($curl);
		return $output;
	}
	 
	 
	//获取xml
	public function xml($xml){
		$p = xml_parser_create();
		xml_parse_into_struct($p, $xml, $vals, $index);
		xml_parser_free($p);
		$data = [];
		foreach ($index as $key=>$value) {
			if($key == 'xml' || $key == 'XML') continue;
			$tag = $vals[$value[0]]['tag'];
			$value = $vals[$value[0]]['value'];
			$data[$tag] = $value;
		}
		return $data;
	}
	
	//支付回调接口
	public function notifyurl(){
		
		$res_xml = file_get_contents("php://input");
		libxml_disable_entity_loader(true);
		$ret = json_decode(json_encode(simplexml_load_string($res_xml,'simpleXMLElement',LIBXML_NOCDATA)),true);
		
		$data = array();
		$data['order_sn'] = $ret['out_trade_no'];
		$data['trade_no'] = $ret['transaction_id'];
		$data['total_fee'] = $ret['total_fee'];
		
		$check_info = DB::name('user_order')->where(array('order_no'=>$data['order_sn']))->find();
		if (!$check_info) {
			echo json_encode(array('state'=>1,'msg'=>'订单信息错误'));
		}

		
		if($ret['return_code'] == 'SUCCESS'){
			$up = array();
			$up['status'] = 2;
			$up['update_time'] = time();
			$res = DB::name('user_order')->where(array('order_no'=>$data['order_sn']))->update($up);//更新订单状态
		}
		// 更新成功 付款成功
		if ($res) {
			$xml = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg>";
			$xml.="</xml>";
			echo $xml;
		}
	}

微信退款

退款需要传订单号和订单金额两个必要参数

  	 /**
     * 默认支付参数配置,可以在这里配置,也可以在初始化的时候,统一传入参数
     * @var array
     */
	private $config = array(
        	'appid'			=> '用到的小程序id', //小程序id
			'mch_id'	 	=> '用到的商户号',//商户号
			'pay_apikey' 	=> '用到的商户key',//商户key
    );
	/**
     * 使用 $this->name=$value 	配置参数
     * @param  string $name 	配置名称
     * @param  string $value    配置值
     */
	public function __set($name,$value){
        if(isset($this->config[$name])) {
            $this->config[$name] = $value;
        }
    }
	
	/**
     * 使用 $this->name 获取配置
     * @param  string $name 配置名称
     * @return multitype    配置值
     */
    public function __get($name) {
        return $this->config[$name];
    }
	
	public function __isset($name){
        return isset($this->config[$name]);
    }
	
	//----------------------------------------------------------退款---------------------------------------------------------
	
	/**
     * 微信退款(POST)
     * @param string(28) $out_trade_no      在微信支付的时候,微信服务器生成的订单流水号,在支付通知中有返回
     * @param string $out_refund_no 		同订单号
     * @param string $total_fee 			微信支付的时候支付的总金额(单位:分)
     * @param string $refund_fee 			此次要退款金额(单位:分)
     * @return string						xml格式的数据
     */
	public function refund($row){
		$config = $this->config;
		//退款参数
		$refundorder = array(
			'appid'			=> $config['appid'],
			'mch_id'		=> $config['mch_id'],
			'nonce_str'		=> $this->getNonceStr(),
			'out_trade_no'	=> $row['order_no'],
			'out_refund_no' => $row['order_no'],
			'total_fee'		=> $row['price']*100,
			'refund_fee'	=> $row['price']*100
		);
		$refundorder['sign'] = self::makeSign($refundorder);
		//请求数据,进行退款
		$xmldata = self::array2xml($refundorder);
		$url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
        $res = self::curl_post_ssl($url, $xmldata);
        if(!$res){
			return array('status'=>0, 'msg'=>"Can't connect the server" );
        }
		// 这句file_put_contents是用来查看服务器返回的结果 测试完可以删除了
		// file_put_contents('./log3.txt',$res,FILE_APPEND);
		$content = self::xml2array($res);
		// if(strval($content['result_code']) == 'FAIL'){
    	//return array('status'=>0, 'msg'=>strval($content['err_code']).':'.strval($content['err_code_des']));
		//     }
		if(strval($content['return_code']) == 'SUCCESS'){
			//退款成功
			//自己写退款成功之后要运行的代码
	    }
		// return $content;
	}
	

	
//---------------------------------------------------------------用到的函数------------------------------------------------------
	/**
     * 将一个数组转换为 XML 结构的字符串
     * @param array $arr 要转换的数组
     * @param int $level 节点层级, 1 为 Root.
     * @return string XML 结构的字符串
     */
    protected function array2xml($arr, $level = 1) {
        $s = $level == 1 ? "<xml>" : '';
        foreach($arr as $tagname => $value) {
            if (is_numeric($tagname)) {
                $tagname = $value['TagName'];
                unset($value['TagName']);
            }
            if(!is_array($value)) {
                $s .= "<{$tagname}>".(!is_numeric($value) ? '<![CDATA[' : '').$value.(!is_numeric($value) ? ']]>' : '')."</{$tagname}>";
            } else {
                $s .= "<{$tagname}>" . $this->array2xml($value, $level + 1)."</{$tagname}>";
            }
        }
        $s = preg_replace("/([\x01-\x08\x0b-\x0c\x0e-\x1f])+/", ' ', $s);
        return $level == 1 ? $s."</xml>" : $s;
    }
	
	/**
	 * 将xml转为array
	 * @param  string 	$xml xml字符串
	 * @return array    转换得到的数组
	 */
	protected function xml2array($xml){   
		//禁止引用外部xml实体
		libxml_disable_entity_loader(true);
		$result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);        
		return $result;
	}
	
	/**
	 * 
	 * 产生随机字符串,不长于32位
	 * @param int $length
	 * @return 产生的随机字符串
	 */
	protected 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;
	}
	
	/**
	* 生成签名
	* @return 签名
	*/
	protected function makeSign($data){
		//获取微信支付秘钥
		$key = $this->config['pay_apikey'];
		// 去空
		$data=array_filter($data);
		//签名步骤一:按字典序排序参数
		ksort($data);
		$string_a=http_build_query($data);
		$string_a=urldecode($string_a);
		//签名步骤二:在string后加入KEY
		$string_sign_temp=$string_a."&key=".$key;
		//签名步骤三:MD5加密
		$sign = md5($string_sign_temp);
		// 签名步骤四:所有字符转为大写
		$result=strtoupper($sign);
		return $result;
	}
	
	/**
	 * 获取IP地址
	 * @return [String] [ip地址]
	 */
	protected function getip() {
        static $ip = '';
        $ip = $_SERVER['REMOTE_ADDR'];
        if(isset($_SERVER['HTTP_CDN_SRC_IP'])) {
            $ip = $_SERVER['HTTP_CDN_SRC_IP'];
        } elseif (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif(isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
            foreach ($matches[0] AS $xip) {
                if (!preg_match('#^(10|172\.16|192\.168)\.#', $xip)) {
                    $ip = $xip;
                    break;
                }
            }
        }
        return $ip;
    }
	
	/**
	 * 微信支付退款发起请求
	 */
	protected function curl_post_ssl($url, $xmldata, $second=30,$aHeader=array()){
		$config = $this->config;
		
		$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,0);
		curl_setopt($ch, CURLOPT_SSLVERSION, 1);
		
		$cert = __DIR__.'/cert/apiclient_cert.pem';
		$key = __DIR__.'/cert/apiclient_key.pem';
		//默认格式为PEM,可以注释
		curl_setopt($ch,CURLOPT_SSLCERT, $cert); //这个是证书的位置绝对路径
		curl_setopt($ch,CURLOPT_SSLKEY, $key); //这个也是证书的位置绝对路径
		//curl_setopt($ch,CURLOPT_CAINFO,$config['rootca']);
	 
		if( count($aHeader) >= 1 ){
			curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
		}
	 
		curl_setopt($ch,CURLOPT_POST, true);
		curl_setopt($ch,CURLOPT_POSTFIELDS,$xmldata);
		$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;
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值