thinkphp微信浏览器内拉起微信支付

Posted Champion-水龙果

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了thinkphp微信浏览器内拉起微信支付相关的知识,希望对你有一定的参考价值。

 

vendor/wxpay/pay.php
<?php
/*
+-----------------------------------+
| 微信支付类 |
+-----------------------------------+
*/
require_once \'config.php\';

class pay{

  public $params = array();
  private $url = \'https://api.mch.weixin.qq.com/pay/unifiedorder\';
  private $prepay_id; //统一下单号

  /**
  *
  * 获取jsapi支付的参数
  * @param array $UnifiedOrderResult 统一支付接口返回的数据
  * @throws WxPayException
  * @return json数据,可直接填入js函数作为参数
  */
  public function GetJsApiParameters()
  {
    $this->doneOrder();
    $timeStamp = time();
    $this->params = array(
      \'appId\' => Config::APPID,
      \'timeStamp\'=> "$timeStamp",
      \'nonceStr\' => $this->getNonceStr(),
      \'package\' => \'prepay_id=\' . $this->prepay_id,
      \'signType\' => \'MD5\'
    );
    $this->params[\'paySign\'] = $this->MakeSign();
  }

  /**
  * 统一下单
  * @return array
  */
  public function doneOrder(){
    $this->params[\'mch_id\'] = Config::MCHID;
    $this->params[\'appid\'] = Config::APPID;
    $this->params[\'sign\'] = $this->MakeSign($this->params);
    $response = $this->FromXml($this->postXmlCurl($this->ToXml(), $this->url, false));
    $this->prepay_id = $response[\'prepay_id\'];

  }

  /**
  * 验证
  * @return array 返回数组格式的notify数据
  */
  public function notify(){
    //获取xml
    $xml = file_get_contents(\'php://input\');
    //转成php数组
    $this->params = $this->FromXml($xml);
    //保存原sign
    $data_sign = $this->params[\'sign\'];
    //sign不参与签名
    unset($this->params[\'sign\']);
    $sign = $this->makeSign();
    //判断签名是否正确 判断支付状态
    if ($sign===$data_sign && $this->params[\'return_code\']==\'SUCCESS\' && $this->params[\'result_code\']==\'SUCCESS\') {
      $result = $this->params;
      $str = \'<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>\';
    }else{
      $result = false;
      $str = \'<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>\';
    }
    echo $str;
    return $result;
  }


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

  /**
  * 格式化参数格式化成url参数
  */
  public function ToUrlParams()
  {
    $buff = \'\';
    foreach($this->params as $k => $v)
    {
      if($k!=\'sign\' && $v!=\'\' && !is_array($v)){
        $buff .= $k . \'=\' . $v . \'&\';
      }
    }
    $buff = trim($buff, \'&\');
    return $buff;
  }

  /**
  * 以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(Config::CURL_PROXY_HOST != "0.0.0.0" && Config::CURL_PROXY_PORT != 0){
      curl_setopt($ch,CURLOPT_PROXY, Config::CURL_PROXY_HOST);
      curl_setopt($ch,CURLOPT_PROXYPORT, Config::CURL_PROXY_PORT);
    }
    curl_setopt($ch,CURLOPT_URL, $url);
    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
    curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);//严格校验
    //设置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, Config::SSLCERT_PATH);
      curl_setopt($ch,CURLOPT_SSLKEYTYPE,\'PEM\');
      curl_setopt($ch,CURLOPT_SSLKEY, Config::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);
      throw new Exception("curl出错,错误码:$error");
    }
  }

  /**
  * 输出xml字符
  * @throws WxPayException
  **/
  public function ToXml()
  {
    if(!is_array($this->params) || count($this->params) <= 0)
    {
      throw new Exception(\'数组数据异常!\');
    }
    $xml = \'<xml>\';
    foreach($this->params as $key=>$val)
    {
      if(is_numeric($val)){
        $xml .= \'<\'.$key.\'>\'.$val.\'</\'.$key.\'>\';
      }else{
        $xml .= \'<\'.$key.\'><![CDATA[\'.$val.\']]></\'.$key.\'>\';
      }
    }
    $xml .= \'</xml>\';
    return $xml;
  }

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

  /**
  *
  * 产生随机字符串,不长于32位
  * @param int $length
  * @return 产生的随机字符串
  */
  public 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;
  }
}
?>

  

 vendor/wxpay/config.php
<?php
/**
* 配置账号信息
*/

class Config
{
//=======【基本信息设置】=====================================
//
/**
* TODO: 修改这里配置为您自己申请的商户信息
* 微信公众号信息配置
*
* APPID:绑定支付的APPID(必须配置,开户邮件中可查看)
*
* MCHID:商户号(必须配置,开户邮件中可查看)
*
* KEY:商户支付密钥,参考开户邮件设置(必须配置,登录商户平台自行设置)
* 设置地址:https://pay.weixin.qq.com/index.php/account/api_cert
*
* APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置, 登录公众平台,进入开发者中心可设置),
* 获取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN
* @var string
*/
const APPID = \'wx000000000000\';
const MCHID = \'xxxxxxx\';
const KEY = \'xxxxxxxxxxxxxxxxxxxxxxxxxxx\';
const APPSECRET = \'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\';

//=======【证书路径设置】=====================================
  /**
  * TODO:设置商户证书路径
  * 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要,可登录商户平台下载,
  * API证书下载地址:https://pay.weixin.qq.com/index.php/account/api_cert,下载之前需要安装商户操作证书)
  * @var path
  */
  const SSLCERT_PATH = \'cert/apiclient_cert.pem\';
  const SSLKEY_PATH = \'cert/apiclient_key.pem\';

//=======【curl代理设置】===================================
  /**
  * TODO:这里设置代理机器,只有需要代理的时候才设置,不需要代理,请设置为0.0.0.0和0
  * 本例程通过curl使用HTTP POST方法,此处可修改代理服务器,
  * 默认CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0,此时不开启代理(如有需要才设置)
  * @var unknown_type
  */
  const CURL_PROXY_HOST = "0.0.0.0";//"10.152.18.220";
  const CURL_PROXY_PORT = 0;//8080;

//=======【上报信息配置】===================================
  /**
  * TODO:接口调用上报等级,默认紧错误上报(注意:上报超时间为【1s】,上报无论成败【永不抛出异常】,
  * 不会影响接口调用流程),开启上报之后,方便微信监控请求调用的质量,建议至少
  * 开启错误上报。
  * 上报等级,0.关闭上报; 1.仅错误出错上报; 2.全量上报
  * @var int
  */
  const REPORT_LEVENL = 0;
}

  

 vendor/cert/apiclient_cert.pem vendor/cert/apiclient_key.pem 控制器Controller
Vendor(\'wechat.pay\');

class Wechat extends Base
{
  public function wxPay(){
    $payobj = new \\pay();    
    $openid    = input(\'post.openid/s\'); 
    $needMoney = input(\'post.money/s\');   
    if (!empty($openid)) {
      //统一下单
      $orderid = $this->build_order_no();
      $notify_url = \'http://xxxxxx/Wechat/notify\';

      $payobj->params = [
        \'openid\' => $openid,
        \'body\' => \'body\',
        \'out_trade_no\' => $orderid,
        \'total_fee\' => $needMoney * 100, 
        \'nonce_str\' => $this->getNonceStr(),
        \'spbill_create_ip\' => getIp(),
        \'notify_url\' => $notify_url,
        \'trade_type\' => \'JSAPI\'
      ];

      $payobj->GetJsApiParameters();
      $data = [
        \'order_num\' => $orderid,
        \'user_open_id\' => $openid,
        \'pay_money\' => $needMoney,
      ];
   

      $oid = Db::name(\'order_pay\')->insert($data);
      if($oid){
        return [\'status\'=>\'0\',\'info\'=>\'ok\',\'data\'=>json_encode($payobj->params)];
      }
      return [\'status\'=>\'-1\',\'info\'=>\'下单失败,请重试\'];

    }
  }

  //微信支付回调
  public function notify(){ 
    $payobj = new \\pay();
    $result = $payobj->notify();
    if($result){
      //成功后回调
      $where[\'order_num\'] = $result[\'out_trade_no\'];
      //根据需求处理逻辑

    }
  }


  

  /**
  * 生成订单号码
  * @return string
  */
  public function build_order_no() {
    return date(\'Ymd\') . substr(implode(NULL, array_map(\'ord\', str_split(substr(uniqid(), 7, 6), 1))), 0, 8) . mt_rand(100,999);
  }

  /**
  * 产生随机字符串,不长于32位
  * @param int $length
  * @return 产生的随机字符串
  */
  public 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;
  }

}

  

 页面上View 引用jquery; 引用layui.js 引用jweixin-1.1.0.js
<script type="text/javascript">

  $.ajax({
    url:"http://xxxxxx/Wechat/wxPay",
    type:\'post\',
    data:{\'money\':money,\'openid\':openid},
    async : false,
    dataType: "JSON",
    success:function(data){
      if(data.status === \'0\'){
        var params = $.parseJSON(data.data);
        // alert(data.url);
        WeixinJSBridge.invoke(\'getBrandWCPayRequest\', params, function(res){
          if(res.err_msg == \'get_brand_wcpay_request:cancel\'){
            layer.open({
              content: \'您消了此次支付\'
              ,skin: \'msg\'
              ,time: 5 //2秒后自动关闭
            });
          }else if(res.err_msg == \'get_brand_wcpay_request:fail\'){
            layer.open({
              content: \'支付失败,请重新支付\'
              ,skin: \'msg\'
              ,time: 5 //2秒后自动关闭
            });
          }else if(res.err_msg == \'get_brand_wcpay_request:ok\'){
            layer.open({
              content: \'支付成功\'
              ,skin: \'msg\'
              ,time: 5 //2秒后自动关闭
            });
            setTimeout(function(){
              window.location.href = \'index.html\';
            },2000);
          }else{
            layer.open({
              content: "未知错误"+res.error_msg
              ,skin: \'msg\'
              ,time: 5 //2秒后自动关闭
            });
          }
        });
    }else{
      layer.open({
        content: data.info
        ,skin: \'msg\'
        ,time: 5 //2秒后自动关闭
      });
      return false;
    }
  }
});

</script>

 

以上是关于thinkphp微信浏览器内拉起微信支付的主要内容,如果未能解决你的问题,请参考以下文章

APP内拉起微信支付的解决方案

iOS H5拉起微信支付

iOS开发-处理H5拉起微信支付返回到Safari情况

android拉起微信支付,付款后崩溃

企业微信中的应用调起微信支付,支付不成功

Android 微信H5支付,无法拉起微信支付页面