java实现微信支付、提现

从我入行以来,第一个老板就给我讲过,微信支付是个坑。
不过因为那时候一来公司内部有封装好的方法,按要求调用就可以了,另外多做小程序,公众号,web端,所以也没自己踩什么坑。
但是!没踩过微信的坑的开发不算是一个完整的开发!在经历了一两年的逍遥以后,我到底还要自己做一次。
首先作为一个后台,我觉得我要说一句良心话,微信对后台还是很友好的,并不是很麻烦和坑,此次测试,百分之七十的坑都是前端踩的。

从准备工作讲起。

首先,想要做微信支付,需要的必备参数,appId,mchId,key,回调路径等等。然后还需要一个证书,要在电脑上配置。
这些几乎都是必备的参数,然后还有一些依赖于微信的sdk。
首先来看一看微信官网:
微信支付开放平台
大概讲一讲,微信支付有几种,其中包括:

  • 扫码支付:就是用户提供二维码,商家扫码支付。(这种适合收银台模式,然后我还没有用过)
  • JSAPI支付:这个就比较魔性了,用户通过消息或扫描二维码在微信内打开网页时,可以调用微信支付完成下单购买的流程。其实说起来真的很绕,反正就是类似于js的一种(这个是需要openId的)。
  • Native支付:目前我觉得最简单的一种支付,就是生成一个二维码,用户扫码付款(钱数在生成二维码时生成,只适合在web端使用)
  • APP支付:这个不用多说了吧?就是APP用来拉取微信授权跳转微信支付页面的。
  • H5支付:这个主要用于触屏版的手机浏览器请求微信支付的场景。可以方便的从外部浏览器唤起微信支付。和jsapi的区别就是是否在微信内部唤起吧。(我没实际用过,也不清楚)
  • 小程序支付: 没话讲,就这样。
  • 人脸支付: 高大上到让我从未接触过。
    然后我这次业务使用的是Navite和APP支付两种方式。

SDK的使用

这个有两种方式,一种就是使用微信官网直接下载的SDK,然后以一个个文件的形式导到你的项目里。
还有一种是git上有一份用于这个的依赖(两种办法因为版本不同所以是有差异的。)

        
            com.github.wxpay
            wxpay-sdk
            0.0.3
        

然后懒惰如我,选择了直接用maven的jar包的导入(中间发生了一些故事,也把微信官方下载的SDK一个个引入了项目中,后来发现功能一样都跑起来了,所以又删除了)。
注意一点,以下的代码都是在引入上面的依赖的前提下实现的(中间涉及到版本的差异很大,所以千万不要弄混了,不然一点微小的区别能调N久)

开始真的实现啦!

1.**配置WXPayConfigImpl**
public class WxPayConfigImpl implements WXPayConfig {
    public static String url = "你设置的回调接口";
    private static WxPayConfigImpl wxPayConfig;
    private byte[] certData = null;

    public static WxPayConfigImpl getInstance() {
        if (wxPayConfig == null) {
            synchronized (WxPayConfigImpl.class) {
                wxPayConfig = new WxPayConfigImpl();
            }
        }
        return wxPayConfig;
    }

