PHP 微信支付类 demo


Posted in PHP onNovember 30, 2015

一切尽在代码中,代码附有注释,欢迎大家参考。

<?php
class WxpayService
{
  protected $mchid;
  protected $appid;
  protected $key;
  public function __construct($mchid, $appid, $key)
  {
    $this->mchid = $mchid; // 微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
    $this->appid = $appid; //公众号APPID 通过微信支付商户资料审核后邮件发送
    $this->key = $key;   //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
  }
  /**
   * @param string $openid 调用【网页授权获取用户信息】接口获取到用户在该公众号下的Openid
   * @param float $totalFee 收款总费用 单位元
   * @param string $outTradeNo 唯一的订单号
   * @param string $orderName 订单名称
   * @param string $notifyUrl 支付结果通知url 不要有问号
   *   https://mp.weixin.qq.com/ 微信支付-开发配置-测试目录
   *   测试目录 http://mp.izhanlue.com/paytest/  最后需要斜线,(需要精确到二级或三级目录)
   * @return string
   */
  public function createJsBizPackage($openid, $totalFee, $outTradeNo, $orderName, $notifyUrl, $timestamp)
  {
    $config = array(
      'mch_id' => $this->mchid,
      'appid' => $this->appid,
      'key' => $this->key,
    );
    $unified = array(
      'appid' => $config['appid'],
      'attach' => '支付',             //商家数据包,原样返回
      '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));
    /*
    <xml>
    <return_code><![CDATA[SUCCESS]]></return_code>
    <return_msg><![CDATA[OK]]></return_msg>
    <appid><![CDATA[wx00e5904efec77699]]></appid>
    <mch_id><![CDATA[1220647301]]></mch_id>
    <nonce_str><![CDATA[1LHBROsdmqfXoWQR]]></nonce_str>
    <sign><![CDATA[ACA7BC8A9164D1FBED06C7DFC13EC839]]></sign>
    <result_code><![CDATA[SUCCESS]]></result_code>
    <prepay_id><![CDATA[wx2015032016590503f1bcd9c30421762652]]></prepay_id>
    <trade_type><![CDATA[JSAPI]]></trade_type>
    </xml>
    */
    $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);
      /*
      NOAUTH 商户无此接口权限
      NOTENOUGH 余额不足
      ORDERPAID 商户订单已支付
      ORDERCLOSED 订单已关闭
      SYSTEMERROR 系统错误
      APPID_NOT_EXIST   APPID不存在
      MCHID_NOT_EXIST MCHID不存在
      APPID_MCHID_NOT_MATCH appid和mch_id不匹配
      LACK_PARAMS 缺少参数
      OUT_TRADE_NO_USED 商户订单号重复
      SIGNERROR 签名错误
      XML_FORMAT_ERROR XML格式错误
      REQUIRE_POST_METHOD 请使用post方法
      POST_DATA_EMPTY post数据为空
      NOT_UTF8 编码格式错误
      */
    }
    //$unifiedOrder->trade_type 交易类型 调用接口提交的交易类型,取值如下:JSAPI,NATIVE,APP
    //$unifiedOrder->prepay_id 预支付交易会话标识 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
    //$unifiedOrder->code_url 二维码链接 trade_type为NATIVE是有返回,可将该参数值生成二维码展示出来进行扫码支付
    $arr = array(
      "appId" => $config['appid'],
      "timeStamp" => $timestamp,
      "nonceStr" => self::createNonceStr(),
      "package" => "prepay_id=" . $unifiedOrder->prepay_id,
      "signType" => 'MD5',
    );
    $arr['paySign'] = self::getSign($arr, $config['key']);
    return $arr;
  }
  public function notify()
  {
    $config = array(
      'mch_id' => $this->mchid,
      'appid' => $this->appid,
      'key' => $this->key,
    );
    $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
    //error_log($postStr, 3, './str.txt');
    /*
    $postStr = '<xml>
    <appid><![CDATA[wx00e5904efec77699]]></appid>
    <attach><![CDATA[支付测试]]></attach>
    <bank_type><![CDATA[CMB_CREDIT]]></bank_type>
    <cash_fee><![CDATA[1]]></cash_fee>
    <fee_type><![CDATA[CNY]]></fee_type>
    <is_subscribe><![CDATA[Y]]></is_subscribe>
    <mch_id><![CDATA[1220647301]]></mch_id>
    <nonce_str><![CDATA[a0tZ41phiHm8zfmO]]></nonce_str>
    <openid><![CDATA[oU3OCt5O46PumN7IE87WcoYZY9r0]]></openid>
    <out_trade_no><![CDATA[550bf2990c51f]]></out_trade_no>
    <result_code><![CDATA[SUCCESS]]></result_code>
    <return_code><![CDATA[SUCCESS]]></return_code>
    <sign><![CDATA[F6F519B4DD8DB978040F8C866C1E6250]]></sign>
    <time_end><![CDATA[20150320181606]]></time_end>
    <total_fee>1</total_fee>
    <trade_type><![CDATA[JSAPI]]></trade_type>
    <transaction_id><![CDATA[1008840847201503200034663980]]></transaction_id>
    </xml>';
    */
    $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
    if ($postObj === false) {
      die('parse xml error');
    }
    if ($postObj->return_code != 'SUCCESS') {
      die($postObj->return_msg);
    }
    if ($postObj->result_code != 'SUCCESS') {
      die($postObj->err_code);
    }
    $arr = (array)$postObj;
    unset($arr['sign']);
    if (self::getSign($arr, $config['key']) == $postObj->sign) {
      // $mch_id = $postObj->mch_id; //微信支付分配的商户号
      // $appid = $postObj->appid; //微信分配的公众账号ID
      // $openid = $postObj->openid; //用户在商户appid下的唯一标识
      // $transaction_id = $postObj->transaction_id;//微信支付订单号
      // $out_trade_no = $postObj->out_trade_no;//商户订单号
      // $total_fee = $postObj->total_fee; //订单总金额,单位为分
      // $is_subscribe = $postObj->is_subscribe; //用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
      // $attach = $postObj->attach;//商家数据包,原样返回
      // $time_end = $postObj->time_end;//支付完成时间
      echo '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
      return $postObj;
    }
  }
  /**
   * curl get
   *
   * @param string $url
   * @param array $options
   * @return mixed
   */
  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;
  }
  /**
   * 例如:
   * appid:  wxd930ea5d5a258f4f
   * mch_id:  10000100
   * device_info: 1000
   * Body:  test
   * nonce_str: ibuaiVcKdpRxkhJA
   * 第一步:对参数按照 key=value 的格式,并按照参数名 ASCII 字典序排序如下:
   * stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_i
   * d=10000100&nonce_str=ibuaiVcKdpRxkhJA";
   * 第二步:拼接支付密钥:
   * stringSignTemp="stringA&key=192006250b4c09247ec02edce69f6a2d"
   * sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7"
   */
  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;
  }
}

