微信手机端及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>