微信小程序支付(附源码)

使用的是JSAPI
API 业务流程文档
https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_4&index=3
实现步骤
1,小程序js 点击事件触发 payoff方法 调起请求 换取openid
2,openid换取成功后 执行统一下单方法xiadan();
3 , 统一下单执行成功后 运行调起支付弹窗 requestPayment

/* 获取用户openid */
  payoff:function(e){
    var that = this;
    /* 获取openid */
    wx.login({
      success:function(res){
        console.log(res);
        console.log(res.code)
        var code= res.code;
        wx.request({
          url:'https://ehome.modongyun.com/api/Play',
          data:{code},
          success:function(res2){
            console.log('返回数据:'+res2)
            console.log('openid:'+res2.data.openid)
            that.setData({
              openid:res2.data.openid
            })
            /* 下单 */
            that.xiadan();
          }
        })
      }
    })
  },
  
  //下单
  xiadan:function(){
    var that = this;
    console.log('openid:',this.data.openid)
    wx.request({
      url:"https://ehome.modongyun.com/api/Play/pay",
      method:'GET',
      header:{'content-type':'application/x-www-form-urlencoded'},
      data:{
        openid:this.data.openid,
        attach:'attach',  //附加消息
        total_fee:'1',      //1分钱
        body:'bodybodybodybody'
      },
      success:function(res){
        console.log('下单',res);
        console.log(res.data);
         
        /* 申请支付  */
        that.requestPayment(res.data);
      }
    })
    
  },
  
  /* 申请支付  */
  requestPayment:function(obj){
    console.log('申请支付:',obj)
    
    
    
    var that = this;
    var pack = obj.package;
  // (obj.timeStamp).toString(),
    console.log('timeStamp1:',obj.timeStamp)
    console.log('package1:',pack)
    var data ={
      'nonceStr':obj.nonceStr,
      'package':pack,
      'paySign':obj.paySign,
      'signType':obj.signType,
      'timeStamp':obj.timeStamp,
      }
      
      console.log(data,'data')
    
    wx.requestPayment({
      'nonceStr':obj.nonceStr,
      'package':pack,
      'paySign':obj.paySign,
      'signType':obj.signType,
      'timeStamp':obj.timeStamp,
      'success':function(res){
        wx.showModal({
          title:'提示',
          content:'成功支付1分',
          showCancel:false
        })
      },
      'fail':function(res){
          console.log('失败的原因1:',res)
      
        console.log('package2:',pack)
        wx.showModal({
          title:'提示',
          content:'支付失败',
          showCancel:false
        })
      },
      'complete':function(res){
        console.log('complete原因1:',res)
      }
      
    })
    
    
    
  }

后台接口实现
前提:请先检查你的小程序是否配置了业务域名 即此网址能否给访问
请检查你的小程序是否关联了商户平台
商户平台是否有支付权限(大部分是有的 偶尔有些商户平台很久没有支付行为会给关闭 需重新开启 应该会有t+1的延迟 )

项目开始
先取到 小程序appId ; 小程序 secret ; 商户id ; 商户api key ;

index() 是换取openid的 传入参数获取就行 基本不会有问题不做详细解释了

pay() 是 统一下单api的方法

这个最让人头疼的就是签名了 大部分时间基本都是调整签名认证是吧这个问题了
签名 就是将所需要的参数 按 开头abcd的方式排序 排序后进行md5加密 然后转成大写就行了
实际上很简单 但是要注意一些问题 如 输入中文 或者 网址时 会被转义 所有就会造成签名认证失败的问题
第二个问题就是注意是要用字符串的 有个时间戳参数必须转换成字符串才行 注意你的参数不要写错了 不然会引起签名失败 我的代码已经做好了这些问题 可以直接使用
统一下单api参数中有个回调的参数 notify_url 就是支付成功后 微信会告诉你已经成功了 然后你需要给与回复 不然微信将会一直发送这个通知给你

notify() 支付成功后的回调地址 回调地址中有个位置 需要支付金额和你数据库的金额相等 目前我只用了1来表示 所以只能支付成功一分钟 需要改成你自己的验证数据库的金额

