• 技术文章 >php教程 >php手册

    ThinkPay最新微信支付公众号支付驱动分享

    2016-12-01 00:00:17原创602
    ThinkPay最新微信支付公众号支付驱动分享
    下面分享个ThinkPay的微信支付驱动,便于大家使用,如有疑问加QQ群讨论105108204 // +----------------------------------------------------------------------
    // | 零云 [ 简单 高效 卓越 ]
    // +----------------------------------------------------------------------
    // | Copyright (c) 2016 http://www.lingyun.net All rights reserved.
    // +----------------------------------------------------------------------
    // | Author: jry <598821125@qq.com>
    // +----------------------------------------------------------------------
    namespace Addons\Pay\ThinkPay\Pay\Driver;

    /**
    * 微信支付驱动
    */
    class Wxpay extends \Addons\Pay\ThinkPay\Pay\Pay
    {
    protected $gateway = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
    protected $orderquery = 'https://api.mch.weixin.qq.com/pay/orderquery';
    protected $config = array(
    'appid' => '',
    'appsecret' => '',
    'mchid' => '',
    'key' => '',
    );

    public function check()
    {
    if (!$this->config['appid'] || !$this->config['appsecret'] || !$this->config['mchid'] || !$this->config['key']) {
    E("微信支付设置有误!");
    }
    return true;
    }

    public function buildRequestForm($pay_data)
    {
    // APP支付
    if (C('IS_API')) {
    $param = array(
    'appid' => $this->config['appid'],
    'mch_id' => $this->config['mchid'],
    'nonce_str' => $this->getNonceStr(),
    'body' => $pay_data['body'],
    'out_trade_no' => $pay_data['out_trade_no'],
    'total_fee' => $pay_data['money'] * 100,
    'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
    'notify_url' => $this->config['notify_url'],
    'trade_type' => 'APP',
    );

    // 签名
    $param['sign'] = $this->MakeSign($param);
    $xml_param = $this->ToXml($param);
    $result = $this->FromXml($this->postXmlCurl($xml_param, $this->gateway));
    if ($result['return_code'] === 'SUCCESS') {
    if ($this->CheckSign($result)) {
    // 统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。
    // 参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay
    $prepayparams = array();
    $prepayparams['appid'] = $result['appid'];
    $prepayparams['partnerid'] = $result['mch_id'];
    $prepayparams['prepayid'] = $result['prepay_id'];
    $prepayparams['noncestr'] = $result['nonce_str'];
    $prepayparams['package'] = 'Sign=WXPay';
    $prepayparams['timestamp'] = time();
    $prepayparams['sign'] = $this->MakeSign($prepayparams); // 签名
    $return['json'] = json_encode($prepayparams);
    return $return;
    }
    }
    } else {
    // 获取用户openId,微信公众号JSAPI支付必须
    $openId = $this->GetOpenid();
    $param = array(
    'appid' => $this->config['appid'],
    'mch_id' => $this->config['mchid'],
    'nonce_str' => $this->getNonceStr(),
    'body' => $pay_data['body'],
    'out_trade_no' => $pay_data['out_trade_no'],
    'total_fee' => $pay_data['money'] * 100,
    'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
    'notify_url' => $this->config['notify_url'],
    'trade_type' => 'JSAPI',
    'openid' => $openId,
    );

    // 签名
    $param['sign'] = $this->MakeSign($param);
    $xml_param = $this->ToXml($param);
    $result = $this->FromXml($this->postXmlCurl($xml_param, $this->gateway));
    if ($result['return_code'] === 'SUCCESS') {
    if ($this->CheckSign($result)) {
    // 获取JSAPI所需参数
    $jsApiParameters = $this->GetJsApiParameters($result);
    $pay_page = <<



    微信支付





    该笔订单支付金额为{$pay_data['money']}







    EOF;
    return $pay_page;
    }
    } else {
    E("微信订单错误!" . $result['return_msg']);
    }
    }
    }

    /**
    * 异步通知验证
    */
    public function verifyNotify($notify)
    {
    //获取通知的数据
    if ($notify['return_code'] === 'SUCCESS') {
    if (!array_key_exists("transaction_id", $notify)) {
    E("输入参数不正确!");
    }
    $param['transaction_id'] = $notify["transaction_id"];
    $param['appid'] = $this->config['appid'];
    $param['mch_id'] = $this->config['mchid'];
    $param['nonce_str'] = $this->getNonceStr();
    $param['sign'] = $this->MakeSign($param);
    $xml_param = $this->ToXml($param);
    $result = $this->FromXml($this->postXmlCurl($xml_param, $this->orderquery));
    if ($this->CheckSign($result)) {
    $result['status'] = ($result['result_code'] == 'SUCCESS') ? true : false;
    $result['money'] = $result['total_fee'] / 100;
    $this->info = $result;
    return true;
    }
    } else {
    E('通知错误');
    }
    }

    /**
    *
    * 获取jsapi支付的参数
    * @param array $UnifiedOrderResult 统一支付接口返回的数据
    * @throws WxPayException
    *
    * @return json数据,可直接填入js函数作为参数
    */
    public function GetJsApiParameters($UnifiedOrderResult)
    {
    if (!array_key_exists("appid", $UnifiedOrderResult)
    || !array_key_exists("prepay_id", $UnifiedOrderResult)
    || $UnifiedOrderResult['prepay_id'] == "") {
    E("参数错误");
    }
    $jsapi['appId'] = $UnifiedOrderResult["appid"];
    $jsapi['timeStamp'] = (string) time();
    $jsapi['nonceStr'] = $this->getNonceStr();
    $jsapi['appId'] = $UnifiedOrderResult["appid"];
    $jsapi['package'] = "prepay_id=" . $UnifiedOrderResult['prepay_id'];
    $jsapi['signType'] = 'MD5';
    $jsapi['paySign'] = $this->MakeSign($jsapi);
    $parameters = json_encode($jsapi);
    return $parameters;
    }

    /**
    * 以post方式提交xml到对应的接口url
    *
    * @param string $xml 需要post的xml数据
    * @param string $url url
    * @param bool $useCert 是否需要证书,默认不需要
    * @param int $second url执行超时时间,默认30s
    * @throws WxPayException
    */
    private static function postXmlCurl($xml, $url, $useCert = false, $second = 30)
    {
    $ch = curl_init();
    //设置超时
    curl_setopt($ch, CURLOPT_TIMEOUT, $second);

    //如果有配置代理这里就设置代理
    // if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"
    // && WxPayConfig::CURL_PROXY_PORT != 0){
    // curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);
    // curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);
    // }
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //严格校验
    //设置header
    curl_setopt($ch, CURLOPT_HEADER, false);
    //要求结果为字符串且输出到屏幕上
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    if ($useCert == true) {
    //设置证书
    //使用证书:cert 与 key 分别属于两个.pem文件
    curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
    curl_setopt($ch, CURLOPT_SSLCERT, WxPayConfig::SSLCERT_PATH);
    curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
    curl_setopt($ch, CURLOPT_SSLKEY, WxPayConfig::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);
    E("curl出错,错误码:$error");
    }
    }

    /*
    * 产生随机字符串,不长于32位
    * @param int $length
    * @return 产生的随机字符串
    */
    public static function getNonceStr($length = 32)
    {
    $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
    $str = "";
    for ($i = 0; $i < $length; $i++) {
    $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
    }
    return $str;
    }

    /**
    * 生成签名
    * @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
    */
    public function MakeSign($param)
    {
    //签名步骤一:按字典序排序参数
    ksort($param);
    $string = $this->ToUrlParams($param);
    //签名步骤二:在string后加入KEY
    $string = $string . "&key=" . $this->config['key'];
    //签名步骤三:MD5加密
    $string = md5($string);
    //签名步骤四:所有字符转为大写
    $result = strtoupper($string);
    return $result;
    }

    /**
    *
    * 检测签名
    */
    public function CheckSign($param)
    {
    $sign = $this->MakeSign($param);
    if ($param['sign'] == $sign) {
    return true;
    } else {
    E("签名错误!");
    }
    }

    /**
    *
    * 拼接签名字符串
    * @param array $urlObj
    *
    * @return 返回已经拼接好的字符串
    */
    public function ToUrlParams($param)
    {
    $buff = "";
    foreach ($param as $k => $v) {
    if ($k != "sign" && $v != "" && !is_array($v)) {
    $buff .= $k . "=" . $v . "&";
    }
    }

    $buff = trim($buff, "&");
    return $buff;
    }

    /**
    * 输出xml字符
    * @throws WxPayException
    **/
    public function ToXml($param)
    {
    if (!is_array($param)
    || count($param) <= 0) {
    E("数组数据异常!");
    }

    $xml = "";
    foreach ($param as $key => $val) {
    if (is_numeric($val)) {
    $xml .= "<" . $key . ">" . $val . "";
    } else {
    $xml .= "<" . $key . ">";
    }
    }
    $xml .= "
    ";
    return $xml;
    }

    /**
    * 将xml转为array
    * @param string $xml
    * @throws WxPayException
    */
    public function FromXml($xml)
    {
    if (!$xml) {
    E("xml数据异常!");
    }
    //将XML转为array
    //禁止引用外部xml实体
    libxml_disable_entity_loader(true);
    $this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    return $this->values;
    }

    /**
    *
    * 通过跳转获取用户的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码
    $baseUrl = urlencode('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
    $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);
    //初始化curl
    $ch = curl_init();
    //设置超时
    curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    // if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"
    // && WxPayConfig::CURL_PROXY_PORT != 0){
    // curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);
    // curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);
    // }
    //运行curl,结果以jason形式返回
    $res = curl_exec($ch);
    curl_close($ch);
    //取出openid
    $data = json_decode($res, true);
    $this->data = $data;
    $openid = $data['openid'];
    return $openid;
    }

    /**
    *
    * 构造获取code的url连接
    * @param string $redirectUrl 微信服务器回跳的url,需要url编码
    *
    * @return 返回构造好的url
    */
    private function __CreateOauthUrlForCode($redirectUrl)
    {
    $urlObj["appid"] = $this->config['appid'];
    $urlObj["redirect_uri"] = "$redirectUrl";
    $urlObj["response_type"] = "code";
    $urlObj["scope"] = "snsapi_base";
    $urlObj["state"] = "#wechat_redirect";
    $bizString = $this->ToUrlParams($urlObj);
    return "https://open.weixin.qq.com/connect/oauth2/authorize?" . $bizString;
    }

    /**
    *
    * 构造获取open和access_toke的url地址
    * @param string $code,微信跳转带回的code
    *
    * @return 请求的url
    */
    private function __CreateOauthUrlForOpenid($code)
    {
    $urlObj["appid"] = $this->config['appid'];
    $urlObj["secret"] = $this->config['appsecret'];
    $urlObj["code"] = $code;
    $urlObj["grant_type"] = "authorization_code";
    $bizString = $this->ToUrlParams($urlObj);
    return "https://api.weixin.qq.com/sns/oauth2/access_token?" . $bizString;
    }
    }

    声明:本文原创发布php中文网,转载请注明出处,感谢您的尊重!如有疑问,请联系admin@php.cn处理
    上一篇:Linux安装pdo_mysql 下一篇:类似于交易猫淘手游交易平台,外带sdk打包后台
    大前端线上培训班

    相关文章推荐

    • 最完整PHP常用工具类大全,• 两千行代码的PHP学习笔记汇总,两千行php学习笔记• php学习的路线图分享及建议• PHP数组常用招式• 使用Apache的rewrite技术

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网