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 相关文章推荐
PHP使用者状态管理功能的应用
Oct 09 PHP
十天学会php(1)
Oct 09 PHP
php5中类的学习
Mar 28 PHP
PHP学习之输出字符串(echo,print,printf,print_r和var_dump)
Apr 17 PHP
php调用新浪短链接API的方法
Nov 08 PHP
php将文本文件转换csv输出的方法
Dec 31 PHP
php+ajax实时刷新简单实例
Feb 25 PHP
PHP如何实现Unicode和Utf-8编码相互转换
Jul 29 PHP
PHP扩展迁移为PHP7扩展兼容性问题记录
Feb 15 PHP
PHP实现执行外部程序的方法详解
Aug 17 PHP
ThinkPHP3.2框架自定义配置和加载用法示例
Jun 14 PHP
php设计模式之职责链模式实例分析【星际争霸游戏案例】
Mar 27 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模板,主要想体现一下思路
2006/12/25 PHP
PHP_MySQL教程-第一天
2007/03/18 PHP
PHP 上传文件大小限制
2009/07/05 PHP
PHP Session机制简介及用法
2014/08/19 PHP
php版微信开发Token验证失败或请求URL超时问题的解决方法
2016/09/23 PHP
Extjs407 getValue()和getRawValue()区别介绍
2013/05/21 Javascript
js简易namespace管理器 实例代码
2013/06/21 Javascript
JS、DOM和JQuery之间的关系示例分析
2014/04/09 Javascript
js仿土豆网带缩略图的焦点图片切换效果实现方法
2015/02/23 Javascript
js使用split函数按照多个字符对字符串进行分割的方法
2015/03/20 Javascript
TypeScript具有的几个不同特质
2015/04/07 Javascript
JS中多步骤多分步的StepJump组件实例详解
2016/04/01 Javascript
jQuery使用正则表达式限制文本框只能输入数字
2016/06/18 Javascript
JS判断form内所有表单是否为空的简单实例
2016/09/09 Javascript
基于jQuery实现Tabs选项卡自定义插件
2016/11/21 Javascript
two.js之实现动画效果示例
2017/11/06 Javascript
详解基于Vue-cli搭建的项目如何和后台交互
2018/06/29 Javascript
JS实现星星海特效
2019/12/24 Javascript
vue学习笔记之Vue中css动画原理简单示例
2020/02/29 Javascript
ES6中Set和Map用法实例详解
2020/03/02 Javascript
微信小程序 wx:for 与 wx:for-items 与 wx:key的正确用法
2020/05/19 Javascript
vue实现下载文件流完整前后端代码
2020/11/17 Vue.js
Python类属性与实例属性用法分析
2015/05/09 Python
Python实现在matplotlib中两个坐标轴之间画一条直线光标的方法
2015/05/20 Python
Python中if __name__ == '__main__'作用解析
2015/06/29 Python
python实现程序重启和系统重启方式
2020/04/16 Python
世界上最大的二手相机店:KEN
2017/05/17 全球购物
在校生钳工实习自我鉴定
2013/09/19 职场文书
初一体育教学反思
2014/01/29 职场文书
招股说明书范本
2014/05/06 职场文书
运动会口号8字
2014/06/07 职场文书
2014四风问题对照检查材料范文
2014/09/15 职场文书
党的群众路线调研报告
2014/11/03 职场文书
2015年幼儿园大班工作总结
2015/04/25 职场文书
Python3 多线程(连接池)操作MySQL插入数据
2021/06/09 Python
Windows10安装Apache2.4的方法步骤
2022/06/25 Servers