private $appid  = 'xxxxxxxx'; //小程序 appId
    private $secret = 'xxxxxxxxxx';  //小程序 secret
    private $mch_id = 'xxxxxxxxxxx'; // 商户id
    private $key = 'xxxxxxxxxxxxxxx'; // 商户api key

    public function index(){

        $code = $_REQUEST['code'];
        $appid = $this -> appid ; // 小程序id
        $secret = $this -> secret ;;

        $url = 'https://api.weixin.qq.com/sns/jscode2session?appid='.$appid.'&secret='.$secret.'&js_code='.$code.'&grant_type=authorization_code';

        $res  = file_get_contents($url);
        echo  $res ;exit();
    }

    public function pay(){
        $appid = $this -> appid ; // 小程序id

        /* 商户号 */
        $mch_id = $this -> mch_id ; // 商户id
        $key =  $this -> key ; // 商户api key

        $openid = $this ->input ->get("openid"); //openid
        $attach = $this ->input ->get("attach"); //附属
        $body = $this ->input ->get("body");
        $total_fee = $this ->input ->get("total_fee"); // 金额 以分为单位

        $out_trade_no =$mch_id.time();

//        log_message("debug","=====支付金额:========".var_export($total_fee,true));

        $WeiXinPayObj = new \biz\common\WeiXinPay($appid,$openid,$mch_id,$key,$out_trade_no,$body,$total_fee,$attach);
        $result= $WeiXinPayObj ->pay();

        echo json_encode($result);exit();



    }

    /* 支付回调通知*/
    public function notify(){

//        log_message("debug","=====进入回调:========");

        $post = file_get_contents('php://input');


        if (empty($post) ){
            // 阻止微信接口反复调用此接口
            log_message("debug","阻止微信接口反复调用此接口");
            $str ='<xml><return_code><![CDATA[SUCCESS]]</return_code><return_msg><![CDATA[OK]]</return_msg></xml>';
            echo $str;
            exit('Notify 非法调用');
        }



        //将XML格式的数据转换为数组
        $arr = $this->XmlToArr($post);

        //验证订单金额
        if($this->checkPrice($arr)){
            $params = [
                'return_code'    => 'SUCCESS',
                'return_msg'    => 'OK'
            ];
            $callback =  $this->ArrToXml($params);
            log_message("debug","=====支付回调成功:========".var_export($callback,true));
            echo $callback; exit();
        }



    }


    //校验订单金额 根据订单号$arr['out_trade_no'] 在商户系统内查询订单金额 并和$arr['total_fee']做比较
    public function checkPrice($arr){
        if($arr['return_code'] == 'SUCCESS' && $arr['result_code'] == 'SUCCESS'){
            log_message("debug","=====这里需要将支付的结果与数据库的进行对比 验证是否一致 ========".var_export($arr['total_fee'],true));
            if($arr['total_fee'] == 1){ //生产环境需要根据订单号在数据库中查询金额
                log_message("debug","=====支付信息回调->订单金额相等!:========");
                return true;
            }else{
                log_message("debug","=====支付信息回调->订单金额不匹配!微信支付系统提交过来的金额为:========".var_export($arr['total_fee'],true));
            }
        }else{
            log_message("debug","=====支付信息回调->通知状态有误!:========".var_export('通知状态有误',true));
        }
    }

    //Xml 文件转数组
    public function XmlToArr($xml)
    {
        if($xml == '') return '';
        libxml_disable_entity_loader(true);
        $arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $arr;
    }



    //数组转XML
    public function ArrToXml($arr)
    {
        if(!is_array($arr) || count($arr) == 0) return '';

        $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;
    }

运行类

<?php
/**
 * Created by PhpStorm.
 * User: S
 * Date: 2020/11/24
 * Time: 10:09
 */

namespace biz\common;


class WeiXinPay
{
    protected $appid;
    protected $mch_id;
    protected $key;
    protected $openid;
    protected $out_trade_no;
    protected $body;
    protected $total_fee;
    protected $attach;


    function __construct($appid,$openid,$mch_id,$key,$out_trade_no,$body,$total_fee,$attach)
    {
        $this -> appid = $appid;
        $this -> openid = $openid;
        $this -> mch_id = $mch_id;
        $this -> key = $key;
        $this -> out_trade_no = $out_trade_no;
        $this -> body = $body;
        $this -> total_fee = $total_fee;
        $this -> attach = $attach;

    }

    public function pay(){
        /* 统一下单接口*/
        $return = $this -> weixinapp();
        return $return;
    }

