关于微信H5支付(微信内支付 和 跳连接支付)
更新:HHH   时间:2023-1-7


好久没有更新博客,最近做项目遇到了微信支付,整理一下需求,目的一个活动页面需要调微信支付!
应用框架THINKPHP5.1

注意:微信支付 需要注意 如果是h6页面调取支付的话,需要静默一个登录状态获取code,这样才可以保证支付调取的参数openid正常,这里的坑填了好久,微信内浏览器h6用户输入完手机号登录后,页面存cookie信息,有时候会不更新获取的openid 导致“下单账号和支付账号不一致,请核对”;

微信内浏览器刷新页面用jq执行: window.location.href = location.href+'?time='+((new Date()).getTime());

直接上代码:前端
function get_form(){
var price1 =$(".get_price1").val();
var price2 =$(".get_price2").val();
var price3 =$(".get_price3").val();
var price4 =$(".get_price4").val();
var pid =$(".pid").val();
var username =$(".username").val();
var mobile =$(".mobile").val();
var address =$(".address").val();

    if(!username && !mobile && !address){
        layer.alert('收货人信息不能为空');
        return false;
    }
    $.post("home/get_from",//get_from 是后台请求支付的逻辑
            {"price1":price1,"price2":price2,"price3":price3,"price4":price4,"username":username,"mobile":mobile,"address":address,"pid":pid},function(apiConfig){
           console.log(apiConfig);

        var ua = navigator.userAgent.toLowerCase();//获取判断用的对象是微信内还是外部
        if (ua.match(/MicroMessenger/i) == "micromessenger") {
           //内部
            WeixinJSBridge.invoke(
                    'getBrandWCPayRequest', {
                        "appId":apiConfig.appId,     //公众号名称,由商户传入
                        "timeStamp":apiConfig.timeStamp,         //时间戳,自1970年以来的秒数
                        "nonceStr":apiConfig.nonceStr, //随机串
                        "package":apiConfig.package,
                        "signType":apiConfig.signType,         //微信签名方式:
                        "paySign":apiConfig.paySign //微信签名
                    },
                    function(res){
                        if(res.err_msg == "get_brand_wcpay_request:ok" ){

                            window.location.href ="方法路径"
                        }
                    });

            if (typeof WeixinJSBridge == "undefined"){
                if( document.addEventListener ){
                    document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
                }else if (document.attachEvent){
                    document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
                    document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
                }
            }else{
                onBridgeReady();
            }

        }else{

     //外部
            var url="支付成功跳转";
            window.location.href =apiConfig.mweb_url+"&redirect_url="+url;

        }

    });
}

后端代码:
静默登录 请求页面的时候通过tp5 redirect重置路由
再进入页面的方法里 查找有没有openid
$sa = Cookie::get('kj_phone');
$uid =$sa['id'];//用户id
$userAgent = $_SERVER['HTTP_USER_AGENT']; // 判断是 公众号支付还是外部网址支付
if (strpos($userAgent, 'MicroMessenger')) { // 公众号支付
$uid =$sa['id'];//用户id
$open_id= Cache::get($uid);
// print_r($open_id);
if(empty($open_id)){
$this->redirect("https://open.weixin.qq.com/connect/oauth3/authorize?appid=Appid&redirect_uri=页面进入后先通过get_open方法获得code获取openid&response_type=code&scope=snsapi_base&state=1#wechat_redirect");

          }

      }

get_open方法
public function get_open(){

    $code1=empty($_GET['code']) ? '' : $_GET['code'];

    $sa =Cookie::get('kj_phone');

   // $appId ='appId ';
    //$secret ='secret';
    $get_token_url = 'https://api.weixin.qq.com/sns/oauth3/access_token?appid=Appid&secret=secre&code=' . $code1 . '&grant_type=authorization_code';
    $json_obj = $this->http_request($get_token_url);
    $json_obj = json_decode($json_obj,true);
    $openid = empty($json_obj['openid']) ? "" : $json_obj['openid'];

    $uid =$sa['id'];//用户id

    Cache::set($uid,$openid,3600);
    if($code1 == ''){

        $this->redirect('重定向到渲染页面');
    }
    if(!empty($open_id)){
        $this->redirect('重定向到渲染页面');
    }else{

        $this->redirect('重定向到渲染页面');
    }

}

get_from:
public function get_from(){
if(request()->isPost()){
$data =input("post.");// 前端传过来的数据 自己写逻辑吧

             $userAgent = $_SERVER['HTTP_USER_AGENT']; // 判断是 公众号支付还是外部网址支付

             if (strpos($userAgent, 'MicroMessenger')) { // 公众号支付

                 $sa = Cookie::get('kj_phone');
                 $uid =$sa['id'];//用户id
                 $open_id= Cache::get($uid);
                 //print_r($open_id);exit;
                 $return = $this->weixinapp($open_id,$body,$order_id,$price,$attact);
                 return $return;
             }else{

                 $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
                 $parameters = array(
                     'appid' => "小程序ID", //小程序ID
                     'mch_id' => "商户号", //商户号
                     'attach'=>$attact,
                     'nonce_str' => $this->createNoncestr(), //随机字符串
                     'body' =>$body, //商品描述
                     'out_trade_no' =>$order_id,//商户订单号
                     'total_fee' => $price * 100, //总金额 单位 分
                     'spbill_create_ip' => '111111', //终端IP
                     'notify_url' => '回调地址', //通知地址  确保外网能正常访问
                     'trade_type' => 'MWEB'//交易类型
                 );

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

                 $xmlData = $this->arrayToXml($parameters);

                 $return = $this->xmlToArray($this->postXmlCurl($xmlData, $url, 60));

                 return $return;
             }

        }

    }
}

