公众号支付_公众号支付配置 - CSDN
精华内容
参与话题
  • 公众号支付 APP支付 扫码支付 刷卡支付 H5支付 小程序支付 (本文档主要介绍公众号支付和扫码支付) 注:微信公众号目前必须是服务认证号,且开通微信支付功能。公众号对应的商户号也必须开通支付功能。 一、...

    微信支付是集成在微信客户端的支付功能,用户可以通过手机完成快速的支付流程。微信支付以绑定银行卡的快捷支付为基础,向用户提供安全、快捷、高效的支付服务。

    • 公众号支付
    • APP支付
    • 扫码支付
    • 刷卡支付
    • H5支付
    • 小程序支付

    (本文档主要介绍公众号支付和扫码支付)

    注:微信公众号目前必须是服务认证号,且开通微信支付功能。公众号对应的商户号也必须开通支付功能。

    一、公众号支付

    公众号支付是用户在微信中打开商户的H5页面,商户在H5页面通过调用微信支付提供的JSAPI接口调起微信支付模块完成支付。应用场景有:

    • 用户在微信公众账号内进入商家公众号,打开某个主页面,完成支付
    • 用户的好友在朋友圈、聊天窗口等分享商家页面连接,用户点击链接打开商家页面,完成支付
    • 将商户页面转换成二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付

    1.  设置JSSDK接口调用域名

    先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。  

    2.  设置授权域名

    开发公众号支付时,在统一下单接口中要求必传用户openid,而获取openid则需要您在公众平台设置获取openid的域名,只有被设置过的域名才是一个有效的获取openid的域名,否则将获取失败。如下图:

     

    3.  设置支付目录

    请确保实际支付时的请求目录与后台配置的目录一致,否则将无法成功唤起微信支付。

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

    注意:这三个地方的域名需使用字母、数字及“-”的组合,不支持IP地址、端口号及短链域名,且填写的域名须通过ICP备案的验证。

    4.   设置支付密钥app_key

    点击帐户中心——API安全——设置API密钥(用于支付是的验签)

    5.   获取openid

      由于openid必须为当前用户在当前支付公众号内的身份标识,所以在智慧校园项目中采取实时获取的方式获得。

      步骤一:用户同意授权,获取code

      访问以下链接获得带code参数的Url:

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

      参数说明

    参数

    是否必须

    说明

    appid

    公众号的唯一标识

    redirect_uri

    授权后重定向的回调链接地址,请使用urlencode对链接进行处理

    response_type

    返回类型,请填写code

    scope

    应用授权作用域,snsapi_base(不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)

    state

    重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节

    #wechat_redirect

    无论直接打开还是做页面302重定向时候,必须带此参数

    注:回调链接一定要urlencode,不然识别不出

      步骤二:通过code换取网页授权access_token

      首先请注意,这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。

      获取code后,请求以下链接获取access_token:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

      参数说明

    参数

    是否必须

    说明

    appid

    公众号的唯一标识

    secret

    公众号的appsecret

    code

    填写第一步获取的code参数

    grant_type

    填写为authorization_code

      返回JSON数据包如下:

    复制代码

    {
       "access_token":"ACCESS_TOKEN",
       "expires_in":7200,
       "refresh_token":"REFRESH_TOKEN",
       "openid":"OPENID",
       "scope":"SCOPE",
       "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" 
    }

    复制代码

      附:关于网页授权access_token和普通access_token的区别

      1、微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证(网页授权access_token),通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息;

      2、其他微信接口,如发微信消息接口,需要通过基础支持中的“获取access_token”接口(https://api.weixin.qq.com/cgi-bin/token)来获取到的普通access_token调用。

    -----------------------------上述5个步骤都为支付的准备步骤。-----------------------------

      支付业务流程及步骤如下图:

     

    6.   微信内H5调起支付

      步骤一:后台统一下单获取jsapi支付参数

      请求地址:https://api.mch.weixin.qq.com/pay/unifiedorder

      请求参数:   appid(公众账号ID),

    mch_id(商户号),

    device_info(设备号),

    nonce_str(随机字符串),

    sign(签名),

    sign_type(签名类型),

    body(商品描述) ,

    detail(商品详情),

    attach(附加数据),

    out_trade_no(商户订单号),

    fee_type(标价币种),

    total_fee(标价金额),

    spbill_create_ip(终端IP),

    time_start(交易起始时间),

    time_expire(交易结束时间),

    notify_url(支付回调地址),

    trade_type(交易类型),

    product_id(商品ID),

    opened(用户标识)

       注:参数值用XML转义即可,CDATA标签用于说明数据不被XML解析器解析。

       具体下单过程可参照微信支付Demo:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

      当请求成功,返回的XML中return_code 和result_code都为SUCCESS时会返回如下:

    复制代码

    <xml>
       <return_code><![CDATA[SUCCESS]]></return_code>
       <return_msg><![CDATA[OK]]></return_msg>
       <appid><![CDATA[wx3556f3b4523gf12r]]></appid>
       <mch_id><![CDATA[10000100]]></mch_id>
       <nonce_str><![CDATA[jfjsdh47fin0jdnv]]></nonce_str>
       <openid><![CDATA[oECxywCpJW8G-ktn3lwZ7MR4Cx2Q]]></openid>
       <sign><![CDATA[8SDVCNKDF89W32SDKCSAA0S32JKSDJJL]]></sign>
       <result_code><![CDATA[SUCCESS]]></result_code>
       <prepay_id><![CDATA[wx201801041172356eidnvnd9qk398324745]]></prepay_id>
       <trade_type><![CDATA[JSAPI]]></trade_type>
    </xml>

    复制代码

      步骤二:H5发起微信支付

      在微信浏览器里面打开H5网页中执行JS调起支付。接口输入输出数据格式为JSON。

      注意:WeixinJSBridge内置对象在其他浏览器中无效,参数名区分大小,大小写错误签名验证会失败。

    引用js:http://res.wx.qq.com/open/js/jweixin-1.0.0.js

      调用支付js如下:

    复制代码

    function onBridgeReady(){
       WeixinJSBridge.invoke(
           'getBrandWCPayRequest', {
               "appId":"wx2421b1c4370ec43b",     //公众号名称,由商户传入     
               "timeStamp":"1395712654",         //时间戳,自1970年以来的秒数     
               "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串     
               "package":"prepay_id=u802345jgfjsdfgsdg888",     
               "signType":"MD5",         //微信签名方式:     
               "paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 
           },
           function(res){     
               if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。 
           }
       ); 
    }
    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();
    }

    复制代码

      支付完成后,微信会将订单参数会以XML方式发送给商户系统回调地址,商户在回调地址接收参数并做相应处理。

      回调的XML格式为:

    复制代码

    <xml>
      <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
      <attach><![CDATA[支付测试]]></attach>
      <bank_type><![CDATA[CFT]]></bank_type>
      <fee_type><![CDATA[CNY]]></fee_type>
      <is_subscribe><![CDATA[Y]]></is_subscribe>
      <mch_id><![CDATA[10000100]]></mch_id>
      <nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str>
      <openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid>
      <out_trade_no><![CDATA[1409811653]]></out_trade_no>
      <result_code><![CDATA[SUCCESS]]></result_code>
      <return_code><![CDATA[SUCCESS]]></return_code>
      <sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign>
      <sub_mch_id><![CDATA[10000100]]></sub_mch_id>
      <time_end><![CDATA[20140903131540]]></time_end>
      <total_fee>1</total_fee>
      <trade_type><![CDATA[JSAPI]]></trade_type>
      <transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id>
    </xml>

    复制代码

    推荐技术文档:

    1.https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1

    2.https://wenku.baidu.com/view/cebde8a764ce0508763231126edb6f1aff007185.html?re=view

     

    二、扫码支付

      扫码支付可分为两种模式,商户根据支付场景选择相应模式。

      【模式一】:

      商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。

      商户支付回调URL设置指引:进入商户平台-->产品中心-->开发配置,进行配置和修改,如下图所示。

      【模式二】:

      商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付,不依赖设置的回调支付URL。

      流程图:

      

      业务流程说明:

    (1)商户后台系统根据用户选购的商品生成订单。

    (2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;

    (3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。

    (4)商户后台系统根据返回的code_url生成二维码。

    (5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。

    (6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。

    (7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。

    (8)微信支付系统根据用户授权完成支付交易。

    (9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。

    (10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。

    (11)未收到支付通知的情况,商户后台系统调用【查询订单API】。

    (12)商户确认订单已支付后给用户发货。

      附:

      前端页面代码:

    //前端页面
    <img src=" MakeQRCode?code_url=@code_url" alt="二维码"/>

       C#生成二维码后台代码:

    复制代码

        //生成二维码方法  
         public FileResult MakeQRCode(string code_url)
            {
                if (string.IsNullOrEmpty(code_url))
                    throw new ArgumentException("code_url");
                //初始化二维码生成工具
                QRCodeEncoder qrCodeEncoder = new QRCodeEncoder();
                qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;
                qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M;
                qrCodeEncoder.QRCodeVersion = 0;
                qrCodeEncoder.QRCodeScale = 4;
                //将字符串生成二维码图片
                Bitmap image = qrCodeEncoder.Encode(code_url, Encoding.Default);
                //保存为PNG到内存流  
                MemoryStream ms = new MemoryStream();
                image.Save(ms, ImageFormat.Jpeg);
                return File(ms.ToArray(), "image/jpeg");
            }

    复制代码

      推荐技术文档:

    1. https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1
    2. http://www.cnblogs.com/vinsonLu/p/5166214.html
    展开全文
  • 公司做公众号时需要接入微信支付,个人根据网上的demo摸索着完成了公司公众号支付和退款功能。现也将代码分享出来,希望对需要朋友有帮助。 一.提交支付的toPay.jsp页面代码:&lt;% String basePath = ...

    公司做公众号时需要接入微信支付,个人根据网上的demo摸索着完成了公司公众号的支付和退款功能。现也将代码分享出来,希望对需要朋友有帮助。 
    一.提交支付的toPay.jsp页面代码:

    <%
        String basePath = request.getScheme() + "://"+ request.getServerName() + request.getContextPath()+ "/";
    %>
    <html>
    <body>
        ....
        <div class="zdx3"><button onclick="pay()">共需支付${sumPrice }元&nbsp;&nbsp;确认支付</button></div>
    </body>
    </html> 
    
    <script>
        function pay() {
            var url="<%=basePath%>wechat/pay?money=${sumPrice}"; //注意此处的basePath是没有端口号的域名地址。如果包含:80,在提交给微信时有可能会提示 “redirect_uri参数错误” 。
            //money为订单需要支付的金额
            //state中存放的为商品订单号
            var weixinUrl="https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri="+encodeURI(url)+"&response_type=code&scope=snsapi_userinfo&state=${orderId}#wechat_redirect";
            window.location.href=encodeURI(weixinUrl);
        }
    
    </script>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    二.后台处理支付功能代码 
    (包含两部分: 
    1.处理支付信息,通过微信接口生成订单号,返回支付页面 
    2.提供一个微信支付完成后的回调接口) 
    第1部分代码:

    /**
     * 用户提交支付,获取微信支付订单接口
     */
    @RequestMapping(value="/pay")
    public ModelAndView pay(HttpServletRequest request,HttpServletResponse response){
        ModelAndView mv = new ModelAndView();
        String GZHID = "wxfd7c065eee11112222";// 微信公众号id
        String GZHSecret = "b5b3a627f5d1f8888888888888";// 微信公众号密钥id
        String SHHID = "111111111";// 财付通商户号
        String SHHKEY = "mmmmmmmmmmmmmmm";// 商户号对应的密钥
    
        /*------1.获取参数信息------- */
        //商户订单号
        String out_trade_no= request.getParameter("state"); 
        //价格
        String money = request.getParameter("money");
        //金额转化为分为单位
        String finalmoney = WeChat.getMoney(money);
        //获取用户的code
        String code = request.getParameter("code");
    
        /*------2.根据code获取微信用户的openId和access_token------- */
        //注: 如果后台程序之前已经得到了用户的openId 可以不需要这一步,直接从存放openId的位置或session中获取就可以。
        //toPay.jsp页面中提交的url路径也就不需要再经过微信重定向。写成:http://localhost:8080/项目名/wechat/pay?money=${sumPrice}&state=${orderId}
        String openid=null;
        try {
            List<Object> list = accessToken(code);
            openid=list.get(1).toString();
        } catch (IOException e) {
            logger.error("根据code获取微信用户的openId出现错误", e);
            mv.setViewName("error");
            return mv;
        }
    
        /*------3.生成预支付订单需要的的package数据------- */
        //随机数 
        String nonce_str= MD5.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
        //订单生成的机器 IP
        String spbill_create_ip = request.getRemoteAddr();
        //交易类型 :jsapi代表微信公众号支付
        String trade_type = "JSAPI";
        //这里notify_url是 微信处理完支付后的回调的应用系统接口url。
        String notify_url ="http://69a6a38e.ngrok.natapp.cn/heyi-console/wechat/weixinNotify";
    
        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        packageParams.put("appid",  GZHID);  
        packageParams.put("mch_id",  SHHID);  
        packageParams.put("nonce_str", nonce_str);  
        packageParams.put("body", "费用");  
        packageParams.put("out_trade_no", out_trade_no);  
        packageParams.put("total_fee", finalmoney);  
        packageParams.put("spbill_create_ip", spbill_create_ip);  
        packageParams.put("notify_url", notify_url);  
        packageParams.put("trade_type", trade_type); 
        packageParams.put("openid", openid); 
    
        /*------4.根据package数据生成预支付订单号的签名sign------- */
        RequestHandler reqHandler = new RequestHandler(request, response);
        reqHandler.init( GZHID,  GZHSecret,  SHHKEY);
        String sign = reqHandler.createSign(packageParams);
    
        /*------5.生成需要提交给统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder 的xml数据-------*/
        String xml="<xml>"+
                "<appid>"+ GZHID+"</appid>"+
                "<mch_id>"+ SHHID+"</mch_id>"+
                "<nonce_str>"+nonce_str+"</nonce_str>"+
                "<sign>"+sign+"</sign>"+
                "<body><![CDATA["+"费用"+"]]></body>"+
                "<out_trade_no>"+out_trade_no+"</out_trade_no>"+
                "<total_fee>"+finalmoney+"</total_fee>"+
                "<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
                "<notify_url>"+notify_url+"</notify_url>"+
                "<trade_type>"+trade_type+"</trade_type>"+
                "<openid>"+openid+"</openid>"+
                "</xml>";
    
        /*------6.调用统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder 生产预支付订单----------*/
        String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        String prepay_id="";
        try {
            prepay_id = GetWxOrderno.getPayNo(createOrderURL, xml);
            if(prepay_id.equals("")){
                mv.addObject("ErrorMsg", "支付错误");
                mv.setViewName("error");
                return mv;
            }
        } catch (Exception e) {
            logger.error("统一支付接口获取预支付订单出错", e);
            mv.setViewName("error");
            return mv;
        }
        /*将prepay_id存到库中*/
        PageData p = new PageData();
        p.put("shopId", out_trade_no);
        p.put("prePayId", prepay_id);
        activityService.updatePrePayId(p);
    
    
        /*------7.将预支付订单的id和其他信息生成签名并一起返回到jsp页面 ------- */
        nonce_str= MD5.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
        SortedMap<String, String> finalpackage = new TreeMap<String, String>();
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String packages = "prepay_id="+prepay_id;
        finalpackage.put("appId",  GZHID);  
        finalpackage.put("timeStamp", timestamp);  
        finalpackage.put("nonceStr", nonce_str);  
        finalpackage.put("package", packages);  
        finalpackage.put("signType", "MD5");
        String finalsign = reqHandler.createSign(finalpackage);
    
        mv.addObject("appid",  GZHID);
        mv.addObject("timeStamp", timestamp);
        mv.addObject("nonceStr", nonce_str);
        mv.addObject("packageValue", packages);
        mv.addObject("paySign", finalsign);
        mv.addObject("success","ok");
        mv.setViewName("wechat/pay");
       return mv;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120

    第2部分代码:

    /**
     * 提交支付后的微信异步返回接口
     */
    @RequestMapping(value="/weixinNotify")
    public void weixinNotify(HttpServletRequest request, HttpServletResponse response){
        String out_trade_no=null;
        String return_code =null;
        try {
            InputStream inStream = request.getInputStream();
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            outSteam.close();
            inStream.close();
            String resultStr  = new String(outSteam.toByteArray(),"utf-8");
            logger.info("支付成功的回调:"+resultStr);
            Map<String, Object> resultMap = parseXmlToList(resultStr);
            String result_code = (String) resultMap.get("result_code");
            String is_subscribe = (String) resultMap.get("is_subscribe");
            String transaction_id = (String) resultMap.get("transaction_id");
            String sign = (String) resultMap.get("sign");
            String time_end = (String) resultMap.get("time_end");
            String bank_type = (String) resultMap.get("bank_type");
    
            out_trade_no = (String) resultMap.get("out_trade_no");
            return_code = (String) resultMap.get("return_code");
    
            request.setAttribute("out_trade_no", out_trade_no);
            //通知微信.异步确认成功.必写.不然微信会一直通知后台.八次之后就认为交易失败了.
            response.getWriter().write(RequestHandler.setXML("SUCCESS", ""));
        }  catch (Exception e) {
            logger.error("微信回调接口出现错误:",e);
            try {
                response.getWriter().write(RequestHandler.setXML("FAIL", "error"));
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        } 
        if(return_code.equals("SUCCESS")){
            //支付成功的业务逻辑
        }else{
            //支付失败的业务逻辑
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    三.微信app中具体支付的jsp页面

    <html>
    <head>
    <script src="js/jquery-1.8.2.min.js" type="text/javascript"></script>
    </head>
    <body onload="javascript:pay();">
        <script type="text/javascript">
            function pay(){
                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();
                }       
            }
            function onBridgeReady(){
               WeixinJSBridge.invoke(
                   'getBrandWCPayRequest', {
                       "appId" : "${appid}",     //公众号名称,由商户传入     
                       "timeStamp": "${timeStamp}",         //时间戳,自1970年以来的秒数     
                       "nonceStr" : "${nonceStr}", //随机串     
                       "package" : "${packageValue}",     
                       "signType" : "MD5",         //微信签名方式:     
                       "paySign" : "${paySign}"    //微信签名 
                   },function(res){
                    if(res.err_msg == "get_brand_wcpay_request:ok"){  
                        alert("微信支付成功!");
                    }else if(res.err_msg == "get_brand_wcpay_request:cancel"){  
                        alert("用户取消支付!");
                    }else{  
                       alert("支付失败!");
                    }  
                }); 
            }
        </script>
    </body>
    </html>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    其他需要用到的相关类和方法:

    金额 元转分:

          /**
             * 元转换成分
             * @param money
             * @return
             */
            public static String getMoney(String amount) {
                if(amount==null){
                    return "";
                }
                // 金额转化为分为单位
                String currency =  amount.replaceAll("\\$|\\¥|\\,", "");  //处理包含, ¥ 或者$的金额  
                int index = currency.indexOf(".");  
                int length = currency.length();  
                Long amLong = 0l;  
                if(index == -1){  
                    amLong = Long.valueOf(currency+"00");  
                }else if(length - index >= 3){  
                    amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));  
                }else if(length - index == 2){  
                    amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);  
                }else{  
                    amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");  
                }  
                return amLong.toString(); 
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    通过微信用户code获取用户的openId:

        /**
         * 通过微信用户的code换取网页授权access_token
         * @return
         * @throws IOException
         * @throws
         */
        public List<Object> accessToken(String code) throws IOException {
            List<Object> list = new ArrayList<Object>();
            String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
                    + WeChat.HYGZHID + "&secret=" + WeChat.HYGZHSecret+ "&code=" + code + "&grant_type=authorization_code";
            HttpClient client = new DefaultHttpClient();
            HttpPost post = new HttpPost(url);
            HttpResponse res = client.execute(post);
            if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                HttpEntity entity = res.getEntity();
                String str = org.apache.http.util.EntityUtils.toString(entity, "utf-8");
                ObjectMapper mapper=new com.fasterxml.jackson.databind.ObjectMapper.ObjectMapper();
                Map<String,Object> jsonOb=mapper.readValue(str, Map.class);
                list.add(jsonOb.get("access_token"));
                list.add(jsonOb.get("openid"));
            }
            return list;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    MD5提取摘要:

    /**
     * MD5加密
     */
    public class MD5 {
    
        private MD5() {}
    
        /**
         * 对传入的数据提取摘要
         * @param buffer
         * @return
         */
        public final static String getMessageDigest(byte[] buffer) {
            char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
            try {
                MessageDigest mdTemp = MessageDigest.getInstance("MD5");
                mdTemp.update(buffer);
                byte[] md = mdTemp.digest();
                int j = md.length;
                char str[] = new char[j * 2];
                int k = 0;
                for (int i = 0; i < j; i++) {
                    byte byte0 = md[i];
                    str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                    str[k++] = hexDigits[byte0 & 0xf];
                }
                return new String(str);
            } catch (Exception e) {
                return null;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    解析微信回调的xml数据:

        /**
         * description: 解析微信通知xml
         * 
         * @param xml
         * @return
         * @author ex_yangxiaoyi
         * @see
         */
        @SuppressWarnings({ "unused", "rawtypes", "unchecked" })
        private static Map parseXmlToList(String xml) {
            Map retMap = new HashMap();
            try {
                StringReader read = new StringReader(xml);
                // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
                InputSource source = new InputSource(read);
                // 创建一个新的SAXBuilder
                SAXBuilder sb = new org.jdom.input.SAXBuilder.SAXBuilder();
                // 通过输入源构造一个Document
                Document doc = (Document) sb.build(source);
                Element root = doc.getRootElement();// 指向根节点
                List<Element> es = root.getChildren();
                if (es != null && es.size() != 0) {
                    for (Element element : es) {
                        retMap.put(element.getName(), element.getValue());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return retMap;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    java文件和需要用到的工具类,我都放到csdn的下载中了。 
    地址:http://download.csdn.net/detail/aofavx/9606697。 
    如果有不对或者少文件的地方,请留言。我给大家补正。

    展开全文
  • 微信公众号支付的那些坑

    千次阅读 2018-06-27 12:29:46
    在之前记录了一下做微信公众号支付的过程,但是有些混乱,之前做的内个也不是直接接的微信官方,而是转接的别人在接的微信官方,他们赚个手续费,在这之后因为app停用了一段时间,上游公司把我们的appid给关掉了,...

    在之前记录了一下做微信公众号支付的过程,但是有些混乱,之前做的内个也不是直接接的微信官方,而是转接的别人在接的微信官方,他们赚个手续费,在这之后因为app停用了一段时间,上游公司把我们的appid给关掉了,所以打算从新接,直接接微信官方,好了这是背景。

    我们做的是公众号支付,也就是在微信网页内部进行调取支付插件进行支付的一个过程

    所以需要到微信官方开通公众号支付 微信官网:https://pay.weixin.qq.com

    1、登录后点击产品中心, 点击公众号支付


    进入后就会看到这个页面


    因为我的已经开通所以就不需要了

    这是官方文档 : https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3

    点击开发配置

    进行配置支付授权目录:也就是你的支付页面所在的目录

    一定是生产环境的,微信不支持 ip +端口 形式的地址   异步通知也不支持,

    所以测试都需要线上真实环境的域名+支付页面所在目录


    登录公众号平台进行配置

    https://mp.weixin.qq.com 

    公众号的按钮在下面

    其次设置你的JS接口安全域名:也就是完整域名如:www.baidu.com


    配置到这里基本就算完成了

    现在我们需要获取几个必须的参数

    appid,mch_id ,加密key

    基本配置按钮也在下面

    显示appid  点击基本配置就会看到了 如:wxf8xxxxxxxxxfca


    mch_id 就是你登录微信商户后台的内个号,如:1594xxxxxxxxx98 


    key 获取,也是在微信商户后台

    这个是自己设置的,看你自己设置了,


    1.        appid APPID (已有)

    wxfxxxxxxxxxxxxxca

    2.        mch_id 商户ID (已有)

    147xxxxxxxxxxx54

    3.        nonce_str 随机字符串 , 生成UUID就可以了;

     UUID.randomUUID().toString().trim().replaceAll("-", "");

    4.        sign 签名 用WXPayUtil中的generateSignature(finalMap<String, String> data, String key)方法,data是将除了sign外,其他10个参数放到map中,key是四大配置参数中的API秘钥(paternerKey)(这里不要着急管它,最后处理它);

    5.        body 所支付的名称

    6.        out_trade_no 自己后台生成的订单号,只要保证唯一就好:如“pay2018062521331”

    7.        total_fee 支付金额 单位:分,为了测试此值给1,表示支付1分钱

    8.        spbill_create_ip IP地址 网上很多ip的方法,自己找,此处测试给“127.0.0.1”

    9.        notify_url 回调地址:这是微信支付成功后,微信那边会带着一大堆参数(XML格式)请求这个地址多次,这个地址做我们业务处理如:修改订单状态,赠送积分等。Ps:支付还没成功还想这么远干嘛,最后再说。地址要公网可以访问。

    10.    trade_type 支付类型 咱们是公众号支付此处给“JSAPI”

    11.    openid 支付人的微信公众号对应的唯一标识,每个人的openid在不同的公众号是不一样的,这11个参数里,最费劲的就是他了,其他的几乎都已经解决,现在开发得到这个参数。

        获得openid的部分内容应该不属于微信支付的范畴,属于微信公众号网页授权的东西,详情请参考微信网页授权:

    https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

    获得openid步骤:

    第一步:用户同意授权,获取code

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

    注意:1. redirect_uri参数:授权后重定向的回调链接地址请使用 urlEncode 对链接进行处理。

    2. scope:用snsapi_base 

        通过此链接可以获取code,可以在一个空页面设置一个a标签,链接至其redirect_uri的地址。点击a标签,即可链接到redirect_uri的地址,并携带code。

    [html] view plain copy
    1. <a href="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx15c*********&redirect_uri=http%3a%2f%2fwww.***.com%2fpay.jsp&response_type=code&cope=snsapi_base#wechat_redirect">去支付页面pay.jsp并携带code</a>  

    第二步:通过code换取网页授权access_token(其实微信支付就没有必要获取access_token,咱们只要其中openid,不是要用户信息,此步结果已经就含有咱们需要的openid了)

    获取code,请求以下链接获取access_token https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

    上一步的code有了,对于此链接的参数就容易了。可是在页面上如何处理是个问题,我是在pay.jsp页面加载完成后将获取code当做参数传异步到后台,在后台中用http相关类发送get请求(可以自行网上查找)。返回的JSON结果为:

    [html] view plain copy
    1. { "access_token":"ACCESS_TOKEN",  
    2. "expires_in":7200,  
    3. "refresh_token":"REFRESH_TOKEN",  
    4. "openid":"OPENID",//就是它,只要这个值  
    5. "scope":"SCOPE" }  

    好了都有了,我们就可以开始写拼装参数了, 参数填写修改成你自己的就可以了

       HashMap<String, Object> reqMap = new HashMap<String, Object>()
                reqMap.put("appid", appid);
                reqMap.put("mch_id", mchid);
                reqMap.put("nonce_str", getUUid());
                reqMap.put("out_trade_no", "订单号");
                reqMap.put("total_fee", 金额);
                reqMap.put("body", "所支付的名称");
                reqMap.put("spbill_create_ip", getIp);
                reqMap.put("notify_url", notifyUrl);
                // reqMap.put("device_info", "WEB");
                reqMap.put("trade_type", "JSAPI");
                reqMap.put("openid", openId);

    下面是工具类

    /** 生成32位编码
     * @return string
     */
        public static String getUUid() {
            String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
            return uuid;
        }
    /**
     * 获取IP
     * @param request
     * @return
     */
        public String getRemortIP(HttpServletRequest request) {
            String remoteAddr = request.getRemoteAddr();
            String forwarded = request.getHeader("X-Forwarded-For");
            String realIp = request.getHeader("X-Real-IP");
            String ip = null;
            if (realIp == null) {
                if (forwarded == null) {
                    ip = remoteAddr;
                } else {
                    ip = remoteAddr + "/" + forwarded.split(",")[0];
                }
            } else {
                if (realIp.equals(forwarded)) {
                    ip = realIp;
                } else {
                    if (forwarded != null) {
                        forwarded = forwarded.split(",")[0];
                    }
                    ip = realIp + "/" + forwarded;
                }
            }
            return ip
        }

    下面进行签名,MD5工具类会贴出了的

     def md5str = createLinkString(reqMap);//排序
                System.out.println("---> md5str:" + md5str + "&key=" + wxapiKey);
                def sign = MD5Tool.md5(md5str + "&key=" + apiKey, charset).toUpperCase();签名并转大写
                reqMap.put("sign", sign)
    /**
     *  SignStr 待签名字符串
     * @param params
     * @return
     */
        public static String createLinkString(Map<String, Object> map) {
            System.out.println("带签名排序params:::::" + map.toString())
            List<String> keys = new ArrayList<String>(map.keySet());
            Collections.sort(keys);
            System.out.println("排序完Keys:::::" + keys.toString())
            def sb = new StringBuffer()
            for (int i = 0; i < keys.size(); i++) {
                String key = keys.get(i);
                Object value = map.get(key)
                System.out.println("key::" + key + "::value::" + value)
                if ("sign".equals(key) || "sign_type".equals(key) || value == null || value == "") {
                    continue;
                }
                if (i == keys.size() - 1) {
                    sb.append(key).append("=").append(value)
                } else {
                    sb.append(key).append("=").append(value).append("&")
                }
            }
            def sbStr = sb.toString()
            System.out.println("===========签名串:" + sbStr)
            return sbStr;
        }
    package eims;
    
    import org.apache.commons.codec.digest.DigestUtils;
    
    import java.io.UnsupportedEncodingException;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.Date;
    //MD5工具类
    public class MD5Tool {
        public static String md5(byte[] data) {
            MessageDigest md5 = null;
            try {
                md5 = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                throw new IllegalStateException("System doesn't support MD5 algorithm.");
            }
            md5.update(data);
    
            byte[] encoded = md5.digest();
            StringBuffer buf = new StringBuffer();
            for (int i = 0; i < encoded.length; i++) {
                if ((encoded[i] & 0xff) < 0x10) {
                    buf.append("0");
                }
                buf.append(Long.toString(encoded[i] & 0xff, 16));
            }
    
            return buf.toString();
        }
        /**
         * 签名字符
         *
         * @param text
         *            要签名的字符
         * @param key
         *            密钥
         *            编码格式
         * @return 签名结果
         */
        public static String sign(String text, String key, String charset) throws Exception {
            text = text + key;
            return DigestUtils.md5Hex(getContentBytes(text, charset));
        }
        /**
         * @param content
         * @param charset
         * @return
         * @throws java.io.UnsupportedEncodingException
         */
        private static byte[] getContentBytes(String content, String charset) {
            if (charset == null || "".equals(charset)) {
                return content.getBytes();
            }
            try {
                return content.getBytes(charset);
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("签名过程中出现错,指定的编码集不对,您目前指定的编码集是:" + charset);
            }
        }
        public static String md5(String str, String charset) {
            if (str == null) return null;
            try {
                return md5( str.getBytes(charset) );
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                throw new IllegalStateException("System doesn't support Charset '" + charset + "'");
            }
        }
    }

    到这里所有的参数都拼装好了,微信要的是XML 格式的所以我们需要进行转换

    def requestXML = mapGetXML(reqMap);
    System.out.println("req xml :\n" + requestXML);
    String response = post(wxUrl, requestXML);
    

    /**
     * map 转 XML
     * @param map
     * @return
     */
        public static String mapGetXML(Map<String, String> map) {
            String xmlStr = null;
            StringBuffer sbf = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
            sbf.append("<xml>");
            for (Map.Entry<String, String> s : map.entrySet()) {
                sbf.append("<")
                        .append(s.getKey())
                        .append(">")
                        .append(s.getValue())
                        .append("</")
                        .append(s.getKey())
                        .append(">");
            }
            sbf.append("</xml>");
            xmlStr = sbf.toString();
            return xmlStr;
        }

    XML也转换好了,我们现在可以直接发送请求了 格式xml

    请求地址  :https://api.mch.weixin.qq.com/pay/unifiedorder

     String response = post(wxUrl, requestXML);

    post方法

    /**
     * 发送xml数据请求到server端
     *
     * @param url
     *            xml请求数据地址
     * @param xmlString
     *            发送的xml数据流
     * @return null发送失败,否则返回响应内容
     */
        public static String post(String url, String xmlFileName) {
            // 关闭
            System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
            System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
            System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "stdout");
    
            // 创建httpclient工具对象
            HttpClient client = new HttpClient();
            // 创建post请求方法
            PostMethod myPost = new PostMethod(url);
            // 设置请求超时时间
            client.setConnectionTimeout(300 * 1000);
            String responseString = null;
            try {
                // 设置请求头部类型
                myPost.setRequestHeader("Content-Type", "text/xml");
                myPost.setRequestHeader("charset", "utf-8");
    
                // 设置请求体,即xml文本内容,注:这里写了两种方式,一种是直接获取xml内容字符串,一种是读取xml文件以流的形式
                // myPost.setRequestBody(xmlString);
    
                // InputStream
                // body=this.getClass().getResourceAsStream("/"+xmlFileName);
                // myPost.setRequestBody(body);
                myPost.setRequestEntity(new StringRequestEntity(xmlFileName, "text/xml", "utf-8"));
                int statusCode = client.executeMethod(myPost);
                if (statusCode == HttpStatus.SC_OK) {
                    BufferedInputStream bis = new BufferedInputStream(myPost.getResponseBodyAsStream());
                    byte[] bytes = new byte[1024];
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    int count = 0;
                    while ((count = bis.read(bytes)) != -1) {
                        bos.write(bytes, 0, count);
                    }
                    byte[] strByte = bos.toByteArray();
                    responseString = new String(strByte, 0, strByte.length, "utf-8");
                    bos.close();
                    bis.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            myPost.releaseConnection();
            return responseString;
        }

    搞了这么多终于看到点结果了


    请求过后 微信端返回的也是XML 不利于我们处理,所以继续转map
      Map<String, Object> respMap = xmlString2Map(response);

    xml 转map

    /**
     * Xml string转换成Map
     * @param xmlStr
     * @return
     */
        public static Map<String, Object> xmlString2Map(String xmlStr) {
            Map<String, Object> map = new HashMap<String, Object>();
            Document doc;
            try {
                doc = DocumentHelper.parseText(xmlStr);
                Element el = doc.getRootElement();
                map = recGetXmlElementValue(el, map);
            } catch (DocumentException e) {
                e.printStackTrace();
            }
            return map;
        }

    转好map 后我们就开始取 prepay_id

    搞了这么久就是为了丫的, 取出来后我们还需要把参数拼装一遍,扔到页面,掉起 JS 插件进行支付

    参数:

    appid 也有

    timeStamp 时间戳 你们new Date();即可,因为我语言是 Groovy 所以需要getTime 才是秒数

    package   prepay_id   已有了

    nonceStr    随机数  getuuid方法就可以了

    signType     固定值  MD5

    sign           上面5个参数的签名结果

    这里值得注意的是package 参数, 这个参数可不是简单的吧prepay_id 放进去

    ,要把 “prepay_id=”这个拼接上里面不能有多余的"或者'符号

    之前没有拼接 ,微信支付的时候返回 缺少total_fee参数, 可是上一步给微信传的时候并没有少,微信返回的都成功了
    所以还是抛页面的时候出现的错误,害我整了好久。



     String retCode = respMap.get("result_code");
                if ("SUCCESS".equals(retCode)) {
                   String prepay_id = respMap.get("prepay_id");
                    Map map = new HashMap();
                    map.put("appId", wxappid);
                    map.put("timeStamp", new Date().getTime());
                    map.put("package", "prepay_id=" + prepay_id);
                    map.put("nonceStr", getUUid());
                    map.put("signType", "MD5");
                    def newmd5str = createLinkString(map);
                    System.out.println("ShoppingCartService---> md5str:" + newmd5str + "&key=" + wxapiKey);
                    def newSign = MD5Tool.md5(newmd5str + "&key=" + wxapiKey, charset).toUpperCase();
                    map.put("paySign", newSign);
                }

    参数装完后直接传到页面,看自己框架了,我就不贴了

    下面直接把页面贴出了 值得注意的是

    package 这个参数 ,在页面是一个域 ,所以在后台传的时候重新改个名 我改的是paypackage 

    <%@ page contentType="text/html;charset=UTF-8" %>
    <html>
    <head>
        <title>微信支付</title>
        <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
    </head>
    <script>
        function onBridgeReady() {
            <%
                System.out.println("incoming...");
                System.out.println(request.getParameter("appId"));
                System.out.println(request.getParameter("timeStamp"));
                System.out.println(request.getParameter("nonceStr"));
                System.out.println(request.getParameter("paypackage"));
                System.out.println(request.getParameter("signType"));
                System.out.println(request.getParameter("paySign"));
                System.out.println('deviceType:::'+request.getParameter("deviceType"));
                System.out.println("incoming...");
            %>
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest', {
                    "appId": "<%= request.getParameter("appId") %>",     //公众号名称,由商户传入
                    "timeStamp": "<%= request.getParameter("timeStamp") %>",         //时间戳,自1970年以来的秒数
                    "nonceStr": "<%= request.getParameter("nonceStr") %>", //随机串
                    "package": "<%= request.getParameter("paypackage") %>",
                    "signType": "<%= request.getParameter("signType") %>",         //微信签名方式:
                    "paySign": "<%= request.getParameter("paySign") %>" //微信签名,paySign 采用统一的微信支付 Sign 签名生成方法,注意这里 appId 也要参与签名,appId 与 config 中传入的 appId 一致,即最后参与签名的参数有appId, timeStamp, nonceStr, package, signType。
                },
                function (res) {
                    if (res.err_msg == "get_brand_wcpay_request:ok") {     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
                        alert('支付成功!');
                       
                    } else if (res.err_msg == "get_brand_wcpay_request:cancel") {
                        alert('已取消微信支付!');
                    } else {
                        alert('支付失败!' + res.err_msg)
                    }
                    /*WeixinJSBridge.call('closeWindow');*/  //关闭微信端窗口
                }
            );
        }
    
        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();
        }
    </script>
    </html>


    到这里就完成了,微信支付只能去生产上面测试,这是比较操蛋的
    如果按照以上的步骤一步步走的话不会出现问题的,如有问题请留言,看到会及时答复(有人看吗,哈哈哈哈哈够呛!!



    展开全文
  • 微信公众号+vue+微信支付

    万次阅读 热门讨论 2018-07-11 14:25:32
    在上一篇文章中本人讲述了微信公众号的授权以及获取用户信息,在此基础上今天这篇博客跟大家分享一下微信支付的流程及代码,还有自己在开发时的一些坑。1、在进行微信支付时,除了需要一个公众号之外,你还需要一个...

    在上一篇文章中本人讲述了微信公众号的授权以及获取用户信息,在此基础上今天这篇博客跟大家分享一下微信支付的流程及代码,还有自己在开发时的一些坑。

    1、在进行微信支付时,除了需要一个公众号之外,你还需要一个微信商户。根据商户申请规则进行商户申请

    2、开始配置开发所需要的参数如下所示:


    这是公众号的基本开发配置,这里在微信授权的时候就已经需要配置了 但是注意AppSecret在你第一次配置的时候就需要备份下来,如果没有备份那么下次使用的话你就需要去重置它


    网页域名授权,你需要填写正确的可以访问的项目域名,确保微信可以访问

    3、公众号配置好后我们需要开始配置微信商户平台的内容了

    商户平台需要进行的配置是:商户号,支付密钥

    商户号一般都是和自己公众号的商户号是一样的

    主要是一个支付密钥的配置


    然后是域名配置 和公众号差不多


    配置支付的参数完成之后就可以开始进行开发了  不过在支付开发中可能会有些许的麻烦  那就是你不能在线下本机测试,因为IP白名单的限制  除非你将自己的本机地址进行地址映射,但是映射操作比较繁琐,如果自己的项目不是很大的话那就自己麻烦一点部署项目然后进行测试吧,如果你想进行映射之后本机测试这里可以推荐一个组件给你访问链接https://natapp.cn/

    4、开始正式的代码书写 这里使用的SDK是(

    (1)后台进行支付配置: 

               //实例配置

                var config = new WeChatConfig()
                {
                    AppId = "wx7a87835d950f228f",
                    AppSecret = "97d8581300ed49326c53db5373e07104",
                    MchId = "1502779501",
                    Notify = "http://medicalapi.xin-lai.com/PayNotify/0/wechat",//这是支付回调路径(不是前端路径,这个很坑)
                    TenPayKey = "wlsoftnewhissong3355180meiyanfei",  
                    PayAppId = "wx7a87835d950f228f"
                };
                WeChatSDKBuilder.Create()
                    //设置日志记录
                    .WithLoggerAction((tag, message) =>
                    {
                        logger.Debug(string.Format("Tag:{0}\tMessage:{1}", tag, message));
                    })
                    //注册Key。不再需要各个控制注册
                    .Register(WeChatFrameworkFuncTypes.GetKey, model =>
                        {
                            return "wx8370799bb85178a8";
                        }
                    )
                    //注册获取配置函数:根据Key获取微信配置(加载一次后续将缓存)
                    .Register(WeChatFrameworkFuncTypes.Config_GetWeChatConfigByKey,
                        model =>
                        {
                            return config;
                        })
                   //注册微信支付配置
                   .Register(WeChatFrameworkFuncTypes.Config_GetWeChatPayConfigByKey, model =>
                        {
                            return config;
                        }
                   )

                    .Build();

    你可以将config写在项目的配置文件中然后进行获取  我这里是直接实例化直接了当

    (2)支付还需要配置回调处理逻辑:

    //支付回调设置
                PayNotifyBuilder
                    .Create()
                //设置日志记录
                .WithLoggerAction((tag, message) =>
                {
                    logger.Debug(string.Format("Tag:{0}\tMessage:{1}", tag, message));
                }).WithPayNotifyFunc(async(input) =>
                {
                    switch (input.Provider)
                    {
                        case "wechat":
                            {
                                var api = new WeChatPayApi();
                                var output = api.PayNotifyHandler(input.Request.Body);
                                if (output.IsSuccess())
                                {
                                    bool theresult = true;
                                    var resultLog = output.ToJsonString();
                                    logger.Debug("微信支付处理成功: " + resultLog);
                                    //判断支付类别并编辑处理
                                    if (output.Attach.StartsWith("门诊"))
                                    {
                                        OutpatientsAppService outpatientsAppService = new OutpatientsAppService(new XmlSerializeAppService());
                               theresult= await outpatientsAppService.LoadChargeListInfo(new Outpatients.Dto.LoadChargeListInfoinput()
                                        {
                                            patCardNo = output.Attach.Substring(2),
                                            psOrdNum = output.TransactionId,
                                            hisOrdNum = output.OutTradeNo,
                                            payMode = "WXPay",
                                            payAmt = Convert.ToDecimal(output.TotalFee) % 100,
                                            payTime =output.TimeEnd,
                                            patCardType= "院内诊疗卡",
                                            agtOrdNum= output.Attach.Substring(2),
                                        });
                                    }
                                    else if (output.Attach.StartsWith("住院"))
                                    {
                                        logger.Debug("进入住院缴费!");
                                    }
                                    else if (output.Attach.StartsWith("挂号"))
                                    {
                                        RegistrationsAppService registrationsAppService = new RegistrationsAppService(new XmlSerializeAppService());
                                        theresult= await registrationsAppService.RegistPay(output.Attach.Substring(2));


                                    }


                                    if (theresult == true)
                                    {
                                        logger.Debug(output.Attach.Substring(0, 2) + "缴费支付完成!");
                                        logger.Debug("缴费卡号!"+output.Attach.Substring(2));
                                    }
                                    else
                                    {
                                        logger.Debug(output.Attach.Substring(0,2)+"缴费支付未完成!");
                                    }
                                }
                                else
                                {
                                    //此处编写失败处理逻辑
                                    var failLog = output.ToJsonString();
                                    logger.Error("微信支付处理失败: " + failLog);


                                    return await Task.FromResult("<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[回调处理失败]]></return_msg></xml>");
                                }

                            }
                            break;
                        default:
                            break;
                    }
                    return null;

                }).Build();

    (3)支付接口的封装与调用

    先是进行微信统一下单

     /// <summary>
        /// 支付接口实现类
        /// </summary>
        [Produces("application/json")]
        [Route(ApiPrefix + "[controller]")]
        public class WhechatPayAppService : AppServiceBase, IwhechatPayAppService
        {
            /// <summary>
            /// 依赖注入
            /// </summary>
            private readonly IHttpContextAccessor _httpContextAccessor;


            public WhechatPayAppService(IHttpContextAccessor httpContextAccessor)
            {
                _httpContextAccessor = httpContextAccessor;
            }
            /// <summary>
            /// 下单 获取微信支付授权
            /// </summary>
            /// <param name="input"></param>
            /// <returns></returns>
            [AbpAllowAnonymous]
            [HttpPost("GetPayurl")]
            public async Task<UnifiedorderResult> GetPayurl(Payinfoinput input)
            {
                //获取实际IP
                string clientIp = null;
                if (clientIp.IsNullOrWhiteSpace())
                    clientIp = _httpContextAccessor.HttpContext?.Connection?.RemoteIpAddress?.ToString();


                clientIp = clientIp ?? "8.8.8.8";


                using (UnitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant, AbpDataFilters.MustHaveTenant))
                {
                    var config = WeChatConfigManager.Current.GetPayConfig();
                    var result = WeChatApisContext.Current.TenPayV3Api.Unifiedorder(new WeChat.SDK.Pays.TenPayV3.UnifiedorderRequest
                    {
                        Body = input.Body,
                        OpenId = input.OpenId,
                        TimeStart = DateTime.Now.ToString("yyyyMMddHHmmss"),
                        TimeExpire = DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"),
                        TradeType = "JSAPI",
                        DeviceInfo = "WEB",
                        TotalFee =Convert.ToInt32(input.Cash * 100).ToString(),
                        OutTradeNo = input.OutTradeNo,
                        SpbillCreateIp = clientIp,
                        Attach=input.Attach,
                    });
                    if (result.ReturnCode == "FAIL")
                        throw new UserFriendlyException(result.Message);
                    var dict = new Dictionary<string, string>
                    {
                        {"appId", result.AppId},
                        {"timeStamp", PayUtil.GetTimestamp()},
                        {"nonceStr", result.NonceStr},
                        {"package", "prepay_id=" + result.PrepayId},
                        {"signType", "MD5"}
                    };
                    dict.Add("paySign", PayUtil.CreateMd5Sign(dict, WeChatConfigManager.Current.GetPayConfig().TenPayKey));


                    return new UnifiedorderResult
                    {
                        MWebUrl = null,
                        PrepayId = "prepay_id=" + result.PrepayId,
                        TradeType = result.TradeType,
                        AppId = result.AppId,
                        paySign= dict["paySign"],
                        nonceStr = result.NonceStr,
                        timeStamp= dict["timeStamp"],
                        msg=result.ErrCodeDes,
                    };
                }
            }

    前端调用统一下单接口 发起支付


    统一下单完成之后  用完成返回的参数唤起支付


    前端js会返回支付的结果给你  同时后台会异步的根据你配置的url返回支付回调的信息 

    坑:当微信支付完成页面收起之后,会回到你原来的页面  但是这时你的这个页面是没有路径的

    在这里一般前端只做简单的页面处理,后台进行具体业务处理如上述支付回调配置所述   这样你的支付就完成了  

    相比微信授权来说 公众号的内容就比较好理解了!  

    展开全文
  • 微信公众号支付H5调用支付详解

    万次阅读 2018-01-16 15:03:26
    最近项目需要微信支付,然后看了下微信公众号支付,,虽然不难,但是细节还是需要注意的,用了大半天时间写了个demo,并且完整的测试了一下支付流程,下面分享一下微信公众号支付的经验。 一、配置公众号微信...
  • 支付--微信公众号支付(JSAPI)

    千次阅读 2017-10-10 10:23:36
    这几天公司微信端项目结束,把微信公众号支付做个整理方便有同样功能需求的同学。先放上一份官方文档 https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=7_1,咱们既然要做支付肯定要先了解人家的规范...
  • 微信公众号支付开发全过程

    万次阅读 2018-07-23 19:19:26
    微信公众号支付开发全过程 需求:把游戏里的商城转移到微信公众号上 微信公众号支付开发的准备过程: 需要注册一个公众号,然后开通微信支付功能(需要5个工作日左右),然后就可以得到APPID,APP_SECRET,商户号...
  • 微信公众号支付是我接触微信支付最复杂的一个了,当时总共弄了五天才弄好,很惭愧. 1,首先咋们还是先看看图,图看懂了一切都好说 2,整个流程大概弄清楚了,需要做准备工作了 code --------------用户点击充值需要...
  • 微信支付已经成功支付,只是在最下面的微信公众号不是自动勾上的,需要手动关注,请问如何做到自动关注?感谢!
  • 微信支付默认关注公众号的规则

    千次阅读 2017-06-20 10:57:33
    支持支付成功默认关注公众号的接口有:刷卡支付接口、扫码支付接口、公众号支付接口,APP支付的默认关注目前只对行业的龙头公司才开放申请.
  • 微信公众号支付测试方法

    万次阅读 2017-11-07 14:47:24
    但是微信公众号支付却不在测试号的测试范围之内。 微信不允许在测试号进行公众号支付测试,所以导致了我们项目对支付进行了改动之后,导致无法在测试号上测试。 在搜索了各种解决方案之后,找到了一个我认为对我们来...
  • 微信公众号支付失败的各种原因

    万次阅读 2016-04-23 09:42:28
    1) 公众号-》微信支付-》开发配置 支付授权目录设置问题,测试环境需要配置测试授权目录和添加测试白名单 2)签名错误: 参数顺序错误,需要按照参数字母排序; 参数名称错误,java中package为关键字需要返回...
  • 微信测试公众号如何进行支付测试

    万次阅读 2018-04-04 11:49:24
    测试号是不具备支付接口的能力,所以想要在测试环境测试微信公众号支付,就必须使用正式账号的appid、key、商户号以及open id: (1)页面的微信JSSDK的配置参数以及签名使用的app id要使用正式号的app id (2)...
  • 微信扫码支付:此公众号并没有这些scope的权限,错误码:10005
  • 微信公众号中调用支付宝手机网站支付

    万次阅读 热门讨论 2019-04-10 13:24:50
    在阿里官网中有解决微信公众号中调用支付宝手机网站支付的方案,就是使用官方提供的ap.js和pay.html,当然,阿里也给了get和post相关的例子: &nbsp;&nbsp;&nbsp;&nbsp;我们以demo_post.htm为...
  • (1)获取AppID以及AppSecret 进入开发 ---》 基本配置 , 获取 AppID以及AppSecret  (2) 配置网页授权域名以及JS接口安全域名 开发 ---》接口权限 ---》网页服务 ---》网页授权 ---》网页授权获取用户基本...
  • 微信支付后默认关注公众号

    万次阅读 2016-04-22 17:43:31
    参考了https://www.zhihu.com/question/30074728/answer/54068627 应该就是,只需要修改一个参数就可以了 ... 是否关注公
  • 使用微信支付已经成为我们日常消费时的习惯,给我们的生活带来很大的便利。现在大家出门都不怕忘带钱包,只要带上手机,电子钱包就如影相随。尽管微信支付十分方便,但它有个功能却给商家带来便利的同时,却给用户...
  • 用户在微信公众号里面付款,输入支付密码后会弹出一个微信页面,页面里面有一个“完成”的按钮。 如果支付后不点击完成按钮,那么:钱已经支付,... ... ...发送通知的地址是之前在统一下单时,配置支付参数的notify...
  • 微信公众号支付后台配置

    千次阅读 2018-04-19 11:01:57
    原地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3所以必须确保支付目录(支付页面的路径,比如www.ni123.com/our/pay/),具体的支付页面为www.ni123.com/our/pay/index.html开发步骤一、设置...
1 2 3 4 5 ... 20
收藏数 43,552
精华内容 17,420
关键字:

公众号支付