WxService.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. package com.kede.wxsdk.service;
  2. import java.io.BufferedReader;
  3. import java.io.ByteArrayInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.math.BigDecimal;
  7. import java.net.InetAddress;
  8. import java.nio.charset.Charset;
  9. import java.security.AlgorithmParameters;
  10. import java.util.Arrays;
  11. import java.util.Date;
  12. import java.util.HashMap;
  13. import java.util.Map;
  14. import java.util.UUID;
  15. import javax.crypto.Cipher;
  16. import javax.crypto.spec.IvParameterSpec;
  17. import javax.crypto.spec.SecretKeySpec;
  18. import javax.servlet.http.HttpServletRequest;
  19. import javax.xml.parsers.DocumentBuilder;
  20. import javax.xml.parsers.DocumentBuilderFactory;
  21. import org.apache.commons.codec.binary.Base64;
  22. import org.apache.commons.collections4.map.HashedMap;
  23. import org.apache.http.HttpResponse;
  24. import org.apache.http.HttpStatus;
  25. import org.apache.http.ParseException;
  26. import org.apache.http.client.ClientProtocolException;
  27. import org.apache.http.client.HttpClient;
  28. import org.apache.http.client.methods.HttpGet;
  29. import org.apache.http.client.methods.HttpPost;
  30. import org.apache.http.entity.StringEntity;
  31. import org.apache.http.impl.client.HttpClientBuilder;
  32. import org.apache.http.util.EntityUtils;
  33. import org.springframework.beans.factory.annotation.Autowired;
  34. import org.springframework.beans.factory.annotation.Value;
  35. import org.springframework.stereotype.Service;
  36. import org.w3c.dom.Node;
  37. import org.w3c.dom.NodeList;
  38. import com.alibaba.fastjson.JSON;
  39. import com.alibaba.fastjson.JSONObject;
  40. import com.kede.common.dao.OrderMapper;
  41. import com.kede.common.dao.UserMapper;
  42. import com.kede.common.error.BusinessException;
  43. import com.kede.common.model.Order;
  44. import com.kede.common.model.Project;
  45. import com.kede.common.model.User;
  46. import com.kede.common.utils.LoggerUtils;
  47. import com.kede.common.utils.StringUtils;
  48. import com.kede.core.mybatis.JDBCIdGenerator;
  49. import com.kede.core.mybatis.page.Pagination;
  50. import com.kede.core.shiro.token.TokenManager;
  51. import com.kede.order.service.impl.OrderService;
  52. import com.kede.wxsdk.WXPayConstants;
  53. import com.kede.wxsdk.WXPayUtil;
  54. /**
  55. * 小程序
  56. * @author Administrator
  57. *
  58. */
  59. @Service
  60. public class WxService {
  61. @Value(value = "${wx.appId}")
  62. private String appId;
  63. @Value(value = "${wx.appSecret}")
  64. private String appSecret;
  65. @Value(value = "${wx.mchId}")
  66. private String mchId;
  67. @Value(value = "${wx.apiKiy}")
  68. private String apiKiy;
  69. @Value(value = "${wx.notifyUrl}")
  70. private String notifyUrl;
  71. @Value(value = "${wx.apiv3}")
  72. private String apiv3;
  73. @Value(value = "${wx.certPath}")
  74. private String certPath;
  75. @Autowired
  76. private JDBCIdGenerator idGenerator;
  77. @Autowired
  78. private OrderService orderService;
  79. @Autowired
  80. private UserMapper userMapper;
  81. @Autowired
  82. private OrderMapper orderMapper;
  83. /**
  84. * 小程序下单
  85. * @param p
  86. * @return
  87. */
  88. public Map<String , String > pushUnifiedOrder(Project p){
  89. WXPay wxpay = null;
  90. Map<String, String> map=new HashMap<>();
  91. try {
  92. IWxPayConfig iWxPayConfig=new IWxPayConfig(appId,mchId,apiKiy,certPath);
  93. wxpay = new WXPay(iWxPayConfig); // *** 注入自己实现的微信配置类, 创建WXPay核心类, WXPay 包括统一下单接口
  94. InetAddress addr = InetAddress.getLocalHost();
  95. String ip = addr.getHostAddress();
  96. Map<String, String> data = new HashMap<String, String>();
  97. data.put("body", p.getName());
  98. String orderNo=idGenerator.generateId().toString();
  99. data.put("out_trade_no", orderNo); // 订单唯一编号, 不允许重复
  100. // 订单金额, 单位分
  101. String amout=p.getAmount().multiply(new BigDecimal(100)).stripTrailingZeros().toPlainString();
  102. String openId=(String) TokenManager.getVal2Session("openId");
  103. // String openId="oGNT65WX5ysEnCUmAvaXOdIrMswA";
  104. if (StringUtils.isBlank(openId)) {
  105. throw new BusinessException("无法获取正确的openId");
  106. }
  107. Order o=new Order();
  108. o.setOrderNo(orderNo);
  109. o.setPayAmount(new BigDecimal(0));
  110. o.setPayStatus(0);
  111. o.setProjectAmount(p.getAmount());
  112. o.setProjectId(p.getId());
  113. o.setProjectName(p.getName());
  114. o.setStatus(0);
  115. o.setUid(TokenManager.getUserId());
  116. if (p.getAmount().compareTo(new BigDecimal("0.01"))>=0) {
  117. data.put("total_fee", amout);
  118. data.put("spbill_create_ip", ip); // 下单ip
  119. data.put("openid", openId); // 微信公众号统一标示openid
  120. data.put("notify_url", notifyUrl); // 订单结果通知, 微信主动回调此接口
  121. data.put("trade_type", "JSAPI"); // 固定填写
  122. Map<String, String> response = wxpay.unifiedOrder(data); // 微信sdk集成方法, 统一下单接口unifiedOrder, 此处请求 MD5加密 加密方式
  123. LoggerUtils.debug(getClass(),"微信支付下单成功, 返回值 response=%s", response);
  124. String prepayId = response.get("prepay_id");
  125. if (prepayId == null) {
  126. throw new BusinessException("无法获取正确的prepayId");
  127. }
  128. orderService.insert(o);
  129. // 前端调起微信支付必要参数
  130. String packages = "prepay_id=" + prepayId;
  131. Map<String, String> wxPayMap = new HashMap<String, String>();
  132. String uuid=UUID.randomUUID().toString().replace("-", "").toLowerCase();
  133. wxPayMap.put("appId", iWxPayConfig.getAppID());
  134. wxPayMap.put("timeStamp", String.valueOf(new Date().getTime()));
  135. wxPayMap.put("nonceStr", uuid);
  136. wxPayMap.put("package", packages);
  137. wxPayMap.put("signType", "MD5");
  138. // 加密串中包括 appId timeStamp nonceStr package signType 5个参数, 通过sdk WXPayUtil类加密
  139. String sign = WXPayUtil.generateSignature(wxPayMap, iWxPayConfig.getKey());
  140. // 返回给前端调起微信支付的必要参数
  141. map.put("prepay_id", prepayId);
  142. map.put("out_trade_no", orderNo);
  143. map.put("paySign", sign);
  144. map.putAll(wxPayMap);
  145. }else {
  146. LoggerUtils.debug(getClass(),"订单下单成功, 返回值 amount=%s", p.getAmount());
  147. o.setPayStatus(1);
  148. orderService.insert(o);
  149. map.put("out_trade_no", orderNo);
  150. map.put("amount", "0");
  151. }
  152. } catch (Exception e) {
  153. e.printStackTrace();
  154. throw new BusinessException("支付异常");
  155. }
  156. return map;
  157. }
  158. /**
  159. * 订单查询
  160. * @param orderNo
  161. * @return
  162. */
  163. public Map<String, String> pushOrderQuery(String orderNo){
  164. // 发起微信支付
  165. WXPay wxpay = null;
  166. Map<String, String> map=new HashMap<>();
  167. try {
  168. IWxPayConfig iWxPayConfig=new IWxPayConfig(appId,mchId,apiKiy,certPath);
  169. wxpay = new WXPay(iWxPayConfig); // *** 注入自己实现的微信配置类, 创建WXPay核心类, WXPay 包括统一下单接口
  170. Map<String, String> data = new HashMap<String, String>();
  171. data.put("out_trade_no", orderNo);
  172. data.put("mchid", mchId);
  173. map = wxpay.orderQuery(data);
  174. LoggerUtils.debug(getClass(),"微信查询成功, 返回值 response={%s}", map);
  175. String total_fee=map.get("total_fee");
  176. String transaction_id=map.get("transaction_id");
  177. String out_trade_no=map.get("out_trade_no");
  178. if (map.get("trade_state").equals("SUCCESS")) {
  179. Order os=orderMapper.selectByPrimaryKey(out_trade_no);
  180. if (os.getPayStatus()!=1) {
  181. insertOrder(total_fee, transaction_id, out_trade_no);
  182. }
  183. }
  184. }catch (Exception e) {
  185. e.printStackTrace();
  186. throw new BusinessException("查询异常");
  187. }
  188. return map;
  189. }
  190. private void insertOrder(String total_fee, String transaction_id, String out_trade_no) {
  191. Order o=new Order();
  192. o.setOrderNo(out_trade_no);
  193. o.setPayStatus(1);
  194. o.setPayAmount(new BigDecimal(total_fee).divide(new BigDecimal(100)));
  195. o.setWxOrderNo(transaction_id);
  196. orderMapper.updateByPrimaryKeySelective(o);
  197. }
  198. /**
  199. * 微信撤销订单
  200. * @param o.
  201. * @return
  202. */
  203. public Map<String, String> pushWxClose(Order o){
  204. // 发起微信支付
  205. WXPay wxpay = null;
  206. Map<String, String> map=new HashMap<>();
  207. try {
  208. IWxPayConfig iWxPayConfig=new IWxPayConfig(appId,mchId,apiKiy,certPath);
  209. wxpay = new WXPay(iWxPayConfig); // *** 注入自己实现的微信配置类, 创建WXPay核心类, WXPay 包括统一下单接口
  210. Map<String, String> data = new HashMap<String, String>();
  211. data.put("out_trade_no", o.getOrderNo());
  212. data.put("mchid", mchId);
  213. map = wxpay.closeOrder(data);
  214. LoggerUtils.debug(getClass(),"微信撤销成功, 返回值 response={%s}", map);
  215. if (o.getPayStatus()!=1) {
  216. Order on=new Order();
  217. on.setOrderNo(o.getOrderNo());
  218. on.setPayStatus(2);
  219. orderMapper.updateByPrimaryKeySelective(on);
  220. }
  221. }catch (Exception e) {
  222. throw new BusinessException("撤销异常");
  223. }
  224. return map;
  225. }
  226. public void wxPayCallBack(HttpServletRequest req) {
  227. BufferedReader reader;
  228. try {
  229. reader = req.getReader();
  230. String line ;
  231. StringBuilder sb = new StringBuilder();
  232. while((line = reader.readLine()) != null) {
  233. sb.append(line);
  234. }
  235. // 2、xml字符串 转换为 map对象
  236. Map<String, String> map = WXPayUtil.xmlToMap(sb.toString());
  237. LoggerUtils.debug(getClass(), "获取参数%s",map.toString());
  238. // 3、尝试从缓存中获取,判断是否已经接收过通知
  239. if (map.get("result_code").equals(WXPayConstants.SUCCESS)
  240. && WXPayConstants.SUCCESS.equals(map.get("return_code"))) {
  241. }
  242. String out_trade_no=map.get("out_trade_no");
  243. String transaction_id=map.get("transaction_id");
  244. String result_code=map.get("result_code");
  245. String total_fee=map.get("total_fee");
  246. if (result_code.equals("SUCCESS")) {
  247. Order os=orderMapper.selectByPrimaryKey(out_trade_no);
  248. if (os.getPayStatus()!=1) {
  249. insertOrder(total_fee, transaction_id, out_trade_no);
  250. }
  251. }
  252. LoggerUtils.debug(getClass(),"\n\n\n\n\n>>>>>>>>>>>>>>>>>>>>>>>>微信回调结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n\n\n\n");
  253. } catch ( Exception e) {
  254. e.printStackTrace();
  255. throw new BusinessException("回调解析异常");
  256. }
  257. }
  258. /**
  259. * XML格式字符串转换为Map
  260. *
  261. * @param strXML XML字符串
  262. * @return XML数据转换后的Map
  263. * @throws Exception
  264. */
  265. public Map<String, Object> xmlToMap(String strXML) throws Exception {
  266. Map<String, Object> data = new HashMap<String, Object>();
  267. DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
  268. DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
  269. InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
  270. org.w3c.dom.Document doc = documentBuilder.parse(stream);
  271. doc.getDocumentElement().normalize();
  272. NodeList nodeList = doc.getDocumentElement().getChildNodes();
  273. for (int idx = 0; idx < nodeList.getLength(); ++idx) {
  274. Node node = nodeList.item(idx);
  275. if (node.getNodeType() == Node.ELEMENT_NODE) {
  276. org.w3c.dom.Element element = (org.w3c.dom.Element) node;
  277. data.put(element.getNodeName(), element.getTextContent());
  278. }
  279. }
  280. try {
  281. stream.close();
  282. } catch (Exception ex) {
  283. }
  284. return data;
  285. }
  286. /**
  287. * IO解析获取微信的数据
  288. *
  289. * @param request
  290. * @return
  291. */
  292. public String getXmlString(HttpServletRequest request) {
  293. BufferedReader reader = null;
  294. String line = "";
  295. String xmlString = null;
  296. try {
  297. reader = request.getReader();
  298. StringBuffer inputString = new StringBuffer();
  299. while ((line = reader.readLine()) != null) {
  300. inputString.append(line);
  301. }
  302. xmlString = inputString.toString();
  303. } catch (Exception e) {
  304. }
  305. return xmlString;
  306. }
  307. /**
  308. * 参数解密
  309. * @param sessionKey
  310. * @param encryptedData
  311. * @param iv
  312. * @return
  313. * @throws Exception
  314. */
  315. public JSONObject pushDecryptData(String sessionKey,String encryptedData, String iv) {
  316. byte[] dataByte = Base64.decodeBase64(encryptedData);
  317. // 加密秘钥
  318. byte[] keyByte = Base64.decodeBase64(sessionKey);
  319. // 偏移量
  320. byte[] ivByte = Base64.decodeBase64(iv);
  321. try {
  322. // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
  323. int base = 16;
  324. if (keyByte.length % base != 0) {
  325. int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
  326. byte[] temp = new byte[groups * base];
  327. Arrays.fill(temp, (byte) 0);
  328. System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
  329. keyByte = temp;
  330. }
  331. // 初始化
  332. Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
  333. SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
  334. AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
  335. parameters.init(new IvParameterSpec(ivByte));
  336. cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
  337. byte[] resultByte = cipher.doFinal(dataByte);
  338. if (null != resultByte && resultByte.length > 0) {
  339. String result = new String(resultByte, "UTF-8");
  340. JSONObject js=JSONObject.parseObject(result);
  341. String mobile=(String) js.get("purePhoneNumber");
  342. User u=new User();
  343. u.setId(userMapper.selectByOpenId((String)TokenManager.getVal2Session("openId")).getId());
  344. u.setMobile(mobile);
  345. userMapper.updateByPrimaryKeySelective(u);
  346. return js;
  347. }
  348. } catch (Exception e) {
  349. e.printStackTrace();
  350. }
  351. return null;
  352. }
  353. public Object getliveinfo(Integer pageNo,Integer pageSize){
  354. String url = String.format("https://api.weixin.qq.com/wxa/business/getliveinfo?access_token=%s", getAccessToken());
  355. Map<String, Object>map=new HashedMap<String, Object>();
  356. map.put("start", (pageNo - 1) * pageSize);
  357. map.put("limit", pageSize);
  358. map=this.getPostMethod(url, map);
  359. Pagination<Object> p=new Pagination<Object>(pageNo, pageSize, map.get("total"), map.get("room_info"));
  360. return p;
  361. }
  362. public String getAccessToken() {
  363. String access_token=(String) TokenManager.getVal2Session("access_token");
  364. String expires_in= (String) TokenManager.getVal2Session("expires_in");
  365. if (access_token!=null&&expires_in!=null) {
  366. expires_in=expires_in+7200;
  367. if (new Date().getTime()<Long.valueOf(expires_in)) {
  368. return access_token;
  369. }
  370. }
  371. String url = String.format(
  372. "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appId,
  373. appSecret);
  374. JSONObject js = this.httpGet(url);
  375. String errcode =js.getString("errcode");
  376. if (errcode!=null) {
  377. throw new BusinessException("获取AccessToekn失败!");
  378. }
  379. access_token = js.getString("access_token");
  380. expires_in = js.getString("expires_in");
  381. TokenManager.setVal2Session("access_token", access_token);
  382. TokenManager.setVal2Session("expires_in", expires_in);
  383. return access_token;
  384. }
  385. public JSONObject httpGet(String url) {
  386. JSONObject jsonResult = null;
  387. try {
  388. HttpClient client = HttpClientBuilder.create().build();//获取DefaultHttpClient请求
  389. HttpGet request = new HttpGet(url);
  390. HttpResponse response = client.execute(request);
  391. if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
  392. String strResult = EntityUtils.toString(response.getEntity(),"UTF-8");//此处设定编码格式
  393. jsonResult = JSON.parseObject(strResult);
  394. } else {
  395. LoggerUtils.error(getClass(), "HttpStatus异常");
  396. }
  397. } catch (IOException e) {
  398. e.printStackTrace();
  399. }
  400. return jsonResult;
  401. }
  402. public JSONObject getPostMethod(String url,Map<String, Object>map){
  403. HttpClient httpClient = HttpClientBuilder.create().build();
  404. HttpPost httpPost = new HttpPost(url);
  405. httpPost.addHeader("Content-type", "application/json");
  406. httpPost.setHeader("Accept", "application/json");
  407. httpPost.setEntity(new StringEntity(JSON.toJSONString(map), Charset.forName("UTF-8")));
  408. HttpResponse response = null;
  409. try {
  410. response = httpClient.execute(httpPost);
  411. if (null == response || response.getStatusLine() == null) {
  412. throw new Exception("Post Request For Url[{}] is not ok. Response is null");
  413. } else if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
  414. throw new Exception("Post Request For Url[{}] is not ok. Response Status Code is {"+response.getStatusLine().getStatusCode()+"}");
  415. }
  416. } catch (ClientProtocolException e) {
  417. e.printStackTrace();
  418. } catch (IOException e) {
  419. e.printStackTrace();
  420. } catch (Exception e) {
  421. e.printStackTrace();
  422. }
  423. String resultString=null;
  424. try {
  425. resultString = EntityUtils.toString(response.getEntity());
  426. } catch (ParseException | IOException e) {
  427. // TODO Auto-generated catch block
  428. e.printStackTrace();
  429. }
  430. JSONObject jsonObj = JSONObject.parseObject(resultString);
  431. return jsonObj;
  432. }
  433. public Map<String, String> pushNativePay(Project p,String tradeType) {
  434. WXPay wxpay = null;
  435. Map<String, String> map = new HashMap<>();
  436. try {
  437. IWxPayConfig iWxPayConfig = new IWxPayConfig(appId, mchId, apiKiy, certPath);
  438. wxpay = new WXPay(iWxPayConfig); // *** 注入自己实现的微信配置类, 创建WXPay核心类, WXPay 包括统一下单接口
  439. InetAddress addr = InetAddress.getLocalHost();
  440. String ip = addr.getHostAddress();
  441. Map<String, String> data = new HashMap<String, String>();
  442. data.put("body", p.getName());
  443. String orderNo = idGenerator.generateId().toString();
  444. data.put("out_trade_no", orderNo); // 订单唯一编号, 不允许重复
  445. // 订单金额, 单位分
  446. String amout = p.getAmount().multiply(new BigDecimal(100)).stripTrailingZeros().toPlainString();
  447. Order o = new Order();
  448. o.setOrderNo(orderNo);
  449. o.setPayAmount(new BigDecimal(0));
  450. o.setPayStatus(0);
  451. o.setProjectAmount(p.getAmount());
  452. o.setProjectId(p.getId());
  453. o.setProjectName(p.getName());
  454. o.setStatus(0);
  455. o.setUid("");
  456. data.put("total_fee", amout);
  457. data.put("spbill_create_ip", ip); // 下单ip
  458. data.put("notify_url", notifyUrl); // 订单结果通知, 微信主动回调此接口
  459. data.put("trade_type", tradeType); // 固定填写
  460. Map<String, String> wxPayMap = new HashMap<String, String>();
  461. // 前端调起微信支付必要参数
  462. String uuid = UUID.randomUUID().toString().replace("-", "").toLowerCase();
  463. wxPayMap.put("appId", iWxPayConfig.getAppID());
  464. wxPayMap.put("timeStamp", String.valueOf(new Date().getTime()));
  465. wxPayMap.put("nonce_str", uuid);
  466. String sign = WXPayUtil.generateSignature(wxPayMap, iWxPayConfig.getKey());
  467. map.put("sign", sign);
  468. map.put("out_trade_no", orderNo);
  469. map = wxpay.unifiedOrder(data); // 微信sdk集成方法, 统一下单接口unifiedOrder, 此处请求 MD5加密
  470. LoggerUtils.debug(getClass(), "微信支付下单成功, 返回值 response=%s", map);
  471. orderService.insert(o);
  472. } catch (Exception e) {
  473. e.printStackTrace();
  474. throw new BusinessException("支付异常");
  475. }
  476. return map;
  477. }
  478. }