zoukankan      html  css  js  c++  java
  • 微信小程序支付、退款

    本文中使用的编程语言是PHP,PHP框架是thinkPHP3.2.3.

    一、支付类,含有支付、退款等接口。

    <?php
    /**
     * 微信支付类
     * Created by PhpStorm.
     * User: 科技
     * Date: 2020/4/26
     * Time: 10:30
     */
    
    namespace Api;
    
    
    class WxPayController
    {
        /**
         * 小程序appid
         * @var string
         */
        private $appId;
    
        /**
         * 小程序secret
         * @var string
         */
        private $appSecret;
    
        /**
         * 商户平台商户id
         * @var string
         */
        private $mchId;
    
        /**
         * 商户平台密钥key
         * @var string
         */
        private $key;
    
        /**
         * 支付回掉地址
         * @var string
         */
        private $notifyUrl;
    
        /**
         * 获取用户唯一标识open_id api 接口地址
         * @var string
         */
        private $code2SessionApiUrl;
    
        /**
         * 统一下单api 接口地址
         * @var string
         */
        private $unifiedOrderApiUrl;
    
        /**
         * 错误信息
         * @var string
         */
        public $errorInfo;
    
        /**
         * WxPay constructor.
         */
        public function __construct()
        {
            $this->appId = C('APP_ID');
            $this->appSecret = C("APP_SECRET");
            $this->mchId = C("MCH_ID");
            $this->key = C("KEY");
            $this->notifyUrl = (isset($_SERVER['REQUEST_SCHEME']) ? ($_SERVER['REQUEST_SCHEME'] . '://') : '') . $_SERVER['HTTP_HOST'].'/Api/Pay/notifyPay.html';
            $this->code2SessionApiUrl = 'https://api.weixin.qq.com/sns/jscode2session';
            $this->unifiedOrderApiUrl = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        }
    
        /**
         * 添加错误信息
         * @param $err
         */
        public function setError($err)
        {
            $this->errorInfo = $err;
        }
    
        /**
         * 获取错误信息
         * @return string
         */
        public function getError()
        {
            return $this->errorInfo;
        }
    
        /**
         * 获取支付签名
         * @param $open_id
         * @param $order_no
         * @param $money
         * @return array
         */
        public function paySign($open_id, $order_no, $money)
        {
            if (!$open_id || !$order_no || !$money) {
                $this->setError('参数错误');
                return [];
            }
    
            $prepay_id = $this->unifiedorder($open_id, $order_no, $money);
    
            if (!$prepay_id) {
                return [];
            }
    
            $params = array(
                'appId' => $this->appId,
                'timeStamp' => time(),
                'nonceStr' => encrypt(16),
                'package' => 'prepay_id=' . $prepay_id,
                'signType' => 'MD5',
            );
    
            $params['paySign'] = $this->getSign($params);
    
            return $params;
        }
    
        /**
         * 异步签名验证
         * @param $data
         * @return bool
         */
        public function checkNotifySign($data)
        {
            if (!$data) {
                return false;
            }
    
            $sign = $data['sign'];
    
            unset($data['sign']);
    
            if ($sign == $this->getSign($data)) {
                return true;
            }
    
            return false;
        }
    
        /**
         * 异步回调处理成功时返回内容
         * @param $msg
         * @return string
         */
        public function notifyReturnSuccess($msg = 'OK')
        {
            return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[' . $msg . ']]></return_msg></xml>';
        }
    
        /**
         * 异步回调处理失败时返回内容
         * @param $msg
         * @return string
         */
        public function notifyReturnFail($msg = 'FAIL')
        {
            return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[' . $msg . ']]></return_msg></xml>';
        }
    
        /**
         * 统一下单
         * @param $open_id
         * @param $order_no
         * @param $money
         * @return string
         */
        private function unifiedOrder($open_id, $order_no, $money)
        {
            $params = [
                'appid' => $this->appId,
                'mch_id' => $this->mchId,
                'nonce_str' => encrypt(16),
                'body' => '食品-订单', //商品简单描述
                'out_trade_no' => $order_no, //商户系统内部订单号
                'total_fee' => priceyuantofen($money), //订单总金额,单位为分
                'spbill_create_ip' => get_client_ip(), //用户端ips
                'notify_url' => $this->notifyUrl, //通知地址
                'trade_type' => 'JSAPI', //交易类型
                'openid' => $open_id, //用户标识
            ];
    
            $params['sign'] = $this->getSign($params);
    
            $xmlData = arrayToXml($params);
    
    
            $returnXml = postXml($xmlData, $this->unifiedOrderApiUrl);
    
            $returnArr = xmlToArray($returnXml);
    
    
            if ($returnArr['return_code'] == 'FAIL') {
    
                $this->setError($returnArr['return_msg']);
                return '';
            }
    
            return $returnArr['prepay_id'];
    
        }
    
        /**
         * 获取签名
         * @param $params
         * @return string
         */
        private function getSign($params)
        {
            if (!$params) {
                return '';
            }
    
            //step1: 排序
            ksort($params);
    
            //step2:格式化参数
            $paramUrl = formatParams($params, false);
    
            //step3:追加key
            $paramUrl = $paramUrl . '&key=' . $this->key;
    
            //step4: md5加密
            $paramUrl = md5($paramUrl);
    
            //step5:转化为大写
            $sign = strtoupper($paramUrl);
    
            return $sign;
        }
    
    
        /*
         * 查询支付结果
         * @param $params
         * @return string
         *
         * */
        public function query($out_trade_no)
        {
            //对外暴露的退款接口
            $result = $this->wxQueryApi($out_trade_no);
            return $result;
        }
    
    
        /*
         * 查询支付结果
         * */
        private function wxQueryApi($out_trade_no)
        {
            //通过微信api进行退款流程
            $parma = array(
                'appid' => $this->appId,
                'mch_id' => $this->mchId,
                'nonce_str' => encrypt(16),
                'out_trade_no' => $out_trade_no,
            );
    
            $parma['sign'] = $this->getSign($parma);
            $xmldata = arrayToXml($parma);
    
            $returnXml = postXml($xmldata, 'https://api.mch.weixin.qq.com/pay/orderquery');
            $result = xmlToArray($returnXml);
            return $result;
        }
    
    
        /*
         * 微信退款
         * @param $params
         * @return string
         *
         * */
        public function refund($out_trade_no, $total_fee, $refund_fee, $out_refund_no)
        {
            //对外暴露的退款接口
            $result = $this->wxRefundApi($out_trade_no, $total_fee, $refund_fee, $out_refund_no);
            return $result;
        }
    
        /*
         * 退款接口
         * */
        private function wxRefundApi($out_trade_no, $total_fee, $refund_fee, $out_refund_no)
        {
            //通过微信api进行退款流程
            $parma = array(
                'appid' => $this->appId,
                'mch_id' => $this->mchId,
                'nonce_str' => encrypt(16),
                'out_refund_no' => $out_refund_no,
                'out_trade_no' => $out_trade_no,
                'total_fee' => priceyuantofen($total_fee),
                'refund_fee' => priceyuantofen($refund_fee),
                'notify_url' => (isset($_SERVER['REQUEST_SCHEME']) ? ($_SERVER['REQUEST_SCHEME'] . '://') : '') . $_SERVER['HTTP_HOST'].'/Api/Pay/notifyRefund.html',
            );
    
            $parma['sign'] = $this->getSign($parma);
            $xmldata = arrayToXml($parma);
            $xmlresult = curl_post_ssl('https://api.mch.weixin.qq.com/secapi/pay/refund', $xmldata,  '/apiclient_cert.pem', '/apiclient_key.pem');
    
            $result = xmlToArray($xmlresult);
            return $result;
        }
    
    
        /*
      * 查看微信退款
      * @param $params
      * @return string
      *
      * */
        public function seeRefund($order_number)
        {
            //对外暴露的退款接口
            $result = $this->wxSeeRefundApi($order_number);
            return $result;
        }
    
    
        /*
         * 查看微信退款接口
         * */
        private function wxSeeRefundApi($order_number)
        {
            //通过微信api进行退款流程
            $parma = array(
                'appid' => $this->appId,
                'mch_id' => $this->mchId,
                'nonce_str' => encrypt(16),
                'out_trade_no' => $order_number,
    
            );
    
            $parma['sign'] = $this->getSign($parma);
            $xmldata = arrayToXml($parma);
    
            $returnXml = postXml($xmldata, 'https://api.mch.weixin.qq.com/pay/refundquery');
    
            $result = xmlToArray($returnXml);
            return $result;
        }
    
    
    
    }
    
    

    二、以上方法中所用的一些工具方法。

    
    /**
     * PHP精确计算  主要用于货币的计算用
     * @param $n1 第一个数
     * @param $symbol 计算符号 + - * / %
     * @param $n2 第二个数
     * @param string $scale 精度 默认为小数点后两位
     * @return  string
     */
    
    function pricecalc($n1, $symbol, $n2, $scale = '2')
    {
        $res = "";
        switch ($symbol) {
            case "+"://加法
                $res = bcadd($n1, $n2, $scale);
                break;
            case "-"://减法
                $res = bcsub($n1, $n2, $scale);
                break;
            case "*"://乘法
                $res = bcmul($n1, $n2, $scale);
                break;
            case "/"://除法
                $res = bcdiv($n1, $n2, $scale);
                break;
            case "%"://求余、取模
                $res = bcmod($n1, $n2, $scale);
                break;
            default:
                $res = "";
                break;
        }
        return $res;
    }
    
    /**
     * 价格由元转分(用于微信支付单位转换)
     * @param $price 金额
     * @return int
     */
    function priceyuantofen($price)
    {
        $price = intval(pricecalc(100, "*", $price));
        return $price;
    }
    
    /**
     * 价格由分转元
     * @param $price 金额
     * @return float
     */
    function pricefentoyuan($price)
    {
        $price = pricecalc(priceformat($price), "/", 100);
        return $price;
    }
    
    /**
     * 价格格式化
     *
     * @param int $price
     * @return string    $price_format
     */
    function priceformat($price)
    {
        $price_format = number_format($price, 2, '.', '');
        return $price_format;
    
    }
    /**
     * array to xml
     * @param $arr
     * @return string
     */
    function arrayToXml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_array($val)) {
                $xml .= "<" . $key . ">" . arrayToXml($val) . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }
    
    /**
     * xml to array
     * @param $xml
     * @return mixed
     */
    function xmlToArray($xml)
    {
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
    
        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
    
        $val = json_decode(json_encode($xmlstring), true);
    
        return $val;
    }
    
    /**
     * 格式化参数 array to key1=valve1&key2=value2
     * @param $params
     * @param $url_encode
     * @return string
     */
    function formatParams($params, $url_encode)
    {
        if (!$params) {
            return '';
        }
    
        $paramUrl = "";
    
        ksort($params);
    
        foreach ($params as $k => $v) {
            if ($url_encode) {
                $v = urlencode($v);
            }
    
            $paramUrl .= $k . "=" . $v . "&";
        }
    
        if (strlen($paramUrl) > 0) {
            $paramUrl = substr($paramUrl, 0, strlen($paramUrl) - 1);
        }
    
        return $paramUrl;
    }
    
    /**
     * curl post xml
     * @param $xml 参数
     * @param $url 请求地址
     * @param int $second 设置超时
     * @return mixed
     */
    function postXml($xml, $url, $second = 60)
    {
        $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); //严格校验
        //设置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_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
        curl_setopt($ch, CURLOPT_TIMEOUT, 40);
        set_time_limit(0);
    
        //运行curl
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }
    
    //需要使用证书的请求
    //file_cert_pem,file_key_pem两个退款必须的文件
    function curl_post_ssl($url, $vars, $file_cert_pem, $file_key_pem, $second = 30, $aHeader = array())
    {
        $ch = curl_init();
        //超时时间
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //这里设置代理,如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '10.206.30.98');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    
        //以下两种方式需选择一种
    
        //第一种方法,cert 与 key 分别属于两个.pem文件
        //默认格式为PEM,可以注释
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
    //        curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/cert.pem');
        curl_setopt($ch, CURLOPT_SSLCERT, __DIR__.$file_cert_pem);
        //默认格式为PEM,可以注释
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
    //        curl_setopt($ch,CURLOPT_SSLKEY,getcwd().'/private.pem');
        curl_setopt($ch, CURLOPT_SSLKEY, __DIR__.$file_key_pem);
    
        //第二种方式,两个文件合成一个.pem文件
    //        curl_setopt($ch, CURLOPT_SSLCERT, getcwd() . '/all.pem');
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
        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);
    //            echo "call faild, errorCode:$error
    ";
    
    //        echo "curl出错,错误码:$error";
            curl_close($ch);
            return false;
        }
    }
    

    要说明的是退款接口wxRefundApi调用的curl_post_ssl方法需要传两个以pem结尾的文件,这两个文件需要传绝对路径,根据代码中的写法,只有把这两个文件放到curl_post_ssl所在文件同一级目录下面,才能正确的找到。

    三、支付回调类。

    <?php
    /**
     * Created by PhpStorm.
     * User: 科技
     * Date: 2020/4/26
     * Time: 10:52
     */
    
    namespace ApiController;
    
    use ApiWxPayController;
    use CommonModelOrderModel;
    
    
    class PayController
    {
    
        /**
         *   状态 0已取消 1已下单 2已支付 3用户申请退款 4订单已完成 5商家同意退款  6微信平台接受退款异步返回申请成功 7异步返回退款申请失败  8统一下单异步返回支付失败  9退款成功  10统一下单异步返回成功
         */
        const STATUS_WAITING = 1;
        const STATUS_FAILED = 2;
        const STATUS_SUCCESS = 3;
        const STATUS_LOSE = 4;
        const STATUS_OTHER = 5;
        const STATUS_PROCESSING = 6;
    
        /**
         * 错误信息
         * @var string
         */
        public $errorInfo;
    
        /**
         * 小程序支付异步回调
         *
         */
        public function notifyPay()
        {
    
            $xml = file_get_contents('php://input', 'r');
            $resData = xmlToArray($xml);
            $wxPay = new WxPayController();
    
            M('pay_log')->add(['type'=>1,'data'=>json_encode($resData),'create_time'=>time()]);
    
            if ($wxPay->checkNotifySign($resData)) {
    
    
                $result = $this->updateNotifyPayOrder($resData['return_code'], $resData['openid'], $resData['out_trade_no'], pricefentoyuan($resData['total_fee']), $resData['transaction_id'], $resData);
    
                if ($result) {
                    $resStr = $wxPay->notifyReturnSuccess();
                } else {
                    $resStr = $wxPay->notifyReturnFail($this->errorInfo);
                }
            } else {
                $resStr = $wxPay->notifyReturnFail('签名验证失败');
            }
            echo $resStr;
        }
    
    
        /**
         * 支付成功修改数据库
         * @param $result 支付状态
         * @param $open_id open_id
         * @param $out_trade_no 客户订单号
         * @param $money 金额
         * @param $wx_trade_no 微信支付订单号
         * @param $data
         * @return bool
         * @throws Exception
         */
        public function updateNotifyPayOrder($result, $open_id, $out_trade_no, $money, $wx_trade_no, $data)
        {
    
            //记录异步通知日志
    
    
            //订单查询
    
            $orderInfo = (new orderModel())->getorderInfo(['order_number' => $out_trade_no], 'id,order_number,shop_id,pay_money,status,user_id');
    
            if (empty($orderInfo)) {
                $this->errorInfo = '订单不存在';
                return false;
    
            }
    
    
            //如果订单状态为成功直接返回
            if (in_array($orderInfo['status'],[2,10,4])) {
                return true;
            }
    
    
            //订单支付状态为未处理
            if ($orderInfo['status'] == 1 || $orderInfo['status'] == 8) {
    
                //验证金额
                if (bccomp($orderInfo['pay_money'],$money,2)) {
                    $this->errorInfo = '订单金额有误';
                    return false;
                }
    
    
                //判断订单支付状态
                if ($result == 'SUCCESS') {
    
                    $model = new OrderModel();
    
                    //开启事务处理机制
                    $model->startTrans();
    
                    //修改订单状态为成功
                    $res_success = (new orderModel())->editOrder(['status' => 10], ['id' => $orderInfo['id']]);
    
                    if ($res_success) {
                        //如果都成立就提交
                        $model->commit();
    
                        return true;
                    }else {
    
                        //先将原先的预处理进行回滚
                        $model->rollback();
                        $this->errorInfo = '订单状态修改失败';
                        return false;
                    }
                } else {
    
                    //修改订单状态为支付失败
                    $res = (new orderModel())->editOrder(['status' => 8], ['id' => $orderInfo['id']]);
    
                    if (!$res) {
    
                        $this->errorInfo = '订单状态修改失败';
                        return false;
    
                    }
    
                }
            }
    
            $this->errorInfo = '订单处理失败!';
            return false;
        }
    
    
    
    
        /**
         * 小程序申请退款异步回调
         *
         */
        public function notifyRefund()
        {
            $xml = file_get_contents('php://input', 'r');
            $resData = xmlToArray($xml);
            $wxPay = new WxPayController();
    
            M('pay_log')->add(['type'=>2,'data'=>json_encode($resData),'create_time'=>time()]);
    
            if ($wxPay->checkNotifySign($resData)) {
    
    
                $result = $this->updateNotifyRefundOrder($resData['return_code'], $resData['out_trade_no'], pricefentoyuan($resData['total_fee']), $resData['transaction_id'], $resData);
    
                if ($result) {
                    $resStr = $wxPay->notifyReturnSuccess();
                } else {
                    $resStr = $wxPay->notifyReturnFail($this->errorInfo);
                }
            } else {
                $resStr = $wxPay->notifyReturnFail('签名验证失败');
            }
            echo $resStr;
        }
    
        /**
         * 退款成功修改数据库
         * @param $result 支付状态
         * @param $open_id open_id
         * @param $out_trade_no 客户订单号
         * @param $money 金额
         * @param $wx_trade_no 微信支付订单号
         * @param $data
         * @return bool
         * @throws Exception
         */
        public function updateNotifyRefundOrder($result, $out_trade_no, $money, $wx_trade_no, $data)
        {
    
            //记录异步通知日志
    
    
            //订单查询
    
            $orderInfo = (new orderModel())->getorderInfo(['order_number' => $out_trade_no], 'id,order_number,pay_money,status,user_id');
    
            if (empty($orderInfo)) {
                $this->errorInfo = '订单不存在';
                return false;
    
            }
    
    
            //如果订单状态为成功直接返回
            if (in_array($orderInfo['status'],[5,6,4])) {
                return true;
            }
    
            //订单支付状态为用户已经申请退款
            if ($orderInfo['status'] == 3 ||$orderInfo['status'] == 7) {
    
    
                //验证金额
                if (bccomp($orderInfo['pay_money'],$money,2)) {
                    $this->errorInfo = '订单金额有误';
                    return false;
                }
    
    
                //判断订单支付状态
                if ($result == 'SUCCESS') {
    
                    $model = new OrderModel();
    
                    //开启事务处理机制
                    $model->startTrans();
    
                    //修改订单状态为成功
                    $res_success = (new orderModel())->editOrder(['status' => 6], ['id' => $orderInfo['id']]);
    
                    if ($res_success) {
                        //如果都成立就提交
                        $model->commit();
    
                        return true;
                    }else {
    
                        //先将原先的预处理进行回滚
                        $model->rollback();
                        $this->errorInfo = '订单状态修改失败';
                        return false;
                    }
    
    
                } else {
    
    
                    //修改订单状态为退款申请失败
                    $res = (new orderModel())->editOrder(['status' => 7], ['id' => $orderInfo['id']]);
    
                    if (!$res) {
    
                        $this->errorInfo = '订单状态修改失败';
                        return false;
    
                    }
    
                }
            }
    
            $this->errorInfo = '订单处理失败!';
            return false;
        }
    
    
    }
    

    四、接口调用。

      /**
         * 订单支付,统一下单接口,获取支付签名
         * @access public
         * @param string $orderId 订单id
         * @param string $token
         * @return array
         */
        public function pay()
        {
    
            try {
    
                $orderInfo = $this->orderHandelCheck($this->data['orderId']);
    
                if ($orderInfo['status'] != 1 ) {
                    $this->json(201, '订单不是待支付状态');
    
                }
    
                //调用支付类
                $wxPay = new WxPayController();
    
                //获取签名
                $paySign = $wxPay->paySign($this->openid, $orderInfo['order_number'], $orderInfo['pay_money']);
    
                if (!$paySign) {
                    $this->json(201, $wxPay->getError());
                }
    
                $this->json(200, '请求成功', $paySign);
    
    
            } catch (Exception $e) {
    //            $this->json($e->getMessage());
                $this->json(0);
            }
        }
    
     /**
         * 查询订单支付结果,以进行下一步动作
         * @access public
         * @param int $orderId 订单id
         * @param string $token
         * @return array
         */
        public function payResult()
        {
    
            try {
    
                $orderInfo = $this->orderHandelCheck($this->data['orderId']);
    
                if (!in_array($orderInfo['status'],[1,10])) {
                    if($orderInfo['status']==8){
                        $this->json(201, '统一下单接口返回失败');
                    }
    
                    $this->json(201, '订单不是待支付状态');
    
                }
    
                //调用支付类
                $wxPay = new WxPayController();
    
                //获取查询结果
                $query_res = $wxPay->query($orderInfo['order_number']);
    
                if ($query_res['return_code']=='SUCCESS') {  //请求成功
                if ($query_res['result_code']=='SUCCESS') {  //订单接收成功
                if ($query_res['trade_state']=='SUCCESS') {  //交易状态
    
                    $model = new OrderModel();
    
                    //开启事务处理机制
                    $model->startTrans();
    
    
                    $ModelThink = new ThinkModel(); // 实例化一个model对象 没有对应任何数据表
    
    
                    $sql = sprintf("SELECT `id`,`order_number`,`shop_id`,`create_time`,`pay_money`,`status` FROM `pre_order` WHERE `id` = %s FOR UPDATE ", $orderInfo['id']);//查询语句
    
                    $orderInfo=$ModelThink->query($sql)[0];
    
    
                    //修改订单状态为成功
                    $res_success = (new orderModel())->editOrder(['pay_status' => 1, 'status' => 2], ['id' => $orderInfo['id']]);
    
                    if (!$res_success) {
                        //先将原先的预处理进行回滚
                        $model->rollback();
                        $this->json(201,'订单状态修改失败');
    
                    }
    
                    //保存账户余额表
    
                    $walletModel = new ShopWalletModel();
    
                    $shopWallet = $walletModel->getShopWalletInfo(['shop_id' => $orderInfo['shop_id']], 'account');
    
                    if ($shopWallet) {
    
                        if ($walletModel->editShopWallet(['account' => bcadd($shopWallet['account'], $orderInfo['pay_money'], 2)], ['shop_id' => $orderInfo['shop_id']])) {   //修改钱包金额
    
                            if ((new ShopWalletFlowModel())->addShopWalletFlow(['shop_id' => $orderInfo['shop_id'], 'account' => $orderInfo['pay_money'], 'create_time' => time(), 'order_id' => $orderInfo['id']])) { //添加钱包流水
                                //如果都成立就提交
                                $model->commit();
    
                                $this->json(200, '支付成功');
                            } else {
                                //先将原先的预处理进行回滚
                                $model->rollback();
                                $this->json(201,'商户钱包流水添加失败!');
    
                            }
    
    
                        } else {
                            //先将原先的预处理进行回滚
                            $model->rollback();
                            $this->json(201,'账户信息保存失败!');
    
                        }
    
                    } else {
    
                        //先将原先的预处理进行回滚
    
                        $model->rollback();
                        $this->json(201,'商户钱包有误!');
    
                    }
    
    
                }elseif($query_res['trade_state']=='REFUND'){
                    $this->json(201, '转入退款');
                }elseif($query_res['trade_state']=='NOTPAY'){
                    $this->json(201, '未支付');
                }elseif($query_res['trade_state']=='CLOSED'){
                    $this->json(201, '已关闭');
                }elseif($query_res['trade_state']=='USERPAYING'){
                    $this->json(201, '用户支付中');
                }elseif($query_res['trade_state']=='PAYERROR'){
                    $this->json(201, '支付失败');
                }else{
                    $this->json(201, '交易状态未知');
                }
    
                }else{
                    $this->json(201, $query_res['err_code_des']);
                }
    
                }else{
                    $this->json(201, $query_res['return_msg']);
                }
    
    
    
    
            } catch (Exception $e) {
                $this->json(0);
            }
        }
    
      /**
         * 请求退款
         * @access public
         * @param string $orderId 订单id
         * @param string $token
         * @return array
         */
    
    public function refund(){
    
       $wxPay = new WxPayController();
    
        //申请退款
        $res_refund = $wxPay->refund($Order_info['order_number'], $Order_info['pay_money'], $Order_info['pay_money'], $Order_info['refund_number']);
    
    
        if ($res_refund['return_code'] == 'SUCCESS') { //接口请求成功
            if ($res_refund['result_code'] == 'SUCCESS') { //退款申请接受成功
    
                if ($model->editOrder(['status' => 5, 'Refund' => ['status' => 2, 'handle_time' => time()]], ['id' => $id], 'Refund')) {
                    $this->success("提交成功,等待微信平台审核通过!", '', true);
    
                }else{
    
                    $this->error("修改订单状态失败,请尽快联系管理员处理!!!", '', true);
                }
    
    
            } else {
    
                $this->error("申请失败,微信接受退款失败," . $res_refund['err_code_des'], '', true);
            }
    
        }else{
            $this->error("请求失败," . $res_refund['return_msg'], '', true);
        }
    
    }
    
     /**
         * 查询订单退款结果,以进行下一步动作
         * @access public
         * @param int $orderId 订单id
         * @param string $token
         * @return array
         */
    function checkRefund(){
    
       $wxPay = new WxPayController();
    
         //退款进度
         $see_res = $wxPay->seeRefund($order_info['order_number']);
    
         if($see_res['return_code']=='SUCCESS'){  //请求成功
    
             if($see_res['result_code']=='SUCCESS'){ //退款申请接收成功
    
             if($see_res['refund_status_0']=='SUCCESS'){ //退款状态
    
                 $model = new OrderModel();
    
                 //由于要计算流水,开启事务
                 //开启事务处理机制
                 $model->startTrans();
    
                 $ModelThink = new ThinkModel(); // 实例化一个model对象 没有对应任何数据表
    
    
                 $sql = sprintf("SELECT `id`,`order_number`,`shop_id`,`create_time`,`pay_money`,`status` FROM `pre_order` WHERE `id` = %s FOR UPDATE ", $order_info['id']);//查询语句
    
                 $order_info=$ModelThink->query($sql)[0];
    
                 if ($model->editOrder(['status' => 9], ['id' => $id])) {
    
                     $walletModel = new ShopWalletModel();
    
                     $shopWallet = $walletModel->getShopWalletInfo(['shop_id' => $order_info['shop_id']], 'account');
    
    
                     if ($shopWallet) {
    
                         if ($walletModel->editShopWallet(['account' => bcsub($shopWallet['account'], $order_info['pay_money'], 2)], ['shop_id' => $order_info['shop_id']])) {
    
                             if ((new ShopWalletFlowModel())->addShopWalletFlow(['shop_id' => $order_info['shop_id'], 'account' => -$order_info['pay_money'], 'create_time' => time(), 'order_id' => $id, 'scene' => 2])) {
                                 //如果都成立就提交
                                 $model->commit();
                                 $this->success("退款成功!", '', true);
    
                             } else {
                                 //先将原先的预处理进行回滚
                                 $model->rollback();
                                 $this->error("流水添加失败,请重试或尽快联系管理员处理!!!", '', true);
                             }
    
    
                         } else {
                             //先将原先的预处理进行回滚
                             $model->rollback();
                             $this->error("钱包操作失败,请重试或尽快联系管理员处理!!!", '', true);
                         }
    
                     } else {
    
                         //先将原先的预处理进行回滚
                         $model->rollback();
                         $this->error("钱包有误,请重试或尽快联系管理员处理!!!", '', true);
                     }
    
    
                 } else {
                     //先将原先的预处理进行回滚
                     $model->rollback();
                     $this->error("修改订单状态失败!请重试或尽快联系管理员处理!!", '', true);
    
                 }
    
    
             }elseif($see_res['refund_status_0']=='REFUNDCLOSE'){
    
                 $this->success("退款关闭,请尽快联系管理员处理!!!", '', true);
             }elseif($see_res['refund_status_0']=='PROCESSING'){
    
                 $this->success("退款处理中!", '', true);
             }elseif($see_res['refund_status_0']=='CHANGE'){
    
                 $this->success("退款退款异常,请尽快联系管理员处理!!!", '', true);
             }
    
    
             }else{
    
    
                 $this->error('退款申请接收失败,'.$see_res['err_code_des'], '', true);
    
             }
         }else{
    
             $this->error('请求失败,'.$see_res['return_msg'], '', true);
    
         }
    
    }
    
    

    接口使用的返回方法:

     protected function json($code=0,$message='',$data=[])
        {
    
            if (empty($message)) {
                //通用提示信息
                switch ($code) {
                    case 200:
                        $message = '请求成功';
                        break;
                    case 0:
                        $message = '请求失败';
                        break;
                    case -2:
                        $message = '参数缺失';
                        break;
                    default:
                        $message = $message;
                        break;
    
                }
            }
    
            return $this -> ajaxReturn(['code' => $code, 'message' => $message, 'data' => $data]);
    
        }
    

    本文参考的主要文章有:

    PHP 微信小程序退款

  • 相关阅读:
    MySQL百万级数据量分页查询方法及其优化
    聊一聊二维码扫描登录原理
    在吗?认识一下JWT(JSON Web Token) ?
    怎样和领导汇报工作,更容易获得升职加薪?谈谈和领导汇报的艺术
    javascript sleep pause 实现
    google 根据地址得ip 并显示
    javascript 闭包
    javascript 对象 telecom
    javascript 对象
    google maker 标注
  • 原文地址:https://www.cnblogs.com/ccdv/p/14188600.html
Copyright © 2011-2022 走看看