php微信支付之公众号支付功能


Posted in PHP onMay 30, 2018

网上的很多PHP微信扫码支付接入教程都颇为复杂,且需要配置和引入较多的文件,本人通过整理后给出一个单文件版的,希望可以给各位想接入微信扫码支付的带来些许帮助和借鉴意义。

将该文件放到你的授权目录下,并在微信里访问这个文件,即可看到演示效果。效果如下:

php微信支付之公众号支付功能 php微信支付之公众号支付功能

需要注意的事项:

1.该文件需放到支付授权目录下,可以在微信支付商户平台->产品中心->开发配置中设置。
2.如提示签名错误可以通过微信支付签名验证工具进行验证:微信公众平台支付接口调试工具

代码如下:

<?php
header('Content-type:text/html; Charset=utf-8');
$mchid = 'xxxxx';   //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
$appid = 'xxxxx'; //微信支付申请对应的公众号的APPID
$appKey = 'xxxxx'; //微信支付申请对应的公众号的APP Key
$apiKey = 'xxxxx'; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥

//①、获取用户openid
$wxPay = new WxpayService($mchid,$appid,$appKey,$apiKey);
$openId = $wxPay->GetOpenid();  //获取openid
if(!$openId) exit('获取openid失败');
//②、统一下单
$outTradeNo = uniqid();  //你自己的商品订单号
$payAmount = 0.01;   //付款金额,单位:元
$orderName = '支付测试'; //订单标题
$notifyUrl = 'https://www.xxx.com/wx/';  //付款成功后的回调地址(不要有问号)
$payTime = time();  //付款时间
$jsApiParameters = $wxPay->createJsBizPackage($openId,$payAmount,$outTradeNo,$orderName,$notifyUrl,$payTime);
$jsApiParameters = json_encode($jsApiParameters);
?>
 <html>
 <head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <title>微信支付样例-支付</title>
  <script type="text/javascript">
   //调用微信JS api 支付
   function jsApiCall()
   {
    WeixinJSBridge.invoke(
     'getBrandWCPayRequest',
     <?php echo $jsApiParameters; ?>,
     function(res){
      WeixinJSBridge.log(res.err_msg);
      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>
 </head>
 <body>
 <br/>
 <font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px"><?php echo $payAmount?>元</span>钱</b></font><br/><br/>
 <div align="center">
  <button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer; color:white; font-size:16px;" type="button" onclick="callpay()" >立即支付</button>
 </div>
 </body>
 </html>
<?php
class WxpayService
{
 protected $mchid;
 protected $appid;
 protected $appKey;
 protected $apiKey;
 public $data = null;

 public function __construct($mchid, $appid, $appKey,$key)
 {
  $this->mchid = $mchid; //https://pay.weixin.qq.com 产品中心-开发配置-商户号
  $this->appid = $appid; //微信支付申请对应的公众号的APPID
  $this->appKey = $appKey; //微信支付申请对应的公众号的APP Key
  $this->apiKey = $key; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
 }

 /**
  * 通过跳转获取用户的openid,跳转流程如下:
  * 1、设置自己需要调回的url及其其他参数,跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize
  * 2、微信服务处理完成之后会跳转回用户redirect_uri地址,此时会带上一些参数,如:code
  * @return 用户的openid
  */
 public function GetOpenid()
 {
  //通过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($baseUrl);
   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;
 }

 /**
  * 统一下单
  * @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, $totalFee, $outTradeNo, $orderName, $notifyUrl, $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' => $orderName,
   'mch_id' => $config['mch_id'],
   'nonce_str' => self::createNonceStr(),
   'notify_url' => $notifyUrl,
   'openid' => $openid,   //rade_type=JSAPI,此参数必传
   'out_trade_no' => $outTradeNo,
   'spbill_create_ip' => '127.0.0.1',
   'total_fee' => intval($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;
 }

 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>";
  file_put_contents('1.txt',$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;
 }
}
?>

github下载地址

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
新版PHP将向Java靠拢
Oct 09 PHP
PHP与MySQL开发中页面乱码的产生与解决
Mar 27 PHP
PHP根据IP地址获取所在城市具体实现
Nov 27 PHP
PHP中iconv函数转码时截断字符问题的解决方法
Jan 21 PHP
PHP四舍五入、取整、round函数使用示例
Feb 06 PHP
修改WordPress中文章编辑器的样式的方法详解
Dec 15 PHP
PHP基于curl后台远程登录正方教务系统的方法
Oct 14 PHP
php实现socket推送技术的示例
Dec 20 PHP
laravel Model 执行事务的实现
Oct 10 PHP
thinkphp框架类库扩展操作示例
Nov 26 PHP
Laravel jwt 多表(多用户端)验证隔离的实现
Dec 18 PHP
PHP文件打开关闭及读写操作示例解析
Aug 06 PHP
php实现微信原生支付(扫码支付)功能
May 30 #PHP
php实现支付宝当面付(扫码支付)功能
May 30 #PHP
PHP ADODB生成HTML表格函数rs2html功能【附错误处理函数用法】
May 29 #PHP
PHP ADODB生成下拉列表框功能示例
May 29 #PHP
Laravel实现短信注册的示例代码
May 29 #PHP
PHP abstract 抽象类定义与用法示例
May 29 #PHP
thinkPHP中U方法加密传递参数功能示例
May 29 #PHP
You might like
论建造顺序的重要性
2020/03/04 星际争霸
php HtmlReplace输入过滤安全函数
2010/07/03 PHP
php查询ip所在地的方法
2014/12/05 PHP
php使用curl并发减少后端访问时间的方法分析
2016/05/12 PHP
php使用函数pathinfo()、parse_url()和basename()解析URL
2016/11/25 PHP
让焦点自动跳转
2006/07/01 Javascript
了解jQuery技巧来提高你的代码
2010/01/08 Javascript
javascript简单实现图片预加载
2014/12/03 Javascript
nodejs中操作mysql数据库示例
2014/12/20 NodeJs
jquery实现点击label的同时触发文本框点击事件的方法
2015/06/05 Javascript
Angularjs 实现分页功能及示例代码
2016/09/14 Javascript
jQuery表单验证简单示例
2016/10/17 Javascript
nodeJS实现路由功能实例代码
2017/06/08 NodeJs
jQuery选择器之属性过滤选择器详解
2017/09/28 jQuery
vue点击input弹出带搜索键盘并监听该元素的方法
2018/08/25 Javascript
layDate插件设置开始和结束时间
2018/11/15 Javascript
[57:50]DOTA2上海特级锦标赛主赛事日 - 4 胜者组决赛Secret VS Liquid第二局
2016/03/05 DOTA
[01:05]DOTA2完美大师赛趣味视频之选手教你打职业
2017/11/23 DOTA
[43:47]完美世界DOTA2联赛PWL S3 LBZS vs Phoenix 第一场 12.09
2020/12/11 DOTA
浅谈python中set使用
2016/06/30 Python
Python的Flask框架中的Jinja2模板引擎学习教程
2016/06/30 Python
python模块简介之有序字典(OrderedDict)
2016/12/01 Python
Python爬虫之模拟知乎登录的方法教程
2017/05/25 Python
python3学习之Splash的安装与实例教程
2018/07/09 Python
Python列表(List)知识点总结
2019/02/18 Python
Python JSON编解码方式原理详解
2020/01/20 Python
Django封装交互接口代码
2020/07/12 Python
深入了解Python 方法之类方法 &amp; 静态方法
2020/08/17 Python
python实现学生信息管理系统源码
2021/02/22 Python
CSS3 选择器 伪类选择器介绍
2012/01/21 HTML / CSS
简历的自我评价范文
2014/02/04 职场文书
cf收人广告词
2014/03/14 职场文书
公证委托书
2014/08/01 职场文书
2014年纪委工作总结
2014/12/05 职场文书
个人职业生涯规划之自我评估篇
2019/09/03 职场文书
Golang 语言控制并发 Goroutine的方法
2021/06/30 Golang