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新手上路(十二)
Oct 09 PHP
Fine Uploader文件上传组件应用介绍
Jan 06 PHP
php存储过程调用实例代码
Feb 03 PHP
解析php DOMElement 操作xml 文档的实现代码
May 10 PHP
PHP通过内置函数memory_get_usage()获取内存使用情况
Nov 20 PHP
php生成html文件方法总结
Dec 01 PHP
php一个解析字符串排列数组的方法
May 12 PHP
PHP面向对象编程之深入理解方法重载与方法覆盖(多态)
Dec 24 PHP
php 类中的常量、静态属性、非静态属性的区别
Apr 09 PHP
PHP使用函数用法详解
Sep 30 PHP
PHP观察者模式定义与用法实例分析
Mar 22 PHP
laravel validate 设置为中文的例子(验证提示为中文)
Sep 29 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
Windows2003 下 MySQL 数据库每天自动备份
2006/12/21 PHP
PHP 获取远程网页内容的代码(fopen,curl已测)
2011/06/06 PHP
PHP解析目录路径的3个函数总结
2014/11/18 PHP
CodeIgniter基于Email类发邮件的方法
2016/03/29 PHP
js+CSS 图片等比缩小并垂直居中实现代码
2008/12/01 Javascript
关于javascript document.createDocumentFragment()
2009/04/04 Javascript
javascript动态改变img的src属性图片不显示的解决方法
2010/10/20 Javascript
微信小程序 上传头像的实例详解
2017/10/27 Javascript
JS实现多物体运动的方法详解
2018/01/23 Javascript
解决Mac node版本升级失败的问题
2018/05/16 Javascript
微信小程序倒计时功能实例代码
2018/07/17 Javascript
在Vue组件中获取全局的点击事件方法
2018/09/06 Javascript
VUEX 数据持久化,刷新后重新获取的例子
2019/11/12 Javascript
详解JavaScript 事件流
2020/09/02 Javascript
原生小程序封装跑马灯效果
2020/10/21 Javascript
分析在Python中何种情况下需要使用断言
2015/04/01 Python
python简单实现基数排序算法
2015/05/16 Python
python中的编码知识整理汇总
2016/01/26 Python
基于python爬虫数据处理(详解)
2017/06/10 Python
selenium3+python3环境搭建教程图解
2018/12/07 Python
Python3 Post登录并且保存cookie登录其他页面的方法
2018/12/28 Python
详解Python 多线程 Timer定时器/延迟执行、Event事件
2019/06/27 Python
解决Python Matplotlib绘图数据点位置错乱问题
2020/05/16 Python
纯css3实现思维导图样式示例
2018/11/01 HTML / CSS
Clarins娇韵诗英国官网:来自法国的天然护肤品牌
2017/04/18 全球购物
基本款天堂:Everlane
2017/05/13 全球购物
兰芝美国网上商城:购买LANEIGE睡眠面膜等
2017/06/30 全球购物
经济实惠的豪华家具:My-Furniture
2019/03/12 全球购物
Notino匈牙利:购买香水和化妆品
2019/04/12 全球购物
编码实现字符串转整型的函数
2012/06/02 面试题
以下为Windows NT 下的32 位C++程序,请计算sizeof 的值
2016/12/07 面试题
大学运动会通讯稿
2014/01/28 职场文书
消防安全责任书范本
2014/04/15 职场文书
群众路线自我剖析范文
2014/11/04 职场文书
瘦西湖导游词
2015/02/03 职场文书
大学生求职简历自我评价
2015/03/02 职场文书