够意思博客够意思博客够意思博客

TP5.0微信APP支付退款

APP支付退款API调用需要双向证书,需要预先设置好API证书,下载证书并把apiclient_cert.pem和apiclient_key.pem放置网站根目录下

实现退款功能

        //APP支付退款
       public function tkWxPay($order,$tk_money,$refund_desc=''){
           $data["appid"] = $this->appid;
           $data["mch_id"] = $this->partnerId;
           $data["nonce_str"] = $this->getRandChar(32);
           $data["out_refund_no"] = date('YmdHis');
                 //out_trade_no和transaction_id二选1
           //$data["out_trade_no"] = $order['order_no'];
           $data["transaction_id"] = $order['out_trade_no'];
           //$data["spbill_create_ip"] = $this->get_client_ip();
           $data["total_fee"] =  $order['pay_money']*100;
           $data["refund_fee"] = $tk_money*100;
           $data["refund_desc"] = $refund_desc;
           //按照参数名ASCII字典序排序并且拼接API密钥生成签名
           $data["sign"] = $this->getSign($data);
           //配置xml最终得到最终发送的数据
           $xml = $this->arrayToXml($data);
           $response = $this->postXmlCurl($xml, self::TK_URL,true);

           //将微信返回的结果xml转成数组
           $params = $this->xmlstr_to_array($response);
           $params["package"] = "Sign=WXPay";

           if($params['return_code'] == "SUCCESS" && $params['result_code'] == "SUCCESS")
           {
               return true; //退款成功
           }else{
               return false;//退款失败
           }
       }

post https请求是需要设置证书路径,否则无法进行退款

//post https请求,CURLOPT_POSTFIELDS xml格式
function postXmlCurl($xml, $url,$cert=false, $second = 30)
{
   //初始化curl
   $ch = curl_init();
   //超时时间
   curl_setopt($ch, CURLOPT_TIMEOUT, $second);
   //这里设置代理,如果有的话
   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文件
       //证书文件请放入服务器的非web目录下
       $sslCertPath = ROOT_PATH.'/apiclient_cert.pem';
       $sslKeyPath =  ROOT_PATH.'/apiclient_key.pem';
       curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
       curl_setopt($ch, CURLOPT_SSLCERT, $sslCertPath);
       curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
       curl_setopt($ch, CURLOPT_SSLKEY, $sslKeyPath);
       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);
   //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);
       return false;
   }
}


如何设置API证书?参照官网文档:API证书设置

微信支付类

<?php
namespace app\common\controller;
use think\Controller;


