java 微信小程序支付
开发前的准备:(必须)
1.小程序标识(appid) 2.商户号(mch_id)3.商户密钥(key)
4.API证书(apiclient_cert.p12)这个证书根据自己需求添加
API证书(我这里用到所以要加,自己不需要的就不要加吧):可以去微信商户平台下载,自己百度
我们使用官网的SDK进行开发: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1

我们将官网的SDK用IDEA打开看看

可以看到目前微信官网最新的SDK是v3.0.9版本
点击README.md可以看到官方给的Demo

直接将SDK的java类复制到项目中,注意也要讲pom文件中的依赖也要添加到自己的项目中

我的API证书放在了这里

第一步:配置MyWXConfig类 继承WXPayConfig 抽象类
/** * @author: zhuhualian * @date: 2019-11-15 10:46 * @description: */ public class MyWXConfig extends WXPayConfig { private byte[] certData; public MyWXConfig() throws Exception { String path = "cert/apiclient_cert.p12"; File file = new File(this.getClass().getClassLoader().getResource(path).getFile()); //File file = new File("/data/project/cert/apiclient_cert.p12");//这个是你API证书放在服务器上的位置(上传服务器时记得换这里) InputStream certStream = new FileInputStream(file); this.certData = new byte[(int) file.length()]; certStream.read(this.certData); certStream.close(); } @Override public String getAppID() { return "xxxxx";//你自己的小程序AppID } @Override public String getMchID() { return "xxxxx";//你自己的微信商户ID } @Override public String getKey() { return "xxxxx";//你自己的微信商户密钥 } @Override public InputStream getCertStream() { ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData); return certBis; } @Override public int getHttpConnectTimeoutMs() { return 8000; } @Override public int getHttpReadTimeoutMs() { return 10000; } @Override IWXPayDomain getWXPayDomain() { IWXPayDomain iwxPayDomain=new IWXPayDomain() { @Override public void report(String domain, long elapsedTimeMillis, Exception ex) { } @Override public DomainInfo getDomain(WXPayConfig config) { return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API,true);//微信工具常量类有 "api.mch.weixin.qq.com"; wxpay.unifiedorder() /pay/unifiedorder } }; return iwxPayDomain; } }
第二步:找到 SDK 中的 WxPay 类 修改里面的代码 
public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport, final boolean useSandbox) throws Exception {
        this.config = config;
        this.notifyUrl = notifyUrl;
        this.autoReport = autoReport;
        this.useSandbox = useSandbox;
        if (useSandbox) {
            this.signType = SignType.MD5; // 沙箱环境
        }
        else {
            //this.signType = SignType.HMACSHA256;
            this.signType = SignType.MD5;  //注意:这点是个坑!   默认是HMACSHAS56加密 一定要修改成MD5  不然无论如何都会报  “微信签名失败” 的错误!
} this.wxPayRequest = new WXPayRequest(config); }
第三步:编写统一下单和返回给小程序需要的参数即可
openid:就是微信用户登录你的小程序授权你获得的openid
outtradeno:订单号
IpUtils:获取IP的工具类,后面会将代码贴出来
交易类型trade_type:
  NATIVE -- 原生支付
  JSAPI -- 公众号支付 - 小程序支付
  MWEB -- H5支付
  APP -- app支付
public Object pay2(String openid,String outtradeno, HttpServletRequest request) throws Exception {
    //统一下单支付
    HashMap<String, String> map = new HashMap<>();
    Map<String, String> data = new HashMap<>();
    data.put("body", "微信支付");                     //商品描述
    data.put("total_fee", "1");                   //  标价金额  单位:分
    data.put("openid", openid);                  //用户标识 trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识
    data.put("out_trade_no", outtradeno + "");                       //商户系统内部订单号
    data.put("nonce_str", WXPayUtil.generateNonceStr());        //随机字符串,长度要求在32位以内。推荐随机数生成算法
    data.put("spbill_create_ip",IpUtils.getIpAddr(request));             //支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP  自定获取ip
    data.put("notify_url", "120.25.214.5:8082/wx/notify");    // 没用到.通知地址:通知url必须为外网可访问的url,不能携带参数。
    data.put("trade_type", "JSAPI");//交易类型
    data.put("sign_type", WXPayConstants.MD5); //签名类型//MyWxPayConfig 配置了一些默认信息 appid,商户号,商户秘钥,请求域名 ..
    MyWXConfig myWxPayConfig = new MyWXConfig();
    WXPay wxpay = new WXPay(myWxPayConfig);
    Map<String, String> rMap = wxpay.unifiedOrder(data);//生成一次签名 sign
    System.out.println(rMap);
    // 下面只是为了生成第二次签名  仅此而已
    String return_code = rMap.get("return_code");//返回状态码
    String result_code = rMap.get("result_code");//结果状态码
    String nonce_str = rMap.get("nonce_str"); //随即字符串
    Long s = System.currentTimeMillis() / 1000;//获取时间戳除以千变字符串
    String timeStamp = String.valueOf(s);
    if ("SUCCESS".equals(return_code) && return_code.equals(result_code)) {
        map.put("appId", myWxPayConfig.getAppID());//你的appid
        map.put("timeStamp", timeStamp);//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
        map.put("nonceStr", nonce_str);
        map.put("package", "prepay_id=" + rMap.get("prepay_id"));
        map.put("signType", "MD5");
        System.out.println("二次签名参数 : " + map);//需要生成二次签名 所用的参数
        //再次签名sign,这个签名用于小程序端调用wx.requesetPayment方法
        String sign = WXPayUtil.generateSignature(map, myWxPayConfig.getKey());//你的商户号key
        map.put("paySign", sign); // 生成签名 重要
        System.out.println("生成的签名paySign : " + sign);
    return map;      //将map响应给前端   微信支付接口需要的参数
    }
    return new Result<>("0",Result.ERROR);
}
IpUtils类
public class IpUtils {
    /* IpUtils工具类方法
     * 获取真实的ip地址
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if(index != -1){
                return ip.substring(0,index);
            }else{
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
            return ip;
        }
        return request.getRemoteAddr();
    }
}
容易遇到的错误 ! 容易遇到的错误 ! 容易遇到的错误 !
1 商户号key 不要与 appid 的secret 弄混淆了
2 SDK 工具类中 Wxpay 类中 this.signType = SignType.HMACSHA256; HMACSHA256 改成 MD5
3 第二次签名需要的五个参数一个不能少 appId,nonceStr,package,signType,timeStamp。 注意 都是以 驼峰命名 不然也会报错
OK,搞定了!!!!!!!!!!!!!!!!!!
                    
                
                
            
        
浙公网安备 33010602011771号