微信小程序支付 退款 订单查询 退款查询

基于thinkphp5框架

支付

/**
     * 预支付请求接口(POST)
     * @param string $openid openid
     * @param string $body 商品简单描述
     * @param string $order_sn 订单编号
     * @param string $total_fee 金额
     * @return  json的数据
     */
    public function prepay()
    {
        tp_log('预支付请求数据===>' . json_encode($_POST), 'prepay', request()->controller());
        $goods_user = db('tf_goods_user')->where(array('order_no' => $_POST['order_no']))->find();
        $goods = db('tf_goods')->where(array('id' => $goods_user['goods_id']))->find();
        //判断产品的数量
        if (($goods['sales_initial'] - $goods['sales_actual']) <= 0) {
            $return['status'] = 0;
            $return['info'] = '此产品已售完';
            exit(json_encode($return));
        }

        $order_no = $_POST['order_no']; //订单号
        $is_sale = $_POST['is_sale'];
        $openid = $_POST['openid'];
        $goods_name = $_POST['goods_name'];
        $pay_price = $_POST['price'];
        $attach['is_sale'] = $_POST['is_sale'];
        $attach['sale_id'] = $_POST['sale_id'];
        $nonce_str = $this->nonce_str();//随机字符串


        $order_no_ssh = $this->get_orderssh(); //商户订单号
/*注意 订单号为自己创建的订单 和支付没有任何关系 商户订单号是支付单号 用来支付的 每当订单号发起支付 就会有新的商户订单号 
商户订单号是不重复的  发起一次支付就要生成新的商户订单号 要保存下来 
订单表要有对应的支付订单表 而订单号与商户订单号是一对多的关系 订单表最终保存的商户订单号是支付成功的那个 
也可以 没调起支付就根据订单号去修改订单表的商户订单号 只有商户订单号才能发起退款 相当于商户订单号统计记录到订单表里面 退款查询等操作  商户订单号  其实订单生成就不会改数据了 
也可以用订单号改为商户订单号 这样支付 取消 在支付 也可以的
*/
(out_trade_no)

//看文章结尾支付订单表


        //组装支付数据
        $data = [
            'appid' => config('pay.APPID'),
            'mch_id' => config('pay.MCHID'),
            'nonce_str' => $nonce_str,
            'body' => $goods_name,  //商品名称组合
            'attach' => json_encode($attach),
            'out_trade_no' => $order_no_ssh,//$order_no,        //订单号 商户订单号
            'total_fee' => intval($pay_price * 100),
            'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
            'notify_url' => config('pay.NOTIFY_URL'),
            'trade_type' => 'JSAPI',
            'openid' => $openid
        ];

        //订单支付表创建订单支付数据
        $p_o_data['createtime'] = time();
        $p_o_data['order_no'] = $order_no;
        $p_o_data['order_no_ssh'] = $order_no_ssh;
        $p_o_data['ready'] = json_encode($data);
        $p_o_return = db('tf_pay_order')->insert($p_o_data);
        if(!$p_o_return){
            //失败
            $return['status'] = -1;
            $return['info'] = $p_o_data;
            exit(json_encode($return));
        }

        // 获取签名
        $sign = $this->makeSign($data);
        $data['sign'] = $sign;
        $xml = $this->toXml($data);
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; //发起支付接口链接
        //发起预支付请求
        $prepay_return_reslut_xml = $this->http_request($url, $xml);
        $xml_to_arr = $this->fromXml($prepay_return_reslut_xml);
        $return_result = json_encode($xml_to_arr, true);
        tp_log('预支付请求返回数据array===>' .$return_result , 'prepay', request()->controller());
        //记录预支付返回信息
        db('tf_goods_order')->where(array('order_no' => $order_no))
            ->update(array(
                'go_pay' => $return_result,
                'updatetime' => time(),
                'updateuser' => $openid
            ));
        if($xml_to_arr['return_code'] == 'SUCCESS' && $xml_to_arr['result_code'] == 'SUCCESS'){
            //成功

            $time = time();
            //临时数组用于签名
            $tmp = [
                'appId' => config('pay.APPID'),
                'nonceStr' => $nonce_str,
                'package' => 'prepay_id='.$xml_to_arr['prepay_id'],
                'signType' => 'MD5',
                'timeStamp' => "$time",
            ];
            $data['timeStamp'] = "$time";//时间戳
            $data['nonceStr'] = $nonce_str;//随机字符串
            $data['signType'] = 'MD5';//签名算法,暂支持 MD5
            $data['package'] = 'prepay_id='.$xml_to_arr['prepay_id'];//统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=*
            $data['paySign'] = $this->makeSign($tmp);//签名,具体签名方案参见微信公众号支付帮助文档;$data['out_trade_no'] = $out_trade_no;


            $return['status'] = 1;
            $return['info'] = $data;
        }else{
            //失败
            $return['status'] = -1;
            $return['info'] = $xml_to_arr;
        }
        exit(json_encode($return));
    }

 //curl请求
    public function http_request($url, $data = null, $headers = array())
    {
        $curl = curl_init();
        if (count($headers) >= 1) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        }
        curl_setopt($curl, CURLOPT_URL, $url);


        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);


        if (!empty($data)) {
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($curl);
        curl_close($curl);
        return $output;
    }

