精华内容
下载资源
问答
  • 上一章讲了微信外部的H5支付,当然同一个页面是不会仅仅满足在微信外部支付的,扩展到JSAPI支付;实际上很多操作都是你的后端小伙伴完成的,感谢后端大大;交互细节:以下是支付场景的交互细节,请认真阅读,设计...

    上一章讲了微信外部的H5支付,当然同一个页面是不会仅仅满足在微信外部支付的,扩展到JSAPI支付;

    实际上很多操作都是你的后端小伙伴完成的,感谢后端大大;

    交互细节:

    以下是支付场景的交互细节,请认真阅读,设计商户页面的逻辑:

    用户打开商户网页选购商品,发起支付,在网页通过JavaScript调用getBrandWCPayRequest接口,发起微信支付请求,用户进入支付流程。

    用户成功支付点击完成按钮后,商户的前端会收到JavaScript的返回值。商户可直接跳转到支付成功的静态页面进行展示。

    商户后台收到来自微信开放平台的支付成功回调通知,标志该笔订单支付成功。

    注:(2)和(3)的触发不保证遵循严格的时序。JS API返回值作为触发商户网页跳转的标志,但商户后台应该只在收到微信后台的支付成功回调通知后,才做真正的支付成功的处理。

    过程:

    一、设置支付目录

    请确保实际支付时的请求目录与后台配置的目录一致(现在已经支持配置根目录,配置后有一定的生效时间,一般5分钟内生效),否则将无法成功唤起微信支付。

    在微信商户平台(pay.weixin.qq.com)设置您的JSAPI支付支付目录,设置路径:商户平台-->产品中心-->开发配置,如图所示。JSAPI支付在请求支付的时候会校验请求来源是否有在商户平台做了配置,所以必须确保支付目录已经正确的被配置,否则将验证失败,请求支付不成功。

    5ff377abc98b

    微信JSAPI支付-支付目录配置

    二、设置授权域名

    开发JSAPI支付时,在统一下单接口中要求必传用户openid,而获取openid则需要您在公众平台设置获取openid的域名,只有被设置过的域名才是一个有效的获取openid的域名,否则将获取失败。而获取openid可以静默授权,这种方式对仅需要openid来说是首选。

    5ff377abc98b

    微信网页授权域名设置

    准备工作做好后,进入实际开发,在需要调用支付JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js

    function onBridgeReady(){

    WeixinJSBridge.invoke(

    'getBrandWCPayRequest', {

    "appId":"********", //公众号名称,由商户传入

    "timeStamp":"1395712654", //时间戳,自1970年以来的秒数

    "nonceStr":"****", //随机串

    "package":"**********",

    "signType":"MD5", //微信签名方式;

    "paySign":"******" //微信签名

    },

    function(res){

    if(res.err_msg == "get_brand_wcpay_request:ok" ){

    // 使用以上方式判断前端返回,微信团队郑重提示:

    //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。

    //do something

    } else{

    //支付失败处理

    }

    });

    }

    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();

    }

    常见的一些错误参见微信说明

    如果此页面需要在微信外部浏览器及其他APP打开,可以参见上一章H5调用微信支付

    展开全文
  • 前言 微信支付-微信H5外部浏览器支付微信支付-微信H5内部浏览器支付「本文」微信支付-PC端扫码支付「待写」 本篇是微信支付系列的第二篇、...扫盲补充:关于微信内部浏览器支付支付时会直接调起微信支付,不同于外...

    前言

    微信支付-微信H5外部浏览器支付
    微信支付-微信H5内部浏览器支付本文
    微信支付-PC端扫码支付待写

    本篇是微信支付系列的第二篇、微信H5内部浏览器支付,关于微信H5外部浏览器唤起微信APP支付,请参考上一篇文章。

    开发环境:Java + SpringBoot + Vue +WxJava(开源SDK)

    扫盲补充:关于微信内部浏览器支付,支付时会直接调起微信支付,不同于外部浏览器支付,内部浏览器支付首先需要获得当前支付用户对该公众号的唯一标识 openId「是否关注都是唯一的」,拿到 openId 后,结合后端其他参数调用微信预支付接口,获得预支付id,然后交由前端发起微信支付,支付成功后回调后端接口。

    如下是正文部分。

    1、获取Code

    要想获得用户唯一标识 openid,首先需要办的事就是获得 code。

    code 部分在本文中交由前端去获取「调用微信authorize授权方法」,拿到 code 后传递给后端换取 openid「用户唯一标识」;通常这个操作都是在用户登录时去实现的,登录成功后同时拿到 openid,而且还可以存(更新)到该用户的数据库方便后面使用。

    前端获取code,具体如下:

    let ua = navigator.userAgent.toLowerCase()
    if (ua.match(/MicroMessenger/i) == 'micromessenger') {
        if (!this.GetQueryString('code')) {
            alert("跳转");
            // this.$vux.toast.text('微信授权中……', 'default')
            let currentUrl = encodeURIComponent(window.location.href)
            window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=我是appid&redirect_uri='+currentUrl+'&response_type=code&scope=snsapi_base&state=STATE&connect_redirect=1#wechat_redirect'
        } else {
            let code = this.GetQueryString('code')
            // 此处调用后端方法,通过 code 换取 openid
        }
    }

    补充:授权链接中的 scope 参数分为 snsapi_base、snsapi_userinfo,snsapi_base 可以获得用户的唯一标识 openid,snsapi_userinfo 则在此基础上获得用户资料「昵称、头像等」

    上述方法中 ua.match(/MicroMessenger/i) 是用来判断是否是微信环境的, GetQueryString 方法用来获取微信中的 code,如果当前浏览器 url 并没有附带 code 参数,那么就会调用微信的 authorize 方法进行授权,授权后获得 code,该方法具体如下:

    GetQueryString (name) {
        let url = new RegExp('(^|&)' + name + '=([^&]*)(&|$)')
        let newUrl = window.location.search.substr(1).match(url)
        if (newUrl != null) {
            return unescape(newUrl[2])
        } else {
            return false
        }
    },

    2、换取openid

    拿到 code 后,下一步就是调用后端接口换取 openid 了, 简单看一下换取 openid 的后端方法:

    try {
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=我是appid&secret=我是secret&grant_type=authorization_code"+
            "&code=" + loginRequest.getCode();
        String body = RestTemplateUtils.get(url,new JSONObject());
        JSONObject jsonObject = JSONObject.parseObject(body);
        Integer errcode = jsonObject.getInteger("errcode");
        if (errcode == null || errcode == 0) {
            String openId = jsonObject.getString("openid");
            //将此次登录的openId,暂且放入user的域里面,支付的时候会用到
            System.out.println("openId:"+openId);
            loginRequest.setOpenId(openId);
            return ResultUtil.success(userService.login(loginRequest));
        }else{
            logger.error("[微信第三方登录] 异常”);
            抛出自定义异常
            throw new CommonException("微信第三方登录异常","");
        }
    } catch (Exception e) {
        logger.error("[微信第三方登录] 异常", e);
        抛出自定义异常
        throw new CommonException("微信第三方登录异常","");
    }

    简单说一下该方法,前端传递 code 致后端方法,后端拿到 code 后,调用 access_token 接口获取 openid,同时完成登录操作。

    至此,已经成功登录并拿到用户 openid 了,接下来就是调用支付接口。

    3、预支付接口

    上边已经提到了,内部浏览器支付是交由前端发起的,但是又依赖于后端的 预支付接口,所以先来看一下后端预支付接口:

    /**
     * 生成订单「微信内部浏览器」
     * @return
     */

    @Transactional
    public Object wxPrepay(Orders orders,String openId) {
        Object result = null;
        try {
            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
            orderRequest.setOutTradeNo(orders.getOrderId());
            orderRequest.setOpenid(openId);
            orderRequest.setBody(“我是商品描述信息");
            orderRequest.setTotalFee(orders.getAmount().multiply(new BigDecimal("
    100")).intValue());
            orderRequest.setSpbillCreateIp(DispatchParams.getInstance().getWechatSpbillCreateIp());
            orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
            result = wxPayService.createOrder(orderRequest);
            if (result instanceof WxPayMpOrderResult) {
                String prepayId = ((WxPayMpOrderResult)result).getPackageValue();
                String paySign = ((WxPayMpOrderResult) result).getPaySign();
                prepayId = prepayId.replace("
    prepay_id=", "");
                orders.setPrepayId(prepayId);
                orders.setSign(paySign);
                ordersDao.updateOrders(orders);
            }
        } catch (WxPayException e) {
            logger.error("
    [微信支付] 异常", e);
            抛出自定义全局异常
            throw new CommonException(WechatStatusEn.WECHAT_CREATE_CODE_ERROR.getErrorMsg()+"
    ':微信支付异常", WechatStatusEn.WECHAT_CREATE_CODE_ERROR.getErrorCode());
        } catch (Exception e) {
            logger.error("[预付款异常]", e);
            抛出自定义全局异常
            throw new CommonException(WechatStatusEn.WECHAT_CREATE_CODE_ERROR.getErrorMsg()+"'
    :预付款异常", WechatStatusEn.WECHAT_CREATE_CODE_ERROR.getErrorCode());
        }
        return result;
    }

    简单说一下预支付方法,首先是根据自己情况创建订单记录,然后就是通过 openid 调用 wxPayService.createOrder 方法「WxJava」获取预支付id,该方法返回的实体为 WxPayMpOrderResult,实体参数为前端调起微信支付的必要参数,具体如下:

    private String appId;
    private String timeStamp;
    private String nonceStr;
    @XStreamAlias("package")
    private String packageValue;
    private String signType;
    private String paySign;

    为啥获得的预支付id没有用到呀?上方返回的参数并没有看到呀!

    其实不然,属性 packageValue 的值为 prepay_id=预支付id ,该参数是必须的。

    4、前端调用,发起支付

    至此,后端基本完成了,我们将参数传递给前端调用,直接模拟返回后的数据:

    假设下方是调用接口返回的数据

    console.log(“我是后端返回的数据 - res:"+JSON.stringify(res))

    const payParam = {
        appId: res.appId,
        nonceStr: res.nonceStr,
        package: res.packageValue,
        timeStamp: res.timeStamp,
        signType: res.signType,
        paySign: res.paySign,
    }


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

    发起支付的 onBridgeReady 方法:

    onBridgeReady(res){
        alert("发起请求:"+JSON.stringify(res));
        WeixinJSBridge.invoke(
            'getBrandWCPayRequest', {
                "appId":res.appId,     //公众号名称,由商户传入
                "timeStamp":res.timeStamp, //时间戳,自1970年以来的秒数
                "nonceStr":res.nonceStr, //随机串
                "package":res.package, // prepay_id=xxx
                "signType":res.signType, //微信签名方式:
                "paySign":res.paySign //微信签名
            },
            function(res){
                alert(JSON.stringify("我是支付返回的信息:\n"+res));
                alert("我是支付返回的信息:\n"+res.err_msg);
                if(res.err_msg == "get_brand_wcpay_request:ok" ){
                    // 使用以上方式判断前端返回,微信团队郑重提示:
                    //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
                    alert("支付成功了");
                }
            }
        );
    }

    5、效果截图

    再来简单总结一下,首先由前端获取 code,获取 code 后传递给后端换取 openid,openid 是预支付必须的参数,前端发起支付时,需要6个参数,此时调用后端预支付接口获取「wxPayService.createOrder」,前端支付成功后同样微信会自动回调后端 notify 接口,具体如下「代码仅供参考」:

    @RequestMapping(value = "/notify")
    @ResponseBody
    public String notify(@RequestBody String body) throws Exception {

            WxPayOrderNotifyResult result = null;
            try {
                result = wxPayService.parseOrderNotifyResult(body);
            } catch (WxPayException e) {
                logger.error("[微信解析回调请求] 异常", e);
                return WxPayNotifyResponse.fail(e.getMessage());
            }
            logger.info("处理微信支付平台的订单支付");
            logger.info(JSONObject.toJSONString(result));


            String appid = result.getAppid();//应用ID
            String attach = result.getAttach();//商家数据包
            String bank_type =result.getBankType();//付款银行
            Integer cash_fee = result.getCashFee();//现金支付金额
            String fee_type = result.getFeeType();//货币种类
            String is_subscribe = result.getIsSubscribe();//是否关注公众账号
            String mch_id = result.getMchId();//商户号
            String nonce_str = result.getNonceStr();//随机字符串
            String openid = result.getOpenid();//用户标识
            String out_trade_no = result.getOutTradeNo();// 获取商户订单号
            String result_code = result.getResultCode();// 业务结果
            String return_code = result.getReturnCode();// SUCCESS/FAIL
            String sign = result.getSign();// 获取签名
            String time_end = result.getTimeEnd();//支付完成时间
            Integer total_fee = result.getTotalFee();// 获取订单金额
            String trade_type = result.getTradeType();//交易类型
            String transaction_id = result.getTransactionId();//微信支付订单号


            //如果成功写入数据库
            if("SUCCESS".equals(return_code)) {// 如果微信返回的结果是success,则修改订单状态
                Orders orders = ordersDao.selectByOrderId(out_trade_no);
                // 验证签名
                if(orders != null){
                    if(!"1".equals(orders.getOrderStatus())){//判断是否订单已经完成了
                        // 判断金额是否跟数据库订单金额一致,放置人为修改
                        if(orders.getAmount().multiply(new BigDecimal("100")).compareTo(new BigDecimal(total_fee)) == 0){
                            //更新订单状态
                            业务逻辑处理部分...
                            return WxPayNotifyResponse.success("订单已经处理成功!");
                        }else{
                            logger.error("微信:金额不一致!");
                            return WxPayNotifyResponse.fail("订单金额不一致");
                        }
                    }else {
                        return WxPayNotifyResponse.success("订单已经处理成功!");
                    }
                }else{
                    return WxPayNotifyResponse.fail("商户订单号不匹配");
                }
            }
            System.out.println("回调成功");
            System.out.println("----返回给微信的xml:" + result);
            return WxPayNotifyResponse.success("支付成功!");
    }

    最后

    博客地址:https://www.cgblog.com/niceyoo

    如果觉得这篇文章有丶东西,不放关注一下我,关注是对我最大的鼓励~

    18年专科毕业后,期间一度迷茫,最近我创建了一个公众号用来记录自己的成长。

    展开全文
  • 微信后台可以回调上图中的地址,上图设置了“回调通知地址”;...手机中进入支付选择界面 ,选择了“微信支付(jsapi微信APP内部 支付)”选项后; 进入下图支付操作 进入此方法执行到1处后,则去...

    微信后台可以回调上图中的地址,上图设置了“回调通知地址”;

    证明公众号中各种地址设置都是正确的;

     但就是不执行上图中的回调处理方法;

     

    ===============还有一点==================

     

    手机中进入支付选择界面 ,选择了“微信支付(jsapi微信APP内部 支付)”选项后;

    进入下图支付操作

    进入此方法执行到1处后,则去微信后台各种组合返回openid等各种信息;

    之后,再进入该 方法中;

    所以此前传到该 页面的各种参数都没了(被微信返回的信息“冲了”,所以前面支付界面 中各种post过来的参数取不到)。。。。

    所以支付界面 做下面调整;

     

    即xxx.runpay?id=xxx&cid=yyy,'get'方式传过去,这样微信回的时候原样返回; 

    就可以以$_GET['cid']的方式取到结果;

    有些相关的问题 就是返回参数时,原来传递过来的参数“丢失”所导致。。。。斟酌斟酌。。。~~

    如: "非法操作:runChongzhi.htmls="

    转载于:https://www.cnblogs.com/ximishuier/p/11408805.html

    展开全文
  • 微信 JSAPI 支付 微信内部起调H5支付 支付完成发送公众号消息模板 JSAPI支付 openID是必传的,所以这里先获取用户openID,想要获取openID,首先我们要拿到code,再获取openID JS: <script> // 强制关注...

    微信 JSAPI 支付 微信内部起调H5支付 支付完成跳转公众号并发送公众号消息模板

    JSAPI支付 openID是必传的,所以这里先获取用户openID,想要获取openID,首先我们要拿到code,再获取openID

    JS:

    <script>
        // 强制关注公众号,获取openid
        getCode = function () {
            var code = getUrlParam('code'); // 截取路径中的code,如果没有就去微信授权,如果已经获取到了就直接传code给后台获取openId
            var local = window.location.href;
            var APPID = 'xxxxxxxxxx';
            if (code == null || code === '') {
                window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' + APPID + '&redirect_uri=' + encodeURIComponent(local) + '&response_type=code&scope=snsapi_base&state=#wechat_redirect'
            } else {
                getOpenId(code) //把code传给后台获取用户信息
            }
        };
    
        //获取地址栏的参数
        getUrlParam= function (name) {
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
            var r = window.location.search.substr(1).match(reg);
            if (r != null) return unescape(r[2]); return null;
        };
        //页面执行调用
        getCode();
    </script>
    

    页面加载 执行 getCode方法,获取URL中的code,如果没有code,起调公众号获取code接口,然后会跳转到 redirect_uri 这个参数对应的URL路径,这个URL后面会自动拼接出请求的code,这里的 redirect_uri 还是本网页链接地址,这样再次执行 getCode方法的时候就能获取到URL链接后面的 code 。

    然后根据 code ajax请求后端 起调公众号获取网页授权access_token接口 ,这个接口会返回 openID 和 access_token

    JS:

    	//把code传给后台,得到openid
        getOpenId = function (code) {
            $.ajax({
                type: 'POST',
                dataType: 'json',
                url: "{:U('getOpenid')}",
                data: { code: code },
                success: function (res) {
                    if (res.status == -1) {
                        alert(result.msg);
                        return false;
                    } else {
                    	// 起调统一下单接口
                        unifiedorder(res.data.openid);
                    }
                }
            });
        };
    

    后端接口PHP :

    function getOpenid(){
            $code = I('post.code');
            $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=xxxxxxxxx&secret=xxxxxxxxxxxxxxxx&code=$code&grant_type=authorization_code";
            $data = file_get_contents($url);
            if($data){
                $this->ajaxReturn(array('data'=>json_decode($data,true),'msg'=>'获取成功','status'=>1));
            }else{
                $this->ajaxReturn(array('data'=>'','msg'=>'获取失败','status'=>0));
            }
        }
    

    拿到 openID 以后传给后端,起调JSAPI支付统一下单接口,拿到参数 prepay_id

    JS:

     	// 起调统一下单接口
        unifiedorder = function(openid){
            $.ajax({
                type: 'POST',
                dataType: 'json',
                url: "{:U('index')}",
                data: { openid: openid },
                success: function (result) {
                    if (result.status == 0) {
                        alert(result.msg);
                        return false;
                    } else {
                        h5();
                    }
                }
            });
        };
    

    PHP:

    public function index()
        {
            if(IS_POST){
                $time = time();
                $key = "支付密钥";
                $post['appid'] = "xxxxxxxxxxxx";
                $post['body'] = '商品购买';
                $post['mch_id'] = "商户ID";
                $post['nonce_str'] = $this->randomkeys(32);      //随机字符串
                $post['notify_url'] = "https://".$_SERVER['HTTP_HOST'] . '/index.php/Api/Native/notify_url';  //支付完成回调地址url,不能带参数;
                $post['openid'] = I('post.openid');  // openID
                $post['out_trade_no'] = $time.rand(10000,99999);  // 订单号
                $post['spbill_create_ip'] = $_SERVER['SERVER_ADDR'];    //服务器终端的ip
                $post['total_fee'] = 1;                        //总金额 最低为一分钱 必须是整数
                $post['trade_type'] = 'JSAPI';                          //交易类型 默认JSAPI
                $sign = $this->MakeSign($post, $key);                   //签名
                $res['key'] = $key;
                $post['sign'] = $sign;
                $post_xml = $this->arrToXml($post);
                //统一下单接口prepay_id
                $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
                $xml = $this->http_request($url, $post_xml);            //POST方式请求http
                $array = $this->xml_to_array($xml);                     //将【统一下单】api返回xml数据转换成数组,全要大写
                if ($array['return_code'] == "SUCCESS" && $array['result_code'] == "SUCCESS") {
                    $sign_arr['appId'] = $array['appid'];
                    $sign_arr['nonceStr'] = $this->randomkeys(32);      //随机字符串
                    $sign_arr['package'] = "prepay_id=".$array['prepay_id'];
                    $sign_arr['signType'] = "MD5";
                    $sign_arr['timeStamp'] = $time;
                    $newSign = $this->MakeSign($sign_arr, $key);                   //签名
                    $result['data'] = $sign_arr;
                    $result['newSign'] = $newSign;
                    $result['status'] = 1;
                    $result['msg'] = '统一下单成功';
                    $this->ajaxReturn($result);
                } else {
                    $result['status'] = 0;
                    $result['msg'] = '统一下单失败';
                    $this->ajaxReturn($result);
                }
            }else{
                $this->display();
            }
        }
    

    这里拿到 prepay_id 这个参数,起到H5支付页面

    JS:

    	// 起调H5支付
        function onBridgeReady() {
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest', {
                    "appId": result.data.appId,     //公众号名称,由商户传入
                    "timeStamp": result.data.timeStamp,         //时间戳,自1970年以来的秒数
                    "nonceStr": result.data.nonceStr, //随机串
                    "package": result.data.package,
                    "signType": "MD5",         //微信签名方式:
                    "paySign": result.newSign //微信签名
                },
                function (res) {
                    if (res.err_msg == "get_brand_wcpay_request:ok") {
                        // 使用以上方式判断前端返回,微信团队郑重提示:
                        //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
                        window.location.href = "https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=xxxxxxMw==#wechat_redirect";
                    }
                });
        }
        
        h5 = function () {
            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();
            }
        };
    

    JSAPI支付 微信内部起调 H5 是要 appId nonceStr package signType timeStamp paySign 这六个参数的,其中 paySign 签名是 前面五个参数的生成的签名,签名加密的类型要和发起统一支付下单接口生成的sign签名一致,完成加密以后将六个参数返回给前端,前端拿到参数调起微信H5支付页面。

    H5支付页面:
    在这里插入图片描述
    支付完成页面:
    在这里插入图片描述
    公众号模板消息发送是在回调接口(支付结果通知接口,以下称呼回调接口)里处理的,回调接口会返回支付人是否关注支付APPID对应的公众号参数 is_subscribe
    在这里插入图片描述
    这个地方可以看到下面有一个关注公众号,如果没有关注公众号,就不能发送公众号模板消息,所以我们要在支付回调接口中进行发送模板消息处理,延时发送模板消息,确保能在支付完成能够点击关注以后再发送消息,但是我们都知道回调接口是有通知频率的,微信总共会发起多次通知,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)。所以我这里做了一个操作记录表,当然我是需要这个操作记录,所以就顺便就能用这个作为判断依据,如果不需要操作记录,可以做一个缓存。

    public function notify_url()
        {
            $xml = file_get_contents('php://input');
            $extend_content = json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA));//转换格式
            file_put_contents('./xcxPay.txt', 'xcx_post33: ' . $xml . "\r\n", FILE_APPEND);
    
            $post = json_decode($extend_content, true);
    
            $postSign = $post['sign'];
            unset($post['sign']);
            /* 微信官方提醒:
             *  商户系统对于支付结果通知的内容一定要做【签名验证】,
             *  并校验返回的【订单金额是否与商户侧的订单金额】一致,
             *  防止数据泄漏导致出现“假通知”,造成资金损失。
             */
           
            $user_sign = $this->MakeSign($post, "xxxxxxxxxxx");   //再次生成签名,与$postSign比较
    
            /*
            * 首先判断,订单是否已经更新为ok,因为微信会总共发送8次回调确认
            * 其次,订单已经为ok的,直接返回SUCCESS
            * 最后,订单没有为ok的,更新状态为ok,返回SUCCESS
            */
            if ($post['return_code'] == 'SUCCESS' && $postSign == $user_sign) {
    
                // 用户openid
                $openid = $post['openid'];
                // 用户是否关注公众账号,Y-关注,N-未关注
                $is_subscribe = $post['is_subscribe'];
    
                $data['post'] = $post;
                if($is_subscribe == "Y"){
                	// 发送公众号模板消息
                    $res = self::sendNotice($openid,$post['out_trade_no']);
                    $data['res'] = $res;
                    M('logs')->add(array('content' => json_encode($data),'orderId' => $post['out_trade_no']));
                    $this->return_success_v1();
                }else{
                    sleep(10);
                    $log = M('logs')->where(array('orderId' => $post['out_trade_no']))->find();
                    if($log){
                        $this->return_success_v1();
                    }else{
                        $res = self::sendNotice($openid,$post['out_trade_no']);
                        $data['res'] = $res;
                        M('logs')->add(array('content' => json_encode($data),'orderId' => $post['out_trade_no']));
                        $this->return_success_v1();
                    }
                }
            } else {
                $this->return_success_v1();
            }
        }
    

    如果已经关注了,直接发送消息,如果没有关注公众号,睡眠10秒以后,查询操作记录表中是否已经有操作记录,这里为什么要查询操作记录表是为了确保回调如果通知了两次,不会发送两条消息模板。

    我这边支付完成以后会跳转一个页面,这个页面是微信公众号的页面,但是现在微信更新到7以后,这个页面在公众号就找不到了,但是这个链接是可以用,这里面 __biz= 后面的参数获取可以参照这个网址H5跳转公众号
    但是这个网址的操作只能在低版本微信上可以,目前的微信已经没有了,我也是在低版本微信中,才找到这个 __biz= 后面的参数,但是我这边并没有作者说的关注按钮一闪即逝的情况,所以我不保证所有手机都可以,当然这个链接也可以换成公众号其他的页面
    https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=xxxxxxxx==#wechat_redirect
    在这里插入图片描述

    发送的模板消息
    在这里插入图片描述

    JS完整代码:

    <script>
        // 强制关注公众号,获取openid
        getCode = function () {
            var code = getUrlParam('code'); // 截取路径中的code,如果没有就去微信授权,如果已经获取到了就直接传code给后台获取openId
            var local = window.location.href;
            var APPID = 'xxxxxxxxxxxxx';
            if (code == null || code === '') {
                window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' + APPID + '&redirect_uri=' + encodeURIComponent(local) + '&response_type=code&scope=snsapi_base&state=#wechat_redirect'
            } else {
                getOpenId(code) //把code传给后台获取用户信息
            }
        };
    
        //把code传给后台,得到openid
        getOpenId = function (code) {
            $.ajax({
                type: 'POST',
                dataType: 'json',
                url: "{:U('getOpenid')}",
                data: { code: code },
                success: function (res) {
                    if (res.status == -1) {
                        alert(result.msg);
                        return false;
                    } else {
                        unifiedorder(res.data.openid);
                    }
                }
            });
        };
    
        // 起调统一下单接口
        unifiedorder = function(openid){
            $.ajax({
                type: 'POST',
                dataType: 'json',
                url: "{:U('index')}",
                data: { openid: openid },
                success: function (result) {
                    if (result.status == 0) {
                        alert(result.msg);
                        return false;
                    } else {
                        h5();
                    }
                }
            });
        };
    
        // 起调H5支付
        function onBridgeReady() {
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest', {
                    "appId": result.data.appId,     //公众号名称,由商户传入
                    "timeStamp": result.data.timeStamp,         //时间戳,自1970年以来的秒数
                    "nonceStr": result.data.nonceStr, //随机串
                    "package": result.data.package,
                    "signType": "MD5",         //微信签名方式:
                    "paySign": result.newSign //微信签名
                },
                function (res) {
                    if (res.err_msg == "get_brand_wcpay_request:ok") {
                        // 使用以上方式判断前端返回,微信团队郑重提示:
                        //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
                        window.location.href = "https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=xxxxxxxx==#wechat_redirect";
                    }
                });
        }
        h5 = function () {
            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();
            }
        };
    
        //获取地址栏的参数
        getUrlParam= function (name) {
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
            var r = window.location.search.substr(1).match(reg);
            if (r != null) return unescape(r[2]); return null;
        };
        //页面执行调用
        getCode();
    </script>
    

    PHP完整代码:

    class NativeController extends Controller{
    
        /**
         * 微信支付
         * @param int $i
         * @return mixed
         */
        public function index()
        {
            if(IS_POST){
                $time = time();
                $key = "xxxxxxxxxxxxxxx";
                $post['appid'] = "xxxxxxxxxxxx";
                $post['body'] = '商品购买';
                $post['mch_id'] = "xxxxxxxxxxx";
                $post['nonce_str'] = $this->randomkeys(32);      //随机字符串
                $post['notify_url'] = "https://".$_SERVER['HTTP_HOST'] . '/index.php/Api/Native/notify_url';  //支付完成回调地址url,不能带参数;
                $post['openid'] = I('post.openid');
                $post['out_trade_no'] = $time.rand(10000,99999);
                $post['spbill_create_ip'] = $_SERVER['SERVER_ADDR'];    //服务器终端的ip
                $post['total_fee'] = 1;                        //总金额 最低为一分钱 必须是整数
                $post['trade_type'] = 'JSAPI';                          //交易类型 默认JSAPI
                $sign = $this->MakeSign($post, $key);                   //签名
                $res['key'] = $key;
                $post['sign'] = $sign;
                $post_xml = $this->arrToXml($post);
                //统一下单接口prepay_id
                $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
                $xml = $this->http_request($url, $post_xml);            //POST方式请求http
                $array = $this->xml_to_array($xml);                     //将【统一下单】api返回xml数据转换成数组,全要大写
                if ($array['return_code'] == "SUCCESS" && $array['result_code'] == "SUCCESS") {
                    $sign_arr['appId'] = $array['appid'];
                    $sign_arr['nonceStr'] = $this->randomkeys(32);      //随机字符串
                    $sign_arr['package'] = "prepay_id=".$array['prepay_id'];
                    $sign_arr['signType'] = "MD5";
                    $sign_arr['timeStamp'] = $time;
                    $newSign = $this->MakeSign($sign_arr, $key);                   //签名
                    $result['data'] = $sign_arr;
                    $result['newSign'] = $newSign;
                    $result['status'] = 1;
                    $result['msg'] = '统一下单成功';
                    $this->ajaxReturn($result);
                } else {
                    $result['status'] = 0;
                    $result['msg'] = '统一下单失败';
                    $this->ajaxReturn($result);
                }
            }else{
                $this->display();
            }
        }
    
        /**
         * 小程序商家模式回调 目前在使用
         */
        public function notify_url()
        {
            $xml = file_get_contents('php://input');
            $extend_content = json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA));//转换格式
            file_put_contents('./xcxPay.txt', 'xcx_post33: ' . $xml . "\r\n", FILE_APPEND);
    
            $post = json_decode($extend_content, true);
    
            $postSign = $post['sign'];
            unset($post['sign']);
            /* 微信官方提醒:
             *  商户系统对于支付结果通知的内容一定要做【签名验证】,
             *  并校验返回的【订单金额是否与商户侧的订单金额】一致,
             *  防止数据泄漏导致出现“假通知”,造成资金损失。
             */
            $user_sign = $this->MakeSign($post, "xxxxxxxxxxxxx");   //再次生成签名,与$postSign比较
    
            /*
            * 首先判断,订单是否已经更新为ok,因为微信会总共发送8次回调确认
            * 其次,订单已经为ok的,直接返回SUCCESS
            * 最后,订单没有为ok的,更新状态为ok,返回SUCCESS
            */
            if ($post['return_code'] == 'SUCCESS' && $postSign == $user_sign) {
    
                // 用户openid
                $openid = $post['openid'];
                // 用户是否关注公众账号,Y-关注,N-未关注
                $is_subscribe = $post['is_subscribe'];
    
                $data['post'] = $post;
                if($is_subscribe == "Y"){
                    $res = self::sendNotice($openid,$post['out_trade_no']);
                    $data['res'] = $res;
                    M('logs')->add(array('content' => json_encode($data),'orderId' => $post['out_trade_no']));
                    $this->return_success_v1();
                }else{
                    sleep(10);
                    $log = M('logs')->where(array('orderId' => $post['out_trade_no']))->find();
                    if($log){
                        $this->return_success_v1();
                    }else{
                        $res = self::sendNotice($openid,$post['out_trade_no']);
                        $data['res'] = $res;
                        M('logs')->add(array('content' => json_encode($data),'orderId' => $post['out_trade_no']));
                        $this->return_success_v1();
                    }
                }
            } else {
                $this->return_success_v1();
            }
        }
    
        /**
         * 随机字符串
         */
        public function randomkeys($length)
        {
            $pattern = '1234567890abcdefghijklmnopqrstuvwxyz
                       ABCDEFGHIJKLOMNOPQRSTUVWXYZ,./&l
                      t;>?;#:@~[]{}-_=+)(*&^%$?!';    //字符池
            $key = '';
            for ($i = 0; $i < $length; $i++) {
                $key .= $pattern{mt_rand(0, 35)};    //生成php随机数
            }
            return $key;
        }
    
        /**
         * 生成签名, $KEY就是支付key
         * @param $params
         * @param $KEY
         * @return string
         */
        public function MakeSign($params, $KEY)
        {
            //签名步骤一:按字典序排序数组参数
            ksort($params);
            $string = $this->ToUrlParams($params);  //参数进行拼接key=value&k=v
            //签名步骤二:在string后加入KEY
            $string = $string . "&key=" . $KEY;
            //签名步骤三:MD5加密
            $string = md5($string);
            //签名步骤四:所有字符转为大写
            $result = strtoupper($string);
            return $result;
        }
    
        public function arrToXml($item)
        {
            $xml = "<xml>";
            foreach ($item as $key => $val) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            }
            $xml .= "</xml>";
            return $xml;
        }
    
        /**
         * 调用接口, $data是数组参数
         * @param $url
         * @param null $data
         * @param array $headers
         * @return bool|string
         */
        public function http_request($url, $data = null, $headers = array())
        {
            $curl = curl_init();
            if (count($headers) >= 1) {
                curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
            }
            curl_setopt($curl, CURLOPT_URL, $url);
    
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
    
            if (!empty($data)) {
                curl_setopt($curl, CURLOPT_POST, 1);
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            }
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
            $output = curl_exec($curl);
            curl_close($curl);
            return $output;
        }
    
        /**
         * 数组转化
         * @param $xml
         * @return bool|mixed
         */
        public function xml_to_array($xml)
        {
            if (!$xml) {
                return false;
            }
            //将XML转为array
            //禁止引用外部xml实体
            libxml_disable_entity_loader(true);
            $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
            return $data;
        }
    
        /**
         * 将参数拼接为url: key=value&key=value
         * @param $params
         * @return string
         */
        public function ToUrlParams($params)
        {
            $string = '';
            if (!empty($params)) {
                $array = array();
                foreach ($params as $key => $value) {
                    $array[] = $key . '=' . $value;
                }
                $string = implode("&", $array);
            }
            return $string;
        }
    
        /**
         * 给微信发送确认订单金额和签名正确,SUCCESS信息 -xzz0521
         */
        private function return_success_v1()
        {
            $xml_post = '<xml>
                        <return_code>SUCCESS</return_code>
                        <return_msg>OK</return_msg>
                        </xml>';
            echo $xml_post;
            exit;
        }
    
        /**
         * 发送模板消息
         * @param $openid
         * @param $out_trade_no
         * @return bool|string
         */
        public function sendNotice($openid,$out_trade_no){
    
            //$token = M('access_token_native')->find();
            $token = M('access_token')->find();
            // token 过期
            if (!$token || $token['expires_in'] <= time() - $token['addtime']) {
                // 重新获取token
                $file_contents = self::getAccessToken();
                $access_token = $file_contents->access_token;
            } else {
                $access_token = $token['token'];
            }
    
            $template_id = "xxxxxxxxxxxxxxx";
    
            $value = array(
                "productType" => array(
                    "value" => "消费金额",
                    "color" => "#4a4a4a"
                ),
                "name" => array(
                    "value" => "0.01元",
                    "color" => "#4a4a4a"
                ),
                "time" => array(
                    "value" => date("Y-m-d H:i:s"),
                    "color" => "#4a4a4a"
                ),
                "remark" => array(
                    "value" => "秀山丽水欢迎您,欢迎前来丽水旅游及购买土特产!",
                    "color" => "#ff0000"
                ),
    
            );
            $url = 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=' . $access_token;
            $dd = array();
            $dd['touser'] = $openid;
            $dd['template_id'] = $template_id;
            $dd['url'] = "https://xxxxxx.com";
            $dd['data'] = $value;                    //模板内容,不填则下发空模板
            $result = $this->https_curl_json($url, $dd, 'json');
            return $result;
        }
    
        /**
         * @return mixed
         */
        public function getAccessToken(){
            $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=xxxxxxxxxxxx&secret=xxxxxxxxxxxx';
            $headerArray =array("Content-type:application/json;","Accept:application/json");
            $curl = curl_init();
            curl_setopt($curl, CURLOPT_URL, $url);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
            curl_setopt($curl,CURLOPT_HTTPHEADER,$headerArray);
            $result = curl_exec($curl);
            curl_close($curl);
            $escape_token = json_decode($result);
            if($escape_token->access_token){
                $data = [
                    'token' => $escape_token->access_token,
                    'expires_in' => $escape_token->expires_in,
                    'addtime' => time(),
                ];
                $res = M('access_token')->find();
                if ($res) {
                    M('access_token')->save($data);
                } else {
                    M('access_token')->add($data);
                }
            }
            return $escape_token;
        }
    
        /**
         * 发送Post 请求
         * @param $url
         * @param $data
         * @param $type
         * @return bool|string
         */
        function https_curl_json($url, $data, $type)
        {
            if ($type == 'json') {
                $headers = array("Content-type: application/json;charset=UTF-8", "Accept: application/json", "Cache-Control: no-cache", "Pragma: no-cache");
                $data = json_encode($data);
            }
            $curl = curl_init();
            curl_setopt($curl, CURLOPT_URL, $url);
            curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
    
            if (!empty($data)) {
                curl_setopt($curl, CURLOPT_POST, 1);
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            }
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
            $output = curl_exec($curl);
            if (curl_errno($curl)) {
                echo 'Errno' . curl_error($curl);//捕抓异常
            }
            curl_close($curl);
            return $output;
        }
    
        /**
         * 获取openID
         */
        function getOpenid(){
            $code = I('post.code');
            $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=xxxxxxxxxxxxx&secret=xxxxxxxxxxxx&code=$code&grant_type=authorization_code";
            $data = file_get_contents($url);
            if($data){
                $this->ajaxReturn(array('data'=>json_decode($data,true),'msg'=>'获取成功','status'=>1));
            }else{
                $this->ajaxReturn(array('data'=>'','msg'=>'获取失败','status'=>0));
            }
        }
    }
    
    展开全文
  • 微信h5支付首先商户平台要开通h5支付,然后关联公众号, 这里要注意的是h5支付不需要openid,而微信jsapi支付需要openid。 然后拿到: $appid = ""; //应用 APPID $mch_id = ""; //微信支付商户号 $key = ""; //...
  • 模拟微信内部跳转至微信支付

    千次阅读 2019-07-27 07:58:36
    模拟微信内部跳转至微信支付前言微信支付步骤介绍具体代码 前言 微信支付比支付宝或者其它方式要来的更复杂一些,但在我这个项目中,大部分的工作量后台都帮我处理好了,实际上只需要调用两个接口,...
  • 注:不论开发微信公众号内部任何功能,都要先熟读微信公众号文档。  1、开发微信公众号第一步,首先得完成以下3步配置  注:此部分转载微信公众号开发文档  步骤一:绑定域名 先登录微信公众平台进入“公众号...
  • 微信支付-微信H5外部浏览器支付「本文」微信H5内部浏览器支付「待写」PC端扫码支付「待写」 一直计划着写一写微信支付相关的文章,希望能加深一下自己的印象,拖了一天又一天… 最近终于空出时间来填坑了,我将文章...
  • 最近负责的一些项目开发,都用到了微信支付微信公众号支付微信H5支付微信扫码支付、APP微信支付)。在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存。 先说注意的第...
  • 可是现在仅仅支持华为还有苹果5s以上的手机呢,那么小米MIUI支持微信指纹支付吗?小米MIUI什么时候可以微信指纹支付呢?业内人士认为MIUI官方已透露了合作消息,相信双方已经取得了实质性进展。近期关于小米6传言甚多...
  • iOS中时间戳的格式要求是32位的int类型image.png一般服务器返回的是字符串需要强制转换成32位int类型的值二、服务器的签名方式错误微信支付:服务器(Java)统一下单获取prepay_id成功,iOS调用微信sdk提示支付验证...
  • H5移动端支持微信支付 [ 微信支付分为微信支付(JSAPI支付官方API)和微信支付(H5支付官方API)] && 支付宝支付 [手机网站支付转 APP 支付 官方API ] 订单生成逻辑:前端请求后端提交订单,后端去和...
  • 最近负责的一些项目开发,都用到了微信支付微信公众号支付微信H5支付微信扫码支付、APP微信支付)。在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存。先说注意的第一...
  • 请确保实际支付时的请求目录与后台配置的目录一致(现在已经支持配置根目录,配置后有一定的生效时间,一般5分钟内生效),否则将无法成功唤起微信支付。 在微信商户平台(pay.weixin.qq.com)设置您的JSAPI支付支付...
  • 微信jsapi支付微信支付

    千次阅读 2019-05-09 15:35:45
    微信h5支付首先商户平台要开通jsapi支付,商户平台要关联公众号, 这里要注意的是h5支付不需要openid,而微信jsapi支付需要openid。jsapi对客户端ip没有要求,而微信h5支付对客户端ip有要求 然后拿到: $appid = "...
  • delphi XE关于微信公众号支付微信零钱支付的便捷解决方案
  • 最近负责的一些项目开发,都用到了微信支付微信公众号支付微信H5支付微信扫码支付、APP微信支付)。在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存。 先说注意的第...
  • 在吊起微信之前,必须要知道微信支付是一个怎么样的过程。【了解一下-官方流程】商户系统和微信支付系统主要交互说明:步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。步骤2:商户后台收到用户支付单,...
  • 最近负责的一些项目开发,都用到了微信支付微信公众号支付微信H5支付微信扫码支付、APP微信支付)。在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存。 先说注意的第...
  • 微信内部使用支付宝接口,绕过微信屏蔽

    万次阅读 热门讨论 2015-08-23 11:25:30
    这段时间在做微信公众平台的开发,里面用到了支付宝支付,但是奇葩的是微信屏蔽了支付宝链接,最终的解决办法:用iframe将支付宝引用进来,其实微信只是屏蔽了支付宝的链接,但底层还是没有屏蔽的。下面贴一下主要的...
  • 最近负责的一些项目开发,都用到了微信支付微信公众号支付微信H5支付微信扫码支付、APP微信支付)。在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存。 先说注意的第一...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 26,185
精华内容 10,474
关键字:

微信内部支付