/* * 微信统一退款接口 * 参数说明: * $res-基层数据,$out_num-退款的订单号,$path-回调接收地址, * $title-退款说明,$money-订单总金额,$refund_money-要退款的金额,$out_refund_no-可选,退款单号 * 返回值说明: * array,键值如下: * status:操作状态,包括:success-操作成功,no_money-余额不足,param_error-参数错误,cert_error-证书错误,has_refund-已经退款了,error-其他错误 * refund_num-退款订单号 * error_info-错误信息描述,当status不为success时返回 * */ function weixin_refund($res, $out_num, $path, $title, $money, $refund_money, $out_refund_no = '') { //微信小程序app_id,对应的appid需要跟商户所对应,别弄错了 $appinfo['appi_d'] = ''; //微信商户的商户号,自己可以微信商户后台进行查询 $appinfo['mch_id'] = ''; //微信的商户号的支付秘钥 $appinfo['pay_secret'] = ''; //下面的是 商户的私钥和公钥,放在public目录下面了 $appinfo['app_wx_cert'] = public_path().'apiclient_cert.pem'; $appinfo['app_wx_key'] = public_path().'apiclient_key.pem'; //获取支付基础信息 $appid = $appinfo['appi_d']; $mch_id = $appinfo['mch_id']; $miyao = $appinfo['pay_secret']; $cert_path['cert'] = $appinfo['app_wx_cert']; $cert_path['key'] = $appinfo['app_wx_key']; if (!$out_refund_no) { //生成退款订单号 $out_refund_no = ikzOutNum('pb'); } /*支付XML数组*/ $pay_arr = array( //以下是必填项 'appid' => $appid,//公众号appid 'mch_id' => $mch_id,//商户号 'nonce_str' => get_rand_str(), //随机字符串表 'out_trade_no' => $out_num, //订单号 'out_refund_no' => $out_refund_no,//退款订单号 'total_fee' => $money * 100,//订单总金额 'refund_fee' => $refund_money * 100,//退款总金额 'refund_desc' => $title,//退款原因 'notify_url' => $path //通知地址 ); $sing_str = get_sign($miyao, $pay_arr); //将拼接好的签名写入数组 $pay_sign = array('sign' => $sing_str); $pay_xml_array = array_merge($pay_arr, $pay_sign); $pay_xml_array = arrayToXml($pay_xml_array); $url = "https://api.mch.weixin.qq.com/secapi/pay/refund"; $responseXml = curl_post_ssl($url, $pay_xml_array, true, $cert_path); $responseObj = (array)simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); //先判断证书是否存在 if ($responseObj !== false) { //证书正确,开始判断退款详情 if ($responseObj['return_code'] == 'SUCCESS') { //请求成功 if ($responseObj['result_code'] == 'SUCCESS') { //汇款成功 $result['status'] = 'success'; } else { //汇款失败,判断失败的信息 if ($responseObj['err_code'] == 'SYSTEMERROR' || $responseObj['err_code'] == 'BIZERR_NEED_RETRY') { //系统错误,再次发起 weixin_refund('', $out_num, $path, $title, $money, $refund_money, $out_refund_no); } elseif ($responseObj['err_code'] == 'NOTENOUGH') { //余额不足 $result['status'] = 'no_money'; } elseif ($responseObj['err_code'] == 'APPID_NOT_EXIST' || $responseObj['err_code'] == 'MCHID_NOT_EXIST') { //参数错误 $result['status'] = 'param_error'; } elseif ($responseObj['err_code'] == 'ERROR' && $responseObj['err_code_des'] == '订单已全额退款') { //参数错误 $result['status'] = 'has_refund'; } else { //其他错误 $result['status'] = 'error'; } $result['error_info'] = $responseObj['err_code_des']; //错误的状态,进行详细记录 recordOrderError($out_num, json_encode($responseObj), '3'); } } else { //系统错误 $result['status'] = 'sys_fail'; $result['error_info'] = '系统错误'; //错误的状态,进行详细记录 recordOrderError($out_num, json_encode($responseObj), '3'); } } else { //系统错误 $result['status'] = 'cert_error'; $result['error_info'] = '证书错误'; //错误的状态,进行详细记录 recordOrderError($out_num, '证书错误', '3'); } $result['refund_num'] = $out_refund_no; return $result; }
//生成随机数 function get_rand_str() { $str = '1234567890abcdefghijklmnopqrstuvwxyz'; $t1 = ''; for ($i = 0; $i < 30; $i++) { $j = rand(0, 35); $t1 .= $str[$j]; } return $t1; }
/** * 生成订单号 * @param $length * @return string * @throws ClientException */ function ikzOutNum($info = '') { return $info . date('YmdHis') . rand(1000000, 9999999); }
/* *生成签名的方法 *@param $my 商户应用密钥 */ function get_sign($my, $hongbao_arr) { ksort($hongbao_arr); //按照ASCII 码 从 小 到 大 排 序 $unSignParaString = formatQueryParaMap($hongbao_arr, false); return sign($unSignParaString, $my); }
//设置请求参数的方法 function arrayToXml($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; }
//请求数据的方法 function curl_post_ssl($url, $vars, $cert = false, $cert_path = '', $second = 30, $aHeader = array()) { $ch = curl_init(); //超时时间 curl_setopt($ch, CURLOPT_TIMEOUT, $second); 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); if ($cert) { //第一种方法,cert 与 key 分别属于两个.pem文件 //默认格式为PEM,可以注释 curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM'); curl_setopt($ch, CURLOPT_SSLCERT, $cert_path['cert']); //默认格式为PEM,可以注释 curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM'); curl_setopt($ch, CURLOPT_SSLKEY, $cert_path['key']); } 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); curl_close($ch); return false; } }