天天看點

微信小程式支付 退款 訂單查詢 退款查詢

基于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;
    }
           

退款

/**
     * 申請退款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配置

微信小程式支付 退款 訂單查詢 退款查詢
微信小程式支付 退款 訂單查詢 退款查詢

繼續閱讀