微信开发之不使用SDK如何实现微信支付?

微信开发 852浏览 评论

做过微信支付的小伙伴都知道,可以直接使用微信公众平台提供的支付demo,但是你会发现有很多没用的文件,而且demo写得异常复杂,文件太多,需要花比较多的时间去熟悉和调试。

这个对于新手小伙伴来说还是有一定的难度,在开发的过程中会遇到很多的坑,当初我第一次实现微信支付也是同样的啦!人生总会有很对第一次的不是,包括...嘿嘿,别想太多啦!

sdk对于有代码洁癖的资深php开发小伙伴来说就简直不能忍了,凭着高超的记忆怎么也要自己来封装一个吧,装逼必备啊有木有!

自己封装也是有很多好处的,能够更完美的嵌入到自身的框架内,能够更加方便的维护和修改,你看几十上百个文件和看一个文件实现同样的功能,那个更加容易些,答案显而易见了!

下面我们来研究一波:


image.png

看下请求的参数主要就是这个【签名和随机字符串】,其他字段的数据都能够很容易的得到,而这个签名和随机字符串需要做一个处理;

签名如何处理?我们写一个函数来搞定他,代码如下

//data就是所有必填参数值,key就是你的支付秘钥(商户中心设置那个32位那个)
function sign(array $data, $key) {
    ksort($data);
    $sign = strtoupper(md5(urldecode(http_build_query($data)).'&key='.$key));
    return $sign;
}

随机字符串如何处理?我们也写一个函数来搞定他

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

第二:微信支付请求数据类型位xml,因此我们还需要一个将数组转为xml的函数,如下

/**
 * @param $values
 * @return string
 * @throws \think\Exception
 * 数组转xml
 */
function ToXml($values){
    if(!is_array($values) || count($values) <= 0) {
        throw new \think\Exception('数组数据异常');
    }

    $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;
}

第三:组装完毕参数之后需要将参数发送到微信支付接口,因此需要一个curl请求函数代码如下(需要自己将参数配置到配置文件中,Config::get()为获取配置文件的微信支付配置信息)

protected static function postXmlCurl($xml, $url, $useCert = false, $second = 30){
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        //如果有配置代理这里就设置代理
        if(Config::get('wx.CURL_PROXY_HOST') != "0.0.0.0"
            && Config::get('wx.CURL_PROXY_PORT') != 0){
            curl_setopt($ch,CURLOPT_PROXY, Config::get('wx.CURL_PROXY_HOST'));
            curl_setopt($ch,CURLOPT_PROXYPORT, Config::get('wx.CURL_PROXY_PORT'));
        }
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        if($useCert == true){
            //设置证书
            //使用证书:cert 与 key 分别属于两个.pem文件
            curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLCERT, Config::get('wx.SSLCERT_PATH'));
            curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLKEY, Config::get('wx.SSLKEY_PATH'));
        }
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if($data){
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            returnFailMsg('10018',"curl出错,错误码:$error");
        }
    }

微信支付需要的几个函数我们就写完了,剩下的就是如何传递参数和调用了。


最后大概是这样,需要自己调调了

class Wechat {
    public $values = [];
    /**
     * @return \think\response\Json
     * 支付
     */
    public function wxPay(){
        //通用参数
        $this->values['appid']  = Config::get('wx.APPID');
        $this->values['mch_id'] = Config::get('wx.MCHID');
        $this->values['nonce_str'] = $this->getNonceStr();
        $this->values['goods_tag'] = 'test';
		$this->values['out_trade_no'] = time().rand(1000,9999); //平台订单号
		$this->values['total_fee'] = 1 * 100; //单位为分*100为元
        $this->values['time_start'] = date("YmdHis");
        $this->values['time_expire'] = date("YmdHis", time() + 600);
        $this->values['notify_url']       = Config::get('wx.NOTIFY');
        if('JSAPI' === $this->values['trade_type']){
            if(empty($this->values['openid']) || !isset($this->values['openid']))
                die('jsAPI支付openid为必传项')
        }
        if('NATIVE' === $this->values['trade_type']){
            $this->values['spbill_create_ip'] = $_SERVER['REMOTE_ADDR']; //获取机器的IP
            if(empty($this->values['product_id'] || !isset($this->values['product_id']))){
                die('NATIVE支付商品id为必传项')
            }
        }
        $sign = $this->sign($this->values,Config::get('wx.KEY')); //签名
        $this->values['sign'] = $sign;
        $xml = $this->ToXml($this->values);//转xml对象
        $response =$this->postXmlCurl($xml, Config::get('wx.PAYURL'), false, 6);//发起post请求
        $this->values = $this->FromXml($response);
        if($this->sign($this->values,Config::get('wx.KEY')) !== $this->values['sign']){
           die('签名错误')
        }
        var_dump($this->values);
    }
	
	public function sign($data, $key) {
		//sign不参与签名
		if($data['sign']){
			unset($data['sign']);
		}
		ksort($data);
		$sign = strtoupper(md5(urldecode(http_build_query($data)).'&key='.$key));
		return $sign;
	}
	
	/**
	 * @param $values
	 * @return string
	 * @throws \think\Exception
	 * 数组转xml
	 */
	function ToXml($values){
		if(!is_array($values) || count($values) <= 0) {
			throw new \think\Exception('数组数据异常');
		}

		$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;
	}
	
	 public 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 $xml
     * @param $url
     * @param bool $useCert
     * @param int $second
     * @return mixed
     * post方式请求
     */
    protected static function postXmlCurl($xml, $url, $useCert = false, $second = 30){
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        //如果有配置代理这里就设置代理
        if(Config::get('wx.CURL_PROXY_HOST') != "0.0.0.0"
            && Config::get('wx.CURL_PROXY_PORT') != 0){
            curl_setopt($ch,CURLOPT_PROXY, Config::get('wx.CURL_PROXY_HOST'));
            curl_setopt($ch,CURLOPT_PROXYPORT, Config::get('wx.CURL_PROXY_PORT'));
        }
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        if($useCert == true){
            //设置证书
            //使用证书:cert 与 key 分别属于两个.pem文件
            curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLCERT, Config::get('wx.SSLCERT_PATH'));
            curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLKEY, Config::get('wx.SSLKEY_PATH'));
        }
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if($data){
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            returnFailMsg('10018',"curl出错,错误码:$error");
        }
    }


    /**
     * 回调
     */
    public function notify(){
        //接受回调参数
        $xml = file_get_contents('php://input');
 
    }


本文连接:http://www.phpbloger.com/article/53.html 文章都为原创,转载请注明出处!

相关文章