PHP实现 APP端微信支付功能


Posted in PHP onJune 22, 2018

前面已经写了手机APP支付宝支付,今天再把手机APP微信支付补上,前期的准备工作在这里就不多说了,可以参考微信支付开发文档,一定要仔细阅读开发文档,可以让你少踩点坑;准备工作完成后就是配置参数,调用统一下单接口,支付后异步回调三部曲啦;

1.我封装好的一个支付类文件,多余的东西都去除掉了,并且把配置参数放到了这个支付类中,只需要修改Weixinpayandroid方法内的几个参数就可以直接复制使用:

class Wxpayandroid
{
 //参数配置
 public $config = array(
    'appid' => "", /*微信开放平台上的应用id*/
    'mch_id' => "", /*微信申请成功之后邮件中的商户id*/
    'api_key' => "", /*在微信商户平台上自己设定的api密钥 32位*/
   );
 //服务器异步通知页面路径(必填)
 public $notify_url = '';
 //商户订单号(必填,商户网站订单系统中唯一订单号)
 public $out_trade_no = '';
 //商品描述(必填,不填则为商品名称)
 public $body = '';
 //付款金额(必填)
 public $total_fee = 0;
 //自定义超时(选填,支持dhmc)
 public $time_expire = '';
 private $WxPayHelper;
 public function Weixinpayandroid($total_fee,$tade_no)
 {
  $this->total_fee = intval($total_fee * 100);//订单的金额 1元
  $this->out_trade_no = $tade_no;// date('YmdHis') . substr(time(), - 5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));//订单号
  $this->body = 'wxpay';//支付描述信息
  $this->time_expire = date('YmdHis', time() + 86400);//订单支付的过期时间(eg:一天过期)
  $this->notify_url = "http://www.ceshi.com/notifyandroid";//异步通知URL(更改支付状态)
  //数据以JSON的形式返回给APP
  $app_response = $this->doPay(); 
  if (isset($app_response['return_code']) && $app_response['return_code'] == 'FAIL') {
   $errorCode = 100;
   $errorMsg = $app_response['return_msg'];
   $this->echoResult($errorCode, $errorMsg);
  } else {
   $errorCode = 0;
   $errorMsg = 'success';
   $responseData = array(
    'notify_url' => $this->notify_url,
    'app_response' => $app_response,
   );
   $this->echoResult($errorCode, $errorMsg, $responseData);
  }
 }
 //接口输出
 function echoResult($errorCode = 0, $errorMsg = 'success', $responseData = array())
 {
  $arr = array(
   'errorCode' => $errorCode,
   'errorMsg' => $errorMsg,
   'responseData' => $responseData,
  );
   exit(json_encode($arr));  //exit可以正常发送给APP json数据
  // return json_encode($arr); //在TP5中return这个json数据,APP接收到的是null,无法正常吊起微信支付
 }
 function getVerifySign($data, $key)
 {
  $String = $this->formatParameters($data, false);
  //签名步骤二:在string后加入KEY
  $String = $String . "&key=" . $key;
  //签名步骤三:MD5加密
  $String = md5($String);
  //签名步骤四:所有字符转为大写
  $result = strtoupper($String);
  return $result;
 }
 function formatParameters($paraMap, $urlencode)
 {
  $buff = "";
  ksort($paraMap);
  foreach ($paraMap as $k => $v) {
   if($k=="sign"){
    continue;
   }
   if ($urlencode) {
    $v = urlencode($v);
   }
   $buff .= $k . "=" . $v . "&";
  }
  $reqPar;
  if (strlen($buff) > 0) {
   $reqPar = substr($buff, 0, strlen($buff) - 1);
  }
  return $reqPar;
 }
 /**
  * 得到签名
  * @param object $obj
  * @param string $api_key
  * @return string
  */
 function getSign($obj, $api_key)
 {
  foreach ($obj as $k => $v)
  {
   $Parameters[strtolower($k)] = $v;
  }
  //签名步骤一:按字典序排序参数
  ksort($Parameters);
  $String = $this->formatBizQueryParaMap($Parameters, false);
  //签名步骤二:在string后加入KEY
  $String = $String."&key=".$api_key;
  //签名步骤三:MD5加密
  $result = strtoupper(md5($String));
  return $result;
 }
 /**
  * 获取指定长度的随机字符串
  * @param int $length
  * @return Ambigous <NULL, string>
  */
 function getRandChar($length){
  $str = null;
  $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
  $max = strlen($strPol)-1;
  for($i=0;$i<$length;$i++){
   $str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
  }
  return $str;
 }
 /**
  * 数组转xml
  * @param array $arr
  * @return string
  */
 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;
 }
 /**
  * 以post方式提交xml到对应的接口url
  *
  * @param string $xml 需要post的xml数据
  * @param string $url url
  * @param bool $useCert 是否需要证书,默认不需要
  * @param int $second url执行超时时间,默认30s
  * @throws WxPayException
  */
 function postXmlCurl($xml, $url, $second=30, $useCert=false, $sslcert_path='', $sslkey_path='')
 {
  $ch = curl_init();
  //设置超时
  curl_setopt($ch, CURLOPT_TIMEOUT, $second);
  curl_setopt($ch,CURLOPT_URL, $url);
  //设置header
  curl_setopt($ch, CURLOPT_HEADER, FALSE);
  //要求结果为字符串且输出到屏幕上
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
  curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
  if($useCert == true){
   curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
   curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
   //设置证书
   //使用证书:cert 与 key 分别属于两个.pem文件
   curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
   curl_setopt($ch,CURLOPT_SSLCERT, $sslcert_path);
   curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
   curl_setopt($ch,CURLOPT_SSLKEY, $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);
   return false;
  }
 }
 /**
  * 获取当前服务器的IP
  * @return Ambigous <string, unknown>
  */
 function get_client_ip()
 {
  if (isset($_SERVER['REMOTE_ADDR'])) {
   $cip = $_SERVER['REMOTE_ADDR'];
  } elseif (getenv("REMOTE_ADDR")) {
   $cip = getenv("REMOTE_ADDR");
  } elseif (getenv("HTTP_CLIENT_IP")) {
   $cip = getenv("HTTP_CLIENT_IP");
  } else {
   $cip = "127.0.0.1";
  }
  return $cip;
 }
 /**
  * 将数组转成uri字符串
  * @param array $paraMap
  * @param bool $urlencode
  * @return string
  */
 function formatBizQueryParaMap($paraMap, $urlencode)
 {
  $buff = "";
  ksort($paraMap);
  foreach ($paraMap as $k => $v)
  {
   if($urlencode)
   {
    $v = urlencode($v);
   }
   $buff .= strtolower($k) . "=" . $v . "&";
  }
  $reqPar;
  if (strlen($buff) > 0)
  {
   $reqPar = substr($buff, 0, strlen($buff)-1);
  }
  return $reqPar;
 }
 /**
  * XML转数组
  * @param unknown $xml
  * @return mixed
  */
 function xmlToArray($xml)
 {
  //将XML转为array
  $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
  return $array_data;
 }
 public function chkParam()
 {
  //用户网站订单号
  if (empty($this->out_trade_no)) {
   die('out_trade_no error');
  } 
  //商品描述
  if (empty($this->body)) {
   die('body error');
  }
  if (empty($this->time_expire)){
   die('time_expire error');
  }
  //检测支付金额
  if (empty($this->total_fee) || !is_numeric($this->total_fee)) {
   die('total_fee error');
  }
  //异步通知URL
  if (empty($this->notify_url)) {
   die('notify_url error');
  }
  if (!preg_match("#^http:\/\/#i", $this->notify_url)) {
   $this->notify_url = "http://" . $_SERVER['HTTP_HOST'] . $this->notify_url;
  }
  return true;
 }
 /**
  * 生成支付(返回给APP)
  * @return boolean|mixed
  */
 public function doPay() {
  //检测构造参数
  $this->chkParam();
  return $this->createAppPara();
 }
 /**
  * APP统一下单
  */
 private function createAppPara()
 {
  $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
  $data["appid"]  = $this->config['appid'];//微信开放平台审核通过的应用APPID
  $data["body"]   = $this->body;//商品或支付单简要描述
  $data["mch_id"]  = $this->config['mch_id'];//商户号
  $data["nonce_str"] = $this->getRandChar(32);//随机字符串
  $data["notify_url"] = $this->notify_url;//通知地址
  $data["out_trade_no"] = $this->out_trade_no;//商户订单号
  $data["spbill_create_ip"] = $this->get_client_ip();//终端IP
  $data["total_fee"]  = $this->total_fee;//总金额
  $data["time_expire"]  = $this->time_expire;//交易结束时间
  $data["trade_type"]  = "APP";//交易类型
  $data["sign"]    = $this->getSign($data, $this->config['api_key']);//签名
  $xml  = $this->arrayToXml($data);
  $response = $this->postXmlCurl($xml, $url);
  //将微信返回的结果xml转成数组
  $responseArr = $this->xmlToArray($response);
  if(isset($responseArr["return_code"]) && $responseArr["return_code"]=='SUCCESS'){
   return $this->getOrder($responseArr['prepay_id']);
  }
  return $responseArr;
 }
 /**
  * 执行第二次签名,才能返回给客户端使用
  * @param int $prepayId:预支付交易会话标识
  * @return array
  */
 public function getOrder($prepayId)
 {
  $data["appid"]  = $this->config['appid'];
  $data["noncestr"] = $this->getRandChar(32);
  $data["package"] = "Sign=WXPay";
  $data["partnerid"] = $this->config['mch_id'];
  $data["prepayid"] = $prepayId;
  $data["timestamp"] = time();
  $data["sign"]  = $this->getSign($data, $this->config['api_key']);
  $data["packagestr"] = "Sign=WXPay";
  return $data;
 }
 /**
  * 异步通知信息验证
  * @return boolean|mixed
  */
 public function verifyNotify()
 {
  $xml = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : ''; 
  if(!$xml){
   return false;
  }
  $wx_back = $this->xmlToArray($xml);
  if(empty($wx_back)){
   return false;
  }
  $checkSign = $this->getVerifySign($wx_back, $this->config['api_key']); 
  if($checkSign=$wx_back['sign']){
   return $wx_back;
  }else{
   return false;
  } 
 }
}