//统一下单接口
private function weixinapp($openid,$body,$orderId,$price,$attact) {

    $unifiedorder = $this->unifiedorder($openid,$body,$orderId,$price,$attact);

    $parameters = array(
        'appId' => "appId", //小程序ID
        'timeStamp' => '' . time() . '', //时间戳
        'nonceStr' => $this->createNoncestr(), //随机串
        'package' => 'prepay_id=' . $unifiedorder['prepay_id'], //数据包
        'signType' => 'MD5'//签名方式
    );
    //签名
    $parameters['paySign'] = $this->getSign($parameters);
    return $parameters;
}
//作用:产生随机字符串,不长于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) {
    //halt($Obj);
    foreach ($Obj as $k => $v) {
        $Parameters[$k] = $v;
    }
    //签名步骤一:按字典序排序参数
    ksort($Parameters);
    $String = $this->formatBizQueryParaMap($Parameters, false);
    //签名步骤二:在string后加入KEY
    $String = $String . "&key=d18e10ad06222c972499296f1cefa481" ;
    //签名步骤三:MD5加密
    $String = md5($String);
    //签名步骤四:所有字符转为大写
    $result_ = strtoupper($String);

    return $result_;
}
///作用:格式化参数,签名过程需要使用
private function formatBizQueryParaMap($paraMap, $urlencode) {
    $buff = "";
    ksort($paraMap);
    foreach ($paraMap as $k => $v) {
        if ($urlencode) {
            $v = urlencode($v);
        }
        $buff .= $k . "=" . $v . "&";
    }
    $reqPar="";
    if (strlen($buff) > 0) {
        $reqPar = substr($buff, 0, strlen($buff) - 1);
    }
    return $reqPar;
}
//数组转换成xml
private function arrayToXml($arr) {
    $xml = "<root>";
    foreach ($arr as $key => $val) {
        if (is_array($val)) {
            $xml .= "<" . $key . ">" . arrayToXml($val) . "</" . $key . ">";
        } else {
            $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
        }
    }
    $xml .= "</root>";
    return $xml;
}
//xml转换成数组
private function xmlToArray($xml) {
    //禁止引用外部xml实体
    libxml_disable_entity_loader(true);
    $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
    $val = json_decode(json_encode($xmlstring), true);
    return $val;
}
function http_curl($url){//用curl传参
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);//关闭ssl验证
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
    curl_setopt($ch,CURLOPT_HEADER, 0);
    $output = curl_exec($ch);
    curl_close($ch);
    return json_decode($output, true);
}
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); //严格校验
    //设置header
    curl_setopt($ch, CURLOPT_HEADER, FALSE);
    //要求结果为字符串且输出到屏幕上
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    //post提交方式
    curl_setopt($ch, CURLOPT_POST, TRUE);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
    curl_setopt($ch, CURLOPT_TIMEOUT, 40);
    set_time_limit(0);
    //运行curl
    $data = curl_exec($ch);
    //返回结果
    if ($data) {
        curl_close($ch);
        return $data;
    } else {
        $error = curl_errno($ch);
        curl_close($ch);
        throw new WxPayException("curl出错,错误码:$error");
    }
}
//回调
private function unifiedorder($openid,$body,$orderId,$price,$attact) {

    $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
    $parameters = array(
        'appid' => "appid", //小程序ID
        'mch_id' =>"mch_id", //商户号
        'attach'=>$attact,
        'nonce_str' => $this->createNoncestr(), //随机字符串
        'body' => $body,
        'out_trade_no'=> $orderId,
        'total_fee' => $price * 100,
        'spbill_create_ip' => '11111111', //终端IP
        'notify_url' => 回调, //通知地址  确保外网能正常访问
        'openid' => $openid, //用户id
        'trade_type' => 'JSAPI'//交易类型
    );

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

    $return = $this->xmlToArray($this->postXmlCurl($xmlData, $url, 60));

    return $return;
}
//回调
public function callback(){
    $xml = file_get_contents("php://input");
    // 将XML 转换为数组
    $xml = json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA));
    $result = json_decode($xml, true);

    //如果成功返回
    if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS'){
        $where['order_id']=$result['out_trade_no'];
        $data_e['states']=2;
        $ud= db('hc_pr_order')->where($where)->update($data_e);
    }
    // 给微信的返回值
    if ($ud) {
        $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>';
    }
    return $str;
    return $result;
}
返回web开发教程...