微信手机端及H5支付model、调用及前台页面单文件版,开封即用

#### 调用:
	class WxpaywapApp extends PaybaseApp
	{
	    private $mchid;
	    private $appid;
	    private $appKey;
	    private $apiKey;
	    private $returnUrl;
	    private $notifyUrl;
	    private $wabapp;
	    /* 构造函数 */
	    public function __construct()
	    {
	        $this->WxpayApp();
	        $this->wabapp = new WebAPP();
	    }
	    public function WxpayApp()
	    {
	        parent::__construct();
	        // 获取支付方式的相关配置信息
	        $model_payment =& m('payment');
	        $payment_info = $model_payment->get('payment_code="pay3in1"');
	        $payment_config = unserialize($payment_info['config']);
	        if(empty($payment_config)){
	          return $this->wabapp->error('未获取到相关配置信息');
	        }
	        /*** 微信配置信息 ***/
	        $this->mchid = $payment_config['wxpay_mchid'];          //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
	        $this->appid = $payment_config['wxpay_appid'];  //公众号APPID 通过微信支付商户资料审核后邮件发送
	        $this->appKey = $payment_config['wxpay_appKey'];   //微信支付申请对应的公众号的APP Key
	        $this->apiKey = $payment_config['wxpay_apiKey'];   //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
	        /*** 配置结束 ***/
	    }
 
	    public function index()
	    {
	        header('Content-type:text/html; Charset=utf-8');
	        if (empty($this->oid)) {
	            return $this->wabapp->error('缺少必要参数!');
	        }
	        // 获取订单信息
	        $model_order =& m('order');
	        if (strstr($this->oid,'?')) {
	            $this->oid = $this->getLeftStr($this->oid,'?');
	        }
	        $order_info = $model_order->get('order_id='.$this->oid);
	        if (empty($order_info)) {
	            return $this->wabapp->error('无此订单');
	        }
	        //订单已支付
	        if ($order_info['status'] > ORDER_PENDING) {
	            return $this->wabapp->error('该订单已支付', '查看订单', $this->ORDER_INFO_URL);
	        }
	        $pay_class =& m('pay_do');
	        $notifyUrl = $pay_class->_create_notify_url('wxpay');     //付款成功后的异步回调地址(不要有问号)
 
	        $openidUrl = $pay_class->_create_openid_notify_url($this->oid);
 
 
	        $wxPay =& m('wxpaywap');
	        $wxPay->setMchid($this->mchid);
	        $wxPay->setAppid($this->appid);
	        $wxPay->setAppKey($this->appKey);
	        $wxPay->setApiKey($this->apiKey);
	        if ($pay_class->isWeixin()) {
				// 微信内支付
				//①、获取用户openid
	            $wxPay->setNotifyUrl($notifyUrl);
	            $wxPay->setTotalFee($order_info['order_amount']);
	            $wxPay->setOutTradeNo($order_info['order_sn']);
	            $wxPay->setOrderName($order_info['order_sn']);
	            $openId = $wxPay->GetOpenid($openidUrl);      //获取openid
	            if(!$openId) {
	                return $this->wabapp->error('获取用户信息失败,请稍候重试', '查看订单', $this->ORDER_INFO_URL);
	            }
	            //②、统一下单
	            $payTime = time();      //付款时间
	            $jsApiParameters = $wxPay->createJsBizPackage($openId,$payTime);
	            $jsApiParameters = json_encode($jsApiParameters);
	            $this->assign('payAmount',$order_info['order_amount']);
	            $this->assign('jsApiParameters',$jsApiParameters);
	            $this->assign('retrunUrl','/index.php?m=wap&c=orders&a=detial&oid='.$this->oid);
	            $this->display('wxpaywap.html');
	        } else {
				// H5页面支付
	            $wxPay->setTotalFee($order_info['order_amount']);
	            $wxPay->setOutTradeNo($order_info['order_sn']);
	            $wxPay->setOrderName($order_info['order_sn']);
	            $wxPay->setNotifyUrl($notifyUrl);
	            $wxPay->setWapUrl('http://www.'.$_SERVER['SERVER_NAME']);
	            $wxPay->setWapName('大美司机');
	            $mwebUrl= $wxPay->createH5JsBizPackage();
 
	            // 如果订单号重复 重新生成订单号
	            if ($mwebUrl == 'repeat') {
	                $order_sn = 'dm'.time();
	                $wxPay->setOutTradeNo($order_sn);
	                $wxPay->setOrderName($order_sn);
	                $mwebUrl= $wxPay->createH5JsBizPackage();
	                if (!empty($mwebUrl)) {
	                    $model_order->edit($this->oid, ['order_sn'=>$order_sn]);
	                } else {
	                    return $this->wabapp->error('网络错误,请稍候重试');
	                }
	            }
	            // 同步回调地址
	            $retrunUrl = $_SERVER['SERVER_NAME'].'/index.php?m=wap&c=payweb&a=wxpayWebNotify&oid='.$this->oid;
	            $this->assign('retrunUrl',$mwebUrl.'&redirect_url='.urlencode('http://'.$retrunUrl));
	            //var_dump($mwebUrl.'&redirect_url='.urlencode('http://'.$retrunUrl));die;
	            $this->display('wxpayh5.html');
	        }
 
	    }
 
	      /* 取左边字符 */
	  	public function getLeftStr($str, $leftStr){
	    		$left = strpos($str, $leftStr);
	    		if(!$left) return '';
	    		return substr($str, 0, $left);
	  	}
	}

