1、首先可以通过服务端来获取openid,openid可以作为自己平台微信用户身份的唯一标识。
/**
* @Description: 获取openId
* @param: code 小程序授权后获得的code
* @Author: zhangpeng32
* @Date: 2018/3/11 17:39
* @Version: 1.0.0
*/
@RequestMapping(value = "/getOpenId", method = RequestMethod.POST)
public BaseResp
2、调用支付统一下单API来获取prepay_id,并将小程序调起支付数据需要签名的字段appId,timeStamp,nonceStr,package再次签名。
特别要注意,调用官方的SDK默认的加密方式为HMACSHA256,SKD里面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;
this.signType = SignType.MD5;
if (useSandbox) {
this.signType = SignType.MD5; // 沙箱环境
}
else {
this.signType = SignType.HMACSHA256;
}
this.wxPayRequest = new WXPayRequest(config);
}
我们在调用统一下单签名的时候,可以直接把这里的sign_type默认改为MD5
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;
this.signType = SignType.MD5;
/* if (useSandbox) {
this.signType = SignType.MD5; // 沙箱环境
}
else {
this.signType = SignType.HMACSHA256;
}*/
this.wxPayRequest = new WXPayRequest(config);
}
使用微信支付的SDK调用微信支付比较简单,最新的SDK也不需要自己处理xml和map之间的转化,接口里面包含了签名的方法和签名验证的方法,在我调用统一下单接口的时候,开始报签名错误,自己要注意一下。可以通过微信签名验证工具来进行验证,工具的地址为:https://pay.weixin.qq.com/wiki/tools/signverify/。此处WXPayUtil为SDK自带的工具类,WXPayUtil.generateSignature生成签名,WXPayUtil.isSignatureValid直接可以在代码中组签名验证。
@RequestMapping(value = "/doUnifiedOrder", method = RequestMethod.POST)
public BaseResp> doUnifiedOrder(@Valid WeixinInVo inVo, HttpServletRequest request, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
List list = bindingResult.getAllErrors();
return new BaseResp>(ResultStatus.FAIL.getErrorCode(), list.get(0).getDefaultMessage(), null);
}
//根据id获取订单信息
SoOut soOut = soService.get(inVo.getSoId());
if (soOut == null) {
return new BaseResp>(ResultStatus.error_so_not_exist);
}
//订单已支付
if (soOut.getSoStatus() != So.SO_STATUS_WAIT_PAID) {
return new BaseResp>(ResultStatus.error_so_paid);
}
//生成的随机字符串
String nonce_str = WXPayUtil.generateNonceStr();
//获取客户端的ip地址
String spbill_create_ip = IpUtils.getIpAddr(request);
int price100 = soOut.getSoAmount().multiply(new BigDecimal(100)).intValue();
//统一下单接口
HashMap data = new HashMap();
data.put("appid", config.getAppID());
data.put("mch_id", config.getMchID());
data.put("nonce_str", nonce_str);
data.put("body", soOut.getSkuName()); //商品描述
data.put("out_trade_no", soOut.getId().toString());//商户订单号
data.put("total_fee", String.valueOf(price100));//支付金额,这边需要转成字符串类型,否则后面的签名会失败
data.put("spbill_create_ip", spbill_create_ip);
data.put("notify_url", PaymentConfig.WX_NOTIFY_URL);//支付成功后的回调地址
data.put("trade_type", PaymentConfig.TRADE_TYPE);//支付方式
data.put("openid", inVo.getOpenId());
//返回给小程序端需要的参数
Map response = new HashMap();
response.put("appId", config.getAppID());
try {
Map rMap = wxpay.unifiedOrder(data);
System.out.println("统一下单接口返回: " + rMap);
String return_code = (String) rMap.get("return_code");//返回状态码
String result_code = (String) rMap.get("result_code");//
String nonceStr = WXPayUtil.generateNonceStr();
response.put("nonceStr", nonceStr);
Long timeStamp = System.currentTimeMillis() / 1000;
if ("SUCCESS".equals(return_code) && return_code.equals(result_code)) {
String prepayid = rMap.get("prepay_id");
// response.put("prepayid", rMap.get("prepay_id"));
response.put("package", "prepay_id="+prepayid);
response.put("signType", "MD5");
response.put("timeStamp", timeStamp + "");//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
System.out.println("二次签名参数response : "+response);
//再次签名,这个签名用于小程序端调用wx.requesetPayment方法
String sign = WXPayUtil.generateSignature(response, PaymentConfig.API_KEY);
response.put("paySign", sign);
System.out.println("生成的签名paySign : "+ sign);
return new BaseResp>(ResultStatus.SUCCESS, response);
}else{
return new BaseResp>(ResultStatus.error_unified_order_fail.getErrorCode(), rMap.get("err_code_des"), null);
}
} catch (Exception e) {
e.printStackTrace();
return new BaseResp>(ResultStatus.FAIL, response);
}
}
注意:所有的签名加密方式一定要全部是MD5,这个问题卡了我两天,最后跟进代码到sdk源码中才找到问题。
3、微信支付结果通知,这里就是统一支付接口定义的回调url,只能在公网上进行测试验证。
比如我定义的是:
//支付成功后的服务器回调url
public static final String WX_NOTIFY_URL = "https://xxxxxx.com/wxpay/wxNotify";
@RequestMapping(value = "/wxNotify")
public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
//读取参数
BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
//sb为微信返回的xml
String notifyData = sb.toString(); //支付结果通知的xml格式数据
System.out.println("支付结果通知的xml格式数据:" + notifyData);
Map notifyMap = WXPayUtil.xmlToMap(notifyData); // 转换成map
String resXml = "";
if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
// 签名正确
if ("SUCCESS".equals(notifyMap.get("result_code"))) {
//这里是支付成功
//////////执行自己的业务逻辑////////////////
String mch_id = (String) notifyMap.get("mch_id"); //商户号
String openid = (String) notifyMap.get("openid"); //用户标识
String out_trade_no = (String) notifyMap.get("out_trade_no"); //商户订单号
String total_fee = (String) notifyMap.get("total_fee");
String transaction_id = (String) notifyMap.get("transaction_id"); //微信支付订单号
//查询订单 根据订单号查询订单 SoOut -订单实体类
Long soId = Long.valueOf(out_trade_no);
SoOut soOut = soService.get(soId);
if (!PaymentConfig.MCH_ID.equals(mch_id) || soOut == null || new BigDecimal(total_fee).compareTo(soOut.getSoAmount().multiply(new BigDecimal(100))) != 0) {
logger.info("支付失败,错误信息:" + "参数错误");
resXml = "" + " " + " " + " ";
} else {
// 注意特殊情况:订单已经退款,但收到了支付结果成功的通知,不应把商户侧订单状态从退款改成支付成功
if (So.SO_STATUS_WAIT_PAID == soOut.getSoStatus()) {//支付的状态判断
//订单状态的修改。根据实际业务逻辑执行
int ret = soService.paid(inVo);
resXml = "" + " " + " " + " ";
} else {
resXml = "" + " " + " " + " ";
logger.info("订单已处理");
}
}
} else {
logger.info("支付失败,错误信息:" + notifyMap.get("err_code"));
resXml = "" + " " + " " + " ";
}
} else {
// 签名错误,如果数据里没有sign字段,也认为是签名错误
resXml = "" + " " + " " + " ";
logger.info("通知签名验证失败");
}
//------------------------------
//处理业务完毕
//------------------------------
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
}
一定要注意参数的大小写,字符编码为UTF-8。
pom.xml增加微信sdk:
com.github.wxpay
wxpay-sdk
0.0.3
另外sdk的github地址为:https://github.com/wxpay/WXPay-SDK-Java。可以自己进行修改和编译打包。
如果有其他问题,请联系我。