一、申请
1、先去微信开放平台申请一个app应用,需要注意的是选择ios和Android两个平台,而不是ios创建一个、安卓创建一个。创建好之后一般会在3~5天左右审核通过,此时去申请开通微信支付,大概需要1~2天。
2、上面申请通过之后,会让你去商户平台验证,验证通过之后,需要你设置密钥(在API安全里面)。都通过之后,我们需要拿到以下三个数据
- appID:应用的id wxe57df9967063cf2c
- mchId:商户id 1509558461
- appKey:密钥 在商户平台中你自己设置的
https://pay.weixin.qq.com/index.php/core/cert/api_cert
二、集成sdk
1、这是我修改后的demo,用idea打开后,maven install打成jar包然后安装到本地仓库
安装命令:mvn install:install-file -Dfile=wxpay-sdk-3.0.9.jar -DgroupId=com.github.wxpay -DartifactId=wxpay-sdk -Dversion=3.0.9 -Dpackaging=jar
或者自己定义路径也可以(个人喜欢自己定义路径 详情请看我 Maven学习笔记 中 Maven 如何引用本地Jar包 的笔记)
然后在pom中引用:(微信sdk需要httpclient的jar包)
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>3.0.9</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
2、说明:之前下载的demo,是存在问题的
(1)、需要自定义 APPWxPayConfig
(2)、需要自己实现 IWXPayDomain接口
(3)、另外源代码中的加密方式默认为 HMACSHA256,被我改成了 默认 MD5(在某些场景下HA256加密 签名永远验证不通过)
(4)、Api中的 sign 和 nonce_str 不需要传递,已经在jar包中内置,但是二次加密还是需要自己在用工具类获取 nonce_str,下面代码中有演示(微信Api官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html)
(5)、同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 来自微信官方文档(微信回调接口 一定要留意这个问题,以下代码中没有处理回调重复的问题,请根据自己的业务和实际情况来注意和处理这个问题)
3、统一下单接口(以下是微信APP支付示例代码)
需要注意的是:两处签名方式要一致(我采取的都是MD5加密),且二次签名的参数要注意,参与二次签名的参数有:appid、noncestr、package(固定就是Sign=WXPay)、partnerid、prepayid、timestamp、key。全都是小写,且时间戳是秒(十位数),key在参与签名的时候放在最后。
如果提示签名不正确(要是一次就正确不得不说你的运气真滴好),请仔细查看,也可参考微信提供的验证签名:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=20_1
@RequestMapping(value="/wxpay_orderStr/ajax")
@ResponseBody
public Result getOrderStr(HttpServletRequest request,
@RequestParam(value="amount")Double amount,
@RequestParam(value="orderNo")String orderNo){
//校验订单并获取订单创建时间时间戳 来拼接订单号
WyPropertyCostsLog wyPropertyCostsLog = propertyCostsService.getPropertyCostsLogById(Integer.valueOf(orderNo));
String createTimeString = null;
if (wyPropertyCostsLog != null && wyPropertyCostsLog.getCosts() == amount) {
Calendar cal = Calendar.getInstance();
cal.setTime(wyPropertyCostsLog.getCreateTime());
createTimeString = String.valueOf(cal.getTimeInMillis());
}else {
return new Result(-1,"该订单不存在!");
}
AppWxPayConfig wxPayConfig = new AppWxPayConfig(appId, key, mchId);
Map<String,String> reqData = new HashMap<String,String>();
reqData.put("body", "融易社区物业缴费");
reqData.put("out_trade_no", createTimeString + "_" + orderNo);
reqData.put("fee_type", "CNY");
reqData.put("total_fee", "1");//微信以分为单位 测试用
//reqData.put("total_fee", String.valueOf(Math.round(amount * 100)));//微信以分为单位
reqData.put("spbill_create_ip", StrKit.getIpAddr(request));
reqData.put("notify_url", notify_url);
reqData.put("trade_type", "APP");//此处指定为APP支付
try {
WXPay wxpay = new WXPay(wxPayConfig);
Map<String, String> resp = wxpay.unifiedOrder(reqData);//统一下单返回结果
System.out.println("统一下单返回数据:" + resp);
if (resp != null) {
String prepayid = resp.get("prepay_id");
//若获取prepayid成功,将相关信息返回客户端
if (prepayid != null && !prepayid.equals("")) {
String noncestr = WXPayUtil.generateNonceStr();
String timestamp = String.valueOf(WXPayUtil.getCurrentTimestamp());
Map<String, String> signMap = new HashMap<String, String>();
signMap.put("appid", appId);
signMap.put("noncestr", noncestr);
signMap.put("package", "Sign=WXPay");
signMap.put("partnerid", mchId);
signMap.put("prepayid", prepayid);
signMap.put("timestamp", timestamp);
Map<String, String> resultMap = new HashMap<String, String>();
resultMap.put("prepayid", prepayid);
resultMap.put("sign", StrKit.generateSignature(signMap,key));
resultMap.put("appid", appId);
resultMap.put("timestamp", timestamp); //等于请求prepayId时的
resultMap.put("noncestr", noncestr); //与请求prepayId时值一致
resultMap.put("package", "Sign=WXPay"); //固定常量
resultMap.put("partnerid", mchId);
System.out.println("返回给app数据:" + resultMap);
return new Result(0,"请求成功",resultMap);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return new Result(-1,"请求失败");
}
/**
* 微信支付:生成MD5加密
* @param data
* @param key
* @return
* @throws Exception
*/
public static String generateSignature(final Map<String, String> data,final String key) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(WXPayConstants.FIELD_SIGN)) {
continue;
}
if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
String result = sb.append("key=" + key).toString();
return MD5(result).toUpperCase();
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String MD5(String data) throws Exception {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**.
* 根据Request获取客户端的IP地址
* @param request 请求对象
* @return 返回IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
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.getRemoteAddr();
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
return ip;
}
4、异步回调
/**
* 异步回调
* @param request
* @return
*/
@RequestMapping(value="/wxpay_notify")
@ResponseBody
public String wxpay_notify(HttpServletRequest request,HttpServletResponse response){
try{
BufferedReader reader = request.getReader();
String line = "";
StringBuffer inputString = new StringBuffer();
try{
while ((line = reader.readLine()) != null) {
inputString.append(line);
}
if(reader!=null){
reader.close();
}
System.out.println("----[微信回调]接收到的报文---"+inputString.toString());
if(!StrKit.isBlank(inputString.toString())){
AppWxPayConfig wxPayConfig = new AppWxPayConfig(appId, key, mchId);
WXPay wxpay = new WXPay(wxPayConfig);
Map<String, String> notifyMap = WXPayUtil.xmlToMap(inputString.toString());// 转换成map
if (notifyMap.get("return_code").equals("SUCCESS")) {
if (notifyMap.get("result_code").equals("SUCCESS")) {
if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
// 签名正确 进行业务处理
String out_trade_no_resp = notifyMap.get("out_trade_no");
Double amount = Double.valueOf(notifyMap.get("total_fee"))/100;
System.out.print("订单号:" + out_trade_no_resp + "---金额:" + +amount);
String[] arr = out_trade_no_resp.split("_");
String out_trade_no = arr[1];
int result = propertyCostsService.updateRecord(out_trade_no,Constants.PAY_SUCCESS,Constants.WXPAY,String.valueOf(amount));
if (result > 0) {
redisTemplate.opsForValue().set("ORDER_STATUS_" + out_trade_no, Constants.PAY_SUCCESS, 60, TimeUnit.MINUTES);
Map<String, String> map = new HashMap<String, String>();
map.put("return_map", "SUCCESS");
map.put("return_msg", "OK");
return WXPayUtil.mapToXml(map);
}
}
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}catch(Exception ex){
ex.printStackTrace();
}
return "";
}
//以下代码是微信小程序支付实例代码
public static void main(String[] args) {
AppWxPayConfig wxPayConfig = new AppWxPayConfig("小程序的AppId(一定要是小程序的APPId)", "微信支付密钥(不是小程序的密钥)", "微信商户号");
Map<String,String> reqData = new HashMap<String,String>();
reqData.put("body", "kaifacheshishuju");
reqData.put("out_trade_no", "fdfdsfsdfes");
reqData.put("fee_type", "CNY");
reqData.put("total_fee", "1");//微信以分为单位 测试用
//reqData.put("total_fee", String.valueOf(amount * 100));//微信以分为单位
reqData.put("spbill_create_ip", "172.19.1.15");
reqData.put("notify_url", " https://kuajiesaoke.natapp1.cc/order/my_app/wxpay_notify");
reqData.put("openid","oxJ_25Y7ygc9SmXp5BXhLuDLCiOY");//获取openId 是使用小程序的密钥获取openId
//JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付,不同trade_type决定了调起支付的方式
//MICROPAY--付款码支付,付款码支付有单独的支付接口,所以接口不需要上传,该字段在对账单中会出现(微信支付的核心就是 trade_type 不同的type值会集成不同的微信支付方式,其他的微信支付大同小异,只有小程序的sign加密规则不一样 所以dome单独体现出来)
reqData.put("trade_type", "JSAPI");//此处指定为小程序支付 MWEB web支付NATIVE
try {
WXPay wxpay = new WXPay(wxPayConfig);
Map<String, String> resp = wxpay.unifiedOrder(reqData);//统一下单返回结果
System.out.println("resp"+ resp);
if (resp != null) {
String prepayid = resp.get("prepay_id");
//若获取prepayid成功,将相关信息返回客户端
if (prepayid != null && !prepayid.equals("")) {
String noncestr = WXPayUtil.generateNonceStr();
String timestamp = String.valueOf(WXPayUtil.getCurrentTimestamp());
//此处需要注意 小程序和sign加密格式个其他支付不一样 格式为下面这种 请留意
Map<String, String> signMap = new HashMap<String, String>();
signMap.put("appId", "APPId");
signMap.put("nonceStr", noncestr);
signMap.put("package", "prepay_id="+prepayid);
signMap.put("signType", "MD5");
signMap.put("timeStamp", timestamp);
Map<String, String> resultMap = new HashMap<String, String>();
resultMap.put("paySign", generateSignature(signMap, "微信密钥"));
resultMap.put("timeStamp", timestamp); //等于请求prepayId时的
resultMap.put("nonceStr", noncestr); //与请求prepayId时值一致
resultMap.put("package", "prepay_id="+prepayid); //固定常量
resultMap.put("signType", "MD5");
System.out.println("resultMap"+resultMap);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
如上代码中 reqData 的Map添加的数据为微信官方必要参数,具体详情,请看如下链接:
https://pay.weixin.qq.com/wiki/doc/api/index.html
/**
* 微信-web支付代码
*/
@RequestMapping(value = "/wxpay_orderStr")
public ResultDTO getOrderStr(HttpServletRequest request,@RequestParam Double price,@RequestParam String orderNo) {
AppWxPayConfig wxPayConfig = wxPayConfig = new AppWxPayConfig("微信APPid","微信密钥","微信商户号");
Map<String, String> reqData = new HashMap<String, String>();
reqData.put("body", "在线订单-"+orderNo);
reqData.put("out_trade_no", orderNo);
reqData.put("fee_type", "CNY");
// reqData.put("total_fee", "1");//微信以分为单位 测试用
reqData.put("total_fee", String.valueOf(Math.round(price * 100)));//微信以分为单位
reqData.put("spbill_create_ip", StrKit.getIpAddr(request));
reqData.put("notify_url", WxPayConfig.APP_NOTIFY_URL);
//JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付,不同trade_type决定了调起支付的方式
//MICROPAY--付款码支付,付款码支付有单独的支付接口,所以接口不需要上传,该字段在对账单中会出现(由此可见 当前支付和微信APP支付大同小异,在小程序支付中也进行了说明(微信支付的核心就是 trade_type),具体详情 请参照微信官方文档(H5支付也相当类似 这里不再叙述))
reqData.put("trade_type", "NATIVE");//此处指定为WEB端支付
try {
WXPay wxpay = new WXPay(wxPayConfig);
Map<String, String> resp = wxpay.unifiedOrder(reqData);//统一下单返回结果
return new ResultDTO(CodeEnum.login_code.getIndex(), "请求成功", resp.get("code_url"));
} catch (Exception e) {
e.printStackTrace();
}
return new ResultDTO(CodeEnum.error_code.getIndex(), "请求失败");
}
在写微信支付的时候也参考了这篇文章:
https://www.cnblogs.com/godwhisper/p/6880060.html