【微信支付】退款


  微信支付文档申请退款的详细介绍,建议大家先把文档看一遍,然后有什么不明白的再百度。这里记录的是我用php进行微信支付退款

1. 下载商户证书

  登录上商户平台,依次【账户中心】-->【账户设置】-->【API安全】-->【证书下载】,解压,把apiclient_cert.pem和apiclient_key.pem两个文件放到项目目录中,分别用常量SSLCERT_PATHSSLKEY_PATH表示文件路径。

	define(SSLCERT_PATH, "apiclient_cert.pem的路径");
	define(SSLKEY_PATH, "apiclient_key.pem的路径");

2. 组装参数

	$params = array(
		'out_trade_no'  =>  $out_trade_no,	//商户订单号
		'total_fee'     =>  $total_fee,   	//订单总金额,以分为单位
		'refund_fee'    =>  $refund_fee,    //退款金额,以分为单位
		'out_refund_no' =>  $mch_id.  date("YmdHis"),
		'op_user_id'    =>  $mch_id,		//商户Id
		'appid'         =>  $appId,			//AppId
		'mch_id'        =>  $mch_id,		
		'nonce_str'     =>  getNonceStr(),//生成随机字符串
	);
	//生成签名
	$params['sign'] = makeSign($params, $key);	//$key微信支付Key
	//转为XML格式
	$xml = toXml($params);
	
	/***************工具函数***************/
	/**
	 * 生成指定长度的随机字符串
	 */
	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 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
	 */
	private function makeSign($values, $key)
	{
		//签名步骤一:按字典序排序参数
		ksort($values);
		$string = $this->toUrlParams($values);
		//签名步骤二:在string后加入KEY
		$string = $string . "&key=".$key;
		//签名步骤三:MD5加密
		$string = md5($string);
		//签名步骤四:所有字符转为大写
		$result = strtoupper($string);
		return $result;
	}
	/**
	 * 格式化参数格式化成url参数
	 */
	function toUrlParams($values){
		$buff = "";
		foreach ($values as $k => $v){
			if($k != "sign" && $v != "" && !is_array($v)){
				$buff .= $k . "=" . $v . "&";
			}
		}
		$buff = trim($buff, "&");
		return $buff;
	}
	/**
	 * 输出xml字符
	 * @throws WxPayException
	 **/
	function toXml($values){
		if(!is_array($values) || count($values) <= 0){
			throw new WxPayException("数组数据异常!");
		}

		$xml = "<xml>";
		foreach ($values as $key=>$val) {
			if (is_numeric($val)){
				$xml.="<".$key.">".$val."</".$key.">";
			}else{
				$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
			}
		}
		$xml.="</xml>";
		return $xml;
	}

3. 发送HTTPS请求

	$url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
	$response = postXmlCurl($xml, $url, true, 6);
	
	/**
	 * 以post方式提交xml到对应的接口url
	 *
	 * @param string $xml  需要post的xml数据
	 * @param string $url  url
	 * @param bool $useCert 是否需要证书,默认不需要
	 * @param int $second   url执行超时时间,默认30s
	 * @throws Exception
	 */
	function postXmlCurl($xml, $url, $useCert = false, $second = 30) {
		$ch = curl_init();
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $second);

		curl_setopt($ch,CURLOPT_URL, $url);
		curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
		curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
		//设置header
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		//要求结果为字符串且输出到屏幕上
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

		if($useCert == true){
			//设置证书
			//使用证书:cert 与 key 分别属于两个.pem文件
			curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
			curl_setopt($ch,CURLOPT_SSLCERT, SSLCERT_PATH);
			curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
			curl_setopt($ch,CURLOPT_SSLKEY, SSLKEY_PATH);
		}
		//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);
			curl_close($ch);
			throw new Exception("curl出错,错误码:$error");
		}
	}

4. 接收HTTPS响应数据

	//Https请求返回的数据是XML格式的,要先转为数组
	$result = fromXml($response);
	if($result['return_code'] == 'SUCCESS'){
		//检查签名
		checkSign($result, $Key);
	}
	
	/**
	 * xml数据转为数组
	 */
	function fromXml($xml){
		if(!$xml){
			throw new Exception("xml数据异常!");
		}
		//将XML转为array
		//禁止引用外部xml实体
		libxml_disable_entity_loader(true);
		$values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
		return $values;
	}
	/**
	 * 检查返回数据的签名
	 */
	function checkSign($values, $key){
		//fix异常
		if(!array_key_exists('sign', $values)){
			throw new Exception("签名错误!");
		}

		$sign = makeSign($values, $key);
		if($values['sign'] == $sign){
			return true;
		}
		throw new Exception("签名错误!");
	}

总结

  微信支付退款和其他普通的API请求一样,只不过是多了证书而已。

posted @ 2017-04-26 20:51  刘一二  阅读(684)  评论(0编辑  收藏  举报