    public WxPayConfigImpl() {
        try {
                        //这个证书的位置不是瞎鸡儿填的,你要在这个路径真的有一个证书
            InputStream is = new FileInputStream("C:\\Windows\\System32\\cert\\apiclient_cert.p12");
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] bs = new byte[1024];
            int cnt = -1;
            while ((cnt = is.read(bs)) != -1) {
                baos.write(bs, 0, cnt);
            }
            is.close();
            certData = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public String getAppID() {
        return "你自己的AppId";
    }

    @Override
    public String getMchID() {
        return "你自己的设备码";
    }

    @Override
    public String getKey() {
        return "你自己的key";
    }

    @Override
    public InputStream getCertStream() {
        ByteArrayInputStream certBis;
        certBis = new ByteArrayInputStream(this.certData);
        return certBis;
    }

    @Override
    public int getHttpConnectTimeoutMs() {
        // TODO Auto-generated method stub
        return 8000;
    }

    @Override
    public int getHttpReadTimeoutMs() {
        // TODO Auto-generated method stub
        return 10000;
    }


    public String getPrimaryDomain() {
        return "api.mch.weixin.qq.com";
    }
    
    public String getNotifyUrl(){
        return url;
    }
}

对,一个configImpl就这么简单的实现了。其中参数是证书路径,一个是appId,设备码,key,回调接口连接是必填项。

Native支付

刚我就说觉得这个是最简单的一种调用,反正我是一次成功的。下面是实现代码:

public R wxPay(String money, String mk, String title, HttpServletRequest request, HttpServletResponse response) {
        try {
            if (mk == null) {
                return R.error().put("msg", "缺少参数,请查证后再访问");
            } else {
                WXPayConfig config = WxWebPayConfigImpl.getInstance();
                SortedMap paramMap = new TreeMap();
                paramMap.put("body", title);
                paramMap.put("out_trade_no", "C" + System.currentTimeMillis());
                paramMap.put("fee_type", "CNY");
                paramMap.put("total_fee", "1");
                paramMap.put("notify_url", WxPayConfigImpl.url);
                paramMap.put("trade_type", "NATIVE");
                paramMap.put("sign_type", WXPayConstants.MD5);
                String ip = getIpAddr(request);
                paramMap.put("spbill_create_ip", ip);
                WXPay pay = new WXPay(config);
                // 1.统一下单
                Map resultMap = pay.unifiedOrder(paramMap);
                String res = resultMap.get("return_code").toString().trim();
                System.out.println(">>>>res==" + res);
                System.out.println(resultMap);
                if ("SUCCESS".equalsIgnoreCase(res)) {
                    return R.ok().put("data", resultMap);
                } else {
                    return null;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
image.png

上图是整理的一些我觉得的注意点,然后点击下单方法后会返回一个路径,这个路径可以打开一个二维码页面,因为我们是前后端分离,所以我走到这步确定res是SUCCESS就ok了。
对了,还要注意一点,这个钱数total_fee应该是前端传来的,不过我这为了demo效果,所以统一写了一分,正常应该写活。
一个完整的微信Navite支付就完成了。

APP支付

刚刚也说了,这个坑百分之七十都是前端的,因为需要什么什么什么配置之类的,我也不知道,反正就是总错,但是真正的java代码还是很简单的。

public R wxAppPay(String money, String mk, String title, HttpServletRequest request, HttpServletResponse response) {
        try {
            Double d = Double.parseDouble(money);
            Integer dd = (int) (d * 100);
            money = String.valueOf(dd);
            WXPayConfig config = WxPayConfigImpl.getInstance();
            SortedMap paramMap = new TreeMap();
            paramMap.put("body", title);
            paramMap.put("out_trade_no", "C" + System.currentTimeMillis());
            paramMap.put("fee_type", "CNY");
            paramMap.put("total_fee",dd);
            paramMap.put("notify_url", WxPayConfigImpl.url);
            paramMap.put("trade_type", "APP");
            paramMap.put("sign_type", WXPayConstants.MD5);
            String ip = getIpAddr(request);
            paramMap.put("spbill_create_ip", ip);
            WXPay pay = new WXPay(config);
            // 1.统一下单
            Map resultMap = pay.unifiedOrder(paramMap);
            String res = resultMap.get("return_code").toString().trim();
            System.out.println(">>>>res==" + res);
            System.out.println(resultMap);
            if ("SUCCESS".equalsIgnoreCase(res)) {
                Map paramMap1 = new HashMap();
                paramMap1.put("appid", config.getAppID());
//              paramMap1.put("total", money);
                paramMap1.put("partnerid", config.getMchID());
                paramMap1.put("prepayid", (String) resultMap.get("prepay_id"));
                paramMap1.put("package", "Sign=WXPay");
                paramMap1.put("noncestr", WXPayUtil.generateNonceStr());
                // 本来生成的时间戳是13位,但是ios必须是10位,所以截取了一下
                paramMap1.put("timestamp", String.valueOf(System.currentTimeMillis()).toString().substring(0, 10));
                String sign2 = WXPayUtil.generateSignature(paramMap1, config.getKey(), SignType.MD5);
                paramMap1.put("sign", sign2);
                Gson gson = new GsonBuilder().disableHtmlEscaping().create();
                String xmlReq = gson.toJson(paramMap1);
                return R.ok().put("data", xmlReq);
            } else {
                return R.error();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return R.error();
        }
    }

我个人觉得这个就比较良心了,起码金额是写活了。然后与上一个的区别就是交易类型app,不过这个前端要配置较多的东西(具体配置啥不太清楚,反正我们项目就这里因为前端少配置总卡住,卡了一天吧)。貌似配置文件,什么什么证书。什么什么包名要一致,还有不能本地测试,会报错-100,要打包测试(反正我们做完是这样的,如果有本地也能测的亲欢迎指点)
我这里郑重声明,app方式的微信支付,配置就这么多!再有任何报错百分之九十九都是前端的了。
这个恶心的一点就是报错不知道原因,很有可能莫名其妙就报个62000,如果两边信任度不高并且没有成功的例子,可能报错了就会两边找错误,前端改前端的,后台改后台的(对,就是我血淋淋的例子),结果改了一天多才发现最初的代码其实就一点问题没有。你能想到多崩溃么?所以希望以后或者别人对自己有点信心吧,反正我上面的代码是跑通了的。

JSAPI支付

这个还有个小问题,一开始我问领导咱们这个项目用的啥方式,跟我说是JSAPI,所以我傻傻的去找了这个方式的官方文档,不过因为后来我改了,所以并没有现成的demo,大概讲一下区别:
与APP方式比,也就是交易类型改一下,并且需要一个openId的参数。这个参数是微信授权获取的,用appid生成的,我反正是作为一个参数的形式从前台拿的,然后剩下别的就是一样的了。
反正两天的微信支付就这么痛苦的完成了,大多数错误犯得莫名其妙也解决的莫名其妙(因为我后端几乎一直都是返回success,都是前端在那 调啊调)
表白我前端,真的辛苦了,啥啥资料都没有,各种需要做的没有列出来,只能错一点改一点。。还有本地不能调试简直坑死了,反正我是真心心疼做微信支付这一块儿的前端,也有可能是因为我们app 是h5开发,没有用安卓或者微信?不太清楚。继续往下说吧。

微信提现

其实本质上微信的提现就是微信企业付款到个人用户
这个还蛮多要求的,大家可以自己看看官网;
微信提现介绍

红色字是硬性要求

然后如果以上要求都满足了的话,就可以测出效果了,因为我们项目中的提现涉及到很多,比如用户账户余额金额的对比,提现成功减去用户余额等代码,所以我这里就不完整的贴方法了。仅仅是把一些必要的步骤提出来:

WXPayConfig config = null;
//这个也是与我做的业务相关的,因为我们这两个APP,也就是两个APPid啥的,如果你们没这么麻烦直接new  WxPayConfigImpl()就ok 了。
                // 5是司机
                if (sysUserEntity.getUserType() == 5) {
                    config = new WxDriverpayConfigImpl();
                    // 不是司机就是企业
                } else {
                    config = new WxPayConfigImpl();
                }
                SortedMap paramMap = new TreeMap();
                paramMap.put("mch_appid", config.getAppID());
                paramMap.put("mchid", config.getMchID());
                paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
                paramMap.put("partner_trade_no", "C" + System.currentTimeMillis());
                paramMap.put("openid", openId);
                paramMap.put("check_name", "NO_CHECK");
                paramMap.put("amount", (money * 100) + "");
                paramMap.put("desc", "提现");
                paramMap.put("spbill_create_ip", getIpAddr(request));
                String sign = WXPayUtil.generateSignature(paramMap, config.getKey(), SignType.MD5);
                paramMap.put("sign", sign);
                WXPay pay = new WXPay(config);
//          Map resMap =  pay.transfer(paramMap);
                String url = "  https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
                Map resMap;
                resMap = pay.processResponseXml(pay.requestWithCert(url, paramMap, config.getHttpConnectTimeoutMs(),
                        config.getHttpReadTimeoutMs()));
                String resultCode = resMap.get("result_code");
                if ("SUCCESS".equalsIgnoreCase(resultCode)) {
//走到这里就是提现操作成功了,可以做你自己的业务逻辑了、
} else {
                    String err_code = resMap.get("err_code");
                    if ("SYSTEMERROR".equalsIgnoreCase(err_code)) {
                        return R.error().put("msg", err_code);
                    } else if ("NOTENOUGH".equalsIgnoreCase(err_code)) {
                        return R.error().put("msg", err_code);
                    } else {
//在这把两个常见的错提了出来,剩下的统一为未知错误了,如果做个更好一些可以直接传错误信息。
                        return R.error().put("msg", "调用微信提现接口未知错误,请联系管理员!");
                    }

至此,一个提现的功能也初步完成了。

其实我这写的都不完整,仅仅是对付实现操作,回调什么的我都没写。因为!!!!领导神奇的工作分配,我这只实现操作,然后回调验证另一个同事写(我也不知道为什么这么分配),当时支付宝也是这么做的,只能笑笑吧。
至此,两天的难受的不行的微信这块儿就告一段落,只能说外界盛传的微信坑名副其实吧。
然后如果稍微帮到了你麻烦点个喜欢点个关注,另外如果遇到我没提到的问题欢迎留言或者私信,我们一起探讨啊?指不定是我踩过的坑呢。也祝大家工作顺顺利利!

你可能感兴趣的:(java实现微信支付、提现)