Class Wxpay
{
       private $appid = '';      //微信开放平台的应用appid
       private $partnerId = ''; //商户号(注册商户平台时,发置注册邮箱的商户id)
       private $key = '';       //商户平台api支付处设置的key
       private $notify_url = '';//支付成功回调地址
       const URL = 'https://api.mch.weixin.qq.com/pay/unifiedorder';   //支付请求地址
       const TK_URL = 'https://api.mch.weixin.qq.com/secapi/pay/refund';//退款请求地址
      function __construct()
       {
           $this->appid =  \think\Config::get('wxpay')['appid'];
           $this->partnerId =   \think\Config::get('wxpay')['mchid'];
           $this->key =  \think\Config::get('wxpay')['key'];
           $this->notify_url = getLocationUrl().'api_wxpay_notify';
       }
       //生成订单
       public function wechat_pay($body, $out_trade_no, $total_fee)
       {
           $data["appid"] = $this->appid;
           $data["body"] = $body;
           $data["mch_id"] = $this->partnerId;
           $data["nonce_str"] = $this->getRandChar(32);
           $data["notify_url"] = $this->notify_url;
           $data["out_trade_no"] = $out_trade_no;
           $data["spbill_create_ip"] = $this->get_client_ip();
           $data["total_fee"] = $total_fee;
           $data["trade_type"] = "APP";
           //按照参数名ASCII字典序排序并且拼接API密钥生成签名
           $data["sign"] = $this->getSign($data);
           //配置xml最终得到最终发送的数据
           $xml = $this->arrayToXml($data);
           $response = $this->postXmlCurl($xml, self::URL);
           //将微信返回的结果xml转成数组
           $params = $this->xmlstr_to_array($response);
           $params["package"] = "Sign=WXPay";
           //返回的结果进行判断。
           if($params['return_code'] == "SUCCESS" && $params['result_code'] == "SUCCESS")
           {
               //根据微信支付返回的结果进行二次签名
               //二次签名所需的随机字符串
              // $data["nonce_str"] = $this->getRandChar(32);
               //二次签名所需的时间戳
               $data['timeStamp'] = time()."";
               //二次签名剩余参数appid,partnerid,prepayid,noncestr,timestamp,package
               $secondSignArray = array(
                   "appid"=>$data['appid'],
                   "noncestr"=>$data['nonce_str'],
                   "package"=>"Sign=WXPay",
                   "partnerid"=>$data['mch_id'],
                   "prepayid"=>$params['prepay_id'],
                   "timestamp"=>$data['timeStamp']
               );
               $secondSignArray['sign'] = $this->getSign($secondSignArray);  //预支付订单签名
               $params['sign'] = $this->getSign($secondSignArray);  //预支付订单签名
               $params['ordersn'] = $data["out_trade_no"]; //订单号
               $params['order_arr'] = $secondSignArray;  //返给前台APP的预支付订单信息
           }
           return $secondSignArray;
           //return $params;
       }
       //进行签名
       function getSign($Obj)
       {
           foreach ($Obj as $k => $v) {
               $Parameters[strtolower($k)] = $v;
           }
           //签名步骤一:按字典序排序参数
           ksort($Parameters);
           $String = $this->formatBizQueryParaMap($Parameters, false);
           //echo "【string】 =".$String."</br>";
           //签名步骤二:在string后加入KEY
           $String = $String . "&key=" . $this->key;
           //签名步骤三:MD5加密
           $result_ = strtoupper(md5($String));
           return $result_;
       }
       //获取指定长度的随机字符串
       private function getRandChar($length)
       {
           $str = null;
           $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
           $max = strlen($strPol) - 1;

           for ($i = 0; $i < $length; $i++) {
               $str .= $strPol[rand(0, $max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
           }

           return $str;
       }
       //获取当前服务器的IP
       function get_client_ip()
       {
           if ($_SERVER['REMOTE_ADDR']) {
               $cip = $_SERVER['REMOTE_ADDR'];
           } elseif (getenv("REMOTE_ADDR")) {
               $cip = getenv("REMOTE_ADDR");
           } elseif (getenv("HTTP_CLIENT_IP")) {
               $cip = getenv("HTTP_CLIENT_IP");
           } else {
               $cip = "unknown";
           }
           return $cip;
       }
       //将数组转成uri字符串
       function formatBizQueryParaMap($paraMap, $urlencode)
       {
           $buff = "";
           $reqPar = '';
           ksort($paraMap);
           foreach ($paraMap as $k => $v) {
               if ($urlencode) {
                   $v = urlencode($v);
               }
               $buff .= strtolower($k) . "=" . $v . "&";
           }
           if (strlen($buff) > 0) {
               $reqPar = substr($buff, 0, strlen($buff) - 1);
           }
           return $reqPar;
       }
       //数组转xml
       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;
       }
       //post https请求,CURLOPT_POSTFIELDS xml格式
       function postXmlCurl($xml, $url,$cert=false, $second = 30)
       {
           //初始化curl
           $ch = curl_init();
           //超时时间
           curl_setopt($ch, CURLOPT_TIMEOUT, $second);
           //这里设置代理,如果有的话
           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文件
               //证书文件请放入服务器的web目录下
               $sslCertPath = ROOT_PATH.'/apiclient_cert.pem';
               $sslKeyPath =  ROOT_PATH.'/apiclient_key.pem';
               curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
               curl_setopt($ch, CURLOPT_SSLCERT, $sslCertPath);
               curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
               curl_setopt($ch, CURLOPT_SSLKEY, $sslKeyPath);
               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);
           //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);
               return false;
           }
       }
       //xml转成数组
       public function xmlstr_to_array($xmlstr)
       {
           $doc = new \DOMDocument();
           $doc->loadXML($xmlstr);
           return $this->domnode_to_array($doc->documentElement);
       }
       public function domnode_to_array($node)
       {
           $output = array();
           switch ($node->nodeType) {
               case XML_CDATA_SECTION_NODE:
               case XML_TEXT_NODE:
                   $output = trim($node->textContent);
                   break;
               case XML_ELEMENT_NODE:
                   for ($i = 0, $m = $node->childNodes->length; $i < $m; $i++) {
                       $child = $node->childNodes->item($i);
                       $v = $this->domnode_to_array($child);
                       if (isset($child->tagName)) {
                           $t = $child->tagName;
                           if (!isset($output[$t])) {
                               $output[$t] = array();
                           }
                           $output[$t][] = $v;
                       } elseif ($v) {
                           $output = (string)$v;
                       }
                   }
                   if (is_array($output)) {
                       if ($node->attributes->length) {
                           $a = array();
                           foreach ($node->attributes as $attrName => $attrNode) {
                               $a[$attrName] = (string)$attrNode->value;
                           }
                           $output['@attributes'] = $a;
                       }
                       foreach ($output as $t => $v) {
                           if (is_array($v) && count($v) == 1 && $t != '@attributes') {
                               $output[$t] = $v[0];
                           }
                       }
                   }
                   break;
           }
           return $output;
       }
       //微信支付成功以后的回调
       public function notify()
       {
           $xml = file_get_contents('php://input');
           $result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
           //用户http_build_query()将数据转成URL键值对形式
           $sign = http_build_query($result);
           //md5处理
           $sign = md5($sign);
           //转大写
           $sign = strtoupper($sign);
           //验签名。默认支持MD5
           if ($sign === $result['sign']) {
               return true;
           } else {
               return false;
           }
       }
       //退款
       public function tkWxPay($order,$tk_money,$refund_desc=''){
           $data["appid"] = $this->appid;
           $data["mch_id"] = $this->partnerId;
           $data["nonce_str"] = $this->getRandChar(32);
           $data["out_refund_no"] = date('YmdHis');
//            $data["out_trade_no"] = $order['order_no'];
           $data["transaction_id"] = $order['out_trade_no'];
           //$data["spbill_create_ip"] = $this->get_client_ip();
           $data["total_fee"] = 0.01*100;//$order['pay_money']*100;
           $data["refund_fee"] = 0.01*100;//$tk_money*100;
           $data["refund_desc"] = $refund_desc;
           //按照参数名ASCII字典序排序并且拼接API密钥生成签名
           $data["sign"] = $this->getSign($data);
           //配置xml最终得到最终发送的数据
           $xml = $this->arrayToXml($data);
           $response = $this->postXmlCurl($xml, self::TK_URL,true);

           //将微信返回的结果xml转成数组
           $params = $this->xmlstr_to_array($response);
           $params["package"] = "Sign=WXPay";

           if($params['return_code'] == "SUCCESS" && $params['result_code'] == "SUCCESS")
           {
               return true; //退款成功
           }else{
               return false;//退款失败
           }
       }
       //查询退款信息
       public function searchTkWxPay($transaction_id){
           $data["appid"] = $this->appid;
           $data["mch_id"] = $this->partnerId;
           $data["nonce_str"] = $this->getRandChar(32);
           $data["transaction_id"] = $transaction_id;
           //按照参数名ASCII字典序排序并且拼接API密钥生成签名
           $data["sign"] = $this->getSign($data);
           //配置xml最终得到最终发送的数据
           $xml = $this->arrayToXml($data);
           $response = $this->postXmlCurl($xml, self::TK_URL);
           //将微信返回的结果xml转成数组
           $params = $this->xmlstr_to_array($response);
           $params["package"] = "Sign=WXPay";

           if($params['return_code'] == "SUCCESS" && $params['result_code'] == "SUCCESS")
           {
               return true; //退款成功
           }else{
               return false;//退款失败
           }
       }
}


本文为够意思原创文章,转载无需和我联系,但请注明来自够意思博客blog.go1s.cn:够意思博客 » TP5.0微信APP支付退款

加载中~