2.创建控制器定义统一下单接口和支付后的异步回调接口:

//异步通知接口
 public function notifyandroid()
 {
  $wxpayandroid = new \Wxpayandroid;  //实例化微信支付类
  $verify_result = $wxpayandroid->verifyNotify();
  if ($verify_result['return_code']=='SUCCESS' && $verify_result['result_code']=='SUCCESS') {
  //商户订单号
  $out_trade_no = $verify_result['out_trade_no'];
  //交易号
  $trade_no  = $verify_result['transaction_id'];
  //交易状态
  $trade_status = $verify_result['result_code'];
  //支付金额
  $total_fee = $verify_result['total_fee']/100;
  //支付过期时间
  $pay_date  = $verify_result['time_end'];
  $order = new Order();
  $ret = $order->getOrderN2($out_trade_no); //获取订单信息
  $total_amount=$ret['money'];
  if ($total_amount==$total_fee) {
   // 验证成功 修改数据库的订单状态等 $result['out_trade_no']为订单号
   //此处写自己的逻辑代码
  }
   exit('<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>');
  }else{
   exit('<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[ERROR]]></return_msg></xml>');
  }
 }
 
 //调用统一下单接口生成预支付订单并把数据返回给APP
 public function wxpayandroid(Request $request)
 {
  $param = $request->param(); //接收值
 
  $tade_no = $param['orderCode'];
  $order = new Order(); //实例化订单
  $ret = $order->getOrderN2($tade_no); //查询订单信息
  $total_fee = $ret['money']; //订单总金额
  
  $wxpayandroid = new \Wxpayandroid;  //实例化微信支付类
  $res = $wxpayandroid->Weixinpayandroid($total_fee,$tade_no); //调用weixinpay方法
  
 }