#### 小程序

```php
//去支付
  goPay: function(t) {
    var that = this
    if (that.data.info.price == 0) {
      that.free_zero(t)
      return 
    }
    var param = {
      total_fee: that.data.info.price,
      openid: app.globalData.openid,
      token: app.globalData.token,
      order_no: that.data.info.order_no,
      price: that.data.info.price,
      goods_name: that.data.info.goods_name,
    };
    
    
    wx.showLoading({
      title: "支付发起中...",
      duration: 1000
    });
    var url = config.prepaid; //自己的接口地址
    util.postrequest(url, param, function(res) {
      console.log(res)
      if (res.data.status == 2) {
        app.gettoken(function(t) {
          if (app.globalData.hasLogin) {
            that.goPay(t)
          } else {
            app.checkuser()
          }
        });
      } else if (res.data.status == 1) {
      //主要是这个部分
        wx.requestPayment({
          timeStamp: res.data.info.timeStamp,
          nonceStr: res.data.info.nonceStr,
          package: res.data.info.package,
          signType: res.data.info.signType,
          paySign: res.data.info.paySign,
          success: function(t) {
            console.log("支付成功")
          },
          fail: function(t) {
            console.log("支付失败")
            wx.showLoading({
              title: "支付失败",
              icon: "success",
              duration: 1500
            })
          },
          complete: function(t) {
            console.log("支付完成")
            if (t.errMsg == "requestPayment:ok") {
              wx.showToast({
                title: "支付成功",
                icon: "success",
                duration: 1500
              })
              setTimeout(function() {
                if (app.globalData.hasLogin) {
                  wx.redirectTo({
                    url: "/pages/mypolicy/mypolicy?tab=2"
                  })
                } else {
                  app.checkuser();
                }
              }, 1500);
            }  
          }
        })
      } else {
        wx.showToast({
          // title: res.data.info ? res.data.info:'请重新下单或重新支付',
          title: '请重新下单或重新支付',
          icon: "none",
          duration: 2000
        });
      }
    })
  },

## 退款

```php
/**
     * 申请退款API
     * @param $transaction_id
     * @param  double $total_fee 账单总金额
     * @param  double $refund_fee 退款金额
     * @return bool
     * @throws BaseException
     */
    public function refund()
    {

        $transaction_id = '4200000712202007274705432240';
        $total_fee = '0.01';
        $refund_fee = '0.01';
        // 当前时间
        $time = time();
        // 生成随机字符串
        $nonceStr = md5($time . $transaction_id . $total_fee . $refund_fee);
        // API参数
        $params = [
            'appid' => config('pay.APPID'),
            'mch_id' => config('pay.MCHID'),
            'nonce_str' => $nonceStr,
            'transaction_id' => $transaction_id,
            'out_refund_no' => $time,
            'total_fee' => $total_fee * 100,
            'refund_fee' => $refund_fee * 100,
            'notify_url' => config('pay.NOTIFY_URL_REFUND'),//退款回调地址
        ];
        // 生成签名
        $params['sign'] = $this->makeSign($params);

        tp_log('退款请求数据===>' . json_encode($params), 'refund', request()->controller());

        // 请求API
        $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
        $result = $this->post($url, $this->toXml($params), true, $this->getCertPem());
        $prepay = $this->fromXml($result);
        // 请求失败
        if (empty($result)) {
            throw new BaseException(['msg' => '微信退款api请求失败']);
        }
        // 格式化返回结果
        $prepay = $this->fromXml($result);
        tp_log('退款返回数据===>' . json_encode($prepay), 'refund', request()->controller());
        // 请求失败
//        if ($prepay['return_code'] === 'FAIL') {
//            throw new BaseException(['msg' => 'return_msg: ' . $prepay['return_msg']]);
//        }
//        if ($prepay['result_code'] === 'FAIL') {
//            throw new BaseException(['msg' => 'err_code_des: ' . $prepay['err_code_des']]);
//        }
        return true;
    }
    /**
     * 模拟POST请求
     * @param $url
     * @param array $data
     * @param bool $useCert
     * @param array $sslCert
     * @return mixed
     */
    public function post($url, $data = [], $useCert = false, $sslCert = [])
    {
        $header = [
            'Content-type: application/json;'
        ];
        $curl = curl_init();
                //如果有配置代理这里就设置代理
//        if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"
//            && WxPayConfig::CURL_PROXY_PORT != 0){
//            curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);
//            curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);
//        }

        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_POST, TRUE);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        if ($useCert == true) {
            // 设置证书:cert 与 key 分别属于两个.pem文件
            curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM');
            curl_setopt($curl, CURLOPT_SSLCERT, $sslCert['certPem']);
            curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($curl, CURLOPT_SSLKEY, $sslCert['keyPem']);
        }
        $result = curl_exec($curl);
        curl_close($curl);
        return $result;
    }

