package com.kede.wxsdk.service; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.net.InetAddress; import java.nio.charset.Charset; import java.security.AlgorithmParameters; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.UUID; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.HttpServletRequest; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.commons.codec.binary.Base64; import org.apache.commons.collections4.map.HashedMap; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.ParseException; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.kede.common.dao.OrderMapper; import com.kede.common.dao.UserMapper; import com.kede.common.error.BusinessException; import com.kede.common.model.Order; import com.kede.common.model.Project; import com.kede.common.model.User; import com.kede.common.utils.LoggerUtils; import com.kede.common.utils.StringUtils; import com.kede.core.mybatis.JDBCIdGenerator; import com.kede.core.mybatis.page.Pagination; import com.kede.core.shiro.token.TokenManager; import com.kede.order.service.impl.OrderService; import com.kede.wxsdk.WXPayConstants; import com.kede.wxsdk.WXPayUtil; /** * 小程序 * @author Administrator * */ @Service public class WxService { @Value(value = "${wx.appId}") private String appId; @Value(value = "${wx.appSecret}") private String appSecret; @Value(value = "${wx.mchId}") private String mchId; @Value(value = "${wx.apiKiy}") private String apiKiy; @Value(value = "${wx.notifyUrl}") private String notifyUrl; @Value(value = "${wx.apiv3}") private String apiv3; @Value(value = "${wx.certPath}") private String certPath; @Autowired private JDBCIdGenerator idGenerator; @Autowired private OrderService orderService; @Autowired private UserMapper userMapper; @Autowired private OrderMapper orderMapper; /** * 小程序下单 * @param p * @return */ public Map pushUnifiedOrder(Project p){ WXPay wxpay = null; Map map=new HashMap<>(); try { IWxPayConfig iWxPayConfig=new IWxPayConfig(appId,mchId,apiKiy,certPath); wxpay = new WXPay(iWxPayConfig); // *** 注入自己实现的微信配置类, 创建WXPay核心类, WXPay 包括统一下单接口 InetAddress addr = InetAddress.getLocalHost(); String ip = addr.getHostAddress(); Map data = new HashMap(); data.put("body", p.getName()); String orderNo=idGenerator.generateId().toString(); data.put("out_trade_no", orderNo); // 订单唯一编号, 不允许重复 // 订单金额, 单位分 String amout=p.getAmount().multiply(new BigDecimal(100)).stripTrailingZeros().toPlainString(); String openId=(String) TokenManager.getVal2Session("openId"); // String openId="oGNT65WX5ysEnCUmAvaXOdIrMswA"; if (StringUtils.isBlank(openId)) { throw new BusinessException("无法获取正确的openId"); } Order o=new Order(); o.setOrderNo(orderNo); o.setPayAmount(new BigDecimal(0)); o.setPayStatus(0); o.setProjectAmount(p.getAmount()); o.setProjectId(p.getId()); o.setProjectName(p.getName()); o.setStatus(0); o.setUid(TokenManager.getUserId()); if (p.getAmount().compareTo(new BigDecimal("0.01"))>=0) { data.put("total_fee", amout); data.put("spbill_create_ip", ip); // 下单ip data.put("openid", openId); // 微信公众号统一标示openid data.put("notify_url", notifyUrl); // 订单结果通知, 微信主动回调此接口 data.put("trade_type", "JSAPI"); // 固定填写 Map response = wxpay.unifiedOrder(data); // 微信sdk集成方法, 统一下单接口unifiedOrder, 此处请求 MD5加密 加密方式 LoggerUtils.debug(getClass(),"微信支付下单成功, 返回值 response=%s", response); String prepayId = response.get("prepay_id"); if (prepayId == null) { throw new BusinessException("无法获取正确的prepayId"); } orderService.insert(o); // 前端调起微信支付必要参数 String packages = "prepay_id=" + prepayId; Map wxPayMap = new HashMap(); String uuid=UUID.randomUUID().toString().replace("-", "").toLowerCase(); wxPayMap.put("appId", iWxPayConfig.getAppID()); wxPayMap.put("timeStamp", String.valueOf(new Date().getTime())); wxPayMap.put("nonceStr", uuid); wxPayMap.put("package", packages); wxPayMap.put("signType", "MD5"); // 加密串中包括 appId timeStamp nonceStr package signType 5个参数, 通过sdk WXPayUtil类加密 String sign = WXPayUtil.generateSignature(wxPayMap, iWxPayConfig.getKey()); // 返回给前端调起微信支付的必要参数 map.put("prepay_id", prepayId); map.put("out_trade_no", orderNo); map.put("paySign", sign); map.putAll(wxPayMap); }else { LoggerUtils.debug(getClass(),"订单下单成功, 返回值 amount=%s", p.getAmount()); o.setPayStatus(1); orderService.insert(o); map.put("out_trade_no", orderNo); map.put("amount", "0"); } } catch (Exception e) { e.printStackTrace(); throw new BusinessException("支付异常"); } return map; } /** * 订单查询 * @param orderNo * @return */ public Map pushOrderQuery(String orderNo){ // 发起微信支付 WXPay wxpay = null; Map map=new HashMap<>(); try { IWxPayConfig iWxPayConfig=new IWxPayConfig(appId,mchId,apiKiy,certPath); wxpay = new WXPay(iWxPayConfig); // *** 注入自己实现的微信配置类, 创建WXPay核心类, WXPay 包括统一下单接口 Map data = new HashMap(); data.put("out_trade_no", orderNo); data.put("mchid", mchId); map = wxpay.orderQuery(data); LoggerUtils.debug(getClass(),"微信查询成功, 返回值 response={%s}", map); String total_fee=map.get("total_fee"); String transaction_id=map.get("transaction_id"); String out_trade_no=map.get("out_trade_no"); if (map.get("trade_state").equals("SUCCESS")) { Order os=orderMapper.selectByPrimaryKey(out_trade_no); if (os.getPayStatus()!=1) { insertOrder(total_fee, transaction_id, out_trade_no); } } }catch (Exception e) { e.printStackTrace(); throw new BusinessException("查询异常"); } return map; } private void insertOrder(String total_fee, String transaction_id, String out_trade_no) { Order o=new Order(); o.setOrderNo(out_trade_no); o.setPayStatus(1); o.setPayAmount(new BigDecimal(total_fee).divide(new BigDecimal(100))); o.setWxOrderNo(transaction_id); orderMapper.updateByPrimaryKeySelective(o); } /** * 微信撤销订单 * @param o. * @return */ public Map pushWxClose(Order o){ // 发起微信支付 WXPay wxpay = null; Map map=new HashMap<>(); try { IWxPayConfig iWxPayConfig=new IWxPayConfig(appId,mchId,apiKiy,certPath); wxpay = new WXPay(iWxPayConfig); // *** 注入自己实现的微信配置类, 创建WXPay核心类, WXPay 包括统一下单接口 Map data = new HashMap(); data.put("out_trade_no", o.getOrderNo()); data.put("mchid", mchId); map = wxpay.closeOrder(data); LoggerUtils.debug(getClass(),"微信撤销成功, 返回值 response={%s}", map); if (o.getPayStatus()!=1) { Order on=new Order(); on.setOrderNo(o.getOrderNo()); on.setPayStatus(2); orderMapper.updateByPrimaryKeySelective(on); } }catch (Exception e) { throw new BusinessException("撤销异常"); } return map; } public void wxPayCallBack(HttpServletRequest req) { BufferedReader reader; try { reader = req.getReader(); String line ; StringBuilder sb = new StringBuilder(); while((line = reader.readLine()) != null) { sb.append(line); } // 2、xml字符串 转换为 map对象 Map map = WXPayUtil.xmlToMap(sb.toString()); LoggerUtils.debug(getClass(), "获取参数%s",map.toString()); // 3、尝试从缓存中获取,判断是否已经接收过通知 if (map.get("result_code").equals(WXPayConstants.SUCCESS) && WXPayConstants.SUCCESS.equals(map.get("return_code"))) { } String out_trade_no=map.get("out_trade_no"); String transaction_id=map.get("transaction_id"); String result_code=map.get("result_code"); String total_fee=map.get("total_fee"); if (result_code.equals("SUCCESS")) { Order os=orderMapper.selectByPrimaryKey(out_trade_no); if (os.getPayStatus()!=1) { insertOrder(total_fee, transaction_id, out_trade_no); } } LoggerUtils.debug(getClass(),"\n\n\n\n\n>>>>>>>>>>>>>>>>>>>>>>>>微信回调结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n\n\n\n"); } catch ( Exception e) { e.printStackTrace(); throw new BusinessException("回调解析异常"); } } /** * XML格式字符串转换为Map * * @param strXML XML字符串 * @return XML数据转换后的Map * @throws Exception */ public Map xmlToMap(String strXML) throws Exception { Map data = new HashMap(); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8")); org.w3c.dom.Document doc = documentBuilder.parse(stream); doc.getDocumentElement().normalize(); NodeList nodeList = doc.getDocumentElement().getChildNodes(); for (int idx = 0; idx < nodeList.getLength(); ++idx) { Node node = nodeList.item(idx); if (node.getNodeType() == Node.ELEMENT_NODE) { org.w3c.dom.Element element = (org.w3c.dom.Element) node; data.put(element.getNodeName(), element.getTextContent()); } } try { stream.close(); } catch (Exception ex) { } return data; } /** * IO解析获取微信的数据 * * @param request * @return */ public String getXmlString(HttpServletRequest request) { BufferedReader reader = null; String line = ""; String xmlString = null; try { reader = request.getReader(); StringBuffer inputString = new StringBuffer(); while ((line = reader.readLine()) != null) { inputString.append(line); } xmlString = inputString.toString(); } catch (Exception e) { } return xmlString; } /** * 参数解密 * @param sessionKey * @param encryptedData * @param iv * @return * @throws Exception */ public JSONObject pushDecryptData(String sessionKey,String encryptedData, String iv) { byte[] dataByte = Base64.decodeBase64(encryptedData); // 加密秘钥 byte[] keyByte = Base64.decodeBase64(sessionKey); // 偏移量 byte[] ivByte = Base64.decodeBase64(iv); try { // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要 int base = 16; if (keyByte.length % base != 0) { int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0); byte[] temp = new byte[groups * base]; Arrays.fill(temp, (byte) 0); System.arraycopy(keyByte, 0, temp, 0, keyByte.length); keyByte = temp; } // 初始化 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); parameters.init(new IvParameterSpec(ivByte)); cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化 byte[] resultByte = cipher.doFinal(dataByte); if (null != resultByte && resultByte.length > 0) { String result = new String(resultByte, "UTF-8"); JSONObject js=JSONObject.parseObject(result); String mobile=(String) js.get("purePhoneNumber"); User u=new User(); u.setId(userMapper.selectByOpenId((String)TokenManager.getVal2Session("openId")).getId()); u.setMobile(mobile); userMapper.updateByPrimaryKeySelective(u); return js; } } catch (Exception e) { e.printStackTrace(); } return null; } public Object getliveinfo(Integer pageNo,Integer pageSize){ String url = String.format("https://api.weixin.qq.com/wxa/business/getliveinfo?access_token=%s", getAccessToken()); Mapmap=new HashedMap(); map.put("start", (pageNo - 1) * pageSize); map.put("limit", pageSize); map=this.getPostMethod(url, map); Pagination p=new Pagination(pageNo, pageSize, map.get("total"), map.get("room_info")); return p; } public String getAccessToken() { String access_token=(String) TokenManager.getVal2Session("access_token"); String expires_in= (String) TokenManager.getVal2Session("expires_in"); if (access_token!=null&&expires_in!=null) { expires_in=expires_in+7200; if (new Date().getTime()map){ HttpClient httpClient = HttpClientBuilder.create().build(); HttpPost httpPost = new HttpPost(url); httpPost.addHeader("Content-type", "application/json"); httpPost.setHeader("Accept", "application/json"); httpPost.setEntity(new StringEntity(JSON.toJSONString(map), Charset.forName("UTF-8"))); HttpResponse response = null; try { response = httpClient.execute(httpPost); if (null == response || response.getStatusLine() == null) { throw new Exception("Post Request For Url[{}] is not ok. Response is null"); } else if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { throw new Exception("Post Request For Url[{}] is not ok. Response Status Code is {"+response.getStatusLine().getStatusCode()+"}"); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } String resultString=null; try { resultString = EntityUtils.toString(response.getEntity()); } catch (ParseException | IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } JSONObject jsonObj = JSONObject.parseObject(resultString); return jsonObj; } public Map pushNativePay(Project p,String tradeType) { WXPay wxpay = null; Map map = new HashMap<>(); try { IWxPayConfig iWxPayConfig = new IWxPayConfig(appId, mchId, apiKiy, certPath); wxpay = new WXPay(iWxPayConfig); // *** 注入自己实现的微信配置类, 创建WXPay核心类, WXPay 包括统一下单接口 InetAddress addr = InetAddress.getLocalHost(); String ip = addr.getHostAddress(); Map data = new HashMap(); data.put("body", p.getName()); String orderNo = idGenerator.generateId().toString(); data.put("out_trade_no", orderNo); // 订单唯一编号, 不允许重复 // 订单金额, 单位分 String amout = p.getAmount().multiply(new BigDecimal(100)).stripTrailingZeros().toPlainString(); Order o = new Order(); o.setOrderNo(orderNo); o.setPayAmount(new BigDecimal(0)); o.setPayStatus(0); o.setProjectAmount(p.getAmount()); o.setProjectId(p.getId()); o.setProjectName(p.getName()); o.setStatus(0); o.setUid(""); data.put("total_fee", amout); data.put("spbill_create_ip", ip); // 下单ip data.put("notify_url", notifyUrl); // 订单结果通知, 微信主动回调此接口 data.put("trade_type", tradeType); // 固定填写 Map wxPayMap = new HashMap(); // 前端调起微信支付必要参数 String uuid = UUID.randomUUID().toString().replace("-", "").toLowerCase(); wxPayMap.put("appId", iWxPayConfig.getAppID()); wxPayMap.put("timeStamp", String.valueOf(new Date().getTime())); wxPayMap.put("nonce_str", uuid); String sign = WXPayUtil.generateSignature(wxPayMap, iWxPayConfig.getKey()); map.put("sign", sign); map.put("out_trade_no", orderNo); map = wxpay.unifiedOrder(data); // 微信sdk集成方法, 统一下单接口unifiedOrder, 此处请求 MD5加密 LoggerUtils.debug(getClass(), "微信支付下单成功, 返回值 response=%s", map); orderService.insert(o); } catch (Exception e) { e.printStackTrace(); throw new BusinessException("支付异常"); } return map; } }