微信小程序退款功能

下面是我封装的关于微信小程序退款功能,自己在线上项目中是可以实现功能的,所以分享给大家。

注意以下几点:1.我使用的框架偏原生,所以里面关于mysql的部分写法都是偏原生的,不必在意;

                          2.对于此项功能,我建了一个退款记录表,如图所示:此表用于记录退款记录【可根据实际业务逻辑进行修改】

3.以下功能仅实现退款操作,实际业务逻辑,可根据返回值进行再次进行具体业务逻辑。 


	 /***************************************
     ****************************************
     ***微信小程序退款***********************
     ****************************************
     ****************************************/
    /**
     * 退款
     *
     * 传参:
     * @param outTradeNo stirng 商户退款单号
     * @param totalFee int 订单金额[单位分]
     * @param refundFee int 退款金额[单位分]
     * @param refund_desc string 退款原因
     * @param openid string 用户openid
     * @param appid string 微信小程序appid
     * @param mch_id string 微信商户号
     * @param tableName string 数据表名称
     *
     * 注意事项:
     * 1.交易时间超过一年的订单无法提交退款
     * 2.微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。
     * 【特别注意】*****一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号*****
     * 3.请求频率限制:150qps,即每秒钟正常的申请退款请求次数不超过150次错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次
     * 4.每个支付订单的部分退款次数不能超过50次
     */
    function refund($outTradeNo,$totalFee,$refundFee,$refund_desc,$openid,$appid,$mch_id,$key,$tableName){
        //查询是否有此商户订单号退款失败的记录;如果有,继续使用已有记录中的商户退款单号来进行再次退款;没有,则生成一个新的商户退款单号
        $mysql = new Mysql();
        $row = $mysql->fetchRow("select * from {$tableName} where openid='{$openid}' and out_trade_no='{$outTradeNo}' order by createTime desc ");
        unset($mysql);
        if($row['orderStatus']==3 && $row['cashStatus']==3 && $row['result_code']==2){//表示此商户订单号最近一次业务为退款失败,继续使用之前的商户退款单号
            $outRefundNo = $row['outRefundNo'];
        }else{//表示此商户订单号最近一次业务成功,生成一个新的商户退款单号
            $outRefundNo = "tk".date("YmdHis").time();//商户退款单号
        }
        $parma = array(
            'appid'=>$appid,//小程序ID
            'mch_id'=>$mch_id,//商户号
            'nonce_str'=> $this->getNonceStr(),//随机字符串
            'out_refund_no'=> $outRefundNo,//商户退款单号
            'out_trade_no'=> $outTradeNo,//商户订单号
            'total_fee'=> $totalFee,//订单金额[单位分]
            'refund_fee'=> $refundFee,//退款金额[单位分]
            'refund_desc'=>$refund_desc,//退款原因
        );
        $parma['sign'] = $this->makeSign($parma,$key);
        $data = [
            'openid'=>$openid,
            'outRefundNo'=>$outRefundNo,
            'out_trade_no'=>$outTradeNo,
            'refund_desc'=>$refund_desc,
            'total_fee'=>$totalFee,
            'refund_fee'=>$refundFee,
            'createTime'=>time(),
            'createIp'=>get_client_ip()
        ];
        $mysql = new Mysql();
        $res = $mysql->insert($data,$tableName);//退款记录
        if($res){
            unset($mysql);
            $xmldata = $this->arrToXml($parma);
            $result =$this->curl_post_ssl('https://api.mch.weixin.qq.com/secapi/pay/refund',$xmldata);
            return $result;
        }else{
            unset($mysql);
            jReturn(ErrorCode::INSERT_FAIL, '由于系统原因,退款失败');
        }
    }


    /**
     * 随机字符串
     * @param int $length
     * @return string
     */
    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;
    }
	
	
	    /**
     * 签名
     * @param $data
     * @return string
     */
    function makeSign($data,$key)
    {
        // 关联排序
        ksort($data);
        // 字典排序
        $str = http_build_query($data);
        // 添加商户密钥
        $str .= '&key=' . $key;
        // 清理空格
        $str = urldecode($str);
        $str = md5($str);
        // 转换大写
        $result = strtoupper($str);
        return $result;
    }
	
	
	    /**
     * 数组转XML
     * @param $data
     * @return string
     */
    function arrToXml($data)
    {
        $xml = "<xml>";
        //  遍历组合
        foreach ($data as $k => $v) {
            $xml .= '<' . $k . '>' . $v . '</' . $k . '>';
        }
        $xml .= '</xml>';
        return $xml;
    }
	
	/**
     * [curl_post_ssl 发送curl_post数据]
     * @param  [type]  $url     [发送地址]
     * @param  [type]  $xmldata [发送文件格式]
     * @param  [type]  $second [设置执行最长秒数]
     * @param  [type]  $aHeader [设置头部]
     * @return [type]           [description]
     */
    function curl_post_ssl($url, $xmldata, $second = 30, $aHeader = array())
    {
        $isdir = $_SERVER['DOCUMENT_ROOT'] . "/oio/global/config/cert/oio/";//证书位置;绝对路径
        $ch = curl_init();//初始化curl

        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);//
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');//证书类型
        curl_setopt($ch, CURLOPT_SSLCERT, $isdir . 'apiclient_cert.pem');//证书位置
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');//CURLOPT_SSLKEY中规定的私钥的加密类型
        curl_setopt($ch, CURLOPT_SSLKEY, $isdir . 'apiclient_key.pem');//证书位置
        curl_setopt($ch, CURLOPT_CAINFO, 'PEM');
        curl_setopt($ch, CURLOPT_CAINFO, $isdir . 'rootca.pem');
        if (count($aHeader) >= 1) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);//设置头部
        }
        curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xmldata);//全部数据使用HTTP协议中的"POST"操作来发送


        $data = curl_exec($ch);//执行回话

        if ($data) {
            curl_close($ch);
            return $this->xmlToArray($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、付费专栏及课程。

余额充值