天天看點

支付--微信公衆号

公衆号支付的接口文檔:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

先寫好必要的方法:

    const API_UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    const API_ORDER_QUERY   = "https://api.mch.weixin.qq.com/pay/orderquery";

    const API_CLOSE_ORDER   = "https://api.mch.weixin.qq.com/pay/closeorder";

    //簽名算法

    public static  function getSign($param, $appKey){

        $param = array_filter($param);

        ksort($param);

        $stringA = "";

        foreach ($param as $key=>$value){

            if (!empty($value)){//值為空則不參與簽名

                $stringA .= $key."=".$value."&";

            }

        }

        $stringSignTemp = $stringA. "key=" .$appKey;

        return strtoupper(md5($stringSignTemp));

    }

    //生成随機數

    public static  function getNonceStr(){

        $nonce_str = '';

        $max    = mt_rand(16,32);

        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

        for ( $i = 0; $i < $max; $i++ ) {

            $nonce_str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);

        }

        return $nonce_str;

    }

    //數組轉XML

    public static 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;

    }

    //XML轉數組

    public static function xmlToArray($xml)

    {

        //禁止引用外部xml實體

        libxml_disable_entity_loader(true);

        $values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);

        return $values;

    }

    public static function curlPost($url, $postData) {

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);    // https請求 不驗證證書和hosts

        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);

        curl_setopt($ch, CURLOPT_URL, $url);

        curl_setopt($ch, CURLOPT_POST, 1);

        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

        $res = curl_exec($ch);

        curl_close($ch);

        return $res;

    }

    調用統一下單接口:

    public function prePay($body,$userIp,$orderNo, $price, $userOpenId){

        $postData = [

            "appid"     => self::APPID,

            "mch_id"     => self::MCHID,

            "nonce_str" => self::getNonceStr(),

            "body"       => $body,

            "out_trade_no"   => $orderNo,

            "total_fee"     => (int)($price*100),//微信以分為機關

            "spbill_create_ip"   => $userIp,

            "notify_url"         => self::notify_url,

            "trade_type"         => "JSAPI",

            "openid"             => $userOpenId,

        ];

        $postData["sign"] = self::getSign($postData, self::APPKEY);

        $postData = Func::arrayToXml($postData);

        $response = Func::xmlToArray(Func::curlPost(self::API_UNIFIED_ORDER, $postData));

        if (isset($response["return_code"]) && $response["return_code"] == "SUCCESS"){

            if ($response["result_code"] == "SUCCESS"){

                $package = "prepay_id=".$response["prepay_id"];

                $res = [

                    "appId"     => self::APPID,

                    "timeStamp" => (string)time(),

                    "nonceStr"   => self::getNonceStr(),

                    "package"   => $package,

                    "signType"   => "MD5"

                ];

                $res["paySign"] = self::getSign($res, self::APPKEY);

                return $res;//調起支付所需參數

            }

        }

        return [];

    }

    public function endOrder($param){

        if (isset($param["return_code"]) && $param["return_code"] == "SUCCESS" && $param["result_code"] == "SUCCESS"){

            $resSign = $param["sign"];

            unset($param["sign"]);

            $orderInfo = "訂單資訊";

            if (empty($orderInfo) || $param["total_fee"] != $orderInfo["price"]*100){

                return false;

            }

            $sign = self::getSign($param, self::APPKEY);

            if ($sign != $resSign){

                return false;//簽名失敗

            }

            if ($param["appid"] != self::APPID){

                return false;

            }

            if ($param["mch_id"] != self::MCHID){

                return false;

            }

            if (1 == $orderInfo["pay_status"]){

                return true;

            }else{

//TODO 處理訂單

                return true;

            }

        }

        return false;

    }

    public function queryOrder($orderNO)

    {

        $nonce_str = self::getNonceStr();

        $params = [

            "appid" => self::APPID,

            "mch_id" => self::MCHID,

            "out_trade_no" => $orderNO,

            "nonce_str" => $nonce_str

        ];

        $params["sign"] = self::getSign($params, self::APPKEY);

        $params = Func::arrayToXml($params);

        $response = Func::xmlToArray(Func::curlPost(self::API_ORDER_QUERY, $params));

        if (isset($response["return_code"]) && $response["return_code"] == "SUCCESS") {

            if ($response["result_code"] == "SUCCESS" && $response["trade_state"] == "SUCCESS"){

                return true;

            }

        }

        return false;

    }

    public function closeOrder($orderNo){

        $nonce_str = self::getNonceStr();

        $params = [

            "appid" => self::APPID,

            "mch_id" => self::MCHID,

            "out_trade_no" => $orderNO,

            "nonce_str" => $nonce_str

        ];

        $params["sign"] = self::getSign($params, self::APPKEY);

        $params = Func::arrayToXml($params);

        $response = Func::xmlToArray(Func::curlPost(self::API_CLOSE_ORDER, $params));

        if (isset($response["return_code"]) && $response["return_code"] == "SUCCESS") {

            $res_sign = $response["sign"];//傳回的簽名

            unset($response["sign"]);

            $gen_sign = self::getSign($response, self::APPKEY);

            if ($res_sign != $gen_sign){

                return false;

            }

            if ($response["appid"] != self::APPID || $response["mch_id"] != self::MCH_ID ){

                return false;

            }

            if ($response["result_code"] == "SUCCESS"){

                return true;

            }

        }

        return false;

    }

商戶訂單支付失敗需要生成新單号重新發起支付,要對原訂單号調用關單,避免重複支付;系統下單後,使用者支付逾時,系統退出不再受理,避免使用者繼續,請調用關單接口。

注意:訂單生成後不能馬上調用關單接口,最短調用時間間隔為5分鐘。調用關單或撤銷接口API之前,需确認支付狀态。

接收異步通知時要注意:

        $param = file_get_contents("php://input");

        $param = Func::xmlToArray($param);