From f9ceb1afc4deb26526f73be1604ade61b02fc548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9C=B0=E5=B9=B3=E7=BA=BF?= <22250530@qq.com> Date: Mon, 17 Aug 2015 15:00:16 +0800 Subject: [PATCH] --- .../service/weixin/WeiXinMPService.java | 2 +- .../service/weixin/WeiXinPayResult.java | 77 ++++++ .../service/weixin/WeiXinPayService.java | 254 ++++++++++++++++++ 3 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 src-plugin/com/wentch/redkale/service/weixin/WeiXinPayResult.java create mode 100644 src-plugin/com/wentch/redkale/service/weixin/WeiXinPayService.java diff --git a/src-plugin/com/wentch/redkale/service/weixin/WeiXinMPService.java b/src-plugin/com/wentch/redkale/service/weixin/WeiXinMPService.java index 189a17a21..a32598b49 100644 --- a/src-plugin/com/wentch/redkale/service/weixin/WeiXinMPService.java +++ b/src-plugin/com/wentch/redkale/service/weixin/WeiXinMPService.java @@ -21,7 +21,7 @@ import javax.annotation.*; * * @author zhangjx */ -public class WeiXinMPService { +public class WeiXinMPService implements Service{ protected static final Type MAPTYPE = new TypeToken>() { }.getType(); diff --git a/src-plugin/com/wentch/redkale/service/weixin/WeiXinPayResult.java b/src-plugin/com/wentch/redkale/service/weixin/WeiXinPayResult.java new file mode 100644 index 000000000..6a41ea52c --- /dev/null +++ b/src-plugin/com/wentch/redkale/service/weixin/WeiXinPayResult.java @@ -0,0 +1,77 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.service.weixin; + +import com.wentch.redkale.service.*; + +/** + * + * @author zhangjx + */ +public class WeiXinPayResult extends RetResult { + + //待支付 + public static final short PAYSTATUS_UNPAY = 10; + + //已支付 + public static final short PAYSTATUS_PAYOK = 30; + + private long orderid; + + private long payid; + + private long payedmoney; + + private short paystatus; + + public WeiXinPayResult() { + } + + public WeiXinPayResult(int retcode) { + super(retcode); + } + + public WeiXinPayResult(long orderid, long payid, short paystatus, long payedmoney, String resultcontent) { + this.orderid = orderid; + this.payid = payid; + this.paystatus = paystatus; + this.payedmoney = payedmoney; + this.setResult(resultcontent); + } + + public long getOrderid() { + return orderid; + } + + public void setOrderid(long orderid) { + this.orderid = orderid; + } + + public long getPayid() { + return payid; + } + + public void setPayid(long payid) { + this.payid = payid; + } + + public long getPayedmoney() { + return payedmoney; + } + + public void setPayedmoney(long payedmoney) { + this.payedmoney = payedmoney; + } + + public short getPaystatus() { + return paystatus; + } + + public void setPaystatus(short paystatus) { + this.paystatus = paystatus; + } + +} diff --git a/src-plugin/com/wentch/redkale/service/weixin/WeiXinPayService.java b/src-plugin/com/wentch/redkale/service/weixin/WeiXinPayService.java new file mode 100644 index 000000000..0fdf8f5e9 --- /dev/null +++ b/src-plugin/com/wentch/redkale/service/weixin/WeiXinPayService.java @@ -0,0 +1,254 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.service.weixin; + +import com.wentch.redkale.convert.json.*; +import com.wentch.redkale.service.*; +import static com.wentch.redkale.service.weixin.WeiXinPayResult.*; +import com.wentch.redkale.util.*; +import java.security.*; +import java.text.*; +import java.util.*; +import java.util.logging.*; +import java.util.regex.*; +import javax.annotation.*; + +/** + * + * @author zhangjx + */ +public class WeiXinPayService implements Service { + + private static final DateFormat FORMAT = new SimpleDateFormat("yyyyMMddHHmmss"); + + private static final Pattern PAYXML = Pattern.compile("<([^/>]+)>(.+)"); // "<([^/>]+)>" + + public static final int PAY_WX_ERROR = 4012101;//微信支付失败 + + public static final int PAY_FALSIFY_ORDER = 4012017;//交易签名被篡改 + + public static final int PAY_STATUS_ERROR = 4012018;//订单或者支付状态不正确 + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + protected final boolean fine = logger.isLoggable(Level.FINE); + + protected final boolean finer = logger.isLoggable(Level.FINER); + + protected final boolean finest = logger.isLoggable(Level.FINEST); + + @Resource(name = "property.wxpay.appid") //公众账号ID + protected String wxpayappid = "wxYYYYYYYYYYYY"; + + @Resource(name = "property.wxpay.mchid") //商户ID + protected String wxpaymchid = "xxxxxxxxxxx"; + + @Resource(name = "property.wxpay.sdbmchid") //子商户ID,受理模式必填 + protected String wxpaysdbmchid = ""; + + @Resource(name = "property.wxpay.key") //签名算法需要用到的秘钥 + protected String wxpaykey = "##########################"; + + @Resource(name = "property.wxpay.certpwd") + protected String wxpaycertpwd = "xxxxxxxxxx"; //HTTP证书的密码,默认等于MCHID + + @Resource(name = "property.wxpay.certpath") //HTTP证书在服务器中的路径,用来加载证书用 + protected String wxpaycertpath = "apiclient_cert.p12"; + + @Resource + protected JsonConvert convert; + + /** + * + * + " + * + " + * + " + * + " + * + " + * + " + * + " + * + " + * + " + *

+ * @param orderid + * @param payid + * @param orderpayid + * @param paymoney + * @param clientAddr + * @param notifyurl + * @param map + * @return + */ + public RetResult> paying(long orderid, long payid, long orderpayid, long paymoney, String clientAddr, String notifyurl, Map map) { + RetResult result = null; + try { + if (!(map instanceof SortedMap)) map = new TreeMap<>(map); + map.put("appid", wxpayappid); + map.put("mch_id", wxpaymchid); + map.put("nonce_str", Long.toHexString(System.currentTimeMillis()) + Long.toHexString(System.nanoTime())); + map.put("body", "三万英尺服务套餐"); + map.put("attach", "" + payid); + map.put("out_trade_no", "" + orderpayid); + map.put("total_fee", "" + paymoney); + map.put("spbill_create_ip", clientAddr); + synchronized (FORMAT) { + map.put("time_expire", FORMAT.format(new Date(System.currentTimeMillis() + 10 * 60 * 60 * 1000))); + } + map.put("notify_url", notifyurl); + { + final StringBuilder sb = new StringBuilder(); + map.forEach((x, y) -> sb.append(x).append('=').append(y).append('&')); + sb.append("key=").append(wxpaykey); + map.put("sign", Utility.binToHexString(MessageDigest.getInstance("MD5").digest(sb.toString().getBytes())).toUpperCase()); + } + if (finest) logger.finest("weixinpaying2: " + orderid + " -> unifiedorder.map =" + map); + Map wxresult = formatXMLToMap(Utility.postHttpContent("https://api.mch.weixin.qq.com/pay/unifiedorder", formatMapToXML(map))); + if (finest) logger.finest("weixinpaying3: " + orderid + " -> unifiedorder.callback =" + wxresult); + if (!"SUCCESS".equals(wxresult.get("return_code"))) return new RetResult<>(PAY_WX_ERROR); + if (!checkSign(wxresult)) return new RetResult(PAY_FALSIFY_ORDER); + /** + * "appId" : "wx2421b1c4370ec43b", //公众号名称,由商户传入 "timeStamp":" 1395712654", //时间戳,自1970年以来的秒数 "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随机串 "package" : + * "prepay_id=u802345jgfjsdfgsdg888", "signType" : "MD5", //微信签名方式: "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 + */ + Map rs = new TreeMap<>(); + rs.put("appId", this.wxpayappid); + rs.put("timeStamp", Long.toString(System.currentTimeMillis() / 1000)); + rs.put("nonceStr", Long.toHexString(System.currentTimeMillis()) + Long.toHexString(System.nanoTime())); + rs.put("package", "prepay_id=" + wxresult.get("prepay_id")); + rs.put("signType", "MD5"); + { + final StringBuilder sb2 = new StringBuilder(); + rs.forEach((x, y) -> sb2.append(x).append('=').append(y).append('&')); + sb2.append("key=").append(wxpaykey); + rs.put("paySign", Utility.binToHexString(MessageDigest.getInstance("MD5").digest(sb2.toString().getBytes())).toUpperCase()); + } + if (finest) logger.finest("weixinpaying4: " + orderid + " -> unifiedorder.result =" + rs); + RetResult rr = new RetResult(rs); + rr.setRetinfo("" + orderpayid); + return rr; + } catch (Exception e) { + logger.log(Level.WARNING, "paying error.", e); + } + return result; + } + + public RetResult closepay(long orderpayid) { + RetResult result = null; + try { + Map map = new TreeMap<>(); + map.put("appid", wxpayappid); + map.put("mch_id", wxpaymchid); + map.put("nonce_str", Long.toHexString(System.currentTimeMillis()) + Long.toHexString(System.nanoTime())); + map.put("out_trade_no", "" + orderpayid); + { + final StringBuilder sb = new StringBuilder(); + map.forEach((x, y) -> sb.append(x).append('=').append(y).append('&')); + sb.append("key=").append(wxpaykey); + map.put("sign", Utility.binToHexString(MessageDigest.getInstance("MD5").digest(sb.toString().getBytes())).toUpperCase()); + } + if (finest) logger.finest("weixinclosepay2: " + orderpayid + " -> closeorder.map =" + map); + Map wxresult = formatXMLToMap(Utility.postHttpContent("https://api.mch.weixin.qq.com/pay/closeorder", formatMapToXML(map))); + if (finest) logger.finest("weixinclosepay3: " + orderpayid + " -> closeorder.callback =" + wxresult); + if (!"SUCCESS".equals(wxresult.get("return_code"))) return new RetResult<>(PAY_WX_ERROR); + if (!checkSign(wxresult)) return new RetResult(PAY_FALSIFY_ORDER); + return new RetResult(wxresult); + } catch (Exception e) { + logger.log(Level.WARNING, "closepay error: " + orderpayid, e); + } + return result; + } + + public WeiXinPayResult checkPay(long orderid, long orderpayid) { + WeiXinPayResult result = new WeiXinPayResult(PAY_STATUS_ERROR); + try { + Map map = new TreeMap<>(); + map.put("appid", wxpayappid); + map.put("mch_id", wxpaymchid); + map.put("out_trade_no", "" + orderpayid); + map.put("nonce_str", Long.toHexString(System.currentTimeMillis()) + Long.toHexString(System.nanoTime())); + { + final StringBuilder sb = new StringBuilder(); + map.forEach((x, y) -> sb.append(x).append('=').append(y).append('&')); + sb.append("key=").append(wxpaykey); + map.put("sign", Utility.binToHexString(MessageDigest.getInstance("MD5").digest(sb.toString().getBytes())).toUpperCase()); + } + Map wxresult = formatXMLToMap(Utility.postHttpContent("https://api.mch.weixin.qq.com/pay/orderquery", formatMapToXML(map))); + return callbackPay(wxresult); + } catch (Exception e) { + logger.log(Level.FINER, "check weixinpay[" + orderid + "] except", e); + return result; + } + } + + /** + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * 10 + * + * + * + *

+ * @param map + * @return + */ + public WeiXinPayResult callbackPay(Map map) { + if (!"SUCCESS".equals(map.get("return_code"))) return new WeiXinPayResult(PAY_WX_ERROR); + if (!(map instanceof SortedMap)) map = new TreeMap<>(map); + if (!checkSign(map)) return new WeiXinPayResult(PAY_FALSIFY_ORDER); + String state = map.get("trade_state"); + if (state == null && "SUCCESS".equals(map.get("result_code")) && Long.parseLong(map.get("total_fee")) > 0) { + state = "SUCCESS"; + } + short paystatus = "SUCCESS".equals(state) ? PAYSTATUS_PAYOK : PAYSTATUS_UNPAY; + return new WeiXinPayResult(Long.parseLong(map.get("out_trade_no")), Long.parseLong(map.get("attach")), paystatus, Long.parseLong(map.get("total_fee")), convert.convertTo(map)); + } + + protected static String formatMapToXML(final Map map) { + final StringBuilder sb = new StringBuilder(); + sb.append(""); + map.forEach((x, y) -> sb.append('<').append(x).append('>').append(y.replace("<", "<").replace(">", ">").replace("&", "&")).append("')); + sb.append(""); + return sb.toString(); + } + + protected boolean checkSign(Map map) { + if (!(map instanceof SortedMap)) map = new TreeMap<>(map); + String sign = map.remove("sign"); + final StringBuilder sb = new StringBuilder(); + map.forEach((x, y) -> sb.append(x).append('=').append(y).append('&')); + sb.append("key=").append(wxpaykey); + try { + return sign.equals(Utility.binToHexString(MessageDigest.getInstance("MD5").digest(sb.toString().getBytes())).toUpperCase()); + } catch (Exception e) { + return false; + } + } + + public static Map formatXMLToMap(final String xml) { + Map map = new TreeMap<>(); + Matcher m = PAYXML.matcher(xml.substring(xml.indexOf('>') + 1)); + while (m.find()) { + String val = m.group(2); + if (val.startsWith("