订单查询

/**
     * 订单查询
     * @param $out_trade_no
     * @return mixed
     * @throws BaseException
     */
    public function orderquery()
    {
         $transaction_id = '4200000712202007274705432240';//微信订单号
        // 当前时间
        $time = time();
        // 生成随机字符串
        $nonce_str = md5($time . mt_rand(00000,99999));
        //API参数
        $params = [
            'appid'        => config('pay.APPID'),             //公众号ID
            'mch_id'       => config('pay.MCHID'),  //商户号
            'transaction_id' => $transaction_id,            //商户订单号
            'nonce_str'    => $nonce_str,             // 随机字符串
        ];
        //生成签名
        $params['sign'] = $this->makeSign($params);
        //请求API
        $url = 'https://api.mch.weixin.qq.com/pay/orderquery';
        $result = $this->post($url, $this->toXml($params));
        $prepay = $this->fromXml($result);
        // 请求失败
        if ($prepay['return_code'] === 'FAIL') {
            throw new BaseException(['msg' => $prepay['return_msg'], 'code' => 0]);
        }
        if ($prepay['result_code'] === 'FAIL') {
            throw new BaseException(['msg' => $prepay['err_code_des'], 'code' => 0]);
        }
        return $prepay;
    }

退款查询

/**
     * 退款查询
     * @param $out_trade_no
     * @return mixed
     * @throws BaseException
     */
    public function refundquery()
    {
        $transaction_id = '4200000712202007274705432240';//微信订单号
        // 当前时间
        $time = time();
        // 生成随机字符串
        $nonce_str = md5($time . mt_rand(00000,99999));
        //API参数
        $params = [
            'appid'        => config('pay.APPID'),             //公众号ID
            'mch_id'       => config('pay.MCHID'),  //商户号
            'transaction_id' => $transaction_id,            //商户订单号
            'nonce_str'    => $nonce_str,             // 随机字符串
        ];
        //生成签名
        $params['sign'] = $this->makeSign($params);
        //请求API
        $url = 'https://api.mch.weixin.qq.com/pay/refundquery';
        $result = $this->post($url, $this->toXml($params));
        $prepay = $this->fromXml($result);
        dump($prepay);die;
        // 请求失败
        if ($prepay['return_code'] === 'FAIL') {
            throw new BaseException(['msg' => $prepay['return_msg'], 'code' => 0]);
        }
        if ($prepay['result_code'] === 'FAIL') {
            throw new BaseException(['msg' => $prepay['err_code_des'], 'code' => 0]);
        }
        return $prepay;
    }