封装一个支付类文件,并把配置参数放到支付类内,再定义控制器创建两个方法,这样两步就可以把手机APP微信支付搞定啦。

总结

以上所述是小编给大家介绍的PHP实现 APP端微信支付功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

PHP 相关文章推荐
一个PHP+MSSQL分页的例子
Oct 09 PHP
PHP 字符串 小常识
Jun 05 PHP
PHP XML error parsing SOAP payload on line 1
Jun 17 PHP
改写函数实现PHP二维/三维数组转字符串
Sep 13 PHP
php利用scws实现mysql全文搜索功能的方法
Dec 25 PHP
JavaScript实现滚动栏效果的方法
Apr 27 PHP
yii2.0使用Plupload实现带缩放功能的多图上传
Dec 22 PHP
Yii使用技巧大汇总
Dec 29 PHP
php中遍历二维数组并以表格的形式输出的方法
Jan 03 PHP
Yii2数据库操作常用方法小结
May 04 PHP
PHP实现的CURL非阻塞调用类
Jul 26 PHP
PHP根据key删除数组中指定的元素
Feb 28 PHP
thinkPHP实现基于ajax的评论回复功能
Jun 22 #PHP
php strftime函数的详细用法
Jun 21 #PHP
PHP获取本周所有日期或者最近七天所有日期的方法
Jun 20 #PHP
ThinkPHP5.0 图片上传生成缩略图实例代码说明
Jun 20 #PHP
thinkPHP框架实现的短信接口验证码功能示例
Jun 20 #PHP
thinkPHP3.2.2框架行为扩展及demo示例
Jun 19 #PHP
Laravel框架实现的记录SQL日志功能示例
Jun 19 #PHP
You might like
PHP的RSA加密解密方法以及开发接口使用
2018/02/11 PHP
PHP简单实现解析xml为数组的方法
2018/05/02 PHP
JavaScript 面向对象编程(1) 基础
2010/05/18 Javascript
Javascript实现滑块滑动改变值的实现代码
2013/04/12 Javascript
深入浅析Extjs中store分组功能的使用方法
2016/04/20 Javascript
jQuery视差滚动效果网页实现方法经验总结
2016/09/29 Javascript
Angular 4 依赖注入学习教程之FactoryProvider的使用(四)
2017/06/04 Javascript
如何理解Vue的作用域插槽的实现原理
2017/08/19 Javascript
javascript、php关键字搜索函数的使用方法
2018/05/29 Javascript
Vue中实现权限控制的方法示例
2019/06/07 Javascript
layui-tree实现Ajax异步请求后动态添加节点的方法
2019/09/23 Javascript
js实现随机点名程序
2020/09/17 Javascript
javascript设计模式 ? 抽象工厂模式原理与应用实例分析
2020/04/09 Javascript
easyUI 实现的后台分页与前台显示功能示例
2020/06/01 Javascript
vue实现单一筛选、删除筛选条件
2020/10/26 Javascript
Python的Django框架中设置日期和字段可选的方法
2015/07/17 Python
python如何在终端里面显示一张图片
2016/08/17 Python
Python使用当前时间、随机数产生一个唯一数字的方法
2017/09/18 Python
基于DATAFRAME中元素的读取与修改方法
2018/06/08 Python
python处理multipart/form-data的请求方法
2018/12/26 Python
python爬虫 2019中国好声音评论爬取过程解析
2019/08/26 Python
Python中关于浮点数的冷知识
2019/09/22 Python
python3 logging日志封装实例
2020/04/08 Python
在jupyter notebook中调用.ipynb文件方式
2020/04/14 Python
python实现简易版学生成绩管理系统
2020/06/22 Python
澳大利亚首个在线预订旅游网站:Wotif
2017/07/19 全球购物
Python使用openpyxl复制整张sheet
2021/03/24 Python
影视广告专业求职信
2014/09/02 职场文书
说好普通话圆梦你我他演讲稿
2014/09/21 职场文书
2014年大学班级工作总结
2014/11/14 职场文书
2014年妇幼保健工作总结
2014/12/08 职场文书
酒店收银员岗位职责
2015/04/07 职场文书
趣味运动会通讯稿
2015/07/18 职场文书
女方家长婚礼答谢词
2015/09/29 职场文书
学习《中小学教师职业道德规范》心得体会
2016/01/18 职场文书
python和anaconda的区别
2022/05/06 Python