1.证书位置
    微信退款---一个文件搞定 - 图1
    2.退款文件

    微信退款---一个文件搞定 - 图2
    3.退款代码

    1. <?php
    2. /*
    3. * 咔咔
    4. * 2018.6.21 微信退款
    5. */
    6. class Refund
    7. {
    8. const REFUND = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
    9. const KEY = 'h5l4P8S3u5n1H5Y4D7E5l9e6J4Y7S8h3';
    10. const CODEURL = 'https://open.weixin.qq.com/connect/oauth2/authorize?';
    11. const OPENIDURL = 'https://api.weixin.qq.com/sns/oauth2/access_token?';
    12. const APPID = 'wxf8a78496174320e9';
    13. const SECRET = '6806ae46548d5a27e0939f12139e7a42';
    14. const MCHID = '1502276551';
    15. const UNURL = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
    16. private $params;
    17. public function __construct($data) {
    18. //组装数据
    19. $this->params = [
    20. 'appid' => self::APPID, //APPID
    21. 'mch_id' => self::MCHID, //商户号
    22. 'nonce_str'=> md5(time()), //随机串
    23. 'sign' => 'md5', //签名方式
    24. 'transaction_id'=> $data['transaction_id'],//微信支付订单号 与商户订单号二选一
    25. //'out_trade_no'=> '', //商户订单号 和微信支付订单号二选一
    26. 'out_refund_no' => $data['out_refund_no'],//退单号
    27. 'total_fee' => $data['total_fee'], //订单金额
    28. 'refund_fee' => $data['refund_fee'] //退款金额
    29. ];
    30. }
    31. //发送退款请求
    32. public function orderRefund(){
    33. //生成签名
    34. $signParams = $this->setSign($this->params);
    35. //将数据转换为xml
    36. $xmlData = $this->ArrToXml($signParams);
    37. //发送请求
    38. return $this->postStr(self::REFUND, $xmlData);
    39. }
    40. //post 字符串到接口
    41. public function postStr($url,$postfields){
    42. $ch = curl_init();
    43. $params[CURLOPT_URL] = $url; //请求url地址
    44. $params[CURLOPT_HEADER] = false; //是否返回响应头信息
    45. $params[CURLOPT_RETURNTRANSFER] = true; //是否将结果返回
    46. $params[CURLOPT_FOLLOWLOCATION] = true; //是否重定向
    47. $params[CURLOPT_POST] = true;
    48. $params[CURLOPT_SSL_VERIFYPEER] = false;//禁用证书校验
    49. $params[CURLOPT_SSL_VERIFYHOST] = false;
    50. //以下是证书相关代码
    51. $params[CURLOPT_SSLCERTTYPE] = 'PEM';
    52. $params[CURLOPT_SSLCERT] = './cert/apiclient_cert.pem';
    53. $params[CURLOPT_SSLKEYTYPE] = 'PEM';
    54. $params[CURLOPT_SSLKEY] = './cert/apiclient_key.pem';
    55. $params[CURLOPT_POSTFIELDS] = $postfields;
    56. curl_setopt_array($ch, $params); //传入curl参数
    57. $content = curl_exec($ch); //执行
    58. curl_close($ch); //关闭连接
    59. return $content;
    60. }
    61. //生成签名
    62. public function getSign($arr){
    63. //去除空值
    64. $arr = array_filter($arr);
    65. if(isset($arr['sign'])){
    66. unset($arr['sign']);
    67. }
    68. //按照键名字典排序
    69. ksort($arr);
    70. //生成url格式的字符串
    71. $str = $this->arrToUrl($arr) . '&key=' . self::KEY;
    72. return strtoupper(md5($str));
    73. }
    74. //获取带签名的数组
    75. public function setSign($arr){
    76. $arr['sign'] = $this->getSign($arr);;
    77. return $arr;
    78. }
    79. public function arrToUrl($arr){
    80. return urldecode(http_build_query($arr));
    81. }
    82. //验证签名
    83. public function chekSign($arr){
    84. $sign = $this->getSign($arr);
    85. if($sign == $arr['sign']){
    86. return true;
    87. }else{
    88. return false;
    89. }
    90. }
    91. //数组转xml
    92. public function ArrToXml($arr)
    93. {
    94. if(!is_array($arr) || count($arr) == 0) return '';
    95. $xml = "<xml>";
    96. foreach ($arr as $key=>$val)
    97. {
    98. if (is_numeric($val)){
    99. $xml.="<".$key.">".$val."</".$key.">";
    100. }else{
    101. $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
    102. }
    103. }
    104. $xml.="</xml>";
    105. return $xml;
    106. }
    107. public function XmlToArr($xml)
    108. {
    109. if($xml == '') return '';
    110. libxml_disable_entity_loader(true);
    111. $arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    112. return $arr;
    113. }
    114. }
    115. // 获取订单号
    116. $orderdata = D('foodorder')->where([
    117. 'id'=>$_SESSION['orderid']
    118. ])->find();
    119. // 传值
    120. $data = [
    121. 'transaction_id' => $orderdata['wx_number'], //微信交易号
    122. 'out_refund_no' => $orderdata['wx_number'], //退款单号
    123. 'total_fee' => $orderdata['cash_fee'], //原订单金额
    124. 'refund_fee' => $orderdata['cash_fee'] //退款金额
    125. ];
    126. // 实例化退款类
    127. $obj = new Refund($data);
    128. // 调用退款方法
    129. $res = $obj->orderRefund();
    130. // 退款微信返回数据
    131. $refunddata = $obj->XmlToArr($res);
    132. if($refunddata['return_code'] == 'SUCCESS'){
    133. $data['wx_refundorder'] = $orderdata['wx_number'];
    134. $data['user_id'] = $orderdata['userid'];
    135. $data['refund_fee'] = $orderdata['cash_fee'];
    136. $data['refund_time'] = time();
    137. $refundorder = D('refundorder')->add($data);
    138. if(!$refundorder){
    139. $log_name= "./Public/Pay/refund_url.log";//log文件路径
    140. file_put_contents($log_name,"【退款成功 , 数据库添加失败通知", FILE_APPEND);
    141. }
    142. }else{
    143. $log_name= "./Public/Pay/refund_url.log";//log文件路径
    144. file_put_contents($log_name,"【退款失败", FILE_APPEND);
    145. }

    4.控制器文件
    微信退款---一个文件搞定 - 图3
    5.控制器代码

    1. // 退款
    2. public function refundmoney() {
    3. $order_id = I('orderid');
    4. session('orderid',$order_id);
    5. vendor('WxPayPubHelper.refund');
    6. // 查询订单表
    7. $orderdata = D('foodorder')->where([
    8. 'id'=>$order_id
    9. ])->find();
    10. // 查询退款后的数据库
    11. $refunddata = D('refundorder')->where([
    12. 'wx_refundorder'=>$orderdata['wx_number']
    13. ])->find();
    14. // 判断是否存在
    15. if($orderdata){
    16. // 修改退款的refund状态
    17. $orderedit = D('foodorder')->where([
    18. 'id'=>session('orderid')
    19. ])->setInc('refund');
    20. // 清除订单id
    21. session('orderid',null);
    22. __success('退款成功',U('/addon/WeiuidDeit/web/foodorderindex'));
    23. }else{
    24. __error('退款失败');
    25. }
    26. }