最近在做微信公众号支付,总结一下经验。
第一步:创建微信预支付订单,并给前台返回签名
@RequestMapping(value="/createWeChatPayOrderParam")
public ApiResult requestWxpay(@RequestParam Map<String, Object> param) throws Exception {
ApiResult result = new ApiResult();
try {
String payParam = param.get("data").toString();
String Info = RC4.decry_RC4(payParam, RC4_KEY);
BaseUtil.getInfoLog().info(Info);
JSONObject jsonInfo = JSONObject.parseObject(Info);
String totalFee = jsonInfo.getString("payment");
String levelId = jsonInfo.getString("id");
//验证页面金额与实际金额是否一致,不一致不允许支付
boolean bo = weChatPayServicve.validatePayment(levelId,totalFee);
if (!bo) {
result.setCode(ApiResult.CODE_400);
result.setMessage("请刷新页面后重新支付!");
return result;
}
String notifyUrl = ADMIN_STSTEM_URL + "weChatPay/wxPayNotify";
String ipaddress = BaseUtil.getRequest().getRemoteAddr();
String outTradeNo = WXPayUtil.getPaymentID();
WechatConfig config = WechatConfig.getInstance();
config.getAppID();
WXPay wxpay = new WXPay(config, SignType.MD5);// 指定签名方式为MD5!!!!!!!!!!!!!!
//创建订单
Map<String, String> map = weChatPayServicve.getPrepayId(wxpay, outTradeNo, totalFee, notifyUrl, ipaddress,
jsonInfo);
if (map != null) {
String appid = config.getAppID();
String timestamp = String.valueOf(WXPayUtil.getCurrentTimestamp());
String nonceStr = WXPayUtil.generateNonceStr();
SortedMap<String, Object> signMap = new TreeMap<String, Object>();
signMap.put("appId", appid);
signMap.put("package", "prepay_id=" + map.get("prepayId"));
signMap.put("timeStamp", timestamp);
signMap.put("nonceStr", nonceStr);
signMap.put("signType", "MD5");
//生成新签名
String sign = PayUtils.getSign(signMap, config.getKey());
Map<String, String> data = new HashMap<>();
data.put("appId", appid);
data.put("package", "prepay_id=" + map.get("prepayId"));
data.put("timeStamp", timestamp);
data.put("nonceStr", nonceStr);
data.put("signType", "MD5");
data.put("paySign", sign);
BaseUtil.getInfoLog().info(signMap.toString());
BaseUtil.getInfoLog().info("签名为:" + sign);
result.setDatas(data);
result.setCode(ApiResult.CODE_200);
result.setMessage(ApiResult.SUCCESS);
} else {
result.setCode(ApiResult.CODE_500);
result.setMessage(ApiResult.FAIL);
}
} catch (Exception e) {
result.setCode(ApiResult.CODE_500);
result.setMessage(ApiResult.FAIL);
e.printStackTrace();
BaseUtil.getErrorLog().error(e.toString());
}
return result;
}
/**
* APP,统一下单,获取预支付ID
* @param outTradeNo
* @param totalFee 元
* @param notifyUrl
* @param ipaddress
*/
@Override
public Map<String, String> getPrepayId(WXPay wxpay, String outTradeNo, String totalFee, String notifyUrl,
String ipaddress,JSONObject jsonInfo) {
String totalFeeFen = String.valueOf(BaseUtil.UnitaryToCent(totalFee));
savePrepayOrder(wxpay, outTradeNo, totalFeeFen, notifyUrl,ipaddress,jsonInfo);
Map<String, String> map = new HashMap<String, String>();
String prepayId = null;
Map<String, String> data = new HashMap<String, String>();
data.put("device_info", "WEB");
data.put("body", "小熊猫-会员充值");
data.put("out_trade_no", outTradeNo);
data.put("total_fee", totalFeeFen);
data.put("spbill_create_ip", ipaddress);
data.put("notify_url", notifyUrl);
data.put("fee_type", "CNY");
data.put("sign_type", "MD5");
data.put("trade_type", "JSAPI");
String openid = jsonInfo.getString("openid");
data.put("openid", openid);
try {
Map<String, String> resp = wxpay.unifiedOrder(data);
boolean bo = wxpay.isPayResultNotifySignatureValid(resp);
BaseUtil.getInfoLog().info("预支付签名:"+bo);
if ("SUCCESS".equals(resp.get("return_code"))) {
prepayId = resp.get("prepay_id");
map.put("prepayId", prepayId);
map.put("sign", resp.get("sign"));
System.out.println("微信统一下单成功");
System.err.println(resp);
} else {
System.err.println("微信统一下单失败:");
BaseUtil.getInfoLog().info("微信统一下单失败,预支付签名:"+bo);
System.err.println(resp);
}
} catch (Exception e) {
System.err.println("微信统一下单异常!");
BaseUtil.getInfoLog().info("微信统一下单异常,预支付签名:"+e.toString());
e.printStackTrace();
}
return map;
}
前台参数通过RC4加密,WechatConfig为微信支付提供的SDK,通过wxpay.unifiedOrder()方法向微信提交数据。
需要注意的是,预支付订单成功后返回给前台的签名需要和前台的拼接参数保持一致,否则会签名不一致导致支付错误!!!
第二步,接收微信支付回调通知
@RequestMapping("/wxPayNotify")
public String wxPayNotify(HttpServletRequest request, HttpServletResponse respose) {
// 返回到微信的参数
String returnWeChat = "";
try {
// 读取返回参数
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
outSteam.close();
inStream.close();
String resultStr = new String(outSteam.toByteArray(), "utf-8");
System.out.println("支付结果通知:"+resultStr);
// 转换为map
Map<String, String> resultMap = PayUtils.parseXmlToList(resultStr);
System.out.println("resultMap:"+resultMap);
// 验证签名是否正确
WechatConfig wxConfig = WechatConfig.getInstance();
WXPay wxPay = new WXPay(wxConfig);
// 返回微信已接到通知
Map<String, String> backMap = new HashMap<String, String>();
backMap.put("return_code", "SUCCESS");
backMap.put("return_msg", "OK");
returnWeChat = WXPayUtil.mapToXml(backMap);
// 签名正确,进行业务操作
if (wxPay.isPayResultNotifySignatureValid(resultMap)) {
//支付成功
if ("SUCCESS".equals(resultMap.get("result_code"))) {
boolean bo = weChatPayServicve.wxPayNotify(resultMap);
if (!bo) {
returnWeChat = "fail";
} else {
WXPayUtil.getLogger().info("接收通知正常");
}
} else {
WXPayUtil.getLogger().info("支付失败");
}
} else {
// 签名错误
WXPayUtil.getLogger().info("微信签名错误");
returnWeChat = "fail";
}
} catch (IOException e) {
WXPayUtil.getLogger().info("参数读取错误:" + e);
returnWeChat = "fail";
e.printStackTrace();
e.getMessage();
try {
OutputStream os = new FileOutputStream(new File("E://test2.log"));
os.write(e.getMessage().getBytes());
os.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} catch (Exception e) {
WXPayUtil.getLogger().info("支付通知处理错误:" + e);
returnWeChat = "fail";
e.printStackTrace();
try {
OutputStream os = new FileOutputStream(new File("E://test3.log"));
os.write(e.getMessage().getBytes());
os.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
return returnWeChat;
}
支付通知回调接口为预支付订单是提供的接口,必须为外网能够访问。
接收微信返回的参数后解析参数,并验证签名,签名正确后进行业务操作,否则返回fail,表示金额未确认。
且须验证返回金额与实际业务解是否一致,保证安全性。