微信公众号支付验证签名失败

这几天开发的项目要做一个微信公众号支付,也就是在微信网页内部进行调取支付插件进行支付的一个过程

所以需要到微信官方开通公众号支付 微信官网: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 获取,也是在微信商户后台

这个是自己设置的,看你自己设置了,
在这里插入图片描述

这里使用微信提供的官方工具类WXPayUtil进行生成随机字符串,也可以使用uuid生成32为随机数
4. sign 签名 用WXPayUtil中的generateSignature(finalMap<String, String> data, String key)方法,data是将除了sign外,其他10个参数放到map中,key是四大配置参数中的API秘钥(paternerKey)(这里不要着急管它,最后处理它);

  1.    body 所支付的名称
    
  2.    out_trade_no 自己后台生成的订单号,只要保证唯一就好:如“pay2018062521331”
    
  3.    total_fee 支付金额 单位:分,为了测试此值给1,表示支付1分钱
    
  4.    spbill_create_ip IP地址 网上很多ip的方法,自己找,此处测试给“127.0.0.1”
    

获取用户真实IP地址

/**
     * 从request中获取请求方IP
     * @param request
     * @return
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            // 若以上方式均为获取到ip则证明获得客户端并没有采用反向代理直接使用getRemoteAddr()获取客户端的ip地址
            ip = request.getRemoteAddr();
        }
        // 多个路由时,取第一个非unknown的ip
        final String[] arr = ip.split(",");
        for (final String str : arr) {
            if (!"unknown".equalsIgnoreCase(str)) {
                ip = str;
                break;
            }
        }
        return ip;
    }
  1.    notify_url 回调地址:这是微信支付成功后,微信那边会带着一大堆参数(XML格式)请求这个地址多次,这个地址做我们业务处理如:修改订单状态,赠送积分等。Ps:支付还没成功还想这么远干嘛,最后再说。地址要公网可以访问。
    
  2. trade_type 支付类型 咱们是公众号支付此处给“JSAPI”

  3. 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 对链接进行处理。

  1. scope:用snsapi_base 。

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

[html] view plain copy
去支付页面pay.jsp并携带code
第二步:通过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
{ “access_token”:“ACCESS_TOKEN”,
“expires_in”:7200,
“refresh_token”:“REFRESH_TOKEN”,
“openid”:“OPENID”,//就是它,只要这个值
“scope”:“SCOPE” }
好了都有了,我们就可以开始写拼装参数了, 参数填写修改成你自己的就可以了

/**
     * 创建支付订单
     * @param out_trade_no
     * @param total_fee
     * @return
     */
    public Map createJSAPI(String out_trade_no, String total_fee) {
        try {
            //封装参数
            Map<String,String> dataMap=new HashMap<>();
            dataMap.put("appid",appid);//应用ID,微信公众账号或开放平台APP的唯一标识
            dataMap.put("mch_id",partner);//商户号
            dataMap.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串

            dataMap.put("body","company-coupon");
            dataMap.put("out_trade_no",out_trade_no); //商户生成的订单号
            dataMap.put("total_fee",total_fee);
            dataMap.put("spbill_create_ip",PayCommonUtil.getIpAddress(http));
            dataMap.put("notify_url",notifyurl);
            //获取用户的openId   "okIDwjg2__0vcyd3HsBBcrd03BMY"    "okIDwjnmMbRX-2oSiM5Qmrq8R9cg"   (String)http.getAttribute("userId")

            //支付类型  JSAPI
            dataMap.put("trade_type","JSAPI");
            dataMap.put("openid",(String)http.getAttribute("userId"));

            String parameter = WXPayUtil.generateSignature(dataMap, partnerkey);

            dataMap.put("sign",parameter);//TODO 签名

            //将map转成xml
            String mapToXml = WXPayUtil.mapToXml(dataMap);

            //执行请求
            HttpClient httpClient=new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
            httpClient.setHttps(true);
            httpClient.setXmlParam(mapToXml);
            httpClient.post();
            String content = httpClient.getContent();
            System.out.println(content);

            //将xml转成map
            Map<String, String> map = WXPayUtil.xmlToMap(content);
            //预付单id
            String prepay_id = map.get("prepay_id");
            //获取参数  传给前台
            Map<String,String> ternMap=new HashMap<String, String>();
            //二次签名参数
            ternMap.put("appId",appid);
            ternMap.put("timeStamp",String.valueOf(System.currentTimeMillis()/1000));
            //注意这里的随机数应为统一下单的随机数
            ternMap.put("nonceStr",dataMap.get("nonce_str"));
            ternMap.put("package","prepay_id="+prepay_id); //预支付交易会话标识,2小时失效
            ternMap.put("signType","MD5");
            // 第二次的签名
            String paySign = WXPayUtil.generateSignature(ternMap, partnerkey);

            ternMap.put("prepay_id",ternMap.get("package"));
            ternMap.put("outTradeNo",out_trade_no);
            ternMap.put("total_fee",total_fee);
            ternMap.put("return_code",map.get("return_code"));
            ternMap.put("result_code",map.get("result_code"));
            ternMap.put("paySign",paySign);
            return ternMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

请求过后 微信端返回的也是XML 不利于我们处理,所以继续转map
Map<String, Object> respMap = WXPayUtil.xmlToMap()
转好map 后我们就开始取 prepay_id了

参数:
appid 也有

timeStamp 时间戳 你们new Date();即可,因为我语言是 Groovy 所以需要getTime 才是秒数
这下面的五个参数名必须一致
在这里插入图片描述

package prepay_id 已有了

nonceStr 随机数 getuuid方法就可以了

signType 固定值 MD5

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

这里值得注意的是package 参数, 这个参数可不是简单的吧prepay_id 放进去
,要把 “prepay_id=”这个拼接上里面不能有多余的"或者’符号
之前没有拼接 ,这里要格外注意 package这个键的参数字段,由于将这个package为键传给前台,前台那边接收不到参数,于是我换了个值,结果坑就坑在这里,找了许久,最后在网上找到一遍文章,才意识到不同的参数会导致生成签名时字典排序不同。这是我在传不同值时所获得生成签名的排序顺序
在这里插入图片描述
结果改回package字段后再生成签名后再换个字段将这个参数值传给前台调起JS支付,这回成功了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值