以上代码大家都能看得懂吧,有哪里不明白的地方欢迎给我留言,我会在第一时间和大家取得联系的。谢谢大家对三水点靠木网站的支持。

PHP 相关文章推荐
使用 eAccelerator加速PHP代码的目的
Mar 16 PHP
php array_unique之后json_encode需要注意
Jan 02 PHP
ThinkPHP的RBAC(基于角色权限控制)深入解析
Jun 17 PHP
php中并发读写文件冲突的解决方案
Oct 25 PHP
PHP资源管理框架Assetic简介
Jun 12 PHP
Windows下的PHP 5.3.x安装 Zend Guard Loader教程
Sep 06 PHP
PHP输入输出流学习笔记
May 12 PHP
php实现在站点里面添加邮件发送的功能
Apr 28 PHP
PHP数组生成XML格式数据的封装类实例
Nov 10 PHP
PHP使用curl函数发送Post请求的注意事项
Nov 26 PHP
Ajax+PHP实现的删除数据功能示例
Feb 12 PHP
php web环境和命令行环境下查找php.ini的位置
Jul 17 PHP
PHP CURL或file_get_contents获取网页标题的代码及两者效率的稳定性问题
Nov 30 #PHP
php curl抓取网页的介绍和推广及使用CURL抓取淘宝页面集成方法
Nov 30 #PHP
PHP curl模拟登录带验证码的网站
Nov 30 #PHP
PHP可变函数学习小结
Nov 29 #PHP
PHP可变变量学习小结
Nov 29 #PHP
PHP中对数组的一些常用的增、删、插操作函数总结
Nov 27 #PHP
详解PHP对数组的定义以及数组的创建方法
Nov 27 #PHP
You might like
php下把数组保存为文件格式的实例应用
2010/02/08 PHP
php模仿asp Application对象在线人数统计实现方法
2015/01/04 PHP
php实现的用户查询类实例
2015/06/18 PHP
PHP+shell实现多线程的方法
2015/07/01 PHP
PHP获取不了React Native Fecth参数的解决办法
2016/08/26 PHP
Win10 下安装配置IIS + MySQL + nginx + php7.1.7
2017/08/04 PHP
PHP通过文件保存和更新信息的方法分析
2019/09/12 PHP
JavaScript中switch判断容易犯错的一个细节
2014/08/27 Javascript
jQuery使用post方法提交数据实例
2015/03/25 Javascript
Bootstrap入门书籍之(三)栅格系统
2016/02/17 Javascript
基于BootStrap Metronic开发框架经验小结【七】数据的导入、导出及附件的查看处理
2016/05/12 Javascript
Vue.JS项目中5个经典Vuex插件
2017/11/28 Javascript
webpack 打包压缩js和css的方法示例
2018/03/20 Javascript
video.js 实现视频只能后退不能快进的思路详解
2018/08/09 Javascript
vue项目首屏加载时间优化实战
2019/04/23 Javascript
Vue 中文本内容超出规定行数后展开收起的处理的实现方法
2019/04/28 Javascript
vue 实现LED数字时钟效果(开箱即用)
2019/12/08 Javascript
JS数据类型STRING使用实例解析
2019/12/18 Javascript
如何使用Javascript中的this关键字
2020/05/28 Javascript
[00:32]2018DOTA2亚洲邀请赛OpTic出场
2018/04/03 DOTA
python实现socket客户端和服务端简单示例
2014/02/24 Python
python定时器(Timer)用法简单实例
2015/06/04 Python
利用django如何解析用户上传的excel文件
2017/07/24 Python
python下载文件记录黑名单的实现代码
2017/10/24 Python
python3.4.3下逐行读入txt文本并去重的方法
2018/04/29 Python
python 获取当天每个准点时间戳的实例
2018/05/22 Python
Python之两种模式的生产者消费者模型详解
2018/10/26 Python
10款最好的Python开发编辑器
2019/07/03 Python
Python画图实现同一结点多个柱状图的示例
2019/07/07 Python
python3实现mysql导出excel的方法
2019/07/31 Python
django 实现celery动态设置周期任务执行时间
2019/11/19 Python
利用CSS3 动画 绘画 圆形动态时钟
2018/03/20 HTML / CSS
html5 利用重力感应实现摇一摇换颜色可用来做抽奖等等
2014/05/07 HTML / CSS
HTML5 input placeholder 颜色修改示例
2014/05/30 HTML / CSS
幼儿教师国培感言
2014/02/19 职场文书
八月迷情观后感
2015/06/11 职场文书