uniapp+thinkphp5实现微信支付(JSAPI支付)

前言

统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返回预支付订单号的接口,目前微信支付所有场景均使用这一接口。下面介绍的是其中JSAPI的支付实现流程与uniapp唤起微信支付流程

流程实现(后端)(PHP)

  1. 创建Wechatpay.PHP文件,放到指定文件目录下(我是放到了extend目录)
<?PHP

  class Wechatpay{


      /**
       * 模拟提交参数,支持https提交 可用于各类api请求
       * @param string $url : 提交的地址
       * @param array $data :POST数组
       * @param string $method : POST/GET,认GET方式
       * @return mixed
       */
      function curl_https($url, $xml='', $useCert=false){

          $ch = curl_init();
          //设置超时
          curl_setopt($ch, CURLOPT_TIMEOUT, 30);
          curl_setopt($ch,CURLOPT_URL, $url);

          //设置header
          curl_setopt($ch, CURLOPT_HEADER, FALSE);
          //要求结果为字符串且输出到屏幕上
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

          if(stripos($url,"https://")!==FALSE){
              curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
              curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
              curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
          }else{
              curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
              curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
          }
          if($useCert == true){
              //设置证书
              //使用证书:cert 与 key 分别属于两个.pem文件
              curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
              curl_setopt($ch,CURLOPT_SSLCERT,"");
              curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
              curl_setopt($ch,CURLOPT_SSLKEY,"");
          }
          //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);
              //echo $error;
          }

      }

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

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


      //数组转XML
      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;
      }

      //将XML转为array
      function xmlToArray($xml)
      {
          //禁止引用外部xml实体
          libxml_disable_entity_loader(true);
          $values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
          return $values;
      }

      /**
       * 获取随机字符串
       * @return mixed
       */
      function getRandString($len=12,$str='ABCDEFGHIJKLMnopQRSTUVWYXZabcdefghijklmnopqrstuvwyxz1234567890'){
          $strlen=strlen($str)-1;
          $string='';
          for ($i=0; $i < $len; $i++) {
              $r=rand(1,$strlen);
              $string=$string.$str[$r];
          }
          return $string;
      }


  }

2.定义公共变量

private $config = array(
        'appid' => "wxa******",    /*微信小程序的appid*/
        'appid_app' => "wx******",    /*微信开放平台上的应用id*/
        'mch_id' => "*******",   /*微信申请成功之后邮件中的商户id*/
        'api_key' => "*************",    /*在微信商户平台上自己设定的api密钥 32位*/
        'notify_url' => 'https://***',  /*支付回调地址,确保可以访问*/
    );

3.支付接口

public function JSPay($busid,$price,$code,$type,$attach){
        
        $businessInfo=$this->BusinessModel->find($busid);
        Loader::import('wechatpay.Wechatpay', EXTEND_PATH,".PHP");
            $wechatpay = new \Wechatpay();
            $url='https://api.mch.weixin.qq.com/pay/unifiedorder';
            
            $parameters=array(
                'appid'=>$this->config["appid"],//appID
                'mch_id'=>$this->config['mch_id'],//商户号
                'openid'=>$businessInfo['openid'],//用户openid
                'nonce_str'=>$wechatpay->getRandString(30),//随机字符串
                'body'=>'购买商品',//商品描述
                'out_Trade_no'=>$code,//商户订单号
                'total_fee'=>$price*100,//总金额 单位 分 
                // 'total_fee'=>1,//总金额 单位 分 
                'spbill_create_ip'=>$this->get_client_ip(),//终端IP
                'notify_url'=>$this->config["notify_url"],//通知地址
                'Trade_type'=>'JSAPI',//交易类型
                'sign_type'=>"MD5",
                'attach'=>$attach
            );
            //参数名ASCII码从小到大排序
            ksort($parameters);
            //统一下单签名
            $String = $wechatpay->ToUrlParams($parameters);
            //签名步骤二:在string后加入KEY
            $String = $String."&key=".$this->config['api_key']; // key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
            //签名步骤三:MD5加密 (一次签名)
            $time=time();
            $parameters['sign']=strtoupper(md5($String));
            $xmlData=$wechatpay->arrayToXml($parameters);
            $return=$wechatpay->xmlToArray($wechatpay->curl_https($url,$xmlData));
            if($return["return_code"]=="SUCCESS" && $return["result_code"]=="SUCCESS"){
                // 从预支付接口返回的参数中取得 prepay_id
                $prepay_id = $return['prepay_id'];
                // 构造二次签名的参数
                $signParams = array(
                    'appId' => $this->config["appid"],
                    'timeStamp' => (string)$time, // 时间戳需与统一下单时的时间戳保持一致
                    'nonceStr' => $wechatpay->getRandString(30), // 随机字符串
                    'package' => 'prepay_id=' . $prepay_id, // 统一下单接口返回的 prepay_id
                    'signType' => 'MD5'
                );
                // 将参数按照参数名 ASCII 码从小到大排序
                ksort($signParams);
                // 拼接成字符串
                $signString = $wechatpay->ToUrlParams($signParams);
                // 在字符串末尾加上商户密钥
                $signString = $signString . "&key=" . $this->config['api_key'];
                // 对签名字符串进行 MD5 加密并转大写 (二次签名)
                $sign = strtoupper(md5($signString));
                // 将签名加入返回给小程序的参数中
                $signParams['paySign'] = $sign;
                // 返回二次签名后的参数给小程序端
                $this->result($signParams,'1','二次签名成功!','json');
            }else{
                echo json_encode(array("status"=>false,"msg"=>$return));
            }
    }
