WxPayService.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. package com.llisoft.pay.service;
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import java.util.HashMap;
  5. import java.util.List;
  6. import java.util.Map;
  7. import java.util.UUID;
  8. import org.apache.commons.codec.digest.DigestUtils;
  9. import org.slf4j.Logger;
  10. import org.slf4j.LoggerFactory;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.beans.factory.annotation.Value;
  13. import org.springframework.stereotype.Service;
  14. import com.llisoft.common.util.DecimalUtil;
  15. import com.llisoft.common.util.HttpUtil;
  16. import com.llisoft.common.util.JsonUtil;
  17. import com.llisoft.common.util.XmlUtil;
  18. /**
  19. * 微信支付
  20. */
  21. @Service
  22. public class WxPayService {
  23. private Logger logger = LoggerFactory.getLogger(WxPayService.class);
  24. /** 微信接口 统一下单 */
  25. public static final String WX_API_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
  26. /** 微信接口 查询订单 */
  27. public static final String WX_API_ORDERQUERY = "https://api.mch.weixin.qq.com/pay/orderquery";
  28. @Value("${mta.domain}")
  29. private String domain;
  30. @Value("${wxpay.mchid}")
  31. private String mchid;
  32. @Value("${wxpay.mchkey}")
  33. private String mchkey;
  34. @Value("${wxpay.appid}")
  35. private String appid;
  36. @Value("${wxpay.secret}")
  37. private String secret;
  38. @Autowired
  39. private PayService payService;
  40. /**
  41. * 支付
  42. * @param paynum
  43. * @param money
  44. * @param body
  45. * @return
  46. */
  47. public String pay(String paynum, int money, String body) {
  48. Map<String, String> paramMap = new HashMap<String ,String>();
  49. paramMap.put("appid", appid); // 公众账号ID
  50. paramMap.put("mch_id", mchid); // 商户号
  51. paramMap.put("nonce_str", getNonceStr()); // 随机字符串
  52. paramMap.put("out_trade_no", paynum); // 商户订单号
  53. paramMap.put("body", body); // 商品描述
  54. paramMap.put("total_fee", String.valueOf(money)); // 支付金额, 单位为【分】
  55. paramMap.put("spbill_create_ip", "127.0.0.1"); // 终端IP, APP和网页支付提交用户端ip
  56. paramMap.put("notify_url", domain+"/wxpay/notify"); // 通知地址
  57. paramMap.put("trade_type", "NATIVE"); // 交易类型, 取值如下:JSAPI,NATIVE,APP
  58. paramMap.put("sign", getSign(paramMap)); // 签名
  59. String xmlParamString = mapToXml(paramMap); // 封装xml格式参数字符串
  60. logger.info("微信统一下单api请求参数: {}", xmlParamString);
  61. String result = HttpUtil.postXml(WX_API_ORDER, xmlParamString);
  62. logger.info("微信统一下单api返回结果: {}", result);
  63. HashMap<String, String> resultMap = XmlUtil.toObject(result, HashMap.class, String.class, String.class);
  64. if ("SUCCESS".equals(resultMap.get("return_code"))) {
  65. return resultMap.get("code_url");
  66. }
  67. logger.error("微信统一下单api调用失败: {}", resultMap);
  68. return null;
  69. }
  70. /**
  71. * 支付 移动端
  72. * @param paynum
  73. * @param money
  74. * @param body
  75. * @return
  76. */
  77. public JsApiPayConfig paym(String paynum, int money, String body, String openid) throws Exception {
  78. Map<String, String> paramMap = new HashMap<String ,String>();
  79. paramMap.put("appid", appid); // 公众账号ID
  80. paramMap.put("mch_id", mchid); // 商户号
  81. paramMap.put("nonce_str", getNonceStr()); // 随机字符串
  82. paramMap.put("out_trade_no", paynum); // 商户订单号
  83. paramMap.put("body", body); // 商品描述
  84. paramMap.put("total_fee", String.valueOf(money)); // 支付金额, 单位为【分】
  85. paramMap.put("spbill_create_ip", "127.0.0.1"); // 终端IP, APP和网页支付提交用户端ip
  86. paramMap.put("notify_url", domain+"/wxpay/notify"); // 通知地址
  87. paramMap.put("trade_type", "JSAPI"); // 交易类型, 取值如下:JSAPI,NATIVE,APP
  88. paramMap.put("openid", openid); // 用户标识, trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。
  89. paramMap.put("sign", getSign(paramMap)); // 签名
  90. String xmlParamString = mapToXml(paramMap); // 封装xml格式参数字符串
  91. logger.info("微信统一下单api请求参数: {}", xmlParamString);
  92. String result = HttpUtil.postXml(WX_API_ORDER, xmlParamString);
  93. logger.info("微信统一下单api返回结果: {}", result);
  94. HashMap<String, String> resultMap = XmlUtil.toObject(result, HashMap.class, String.class, String.class);
  95. if ("SUCCESS".equals(resultMap.get("return_code"))) {
  96. String prepayid = resultMap.get("prepay_id");
  97. if (prepayid!=null && !prepayid.trim().isEmpty()) {
  98. // 微信js支付参数
  99. paramMap = new HashMap<String, String>();
  100. paramMap.put("appId", appid);
  101. paramMap.put("nonceStr", getNonceStr());
  102. paramMap.put("timeStamp", getTimeStamp());
  103. paramMap.put("package", "prepay_id=" + prepayid);
  104. paramMap.put("signType", "MD5");
  105. paramMap.put("paySign", getSign(paramMap));
  106. paramMap.put("packageStr", paramMap.get("package")); // 解决java中package关键字问题
  107. JsApiPayConfig jsApiPayConfig = JsonUtil.toObject(JsonUtil.toJson(paramMap), JsApiPayConfig.class);
  108. // 微信jsapi初始化参数 // TODO 未完待续,需要集成微信公众号网页授权
  109. // String result = HttpUtil.get(authurl + "/jsapi/config");
  110. // logger.info("调用微信授权服务: 获取jsapi初始化参数: {}", result);
  111. // jsApiPayConfig.setInitConfig(JsonUtil.toObject(result, JsApiConfig.class));
  112. // logger.info("返回微信js支付需要的参数信息: {}", JsonUtil.toJson(jsApiPayConfig));
  113. return jsApiPayConfig;
  114. }
  115. }
  116. logger.error("微信统一下单api调用失败: {}", resultMap);
  117. return null;
  118. }
  119. /**
  120. * 查询
  121. * @param paynum
  122. * @return
  123. */
  124. public Map<String, String> query(String paynum) {
  125. Map<String, String> paramMap = new HashMap<String ,String>();
  126. paramMap.put("appid", appid); // 公众账号ID
  127. paramMap.put("mch_id", mchid); // 商户号
  128. paramMap.put("nonce_str", getNonceStr()); // 随机字符串
  129. paramMap.put("out_trade_no", paynum); // 商户订单号
  130. paramMap.put("sign", getSign(paramMap)); // 签名
  131. // 封装xml格式参数字符串
  132. String xmlParamString = mapToXml(paramMap);
  133. logger.info("微信订单查询api请求参数: {}", xmlParamString);
  134. String result = HttpUtil.postXml(WX_API_ORDERQUERY, xmlParamString);
  135. logger.info("微信订单查询api返回结果: {}", result);
  136. Map<String, String> resultMap = XmlUtil.toObject(result, HashMap.class, String.class, String.class);
  137. return resultMap;
  138. }
  139. /**
  140. * 处理微信支付返回结果
  141. * @param resultMap
  142. * @return
  143. * @author YangJie
  144. * @throws Exception
  145. * @throws Throwable
  146. * @createTime 2015年3月31日 下午5:36:27
  147. */
  148. public boolean dispose(Map<String, String> resultMap) throws Exception {
  149. // 判断通信标识
  150. if (!"SUCCESS".equals(resultMap.get("return_code"))) {
  151. logger.error("微信支付处理: 通信标识异常: {}", resultMap.get("return_code"));
  152. return false;
  153. }
  154. // 验证签名
  155. if (!getSign(resultMap).equals(resultMap.get("sign"))) {
  156. logger.error("微信支付处理: 签名验证失败: {}", resultMap.get("sign"));
  157. return false;
  158. }
  159. // 判断交易状态
  160. if (!"SUCCESS".equals(resultMap.get("result_code"))) {
  161. logger.error("微信支付处理: 交易状态异常: {}", resultMap.get("result_code"));
  162. return false;
  163. }
  164. // 完成支付状态
  165. String paynum = resultMap.get("out_trade_no"); // paynum
  166. String tradenum = resultMap.get("trade_no"); // 支付宝交易号
  167. int totalAmount = DecimalUtil.toInt(resultMap.get("total_amount")); // 实际支付金额
  168. return payService.finish(paynum, tradenum, totalAmount, PayService.FLAG_NOTIFY);
  169. }
  170. /**
  171. * 获取随机字符串
  172. * @return 32字符以内
  173. * @author YangJie
  174. * @createTime 2015年3月26日 下午5:12:47
  175. */
  176. private String getNonceStr(){
  177. return UUID.randomUUID().toString().replace("-", "");
  178. }
  179. /**
  180. * 获取时间戳
  181. * @return 精确到秒
  182. * @author YangJie
  183. * @createTime 2015年3月26日 下午5:12:47
  184. */
  185. private String getTimeStamp(){
  186. return String.valueOf(System.currentTimeMillis()/1000);
  187. }
  188. /**
  189. * 获取微信认证签名(md5)
  190. * @param paramMap
  191. * @author YangJie
  192. * @createTime 2015年3月26日 下午2:48:17
  193. */
  194. private String getSign(Map<String, String> paramMap) {
  195. List<String> keyList = new ArrayList<String>(paramMap.keySet()); // 获取参数key
  196. Collections.sort(keyList); // key 排序
  197. StringBuffer paramBuffer = new StringBuffer();
  198. String value = null;
  199. for (String key : keyList) { // 循环封装非空参数
  200. value = paramMap.get(key);
  201. if (key != null && !key.equals("sign") && value!=null && !value.isEmpty()) {
  202. paramBuffer.append(key).append("=").append(value).append("&");
  203. }
  204. } // 添加私钥
  205. paramBuffer.append("key=").append(mchkey);
  206. return DigestUtils.md5Hex(paramBuffer.toString()).toUpperCase();
  207. }
  208. /**
  209. * 获取xml格式参数字符串
  210. * @param paramMap
  211. * @return
  212. * @author YangJie
  213. * @createTime 2015年3月26日 下午3:06:26
  214. */
  215. private String mapToXml(Map<String, String> paramMap) {
  216. StringBuilder xmlBuilder = new StringBuilder("<xml>");
  217. for (String key : paramMap.keySet()) {
  218. if (key != null && paramMap.get(key)!=null && !paramMap.get(key).isEmpty()) {
  219. xmlBuilder.append("<").append(key).append(">").append(paramMap.get(key))
  220. .append("</").append(key).append(">");
  221. }
  222. }
  223. xmlBuilder.append("</xml>");
  224. return xmlBuilder.toString();
  225. }
  226. /**
  227. * 微信js api 初始化参数
  228. */
  229. public class JsApiConfig{
  230. private String appId;
  231. private String timestamp;
  232. private String nonceStr;
  233. private String signature;
  234. public String getAppId() {
  235. return appId;
  236. }
  237. public void setAppId(String appId) {
  238. this.appId = appId;
  239. }
  240. public String getTimestamp() {
  241. return timestamp;
  242. }
  243. public void setTimestamp(String timestamp) {
  244. this.timestamp = timestamp;
  245. }
  246. public String getNonceStr() {
  247. return nonceStr;
  248. }
  249. public void setNonceStr(String nonceStr) {
  250. this.nonceStr = nonceStr;
  251. }
  252. public String getSignature() {
  253. return signature;
  254. }
  255. public void setSignature(String signature) {
  256. this.signature = signature;
  257. }
  258. }
  259. /**
  260. * 微信js api 支付参数
  261. */
  262. public class JsApiPayConfig{
  263. private String appId;
  264. private String timeStamp;
  265. private String nonceStr;
  266. private String packageStr;
  267. private String signType;
  268. private String paySign;
  269. private JsApiConfig initConfig;
  270. public String getAppId() {
  271. return appId;
  272. }
  273. public void setAppId(String appId) {
  274. this.appId = appId;
  275. }
  276. public String getTimeStamp() {
  277. return timeStamp;
  278. }
  279. public void setTimeStamp(String timeStamp) {
  280. this.timeStamp = timeStamp;
  281. }
  282. public String getNonceStr() {
  283. return nonceStr;
  284. }
  285. public void setNonceStr(String nonceStr) {
  286. this.nonceStr = nonceStr;
  287. }
  288. public String getPackageStr() {
  289. return packageStr;
  290. }
  291. public void setPackageStr(String packageStr) {
  292. this.packageStr = packageStr;
  293. }
  294. public String getSignType() {
  295. return signType;
  296. }
  297. public void setSignType(String signType) {
  298. this.signType = signType;
  299. }
  300. public String getPaySign() {
  301. return paySign;
  302. }
  303. public void setPaySign(String paySign) {
  304. this.paySign = paySign;
  305. }
  306. public JsApiConfig getInitConfig() {
  307. return initConfig;
  308. }
  309. public void setInitConfig(JsApiConfig initConfig) {
  310. this.initConfig = initConfig;
  311. }
  312. }
  313. }