#### model:

	class WxpaywapModel extends BaseModel
	{
	    protected $mchid;
	    protected $appid;
	    protected $appKey;
	    protected $apiKey;
	    protected $notifyUrl;
	    protected $totalFee;
	    protected $outTradeNo;
	    protected $orderName;
	    // h5支付
	    protected $wapName; //WAP 网站名
	    protected $wapUrl;  //WAP网站URL地址
	    public $data = null;
 
	    public function __construct()
	    {
	    }
 
	    public function setMchid($mchid)
	    {
	        $this->mchid = $mchid;
	    }
	    public function setAppid($appid)
	    {
	        $this->appid = $appid;
	    }
	    public function setApiKey($apikey)
	    {
	        $this->apiKey = $apikey;
	    }
	    public function setAppKey($appKey)
	    {
	        $this->appKey = $appKey;
	    }
	    public function setNotifyUrl($notifyUrl)
	    {
	        $this->notifyUrl = $notifyUrl;
	    }
	    public function setTotalFee($payAmount)
	    {
	        $this->totalFee = $payAmount;
	    }
	    public function setOutTradeNo($outTradeNo)
	    {
	        $this->outTradeNo = $outTradeNo;
	    }
	    public function setOrderName($orderName)
	    {
	        $this->orderName = $orderName;
	    }
	    public function setOpenId($openId)
	    {
	        $this->openId = $openId;
	    }
	    public function setWapName($wapName)
	    {
	        $this->wapName = $wapName;
	    }
	    public function setWapUrl($wapUrl)
	    {
	        $this->wapUrl = $wapUrl;
	    }
 
	    /**
	     * 发起订单 h5
	     * @return array
	     */
	    public function createH5JsBizPackage()
	    {
	        $config = array(
	            'mch_id' => $this->mchid,
	            'appid' => $this->appid,
	            'key' => $this->apiKey,
	        );
	        $scene_info = array(
	            'h5_info' =>array(
	                'type'=>'Wap',
	                'wap_url'=>$this->wapUrl,
	                'wap_name'=>$this->wapName,
	            )
	        );
	        $unified = array(
	            'appid' => $config['appid'],
	            'attach' => 'pay',             //商家数据包,原样返回,如果填写中文,请注意转换为utf-8
	            'body' => $this->orderName.time(),
	            'mch_id' => $config['mch_id'],
	            'nonce_str' => self::createNonceStr(),
	            'notify_url' => $this->notifyUrl,
	            'out_trade_no' => $this->outTradeNo,
	            'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
	            'total_fee' => intval($this->totalFee * 100),       //单位 转为分
	            'trade_type' => 'MWEB',
	            'scene_info'=>json_encode($scene_info)
	        );
	        $unified['sign'] = self::getSign($unified, $config['key']);
	        $responseXml = self::curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', self::arrayToXml($unified));
 
	        $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
	        if ($unifiedOrder->return_code != 'SUCCESS') {
	            die($unifiedOrder->return_msg);
	        }
	        if ($unifiedOrder->result_code == 'FAIL') {
	            //die($unifiedOrder->err_code_des);
	            return 'repeat';
	        }
	        if($unifiedOrder->mweb_url){
	            return $unifiedOrder->mweb_url;
	        }
	        exit('error');
	    }
 
 
	    /**
	     * 微信内支付
	     * @param string $openid 调用【网页授权获取用户信息】接口获取到用户在该公众号下的Openid
	     * @param float $totalFee 收款总费用 单位元
	     * @param string $outTradeNo 唯一的订单号
	     * @param string $orderName 订单名称
	     * @param string $notifyUrl 支付结果通知url 不要有问号
	     * @param string $timestamp 支付时间
	     * @return string
	     */
	    public function createJsBizPackage($openid, $timestamp)
	    {
	        $config = array(
	            'mch_id' => $this->mchid,
	            'appid' => $this->appid,
	            'key' => $this->apiKey,
	        );
	        $orderName = iconv('GBK','UTF-8',$orderName);
	        $unified = array(
	            'appid' => $config['appid'],
	            'attach' => 'pay',             //商家数据包,原样返回,如果填写中文,请注意转换为utf-8
	            'body' => $this->outTradeNo,
	            'mch_id' => $config['mch_id'],
	            'nonce_str' => self::createNonceStr(),
	            'notify_url' => $this->notifyUrl,
	            'openid' => $openid,            //rade_type=JSAPI,此参数必传
	            'out_trade_no' => $this->outTradeNo,
	            'spbill_create_ip' => '127.0.0.1',
	            'total_fee' => intval($this->totalFee * 100),       //单位 转为分
	            'trade_type' => 'JSAPI',
	        );
	        $unified['sign'] = self::getSign($unified, $config['key']);
	        $responseXml = self::curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', self::arrayToXml($unified));
	        $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
	        if ($unifiedOrder === false) {
	            die('parse xml error');
	        }
	        if ($unifiedOrder->return_code != 'SUCCESS') {
	            die($unifiedOrder->return_msg);
	        }
	        if ($unifiedOrder->result_code != 'SUCCESS') {
	            die($unifiedOrder->err_code);
	        }
	        $arr = array(
	            "appId" => $config['appid'],
	            "timeStamp" => "$timestamp",        //这里是字符串的时间戳,不是int,所以需加引号
	            "nonceStr" => self::createNonceStr(),
	            "package" => "prepay_id=" . $unifiedOrder->prepay_id,
	            "signType" => 'MD5',
	        );
	        $arr['paySign'] = self::getSign($arr, $config['key']);
	        return $arr;
	    }
 
	    /**
	     * 通过跳转获取用户的openid,跳转流程如下:
	     * 1、设置自己需要调回的url及其其他参数,跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize
	     * 2、微信服务处理完成之后会跳转回用户redirect_uri地址,此时会带上一些参数,如:code
	     * @return 用户的openid
	     */
	    public function GetOpenid($openidUrl = '')
	    {
	        //通过code获得openid
	        if (!isset($_GET['code'])){
	            //触发微信返回code码
	            //$scheme = $_SERVER['HTTPS']=='on' ? 'https://' : 'http://';
	            //$baseUrl = urlencode($scheme.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']);
	            $url = $this->__CreateOauthUrlForCode($openidUrl);
	            Header("Location: $url");
	            exit();
	        } else {
	            //获取code码,以获取openid
	            $code = $_GET['code'];
	            $openid = $this->getOpenidFromMp($code);
	            return $openid;
	        }
	    }
 
	    /**
	     * 通过code从工作平台获取openid机器access_token
	     * @param string $code 微信跳转回来带上的code
	     * @return openid
	     */
	    public function GetOpenidFromMp($code)
	    {
	        $url = $this->__CreateOauthUrlForOpenid($code);
	        $res = self::curlGet($url);
	        //取出openid
	        $data = json_decode($res,true);
	        $this->data = $data;
	        $openid = $data['openid'];
	        return $openid;
	    }
 
	    /**
	     * 构造获取open和access_toke的url地址
	     * @param string $code,微信跳转带回的code
	     * @return 请求的url
	     */
	    private function __CreateOauthUrlForOpenid($code)
	    {
	        $urlObj["appid"] = $this->appid;
	        $urlObj["secret"] = $this->appKey;
	        $urlObj["code"] = $code;
	        $urlObj["grant_type"] = "authorization_code";
	        $bizString = $this->ToUrlParams($urlObj);
	        return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString;
	    }
 
	    /**
	     * 构造获取code的url连接
	     * @param string $redirectUrl 微信服务器回跳的url,需要url编码
	     * @return 返回构造好的url
	     */
	    private function __CreateOauthUrlForCode($redirectUrl)
	    {
	        $urlObj["appid"] = $this->appid;
	        $urlObj["redirect_uri"] = "$redirectUrl";
	        $urlObj["response_type"] = "code";
	        $urlObj["scope"] = "snsapi_base";
	        $urlObj["state"] = "STATE"."#wechat_redirect";
	        $bizString = $this->ToUrlParams($urlObj);
	        return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString;
	    }
 
	    /**
	     * 拼接签名字符串
	     * @param array $urlObj
	     * @return 返回已经拼接好的字符串
	     */
	    private function ToUrlParams($urlObj)
	    {
	        $buff = "";
	        foreach ($urlObj as $k => $v)
	        {
	            if($k != "sign") $buff .= $k . "=" . $v . "&";
	        }
	        $buff = trim($buff, "&");
	        return $buff;
	    }
 
	    public static function curlGet($url = '', $options = array())
	    {
	        $ch = curl_init($url);
	        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
	        if (!empty($options)) {
	            curl_setopt_array($ch, $options);
	        }
	        //https请求 不验证证书和host
	        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
	        $data = curl_exec($ch);
	        curl_close($ch);
	        return $data;
	    }
 
	    public static function curlPost($url = '', $postData = '', $options = array())
	    {
	        if (is_array($postData)) {
	            $postData = http_build_query($postData);
	        }
	        $ch = curl_init();
	        curl_setopt($ch, CURLOPT_URL, $url);
	        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	        curl_setopt($ch, CURLOPT_POST, 1);
	        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
	        curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
	        if (!empty($options)) {
	            curl_setopt_array($ch, $options);
	        }
	        //https请求 不验证证书和host
	        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
	        $data = curl_exec($ch);
	        curl_close($ch);
	        return $data;
	    }
 
	    public static function createNonceStr($length = 16)
	    {
	        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
	        $str = '';
	        for ($i = 0; $i < $length; $i++) {
	            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
	        }
	        return $str;
	    }
	    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;
	    }
 
	    public static function getSign($params, $key)
	    {
	        ksort($params, SORT_STRING);
	        $unSignParaString = self::formatQueryParaMap($params, false);
	        $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
	        return $signStr;
	    }
	    protected static function formatQueryParaMap($paraMap, $urlEncode = false)
	    {
	        $buff = "";
	        ksort($paraMap);
	        foreach ($paraMap as $k => $v) {
	            if (null != $v && "null" != $v) {
	                if ($urlEncode) {
	                    $v = urlencode($v);
	                }
	                $buff .= $k . "=" . $v . "&";
	            }
	        }
	        $reqPar = '';
	        if (strlen($buff) > 0) {
	            $reqPar = substr($buff, 0, strlen($buff) - 1);
	        }
	        return $reqPar;
	    }
	}