    private function weixinapp(){
        // 统一下单接口
        $unifiedorder = $this -> unifiedorder();

//        log_message("debug","=====第一次签名成功了 ========".var_export($unifiedorder,true));

        $parameters = array(
            'appId' => $this -> appid,  // 小程序id
            'nonceStr' => $this -> createNoncestr(),  // 随机串
            'package' => 'prepay_id='.$unifiedorder['prepay_id'],//数据包
            'signType' => 'MD5', //签名方式
            'timeStamp' => strval(time())  // 时间戳
        );

//
//        log_message("debug","===== 二次签名的东西 ========".var_export($parameters,true));
        // 签名
        $parameters['paySign'] = $this -> getSign($parameters);

//        log_message("debug","===== 预支付成功的结果 ========".var_export($parameters,true));
        return $parameters;

    }


    /*统一下单接口*/
    private  function unifiedorder(){
        // https://api.mch.weixin.qq.com/pay/unifiedorder
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';

        $parameters = array(
            'appid' => $this -> appid,   //小程序id
            'attach' => $this -> attach,
            'mch_id' => $this -> mch_id, // 商户号
            'nonce_str' => $this -> createNoncestr(), // 随机字符串
            'body' => $this -> body,
            'out_trade_no' => $this -> out_trade_no,  // 商户订单号
            'total_fee' => floatval(0.01*100),   // 总金额 单位 分
            'spbill_create_ip' => $_SERVER['REMOTE_ADDR'], //终端ip
            'notify_url' =>'https://ehome.modongyun.com/api/Play/notify', //通知地址 确保外网能正常访问
            'openid' => $this ->openid,  //用户id
            'trade_type' => 'JSAPI'      // 交易类型
        );


        // 统一下单签名
        $parameters['sign'] = $this -> getSign($parameters);
        $xmlData = $this -> arrayToXml($parameters);


        $return = $this -> xmlToArray($this->postXmlCurl($xmlData,$url,60));
//        log_message("debug","===== 统一下单api接口  ========".var_export($return,true));

        return $return;

    }
    private  static function postXmlCurl($xml,$url,$second=30){

        $ch = curl_init();
        curl_setopt($ch,CURLOPT_TIMEOUT,$second);
        curl_setopt($ch, CURLOPT_URL, $url); // 要访问的地址
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // 对认证证书来源的检查
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); // 从证书中检查SSL加密算法是否存在
        curl_setopt($ch, CURLOPT_HEADER, FALSE); // 显示返回的Header区域内容
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); // 获取的信息以文件流的形式返回
        curl_setopt($ch, CURLOPT_POST, TRUE); // 发送一个常规的Post请求
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); // Post提交的数据包

        curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,20);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环

        set_time_limit(0);

        $data = curl_exec($ch);

//        log_message("debug","=====支付信息3->xml:========".var_export($xml,true));

        log_message("debug","=====支付信息3->订单号3:========".var_export($data,true));



        if ($data){
            curl_close($ch);
            return $data;
        }else{
            $error = curl_errno($ch);
            curl_close($ch);
            log_message("debug","=====支付失败:========".var_export($error,true));
        }



    }


    /* 数组转换为xml*/
    private function arrayToXml($arr){
        $xml = "<root>";
        foreach ($arr as $key => $val){
            if (is_array($val)){
                $xml .="<".$key.">" .$this->arrayToXml($val)."</".$key.">";
            }else{
                $xml .="<".$key.">".$val."</".$key.">";
            }
        }
        $xml .= '</root>';

        return $xml;
    }

    /* xml 转换成数组 */
    private function  xmlToArray($xml){

        libxml_disable_entity_loader(true);

        $xmlstring = simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOCDATA);

        $val = json_decode(json_encode($xmlstring),true);

        return $val;
    }



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

    }

    /* 作用 生成签名 */
    private function getSign($obj){

        foreach ( $obj as $k => $v ){
            $parameters[$k] = $v;
        }

        // 签名步骤一 按字典序排序参数
        Ksort($parameters);
//        $String =$this ->formatBizQueryParaMap($parameters,false);

        $String = urldecode( http_build_query($parameters) );


//        log_message("debug","=====支付签名排序2:========".var_export($String,true));

        // 签名步骤二 :在string后加入KEY
        $String = $String."&key=".$this ->key;

        // 签名步骤三 MD5加密
        $String = md5($String);

        // 签名第四步 所有字符串转大写
        $result = strtoupper($String);

        return $result;

    }





}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值