从准备工作讲起。
首先,想要做微信支付,需要的必备参数,appId,mchId,key,回调路径等等。然后还需要一个证书,要在电脑上配置。
这些几乎都是必备的参数,然后还有一些依赖于微信的sdk。
首先来看一看微信官网:
微信支付开放平台 大概讲一讲,微信支付有几种,其中包括:
- 扫码支付:就是用户提供二维码,商家扫码支付。(这种适合收银台模式,然后我还没有用过)
- JSAPI支付:这个就比较魔性了,用户通过消息或扫描二维码在微信内打开网页时,可以调用微信支付完成下单购买的流程。其实说起来真的很绕,反正就是类似于js的一种(这个是需要openId的)。
- Native支付:目前我觉得最简单的一种支付,就是生成一个二维码,用户扫码付款(钱数在生成二维码时生成,只适合在web端使用)
- APP支付:这个不用多说了吧?就是APP用来拉取微信授权跳转微信支付页面的。
- H5支付:这个主要用于触屏版的手机浏览器请求微信支付的场景。可以方便的从外部浏览器唤起微信支付。和jsapi的区别就是是否在微信内部唤起吧。(我没实际用过,也不清楚)
- 小程序支付: 没话讲,就这样。
- 人脸支付: 高大上到让我从未接触过。
然后我这次业务使用的是Navite和APP支付两种方式。
SDK的使用
这个有两种方式,一种就是使用微信官网直接下载的SDK,然后以一个个文件的形式导到你的项目里。
还有一种是git上有一份用于这个的依赖(两种办法因为版本不同所以是有差异的。)
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
然后懒惰如我,选择了直接用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<String, String> paramMap = new TreeMap<String, String>();
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<String, String> 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;
}
}
上图是整理的一些我觉得的注意点,然后点击下单方法后会返回一个路径,这个路径可以打开一个二维码页面,因为我们是前后端分离,所以我走到这步确定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<String, String> paramMap = new TreeMap<String, String>();
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<String, String> 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<String, String> paramMap1 = new HashMap<String, String>();
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<String, String> paramMap = new TreeMap<String, String>();
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<String,String> resMap = pay.transfer(paramMap);
String url = " https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
Map<String, String> 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", "调用微信提现接口未知错误,请联系管理员!");
}
至此,一个提现的功能也初步完成了。
希望能帮到你