支付成功,进行回调

public function index()
    {
        $data = file_get_contents('php://input');
        $data = $this->FromXml($data);
        tp_log('支付回调数据===>' . json_encode($data), 'index', request()->controller());
        // 保存微信服务器返回的签名sign
        $data_sign = $data['sign'];
        // sign不参与签名算法
        unset($data['sign']);
        $sign = $this->makeSign($data);//回调验证签名

        $str_success = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        $str_error = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';

        if (($sign === $data_sign) && ($data['return_code'] == 'SUCCESS') && ($data['result_code'] == 'SUCCESS')) {
       // 支付成功 进行你的逻辑处理
        }
echo $str_success;//str_error  告知微信 你已的逻辑处理完毕 不用再推送或再次推送你结果
   }

退款成功 进行回调

/*
     * 小程序 退款结果通知
     */
    public function refund(){

        $data = file_get_contents('php://input');
        $data = $this->FromXml($data);
        tp_log('退款回调数据===>' . json_encode($data), 'refund', request()->controller());

        //对加密的字符串解密
        $req_info_xml = openssl_decrypt(base64_decode($data['req_info']), 'aes-256-ecb', md5(config('pay.KEY')),OPENSSL_RAW_DATA);
        $req_info = $this->FromXml($req_info_xml);

//        // 保存微信服务器返回的签名sign
//        $data_sign = $data['sign'];
//        // sign不参与签名算法
//        unset($data['sign']);
//        $sign = $this->makeSign($data);//回调验证签名
//
//        $str_success = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
//        $str_error = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
//
//
//
//        if (($sign === $data_sign) && ($data['return_code'] == 'SUCCESS') && ($data['result_code'] == 'SUCCESS')) {
//            tp_log('退款成功===>', 'refund', request()->controller());
//            //去修改订单的状态 和支付回调的一样 修改成功 告知微信 不在推送
//        }
    }

用到的方法

/**
     * 生成签名
     * @param $values
     * @return string 本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
     */
    private function makeSign($values)
    {
        //签名步骤一:按字典序排序参数
        ksort($values);
        $string = $this->toUrlParams($values);
        //签名步骤二:在string后加入KEY
        $string = $string . '&key=' . config('pay.KEY');
        //签名步骤三:MD5加密
        $string = md5($string);
        //签名步骤四:所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }
    private function ToUrlParams($array)
    {
        $buff = "";
        foreach ($array as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }
        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     * 输出xml字符
     * @param $values
     * @return bool|string
     */
    private function toXml($values)
    {
        if (!is_array($values)
            || count($values) <= 0
        ) {
            return false;
        }
        $xml = "<xml>";
        foreach ($values as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * 将xml转为array
     * @param $xml
     * @return mixed
     */
    private function fromXml($xml)
    {
        // 禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    /**
     * 获取cert证书文件
     * @return array
     * @throws BaseException
     */
    private function getCertPem()
    {
//        if (empty($this->config['cert_pem']) || empty($this->config['key_pem'])) {
//            throw new BaseException(['msg' => '请先到后台小程序设置填写微信支付证书文件']);
//        }
        // cert目录
        $filePath = EXTEND_PATH.'wxpay/cert/';
        return [
            'certPem' => $filePath . 'apiclient_cert.pem',
            'keyPem' => $filePath . 'apiclient_key.pem'
        ];
    }
/**
     * 生成商户订单号
     */
    public function get_orderssh()
    {
        return date("YmdHis") . mt_rand(10000000, 99999999);
    }

证书路径
在这里插入图片描述
config配置
在这里插入图片描述
在这里插入图片描述
支付订单表

CREATE TABLE `t_tf_pay_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_no` varchar(255) DEFAULT NULL COMMENT '订单号',
  `order_no_ssh` varchar(255) DEFAULT NULL COMMENT '商户订单号',
  `param` text COMMENT '支付返回全部参数、数据',
  `transaction_id` varchar(255) DEFAULT NULL COMMENT '微信支付流水号',
  `createtime` int(11) DEFAULT NULL COMMENT '创建时间',
  `updatetime` int(11) DEFAULT NULL COMMENT '修改时间',
  `ready` text COMMENT '申请预支付数据',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COMMENT='支付订单表';
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值