/*
    获取当前服务器的IP
*/
    public function get_client_ip(){
        if ($_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 = "unkNown";
        }
        return $cip;
    }

  1. 支付回调
//微信支付回调接口
public function wxpaynotify(){
        $xml = file_get_contents('PHP://input');
        Loader::import('wechatpay.Wechatpay', EXTEND_PATH,".PHP");
        $wechatpay = new \Wechatpay();

        //将服务器返回的XML数据转化为数组
        $data = $wechatpay->xmlToArray($xml);
        // 保存微信服务器返回的签名sign
        $data_sign = $data['sign'];
        // sign不参与签名算法
        unset($data['sign']);
        $sign = $wechatpay->ToUrlParams($data);
        $payData=$sign;
        $sign=strtoupper(md5($sign."&key=".$this->config["api_key"]));
        // 判断签名是否正确  判断支付状态
        if ( ($sign===$data_sign) && ($data['result_code']=='SUCCESS') ) {
            //支付成功
            //其他操作
            $result = true;
        }else{
            $result = false;
        }
        // 返回状态给微信服务器
        if ($result) {
            $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        }else{
            $str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
        }
        echo $str;

    }

流程实现(前端)(uniapp)

// 调用 wx.requestPayment 方法发起支付请求
wx.requestPayment({
	appId: response.appid,
	timeStamp: String(response.timeStamp),
	nonceStr: response.nonceStr,
	package: response.package,
	signType: response.signType,
	paySign: response.paySign,
	success(res) {
		// 支付成功的处理逻辑
		console.log('支付成功', res);
		uni.showToast({
			title: '支付成功!',
			icon: 'none',
			duration: 1000
		})
	},
	fail(res) {
		// 支付失败的处理逻辑
		console.log('支付失败', res);
		uni.showToast({
			title: '已取消支付',
			icon: 'none',
			duration: 2000
		})
	}
});

原文地址:https://www.cnblogs.com/nothavebug/p/18271800

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返回预支付订单号的接口,目前微信支付所有场景均使用这一接口。下面介绍的是其中NATIVE的支付实现流程与PC端实现扫码支付流程
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返回预支付订单号的接口,目前微信支付所有场景均使用这一接口。下面介绍的是其中APP的支付的配置与实现流程
前言 之前做了微信登录,所以总结一下微信授权登录并获取用户信息这个功能的开发流程。 配置 1.首先得在微信公众平台申请一下微信小程序账号并获取到小程序的AppID和AppSecret https://mp.weixin.qq.com/cgi-bin/loginpage?url=%2Fwxamp%2F
FastAdmin是我第一个接触的后台管理系统框架。FastAdmin是一款开源且免费商用的后台开发框架,它基于ThinkPHP和Bootstrap两大主流技术构建的极速后台开发框架,它有着非常完善且强大的功能和便捷的开发体验,使我逐渐喜欢上了它。
之前公司需要一个内部的通讯软件,就叫我做一个。通讯软件嘛,就离不开通讯了,然后我就想到了长连接。这里本人用的是GatewayWorker框架。
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返回预支付订单号的接口,目前微信支付所有场景均使用这一接口。下面介绍的是其中JSAPI的支付实现流程
服务器优化必备:深入了解PHP8底层开发原理
Golang的网络编程:如何快速构建高性能的网络应用?
Golang和其他编程语言的对比:为什么它的开发效率更高?
PHP8底层开发原理揭秘:如何利用新特性创建出色的Web应用
将字符重新排列以形成回文(如果可能)在C++中
掌握PHP8底层开发原理和新特性:创建高效可扩展的应用程序
服务器性能优化必学:掌握PHP8底层开发原理
PHP8新特性和底层开发原理详解:优化应用性能的终极指南
将 C/C++ 代码转换为汇编语言
深入研究PHP8底层开发原理:创建高效可扩展的应用程序
C++程序查找法向量和迹
PHP8底层开发原理实战指南:提升服务器效能
重排数组,使得当 i 为偶数时,arr[i] >= arr[j],当 i 为奇数时,arr[i] <= arr[j],其中 j < i,使用 C++ 语言实现
Golang的垃圾回收:为什么它可以减少开发人员的负担?