#### 前台页面

> 微信内支付


	    <script type="text/javascript">
	        //调用微信JS api 支付
	        function jsApiCall()
	        {
	            WeixinJSBridge.invoke(
	                'getBrandWCPayRequest',
	                {$jsApiParameters},
	                function(res){
	                    WeixinJSBridge.log(res.err_msg);
	                    // document.getElementById('res').innerHTML = JSON.stringify(res);
	                    // document.getElementById('err_code').innerHTML = res.err_code;
	                    // document.getElementById('err_desc').innerHTML = res.err_desc;
	                    // document.getElementById('err_msg').innerHTML = res.err_msg;
	                    if (res.err_msg == 'get_brand_wcpay_request:ok') {
	                        window.location.href = '{$retrunUrl}';
	                    }
	                    //alert(res.err_code+res.err_desc+res.err_msg);
	                }
	            );
	        }
	        function callpay()
	        {
	            if (typeof WeixinJSBridge == "undefined"){
	                if( document.addEventListener ){
	                    document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
	                }else if (document.attachEvent){
	                    document.attachEvent('WeixinJSBridgeReady', jsApiCall);
	                    document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
	                }
	            }else{
	                jsApiCall();
	            }
	        }
	    </script>

> H5支付

	<script>
	$(function(){
	  setTimeout(function(){
	    window.location.href = "{$retrunUrl}";
	  },1500);
	});
	</script>