From 2ffd119ef031073b44e9f14ecf6f26631282641c Mon Sep 17 00:00:00 2001 From: zy_Java <654600784@qq.com> Date: Fri, 17 Sep 2021 09:57:33 +0800 Subject: [PATCH] 20210917 --- wechatutil/pom.xml | 6 + .../ccsens/wechatutil/api/WxController.java | 19 +- .../wechatutil/bean/dto/WechatCode.java | 2 +- .../wechatutil/bean/po/MiniProgramUser.java | 2 - .../wechatutil/bean/po/WxOauth2UserInfo.java | 9 - .../wechatutil/exception/PayException.java | 14 + .../ccsens/wechatutil/payutil/AesUtil.java | 67 ++ .../ccsens/wechatutil/payutil/HttpsUtil.java | 132 ++++ .../wechatutil/payutil/JacksonUtil.java | 163 +++++ .../wechatutil/payutil/WxMessageUtil.java | 151 ++++ .../wechatutil/payutil/WxPayDirectUtils.java | 322 ++++++++ .../payutil/WxPayProviderUtils.java | 687 ++++++++++++++++++ .../wechatutil/payutil/WxTokenUtil.java | 64 ++ .../wechatutil/service/IWxMessageService.java | 8 + .../wechatutil/service/WxMessageService.java | 21 +- .../ccsens/wechatutil/service/WxService.java | 4 +- .../ccsens/wechatutil/util/WxConstant.java | 11 + 17 files changed, 1653 insertions(+), 29 deletions(-) create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/exception/PayException.java create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/payutil/AesUtil.java create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/payutil/HttpsUtil.java create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/payutil/JacksonUtil.java create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxMessageUtil.java create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxPayDirectUtils.java create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxPayProviderUtils.java create mode 100644 wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxTokenUtil.java diff --git a/wechatutil/pom.xml b/wechatutil/pom.xml index cb1c2376..b8bebfb3 100644 --- a/wechatutil/pom.xml +++ b/wechatutil/pom.xml @@ -28,6 +28,12 @@ 1.0-SNAPSHOT + + + com.github.wechatpay-apiv3 + wechatpay-apache-httpclient + 0.2.2 + diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/api/WxController.java b/wechatutil/src/main/java/com/ccsens/wechatutil/api/WxController.java index 112163c0..c120ac4a 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/api/WxController.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/api/WxController.java @@ -1,6 +1,8 @@ package com.ccsens.wechatutil.api; import com.ccsens.util.JsonResponse; +import com.ccsens.wechatutil.bean.dto.WxMessageDto; +import com.ccsens.wechatutil.service.IWxMessageService; import com.ccsens.wechatutil.service.IWxService; import com.ccsens.wechatutil.bean.po.MiniProgramUser; import com.ccsens.wechatutil.bean.po.WxOauth2UserInfo; @@ -27,6 +29,8 @@ public class WxController { @Resource private IWxService miniProgramService; + @Resource + private IWxMessageService wxMessageService; @ApiOperation(value = "小程序根据code获取appId", notes = "小程序") @RequestMapping(value = "/signinByMini", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}) @@ -35,14 +39,14 @@ public class WxController { return JsonResponse.newInstance().ok(); } - @ApiOperation(value = "公众号/网页二维码登录时 根据code获取appId", notes = "公众号/网页二维码") + @ApiOperation(value = "公众号/网页二维码登录", notes = "公众号/网页二维码") @RequestMapping(value = "/signinByH5", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}) public JsonResponse signinByH5(@ApiParam @Validated String code) throws Exception{ WxOauth2UserInfo wxUser = miniProgramService.signinByH5(code); return JsonResponse.newInstance().ok(); } - @ApiOperation(value = "生成小程序码-方案A", notes = "公众号/网页二维码") + @ApiOperation(value = "生成小程序码-方案A", notes = "") @RequestMapping(value = "/getWxCodeA", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}) public JsonResponse getWxCodeA(@ApiParam @Validated WechatCode.WechatCodeA wechatCodeA, String path) throws Exception{ miniProgramService.getWxCodeA(wechatCodeA, path); @@ -50,7 +54,7 @@ public class WxController { } - @ApiOperation(value = "生成小程序码-方案B", notes = "公众号/网页二维码") + @ApiOperation(value = "生成小程序码-方案B", notes = "") @RequestMapping(value = "/getWxCodeB", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}) public JsonResponse getWxCodeB(@ApiParam @Validated WechatCode.WechatCodeB wechatCodeB,String path) throws Exception{ wechatCodeB = new WechatCode.WechatCodeB(); @@ -60,10 +64,17 @@ public class WxController { return JsonResponse.newInstance().ok(); } - @ApiOperation(value = "生成小程序码-方案B", notes = "公众号/网页二维码") + @ApiOperation(value = "生成小程序码-方案B", notes = "") @RequestMapping(value = "/getWxCodeC", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}) public JsonResponse getWxCodeC(@ApiParam @Validated WechatCode.WechatCodeC wechatCodeC,String path) throws Exception{ miniProgramService.getWxCodeC(wechatCodeC,path); return JsonResponse.newInstance().ok(); } + + @ApiOperation(value = "群机器人发送消息", notes = "") + @RequestMapping(value = "/robotMessage", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}) + public JsonResponse robotMessage(@ApiParam @Validated WxMessageDto.RobotMessage robotMessage) throws Exception{ + wxMessageService.sendRobotInfo(robotMessage); + return JsonResponse.newInstance().ok(); + } } diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/WechatCode.java b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/WechatCode.java index 5b969d43..37ce38ba 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/WechatCode.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/WechatCode.java @@ -31,7 +31,7 @@ public class WechatCode { public static class WechatCodeB{ //参数 --最大32个可见字符 public String scene; - //小程序主页地址 + //小程序地址 public String page; //二维码的宽度,单位 px,最小 280px,最大 1280px 默认430 public Integer width; diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/MiniProgramUser.java b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/MiniProgramUser.java index 71d6d969..cd3bd1ca 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/MiniProgramUser.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/MiniProgramUser.java @@ -11,6 +11,4 @@ public class MiniProgramUser extends WxBaseEntity { public String openid; public String session_key; public String unionid; - public Integer errcode; - public String errmsg; } diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/WxOauth2UserInfo.java b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/WxOauth2UserInfo.java index 50a13187..0677a8a1 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/WxOauth2UserInfo.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/WxOauth2UserInfo.java @@ -13,31 +13,22 @@ import java.util.List; public class WxOauth2UserInfo extends WxBaseEntity { @JsonProperty("openid") private String openId; - @JsonProperty("nickname") private String nickname; - @JsonProperty("sex") private int sex; - @JsonProperty("province") private String province; - @JsonProperty("city") private String city; - @JsonProperty("country") private String country; - @JsonProperty("headimgurl") private String headImgUrl; - @JsonProperty("privilege") private List privilegeList; - @JsonProperty("unionid") private String unionId; - @JsonProperty("language") private String language; } diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/exception/PayException.java b/wechatutil/src/main/java/com/ccsens/wechatutil/exception/PayException.java new file mode 100644 index 00000000..73434ff9 --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/exception/PayException.java @@ -0,0 +1,14 @@ +package com.ccsens.wechatutil.exception; + +public class PayException extends RuntimeException{ + private int code; + public PayException(int code,String message){ + super(message); + this.code = code; + } + + public PayException(String message){ + super(message); + this.code = -1; + } +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/AesUtil.java b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/AesUtil.java new file mode 100644 index 00000000..8e09e57b --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/AesUtil.java @@ -0,0 +1,67 @@ +package com.ccsens.wechatutil.payutil; + +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * @author :mr.zhangsan + * @date :Created in 2021/6/21 21:17 + * @version v1.0: + */ +@Slf4j +public class AesUtil { + + static final int KEY_LENGTH_BYTE = 32; + static final int TAG_LENGTH_BIT = 128; + private final byte[] aesKey; + + public AesUtil(byte[] key) { + if (key.length != KEY_LENGTH_BYTE) { + throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节"); + } + this.aesKey = key; + } + + /** + * 对加密的授权/解除授权结果进行解密 + * @param associatedData + * @param nonce + * @param ciphertext + * @return + * @throws GeneralSecurityException + * @throws IOException + */ + public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext) + throws GeneralSecurityException, IOException { + try { + log.info("---------1111"); + log.info("cipher" + Cipher.getInstance("AES/GCM/NoPadding")); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + + log.info("key" + new SecretKeySpec(aesKey, "AES")); + SecretKeySpec key = new SecretKeySpec(aesKey, "AES"); + log.info("spec" + new GCMParameterSpec(TAG_LENGTH_BIT, nonce)); + GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce); + log.info("------------1"); + cipher.init(Cipher.DECRYPT_MODE, key, spec); + log.info("------------2"); + cipher.updateAAD(associatedData); + log.info("------------3"); + return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8"); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new IllegalStateException(e); + } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { + throw new IllegalArgumentException(e); + } + } +} \ No newline at end of file diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/HttpsUtil.java b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/HttpsUtil.java new file mode 100644 index 00000000..0a4bffc0 --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/HttpsUtil.java @@ -0,0 +1,132 @@ +package com.ccsens.wechatutil.payutil; + +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContexts; +import org.apache.http.conn.ssl.TrustSelfSignedStrategy; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +import javax.net.ssl.*; +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.security.KeyStore; + +/** + * @author :mr.zhangsan + * @date :Created in 2021/6/21 23:21 + * @version v1.0: + */ +public class HttpsUtil { + + /** + * 发送https请求 使用PKCS12类型证书 + * + * @param requestUrl 请求地址 + * @param requestMethod 请求方式(GET、POST) + * @param postStr 提交的数据 + * @return String(Json) + */ + public static String httpsRequest(String requestUrl, String requestMethod, String postStr,String pKCS12Path,String pKCS12Pwd) throws Exception { + SSLContext sc = null; + FileInputStream instream = null; + KeyStore keyStore = null; + + keyStore = KeyStore.getInstance("PKCS12"); + instream = new FileInputStream(new File(pKCS12Path)); + keyStore.load(instream,pKCS12Pwd.toCharArray()); + instream.close(); + + SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial( + keyStore, pKCS12Pwd.toCharArray()).build(); + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( + sslcontext, + new String[] { "TLSv1" }, + null, + SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER + ); + CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf) .build(); + HttpPost httpost = new HttpPost(requestUrl); + httpost.addHeader("Connection", "keep-alive"); + httpost.addHeader("Accept", "*/*"); + httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); + httpost.addHeader("Host", "api.mch.weixin.qq.com"); + httpost.addHeader("X-Requested-With", "XMLHttpRequest"); + httpost.addHeader("Cache-Control", "max-age=0"); + httpost.addHeader("User-Agent", + "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); + httpost.setEntity(new StringEntity(postStr, "UTF-8")); + CloseableHttpResponse response = httpclient.execute(httpost); + HttpEntity entity = response.getEntity(); + String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); + EntityUtils.consume(entity); + + return jsonStr; + } + + /** + * 设置信任自签名证书 + * + * @param keyStorePath 密钥库路径 + * @param keyStorepass 密钥库密码 + * @return + */ + + public static SSLContext custom(String keyStorePath, String keyStorepass) { + SSLContext sc = null; + FileInputStream instream = null; + KeyStore trustStore = null; + try { + trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + instream = new FileInputStream(new File(keyStorePath)); + trustStore.load(instream, keyStorepass.toCharArray()); + // 相信自己的CA和所有自签名的证书 + sc = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build(); + // 构造 javax.net.ssl.TrustManager 对象 + TrustManagerFactory tmf = + TrustManagerFactory.getInstance("SunX509", "SunJSSE"); + tmf.init(trustStore); + TrustManager tms [] = tmf.getTrustManagers(); + // 使用构造好的 TrustManager 访问相应的 https 站点 + SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); + sslContext.init(null, tms, new java.security.SecureRandom()); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + instream.close(); + } catch (IOException e) { + } + } + return sc; + } + + public static String sendGet(String url, String charset, int timeout) { + String result = ""; + try { + URL u = new URL(url); + try { + URLConnection conn = u.openConnection(); + conn.connect(); + conn.setConnectTimeout(timeout); + BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), charset)); + String line = ""; + while ((line = in.readLine()) != null) { + result = result + line; + } + in.close(); + } catch (IOException e) { + return result; + } + } catch (MalformedURLException e) { + return result; + } + return result; + } +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/JacksonUtil.java b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/JacksonUtil.java new file mode 100644 index 00000000..362ce8a6 --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/JacksonUtil.java @@ -0,0 +1,163 @@ +package com.ccsens.wechatutil.payutil; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +/** + * @author :mr.zhangsan + * @date :Created in 2021/6/21 22:41 + * @version v1.0: + */ +public class JacksonUtil { + private static ObjectMapper getDefaultObjectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + return objectMapper; + } + + private static XmlMapper getDefaultXmlMapper() { + XmlMapper xmlMapper = new XmlMapper(); + xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + + return xmlMapper; + } + + public static String getJsonValue(String json,String key) throws IOException{ + JsonNode jsonNode = getDefaultObjectMapper().readTree(json); + return jsonNode.get(key).toString(); + } + + public static T jsonToBean(String json, Class clazz, boolean isSet) throws IOException { + T t = null; + if (!isSet) { + t = getDefaultObjectMapper().readValue(json, clazz); + } + else { + t = getDefaultObjectMapper().readValue(json, new TypeReference() { + }); + } + return t; + } + + + /** + * 获取泛型的Collection Type + * @param collectionClass 泛型的Collection + * @param elementClasses 元素类 + * @return JavaType Java类型 + * @since 1.0 + */ + public static JavaType getCollectionType(ObjectMapper mapper, Class collectionClass, Class... elementClasses) { + return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); + } + + //------------------------------------ Json and Bean ----------------------------0 + public static List jsonToBean(String json, Class clazz, Class setClazz) throws IOException { + List tList = null; + ObjectMapper mapper = getDefaultObjectMapper(); + JavaType javaType = getCollectionType(mapper,setClazz,clazz); + tList = getDefaultObjectMapper().readValue(json, javaType); + return tList; + } + + public static String beanToJson(T bean) throws JsonProcessingException { + String json = null; + json = getDefaultObjectMapper().writeValueAsString(bean); + return json; + } + + public static String beanToJson(T bean, boolean pretty) throws JsonProcessingException { + String json = null; + if (!pretty) { + json = getDefaultObjectMapper().writeValueAsString(bean); + } else { + json = getDefaultObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(bean); + } + return json; + } + + //------------------------------------ Json and Map ----------------------------0 + public static Map jsonToMap(String json) throws IOException { + Map map = null; + ObjectMapper mapper = getDefaultObjectMapper(); + map = getDefaultObjectMapper().readValue(json,Map.class); + return map; + } + + public static String mapToJson(Map map) throws JsonProcessingException { + String json = null; + json = getDefaultObjectMapper().writeValueAsString(map); + return json; + } + + public static T mapToBean(Map map,Class clazz) { + + try { + return jsonToBean(mapToJson(map),clazz,false); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + //------------------------------------ Xml and Bean ----------------------------0 + public static T xmlToBean(InputStream xmlInStream, Class clazz) throws IOException { + T t = null; + t = getDefaultXmlMapper().readValue(xmlInStream, clazz); + return t; + } + + public static String beanToXml(T bean) throws JsonProcessingException { + String xmlString = null; + xmlString = getDefaultXmlMapper().writeValueAsString(bean); + return xmlString; + } + + //这个方法可能有点问题,必须配合其他jar包才能使用 +// +// org.codehaus.woodstox +// woodstox-core-asl +// 4.4.1 +// +// public static String beanToXml(T bean, boolean pretty) throws JsonProcessingException { +// +// String xmlString = null; +// if (!pretty) +// xmlString = getDefaultXmlMapper().writeValueAsString(bean); +// else +// xmlString = getDefaultXmlMapper().writerWithDefaultPrettyPrinter().writeValueAsString(bean); +// return xmlString; +// } + + public static String beanToXml(T bean,String root) throws JsonProcessingException { + String xmlString = null; + xmlString = getDefaultXmlMapper().writer().with(SerializationFeature.WRAP_ROOT_VALUE) + .withRootName(root).writeValueAsString(bean); + return xmlString; + } + + //------------------------------------ Xml and Map ---------------------------- + public static Map xmlToMap(InputStream xmlInStream) throws IOException { + Map map = getDefaultXmlMapper().readValue(xmlInStream, Map.class); + return map; + } + + public static String mapToXml(Map map,String root) throws JsonProcessingException { + String xmlString = null; + xmlString = getDefaultXmlMapper().writer().with(SerializationFeature.WRAP_ROOT_VALUE) + .withRootName(root).writeValueAsString(map); + return xmlString; + } +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxMessageUtil.java b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxMessageUtil.java new file mode 100644 index 00000000..58b42281 --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxMessageUtil.java @@ -0,0 +1,151 @@ +package com.ccsens.wechatutil.payutil; + +import cn.hutool.core.collection.CollectionUtil; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + + +/** + * @author :mr.zhangsan + * @date :Created in 2021/4/15 21:11 + * @version v1.0: + */ +@Slf4j +public class WxMessageUtil { + /** + * 微信发送订阅消息 + */ + private static final String URL_SEND_SUBSCRIBE_MESSAGE + = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=%1$s"; + + private static String SUBSCRIBE_MSG_ID_BUY = "bqdx4FW51G-JLU6HjL6LyvUbjpo4rFK6CPor2-w3VwM"; + private static String SUBSCRIBE_MSG_ID_ORDERSEND = "5-rhM8h3x1W82tHCt18g9iXyGELyUKQtBDZiFjgItOo"; + + public enum Packet_Notify_Type { + Buy(1, "购买"), + ReceiveSubscriBuyData(2, "订单发货"); + + public int value; + public String phase; + + Packet_Notify_Type(int value, String thePhase) { + this.value = value; + this.phase = thePhase; + } + } + + @Getter + @Setter + private static class CommonEntity { + private String value; + } + + @Getter + @Setter + public static class SubscribeEntity { + private String touser; + private String template_id; + private String page; + private T data; + } + + @Getter + @Setter + public static class ReceiveSubscriBuyData { + public ReceiveSubscriBuyData(List params) { + if (CollectionUtil.isNotEmpty(params) && params.size() >= 2) { + character_string1 = new CommonEntity(); + amount3 = new CommonEntity(); + thing4 = new CommonEntity(); + time2 = new CommonEntity(); + number11 = new CommonEntity(); + character_string1.setValue(params.get(0)); + amount3.setValue(params.get(1)); + thing4.setValue(params.get(2)); + time2.setValue(params.get(3)); + number11.setValue(params.get(4)); + } + } + + private CommonEntity character_string1;//订单编号 + private CommonEntity amount3;//订单金额 + private CommonEntity thing4;//商品名称 + private CommonEntity time2;//支付时间 + private CommonEntity number11;//购买数量 + } + + @Getter + @Setter + public static class ReceiveSubscriOrderSendData { + public ReceiveSubscriOrderSendData(List params) { + if (CollectionUtil.isNotEmpty(params) && params.size() >= 2) { + thing1 = new CommonEntity(); + character_string2 = new CommonEntity(); + date3 = new CommonEntity(); + thing4 = new CommonEntity(); + character_string5 = new CommonEntity(); + thing1.setValue(params.get(0)); + character_string2.setValue(params.get(1)); + date3.setValue(params.get(2)); + thing4.setValue(params.get(3)); + character_string5.setValue(params.get(4)); + } + } + + private CommonEntity thing1;//商品名称 + private CommonEntity character_string2;//订单号 + private CommonEntity date3;//发货时间 + private CommonEntity thing4;//快递公司 + private CommonEntity character_string5;//快递单号 + } + + /** + * 发送微信订阅消息 + * + * @param notifyType 通知类型 + * @param toUser 用户openid + * @param page 小程序页面路径 + * @param params 小程序模板数据 + * @return + * @throws Exception +// */ +// public static boolean sendSubcribeMsg(Packet_Notify_Type notifyType, String toUser, String page, List params) throws Exception { +// String ret = null; +// String url = String.format(URL_SEND_SUBSCRIBE_MESSAGE, WxTokenUtil.getToken()); +// switch (notifyType) { +// //订单支付成功通知 +// case Buy: { +// SubscribeEntity t = new SubscribeEntity<>(); +// t.setTouser(toUser); +// t.setTemplate_id(SUBSCRIBE_MSG_ID_BUY); +// t.setPage(page); +// t.setData(new ReceiveSubscriBuyData(params)); +// System.out.println(JacksonUtil.beanToJson(t)); +// ret = HttpsUtil.httpsRequest(url, "POST", pers.wei.wx.pay.util.JacksonUtil.beanToJson(t)); +// break; +// } +// //订单发货 +// case ReceiveSubscriBuyData: { +// SubscribeEntity t = new SubscribeEntity<>(); +// t.setTouser(toUser); +// t.setTemplate_id(SUBSCRIBE_MSG_ID_ORDERSEND); +// t.setData(new ReceiveSubscriOrderSendData(params)); +// System.out.println(JacksonUtil.beanToJson(t)); +// ret = httpsRequest(url, "POST", JacksonUtil.beanToJson(t)); +// break; +// } +// default: { +// break; +// } +// } +// //ok: {"errcode":0,"errmsg":"ok"} +// //err: {"errcode":43101,"errmsg":"user refuse to accept the msg hint: [V_GEdA02503942]"} +// System.out.println("Send Subscribe Message:" + ret); +// String errCode = pers.wei.wx.pay.util.JacksonUtil.getJsonValue(ret, "errcode"); +// System.out.println(errCode); +// return errCode.equals("0"); +// } +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxPayDirectUtils.java b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxPayDirectUtils.java new file mode 100644 index 00000000..c4039a1a --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxPayDirectUtils.java @@ -0,0 +1,322 @@ +package com.ccsens.wechatutil.payutil; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.ccsens.wechatutil.exception.PayException; +import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; +import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier; +import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner; +import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials; +import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator; +import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.StringEntity; +import org.apache.http.util.EntityUtils; + +import javax.servlet.http.HttpServletRequest; +import java.io.*; +import java.math.BigInteger; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.security.Signature; +import java.util.Base64; +import java.util.UUID; + +/** + * 微信直连商户支付 + * @author :mr.zhangsan + * @date :Created in 2021/6/18 23:58 + * @version v1.0: + */ +@Slf4j +public class WxPayDirectUtils { + + //商户支付URL + private static final String URL_TRANSACTIONS + = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; + //测试小程序号appid + public static final String TK_APPID = "wxa26bb3e3f1d2d998"; + //测试商户号 + public static final String TK_MCHID = "1603930405"; + //测试小程序秘钥secret + public static final String TK_SECRET = "162cc6eb70bc341bc67fea210796be21"; + //商户私钥 + private static PrivateKey privateKey; + //版本>=0.1.5可使用 AutoUpdateCertificatesVerifier 类替代默认的验签器。 + // 它会在构造时自动下载商户对应的微信支付平台证书,并每隔一段时间(默认为1个小时)更新证书。 + private static AutoUpdateCertificatesVerifier verifier = null; + //WechatPayHttpClient + private static HttpClient httpClient = null; + //测试商户证书存放路径 + private static final String Client_Key_Path = "C:\\home\\fxxt\\cert_tk\\apiclient_key.pem"; + //测试商户v3key + private static final String DIRECT_V3_Key = "5IDIy173oyjSGPZHuP0lc2V3UqFBO55M"; + //测试证书序列号 + private static final String DIRECT_CERT_SERIAL_NO = "368344414C13EA2ABF3A59A8CF3F31E99ACE0058"; + + + /** + * 支付订单状态 + */ + public enum PayOrderStatus { + SUCCESS, NOTPAY, CLOSED, REVOKED, USERPAYING, PAYERROR, FAILED, UNKNOWN + } + + + /** + * 商户统一下单支付 + * + * 应用ID appid string[1,32] 是 body 由微信生成的应用ID,全局唯一。请求基础下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的APPID + * 示例值:wxd678efh567hg6787 + * 直连商户号 mchid string[1,32] 是 body 直连商户的商户号,由微信支付生成并下发。 + * 示例值:1230000109 + * 商品描述 description string[1,127] 是 body 商品描述 + * 示例值:Image形象店-深圳腾大-QQ公仔 + * 商户订单号 out_trade_no string[6,32] 是 body 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一 + * 示例值:1217752501201407033233368018 + * 交易结束时间 time_expire string[1,64] 否 body 订单失效时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。 + * 示例值:2018-06-08T10:34:56+08:00 + * 附加数据 attach string[1,128] 否 body 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用 + * 示例值:自定义数据 + * 通知地址 notify_url string[1,256] 是 + */ + public static JSONObject transactions(String openid, String outTradeNo, String description, String notifyUrl, int totalFee) throws Exception { + HttpPost httpPost = new HttpPost(URL_TRANSACTIONS); + httpPost.addHeader("Accept", "application/json"); + httpPost.addHeader("Content-type", "application/json; charset=utf-8"); + + JSONObject params = new JSONObject(); + JSONObject amount = new JSONObject(); + JSONObject payer = new JSONObject(); + payer.put("openid", openid); + + amount.put("total", totalFee); + amount.put("currency", "CNY"); + + params.put("appid", TK_APPID); + params.put("mchid", TK_MCHID); + params.put("out_trade_no", outTradeNo); + params.put("description", description); + params.put("notify_url", notifyUrl); + params.put("amount", amount); + params.put("payer", payer); + + httpPost.setEntity(new StringEntity(params.toJSONString(), "UTF-8")); + CloseableHttpResponse response = (CloseableHttpResponse) getWechatPayHttpClient().execute(httpPost); + String bodyAsString = EntityUtils.toString(response.getEntity()); + JSONObject respJson = JSONObject.parseObject(bodyAsString); + + Object prepayId = respJson.get("prepay_id"); + Object code = respJson.get("code"); + + if (code != null) { + throw new Exception(respJson.get("message").toString()); + } + JSONObject result = new JSONObject(); + //根据返回的prepay_id生成发起支付需要的参数签名返回前端 + + result.put("appId", TK_APPID); + result.put("timeStamp", System.currentTimeMillis() / 1000); + result.put("nonceStr", createNonceStr()); + result.put("package", "prepay_id=" + prepayId); + result.put("signType", "RSA"); + + String message = TK_APPID + "\n" + + result.get("timeStamp") + "\n" + + result.get("nonceStr") + "\n" + + result.get("package") + "\n"; + String sign = wxPayReqSign(message.getBytes("utf-8")); + result.put("paySign", sign); + return result; + } + + /** + * 解析支付完毕微信通知参数 + * + * signature 头部签名 Wechatpay-Signature + * body json消息 + * @return + * @throws IOException 参数名 变量 类型[长度限制] 必填 描述 + */ + public static JSONObject rechargeResult(HttpServletRequest request) throws Exception { + log.info("解析支付完毕微信通知参数"); + //一、验证签名 + //1.1获取平台证书 + getVerifier(); + //1.2检查平台证书序列号 + String wepaySerial = request.getHeader("Wechatpay-Serial"); + if (wepaySerial == null) { + throw new PayException(-9, "平台证书序列号为空"); + } + log.info("获取平台证书序列号"); + //获取平台证书序列号 + BigInteger currentSerial = getVerifier().getValidCertificate().getSerialNumber(); + if (StrUtil.isEmpty(wepaySerial) || !wepaySerial.equalsIgnoreCase(currentSerial.toString(16))) { + throw new PayException(-9, "证书序列号不一致"); + } + log.info("3构造验签名串"); + //1.3构造验签名串 + String wepayNonce = request.getHeader("Wechatpay-Nonce"); + String wepayTimestamp = request.getHeader("Wechatpay-Timestamp"); + String wepayBody = null; + BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream())); + String line = null; + StringBuilder body = new StringBuilder(); + while ((line = br.readLine()) != null) { + body.append(line); + } + if (StrUtil.isEmpty(body.toString())) { + throw new PayException(-9, "body is null"); + } + wepayBody = body.toString(); + + + String message = wepayTimestamp + "\n" + + wepayNonce + "\n" + + body.toString() + "\n"; + log.info("获取应答签名"); + //1.4获取应答签名 + String wepySignature = request.getHeader("Wechatpay-Signature"); + + //1.5验证签名 + boolean rr = getVerifier().verify(wepaySerial, + message.getBytes(StandardCharsets.UTF_8),wepySignature); + if (!rr){ + //TODO 签名验证失败抛异常 + } +// String sign = wxPayRespSign(message.getBytes("utf-8")); +// if (!wepySignature.equals(sign)) { +// throw new PayException(-9, "验证签名不一致"); +// } + log.info("解密数据"); + //二、解密数据 + JSONObject jsonObject = JSONObject.parseObject(wepayBody); + JSONObject resource = (JSONObject) jsonObject.get("resource"); + AesUtil aesUtil = new AesUtil(DIRECT_V3_Key.getBytes("utf-8")); + //解密 + String decryptToString = aesUtil.decryptToString( + resource.get("associated_data").toString().getBytes(), + resource.get("nonce").toString().getBytes(), + resource.get("ciphertext").toString()); + JSONObject parseObject = JSONObject.parseObject(decryptToString); + return parseObject; + } + + private static PayOrderStatus getPayOrderStatus(URIBuilder uriBuilder) throws URISyntaxException, IOException { + HttpGet httpGet = new HttpGet(uriBuilder.build()); + httpGet.addHeader("Accept", "application/json"); + + CloseableHttpResponse response = (CloseableHttpResponse) getWechatPayHttpClient().execute(httpGet); + + String bodyAsString = EntityUtils.toString(response.getEntity()); + + JSONObject jsonObject = JSONObject.parseObject(bodyAsString); + log.info("json:{}", jsonObject); + Object trade_state = jsonObject.get("trade_state"); + if (trade_state != null) { + String tradeState = trade_state.toString(); + log.info("PayOrderStatus:{}",tradeState); + if ("SUCCESS".equals(tradeState)) { + return PayOrderStatus.SUCCESS; + } else if ("USERPAYING".equals(tradeState)) { + return PayOrderStatus.USERPAYING; + } else { + return PayOrderStatus.FAILED; + } + } + return PayOrderStatus.UNKNOWN; + } + + /** + * 商户订单号查询 + * + * @param transactionId 微信订单号 + * @return + * @throws IOException + * @throws URISyntaxException + */ + public static PayOrderStatus wechatOrder(String transactionId) throws IOException, URISyntaxException { + URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + transactionId + "?mchid=" + TK_MCHID); + return getPayOrderStatus(uriBuilder); + } + + /** + * 根据商户证书,获取私钥 + * + * @return + * @throws FileNotFoundException + */ + public static PrivateKey getPrivateKey() throws FileNotFoundException { + if (privateKey == null) { + privateKey = PemUtil.loadPrivateKey( + new FileInputStream(Client_Key_Path)); + } + return privateKey; + } + + /** + * 依据商户证书私钥进行签名 + * + * @param message + * @return + * @throws Exception + */ + public static String wxPayReqSign(byte[] message) throws Exception { + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initSign(getPrivateKey()); + sign.update(message); + + return Base64.getEncoder().encodeToString(sign.sign()); + } + + /** + * 根据微信平台证书获取httpclient + * + * @return + * @throws FileNotFoundException + * @throws UnsupportedEncodingException + */ + public static HttpClient getWechatPayHttpClient() throws FileNotFoundException, UnsupportedEncodingException { + if (httpClient == null) { + //构造WechatPayHttpClientBuilder + WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() + .withMerchant(TK_MCHID, DIRECT_CERT_SERIAL_NO, getPrivateKey()) + .withValidator(new WechatPay2Validator(getVerifier())); + + // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新 + httpClient = builder.build(); + } + return httpClient; + } + + /** + * 自动获取微信平台证书 + * + * @throws UnsupportedEncodingException + */ + public static AutoUpdateCertificatesVerifier getVerifier() throws FileNotFoundException { + //不需要传入微信支付证书了 + if (verifier == null) { + verifier = new AutoUpdateCertificatesVerifier( + new WechatPay2Credentials(TK_MCHID, new PrivateKeySigner(DIRECT_CERT_SERIAL_NO, getPrivateKey())), + DIRECT_V3_Key.getBytes(StandardCharsets.UTF_8)); + } + return verifier; + } + + + /** + * 生成随机字符串 + * + * @return + */ + public static String createNonceStr() { + return UUID.randomUUID().toString().replace("-", ""); + } +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxPayProviderUtils.java b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxPayProviderUtils.java new file mode 100644 index 00000000..908f21f5 --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxPayProviderUtils.java @@ -0,0 +1,687 @@ +package com.ccsens.wechatutil.payutil; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.ccsens.util.HttpsUtil; +import com.ccsens.wechatutil.exception.PayException; +import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; +import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier; +import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner; +import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials; +import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator; +import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.StringEntity; +import org.apache.http.util.EntityUtils; +import javax.servlet.http.HttpServletRequest; +import java.io.*; +import java.math.BigInteger; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.security.Signature; +import java.util.*; + +/** + * 微信服务商支付 + * @author :mr.zhangsan + * @date :Created in 2021/5/15 22:24 + * @version v1.0: + */ +@Slf4j +public class WxPayProviderUtils { + + /** + * 支付订单状态 + */ + public enum PayOrderStatus { + SUCCESS, NOTPAY, CLOSED, REVOKED, USERPAYING, PAYERROR, FAILED, UNKNOWN + } + + private static final String URL_MERCHANT_PAY + = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi"; + //退款url + private static final String URL_REFUND_PAY + = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; + //查询退款url + private static final String SEL_URL_REFUND_PAY + = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; + //付款到零钱url + private static final String URL_PAYTO_USER + = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; + //商户支付URL + private static final String URL_TRANSACTIONS + = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; + //测试小程序APPID + private static final String appid = "wxf30402e727f0840a"; + //测试小程序secret + private static final String secret = "70402290ac83857c7324b0ee881b544a"; + //商户支付 + //服务商API秘钥key + private static final String KEY = "5a689a2d6b8c4ff499c23d998fde0941"; + // 服务商应用ID(公众号appid) + private static final String SPAPPID = "wx65d45856040e9a4a"; + // 服务商户号 + private static final String SPMECHID = "1610985721"; + //子商户应用ID + private static final String SUBAPPID = appid; + // 子商户号 + private static final String SUBMCHID = "1611259929"; + //服务商证书序列号 + private static final String CERT_SERIAL_NO = "1251E728C5B765190B641C9B45B2A18A4DDEC553"; + //微信提现url + private static final String URL_PAY_TO_USR_WX + = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; + //服务商证书存放路径 + private static final String Client_Key_Path = "C:\\home\\fxxt\\cert_fws\\apiclient_key.pem"; + //服务商APIv3Key + private static final String API_V3_Key = "gRcJ8bpi5imIm4qbbRch5iSSD77DFCKY"; + //测试商户证书存放路径 + public static final String PATH_WX_CRET = "C:\\home\\fxxt\\cert_zyc\\apiclient_key.pem"; + //测试商户私钥 + private static PrivateKey privateKey; + //版本>=0.1.5可使用 AutoUpdateCertificatesVerifier 类替代默认的验签器。 + // 它会在构造时自动下载商户对应的微信支付平台证书,并每隔一段时间(默认为1个小时)更新证书。 + private static AutoUpdateCertificatesVerifier verifier = null; + //WechatPayHttpClient + private static HttpClient httpClient = null; + //加密方式 + private static final String schema = "WECHATPAY2-SHA256-RSA2048"; + + /** + * 私有构造 不允许生成实例, 只允许调用静态方法 + */ + private WxPayProviderUtils() { + + } + + /** + * 生成随机字符串 + * + * @return + */ + public static String createNonceStr() { + return UUID.randomUUID().toString().replace("-", ""); + } + + + /** + * 依据商户证书私钥进行签名 + * + * @param message + * @return + * @throws Exception + */ + private static String wxPayReqSign(byte[] message) throws Exception { + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initSign(getPrivateKey()); + sign.update(message); + + return Base64.getEncoder().encodeToString(sign.sign()); + } + + /** + * 依据微信支付平台证书(公钥)进行签名 + * + * @param message + * @return + * @throws Exception + */ + private static boolean wxPayRespSign(byte[] message,String signature) throws Exception { + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initVerify(getVerifier().getValidCertificate()); + sign.update(message); + return sign.verify(Base64.getDecoder().decode(signature)); + } + + /** + * 根据商户证书,获取私钥 + * + * @return + * @throws FileNotFoundException + */ + public static PrivateKey getPrivateKey() throws FileNotFoundException { + if (privateKey == null) { + privateKey = PemUtil.loadPrivateKey( + new FileInputStream(Client_Key_Path)); + } + return privateKey; + } + + /** + * 自动获取微信平台证书 + * + * @throws UnsupportedEncodingException + */ + public static AutoUpdateCertificatesVerifier getVerifier() throws FileNotFoundException { + //不需要传入微信支付证书了 + if (verifier == null) { + verifier = new AutoUpdateCertificatesVerifier( + new WechatPay2Credentials(SPMECHID, new PrivateKeySigner(CERT_SERIAL_NO, getPrivateKey())), + API_V3_Key.getBytes(StandardCharsets.UTF_8)); + } + return verifier; + } + + /** + * 根据微信平台证书获取httpclient + * + * @return + * @throws FileNotFoundException + * @throws UnsupportedEncodingException + */ + public static HttpClient getWechatPayHttpClient() throws FileNotFoundException, UnsupportedEncodingException { + if (httpClient == null) { + //构造WechatPayHttpClientBuilder + WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() + .withMerchant(SPMECHID, CERT_SERIAL_NO, getPrivateKey()) + .withValidator(new WechatPay2Validator(getVerifier())); + + // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新 + httpClient = builder.build(); + } + return httpClient; + } + + /** + * 服务商统一下单支付 + */ + public static JSONObject merchantPreparyPay(String openid, String outTradeNo, String description, String notifyUrl, int totalFee) throws Exception { + HttpPost httpPost = new HttpPost(URL_MERCHANT_PAY); + httpPost.addHeader("Accept", "application/json"); + httpPost.addHeader("Content-type", "application/json; charset=utf-8"); + + JSONObject params = new JSONObject(); + JSONObject amount = new JSONObject(); + JSONObject payer = new JSONObject(); + payer.put("sub_openid", openid); + + amount.put("total", totalFee); + amount.put("currency", "CNY"); + + params.put("sp_mchid", SPMECHID); + params.put("sub_mchid", SUBMCHID); + log.info("out_trade_no:{}", outTradeNo); + params.put("out_trade_no", outTradeNo); + params.put("sp_appid", SPAPPID); + params.put("sub_appid", SUBAPPID); + params.put("description", description); + params.put("notify_url", notifyUrl); + params.put("amount", amount); + params.put("payer", payer); + + httpPost.setEntity(new StringEntity(params.toJSONString(), "UTF-8")); + CloseableHttpResponse response = (CloseableHttpResponse) getWechatPayHttpClient().execute(httpPost); + String bodyAsString = EntityUtils.toString(response.getEntity()); + JSONObject respJson = JSONObject.parseObject(bodyAsString); + Object prepayId = respJson.get("prepay_id"); + Object code = respJson.get("code"); + + if (code != null) { + throw new Exception(respJson.get("message").toString()); + } + JSONObject result = new JSONObject(); + //根据返回的prepay_id生成发起支付需要的参数签名返回前端 + + result.put("appId", SUBAPPID); + result.put("timeStamp", System.currentTimeMillis() / 1000); + result.put("nonceStr", createNonceStr()); + result.put("package", "prepay_id=" + prepayId); + result.put("signType", "RSA"); + + log.info("准备签名"); + String message = SUBAPPID + "\n" + + result.get("timeStamp") + "\n" + + result.get("nonceStr") + "\n" + + result.get("package") + "\n"; + String sign = wxPayReqSign(message.getBytes(StandardCharsets.UTF_8)); + log.info("sign:{}", sign); + result.put("paySign", sign); + return result; + } + + /** + * 解析支付完毕微信通知参数 + *

+ * signature 头部签名 Wechatpay-Signature + * body json消息 + * + * @return + * @throws IOException 参数名 变量 类型[长度限制] 必填 描述 + */ + public JSONObject rechargeResult(HttpServletRequest request) throws Exception { + //一、验证签名 + //1.1获取平台证书 + getVerifier(); + //1.2检查平台证书序列号 + String wepaySerial = request.getHeader("Wechatpay-Serial"); + if (wepaySerial == null) { + throw new PayException(-9, "平台证书序列号为空"); + } + //获取平台证书序列号 + BigInteger currentSerial = getVerifier().getValidCertificate().getSerialNumber(); + log.info("currentSerial:{} " + currentSerial.toString(16)); + log.info("wepaySerial:{} " + wepaySerial); + if (StrUtil.isEmpty(wepaySerial) || !wepaySerial.equalsIgnoreCase(currentSerial.toString(16))) { + throw new PayException(-9, "证书序列号不一致"); + } + //1.3构造验签名串 + String wepayNonce = request.getHeader("Wechatpay-Nonce"); + String wepayTimestamp = request.getHeader("Wechatpay-Timestamp"); + String wepayBody = null; + BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)); + String line = null; + StringBuilder body = new StringBuilder(); + while ((line = br.readLine()) != null) { + body.append(line); + } + if (StrUtil.isEmpty(body.toString())) { + throw new PayException(-9, "body is null"); + } + wepayBody = body.toString(); + + String message = wepayTimestamp + "\n" + + wepayNonce + "\n" + + body.toString() + "\n"; + //1.4获取应答签名 + String wepySignature = request.getHeader("Wechatpay-Signature"); + log.info("----------------1,{}\n----{}", wepySignature, message); + + + //1.5验证签名 + boolean rr = getVerifier().verify(wepaySerial, + message.getBytes(StandardCharsets.UTF_8), wepySignature); + +// boolean rr = wxPayRespSign(message.getBytes("utf-8"), wepySignature); + if (!rr) { + //TODO 签名验证失败抛异常 + throw new PayException(-9, "验证签名不一致"); + } + log.info("+++++++++++zzc 签名调用+++++++++++++++++++++ "); + //二、解密数据 + JSONObject jsonObject = JSONObject.parseObject(wepayBody); + JSONObject resource = (JSONObject) jsonObject.get("resource"); + AesUtil aesUtil = new AesUtil(API_V3_Key.getBytes("utf-8")); + //解密 + String decryptToString = aesUtil.decryptToString( + resource.get("associated_data").toString().getBytes(), + resource.get("nonce").toString().getBytes(), + resource.get("ciphertext").toString()); + JSONObject parseObject = JSONObject.parseObject(decryptToString); + return parseObject; + } + + private static PayOrderStatus getPayOrderStatus(URIBuilder uriBuilder) throws URISyntaxException, IOException { + HttpGet httpGet = new HttpGet(uriBuilder.build()); + httpGet.addHeader("Accept", "application/json"); + + CloseableHttpResponse response = (CloseableHttpResponse) getWechatPayHttpClient().execute(httpGet); + + String bodyAsString = EntityUtils.toString(response.getEntity()); + + JSONObject jsonObject = JSONObject.parseObject(bodyAsString); + log.info("json:{}", jsonObject); + Object trade_state = jsonObject.get("trade_state"); + if (trade_state != null) { + String tradeState = trade_state.toString(); + if ("SUCCESS".equals(tradeState)) { + return PayOrderStatus.SUCCESS; + } else if ("USERPAYING".equals(tradeState)) { + return PayOrderStatus.USERPAYING; + } else { + return PayOrderStatus.FAILED; + } + } + return PayOrderStatus.UNKNOWN; + } + + /** + * 服务商查询订单 + * + * @param orderNo + * @throws Exception + */ + public static PayOrderStatus queryOrder(String orderNo) throws IOException, URISyntaxException { + URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/id/" + orderNo + "?sp_mchid=" + SPMECHID + "&sub_mchid=" + SUBMCHID); + return getPayOrderStatus(uriBuilder); + } + + /** + * 商户订单号查询 + * + * @param transactionId 微信订单号 + * @return + * @throws IOException + * @throws URISyntaxException + */ + public static PayOrderStatus wechatOrder(String transactionId) throws IOException, URISyntaxException { + URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/" + transactionId + "?sp_mchid=" + SPMECHID + "&sub_mchid=" + SUBMCHID); + return getPayOrderStatus(uriBuilder); + } + + + /** + * 服务商关闭订单 + * + * @param orderNo 商户订单号 + */ + public static void closeOrder(String orderNo) throws IOException { + HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/" + orderNo + "/close"); + httpPost.addHeader("Accept", "application/json"); + httpPost.addHeader("Content-type", "application/json; charset=utf-8"); + + JSONObject params = new JSONObject(); + params.put("sp_mchid", SPMECHID); + params.put("sub_mchid", SUBMCHID); + + httpPost.setEntity(new StringEntity(params.toJSONString(), "UTF-8")); + getWechatPayHttpClient().execute(httpPost); + //接口响应204,无内容 + } + + + /** + * 服务商退款 + * + * @param refund 退款金额 + * @param outTradeNo 商户订单号 + * @param outRefundNo 商户退款单号 + * @param notifyUrl 退款回调url + * @param total 原订单金额 + * @return + * @throws Exception + */ + public static String refunds(int refund, String outTradeNo, String outRefundNo, String notifyUrl, int total) throws Exception { + HttpPost httpPost = new HttpPost(URL_REFUND_PAY); + httpPost.addHeader("Accept", "application/json"); + httpPost.addHeader("Content-type", "application/json; charset=utf-8"); + + JSONObject params = new JSONObject(); + JSONObject amount = new JSONObject(); + + //金额信息 + amount.put("refund", refund); + //原订单金额 + amount.put("total", total); + //退款币种 + amount.put("currency", "CNY"); + //子商户号 否 + params.put("sub_mchid", SUBMCHID); +//// 微信支付订单号 (二选一) +// params.put("transaction_id", SUBMCHID); + //商户订单号 (二选一) + params.put("out_trade_no", outTradeNo); + //商户退款单号 + log.info("商户退款单号----------------------------------------out_trade_no:{}", outRefundNo); + params.put("out_refund_no", outRefundNo); + //退款结果回调url 否 + params.put("notify_url", notifyUrl); + //金额信息 + params.put("amount", amount); + + httpPost.setEntity(new StringEntity(params.toJSONString(), "UTF-8")); + CloseableHttpResponse response = (CloseableHttpResponse) getWechatPayHttpClient().execute(httpPost); + String bodyAsString = EntityUtils.toString(response.getEntity()); + JSONObject respJson = JSONObject.parseObject(bodyAsString); + + log.info("respJson:{}", respJson); + Object result = respJson.get("status"); + String status = result.toString(); + if ("SUCCESS".equals(status) || "PROCESSING".equals(status)) { + return status; + } else { + throw new PayException("[" + respJson.get("err_code") + "]" + + respJson.get("err_code_des")); + } + } + + /** + * 查询单笔退款 + */ + public static JSONObject selRechargeResult(String out_refund_no) throws Exception { + HttpGet httpGet = new HttpGet(SEL_URL_REFUND_PAY + "/" + out_refund_no + "?sub_mchid=" + SUBMCHID); + httpGet.addHeader("Accept", "application/json"); + httpGet.addHeader("Content-type", "application/json; charset=utf-8"); + httpGet.addHeader("Wechatpay-Serial", getVerifier().getValidCertificate().getSerialNumber().toString(16)); + + CloseableHttpResponse response = (CloseableHttpResponse) getWechatPayHttpClient().execute(httpGet); + String bodyAsString = EntityUtils.toString(response.getEntity()); + return JSONObject.parseObject(bodyAsString); + } + + /** + * 微信退款结果通知 + */ + public static JSONObject tuiKuanResult(HttpServletRequest request) throws Exception { + //一、验证签名 + //1.1获取平台证书 + getVerifier(); + //1.2检查平台证书序列号 + String wepaySerial = request.getHeader("Wechatpay-Serial"); + if (wepaySerial == null) { + throw new PayException(-9, "平台证书序列号为空"); + } + //获取平台证书序列号 + BigInteger currentSerial = getVerifier().getValidCertificate().getSerialNumber(); + if (StrUtil.isEmpty(wepaySerial) || !wepaySerial.equalsIgnoreCase(currentSerial.toString(16))) { + throw new PayException(-9, "证书序列号不一致"); + } + String wepayNonce = request.getHeader("Wechatpay-Nonce"); + String wepayTimestamp = request.getHeader("Wechatpay-Timestamp"); + String wepayBody = null; + BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream(),StandardCharsets.UTF_8)); + String line = null; + StringBuilder body = new StringBuilder(); + while ((line = br.readLine()) != null) { + body.append(line); + } + if (StrUtil.isEmpty(body.toString())) { + throw new PayException(-9, "body is null"); + } + wepayBody = body.toString(); + + String message = wepayTimestamp + "\n" + + wepayNonce + "\n" + + body.toString() + "\n"; + //1.4获取应答签名 + String wepySignature = request.getHeader("Wechatpay-Signature"); + + //1.5验证签名 + boolean rr = getVerifier().verify(wepaySerial, + message.getBytes(StandardCharsets.UTF_8),wepySignature); + if (!rr){ + } + JSONObject jsonObject = JSONObject.parseObject(wepayBody); + JSONObject resource = (JSONObject) jsonObject.get("resource"); + AesUtil aesUtil = new AesUtil(API_V3_Key.getBytes("utf-8")); + //解密 + String decryptToString = aesUtil.decryptToString( + resource.get("associated_data").toString().getBytes(),//附加数据 + resource.get("nonce").toString().getBytes(),//随机串 + resource.get("ciphertext").toString());//数据密文 + JSONObject parseObject = JSONObject.parseObject(decryptToString); + return parseObject; + } + + + /** + * 企业付款到零钱 + * + * @param partner_trade_no 商户订单号 + * @param openid 用户openid + * @param total_fee 金额 + * @param desc 付款备注 + * @param spbill_create_ip Ip地址 + * @return + * @throws Exception + */ + public static Map payToUsrWx( + String partner_trade_no, String openid, int total_fee, String desc, String spbill_create_ip) + throws Exception { + //1.构造请求字符串 + Map reqMap = new HashMap(); + reqMap.put("mch_appid", SUBAPPID); + reqMap.put("mchid", SUBMCHID); + //reqMap.put("device_info",null); //不能添加空字段,Jackson会生成闭包xml 导致签名失败 + reqMap.put("nonce_str", createNonceStr()); + reqMap.put("partner_trade_no", partner_trade_no); + reqMap.put("openid", openid); + reqMap.put("check_name", "NO_CHECK"); + //reqMap.put("re_user_name",null); + reqMap.put("amount", total_fee + ""); + reqMap.put("desc", desc); + reqMap.put("spbill_create_ip", spbill_create_ip); + reqMap.put("sign", createSign(reqMap)); + + //2.发送付款请求 + String reqrXml = JacksonUtil.mapToXml(paraFilterEmpty(reqMap), "xml"); + String respXml = HttpsUtil.httpsRequest(URL_PAY_TO_USR_WX, "POST", reqrXml, + PATH_WX_CRET, SUBMCHID); + System.out.println("---------------PayToUsrWx Request Xml-----------------"); + System.out.println(reqrXml); + System.out.println("---------------PayToUsrWx Response Xml-----------------"); + System.out.println(respXml); + System.out.println("---------------PayToUsrWx end-----------------"); + + //3.判断成功失败 + Map respMap = JacksonUtil.xmlToMap(new ByteArrayInputStream(respXml.getBytes(StandardCharsets.UTF_8))); + String return_code, result_code; + return_code = String.valueOf(respMap.get("return_code")); + result_code = String.valueOf(respMap.get("result_code")); + + if (!StrUtil.isEmpty(return_code) && !StrUtil.isEmpty(result_code)) { + if (return_code.equals("SUCCESS") && return_code.equals(result_code)) { + return respMap; + } else { + throw new PayException("[" + respMap.get("err_code") + "]" + + respMap.get("err_code_des")); + } + } else { + throw new PayException(String.valueOf(respMap.get("return_msg"))); + } + //return null; + } + + /** + * Create Sign + * + * @param sParaTemp + * @return + */ + private static String createSign(Map sParaTemp) throws FileNotFoundException { + // 除去数组中的空值和签名参数 + Map sPara = paraFilter(sParaTemp); + String prestr = createLinkString(sPara); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串 + //MD5运算生成签名 + String sign = sign(prestr, getPrivateKey().toString(), "utf-8").toUpperCase(); +// return sign; + return sign; + } + + /** + * 签名字符串 + * + * @param text 需要签名的字符串 + * @param key 密钥 + * @param input_charset 编码格式 + * @return 签名结果 + */ + private static String sign(String text, String key, String input_charset) { + text = text + "&key=" + key; + return DigestUtils.md5Hex(getContentBytes(text, input_charset)); + } + + /** + * @param content + * @param charset + * @return + * @throws + * @throws UnsupportedEncodingException + */ + private static byte[] getContentBytes(String content, String charset) { + if (charset == null || "".equals(charset)) { + return content.getBytes(); + } + try { + return content.getBytes(charset); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset); + } + } + + /** + * 除去数组中的空值和签名参数 + * + * @param sArray 签名参数组 + * @return 去掉空值与签名参数后的新签名参数组 + */ + private static Map paraFilter(Map sArray) { + Map result = new HashMap(); + if (sArray == null || sArray.size() <= 0) { + return result; + } + for (String key : sArray.keySet()) { + Object objVal = sArray.get(key); + if (objVal == null) { + continue; + } + String value = String.valueOf(objVal); + if (StrUtil.isEmpty(value) || key.equalsIgnoreCase("sign") || key.equalsIgnoreCase("sign_type")) { + continue; + } + result.put(key, value); + } + return result; + } + + /** + * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串 + * + * @param params 需要排序并参与字符拼接的参数组 + * @return 拼接后字符串 + */ + private static String createLinkString(Map params) { + List keys = new ArrayList(params.keySet()); + Collections.sort(keys); + String prestr = ""; + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + String value = params.get(key); + if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符 + prestr = prestr + key + "=" + value; + } else { + prestr = prestr + key + "=" + value + "&"; + } + } + return prestr; + } + + + /** + * 除去数组中的空值和签名参数 + * + * @param sArray 签名参数组 + * @return 去掉空值与签名参数后的新签名参数组 + */ + private static Map paraFilterEmpty(Map sArray) { + Map result = new HashMap(); + if (sArray == null || sArray.size() <= 0) { + return result; + } + for (String key : sArray.keySet()) { + Object objVal = sArray.get(key); + if (objVal == null) { + continue; + } + String value = String.valueOf(objVal); + result.put(key, value); + } + return result; + } +} \ No newline at end of file diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxTokenUtil.java b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxTokenUtil.java new file mode 100644 index 00000000..bd77ea6a --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxTokenUtil.java @@ -0,0 +1,64 @@ +package com.ccsens.wechatutil.payutil; + +import com.alibaba.fastjson.JSONObject; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.util.EntityUtils; + +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * @author :mr.zhangsan + * @date :Created in 2021/4/15 21:11 + * @version v1.0: + */ + +public class WxTokenUtil { + + //测试APPID + private static final String appid = "wxf30402e727f0840a"; + //测试secret + private static final String secret = "70402290ac83857c7324b0ee881b544a"; + // 测试商户号 + private static final String SUBMCHID = "1611259929"; + + /** + * 获取小程序全局唯一后台接口调用凭据(access_token) + */ + private static final String URL_GETTOKEN_PAY + = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret; + + public static String getToken() throws Exception{ + + HttpGet httpGet = new HttpGet(URL_GETTOKEN_PAY); + httpGet.addHeader("Accept", "application/json"); + httpGet.addHeader("Content-type", "application/json; charset=utf-8"); + try { + httpGet.addHeader("Wechatpay-Serial", WxPayProviderUtils.getVerifier().getValidCertificate().getSerialNumber().toString(16)); + }catch (FileNotFoundException e) { + e.printStackTrace(); + throw new FileNotFoundException("获取微信平台证书失败"); + } + String bodyAsString = ""; + try { + CloseableHttpResponse response = (CloseableHttpResponse) WxPayProviderUtils.getWechatPayHttpClient().execute(httpGet); + try { + bodyAsString = EntityUtils.toString(response.getEntity()); + }catch (IOException e) { + e.printStackTrace(); + throw new FileNotFoundException("bodyAsString转换错误"); + } + }catch (IOException e) { + e.printStackTrace(); + throw new FileNotFoundException("根据微信平台证书获取httpclient失败"); + } + JSONObject respJson = JSONObject.parseObject(bodyAsString); + Object access_token = respJson.get("access_token"); + if (access_token != null) { + return access_token.toString(); + }else { + throw new Exception("获取WxToken失败"); + } + } +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/service/IWxMessageService.java b/wechatutil/src/main/java/com/ccsens/wechatutil/service/IWxMessageService.java index 3dc1a9f4..84d5c5ab 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/service/IWxMessageService.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/service/IWxMessageService.java @@ -1,4 +1,12 @@ package com.ccsens.wechatutil.service; +import com.ccsens.wechatutil.bean.dto.WxMessageDto; + public interface IWxMessageService { + /** + * 群机器人发送消息 + * @param robotMessage + * @throws Exception + */ + void sendRobotInfo(WxMessageDto.RobotMessage robotMessage)throws Exception; } diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/service/WxMessageService.java b/wechatutil/src/main/java/com/ccsens/wechatutil/service/WxMessageService.java index de5ee80c..4c2c36bd 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/service/WxMessageService.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/service/WxMessageService.java @@ -3,8 +3,8 @@ package com.ccsens.wechatutil.service; import cn.hutool.core.collection.CollectionUtil; import com.ccsens.util.JacksonUtil; import com.ccsens.wechatutil.bean.dto.WxMessageDto; +import com.ccsens.wechatutil.util.WxConstant; import lombok.extern.slf4j.Slf4j; -import org.json.JSONObject; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -12,7 +12,11 @@ import org.springframework.transaction.annotation.Transactional; import java.io.*; import java.net.URL; import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +/** + * @author 逗 + */ @Slf4j @Service @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) @@ -23,10 +27,11 @@ public class WxMessageService implements IWxMessageService { * @param robotMessage 机器人消息 * @throws Exception 异常 */ + @Override public void sendRobotInfo(WxMessageDto.RobotMessage robotMessage)throws Exception { WxMessageDto.WxRobotVo wxRobotVo = new WxMessageDto.WxRobotVo(); wxRobotVo.setMsgtype(robotMessage.getMsgType()); - if("text".equalsIgnoreCase(robotMessage.getMsgType())){ + if(WxConstant.TEXT.equalsIgnoreCase(robotMessage.getMsgType())){ WxMessageDto.WxRobotText wxRobotText = new WxMessageDto.WxRobotText(); wxRobotText.setContent(robotMessage.getContent()); if(CollectionUtil.isNotEmpty(robotMessage.getMentionedList())){ @@ -36,7 +41,7 @@ public class WxMessageService implements IWxMessageService { wxRobotText.setMentioned_mobile_list(robotMessage.getMentionedMobileList()); } wxRobotVo.setText(wxRobotText); - }else if("markdown".equalsIgnoreCase(robotMessage.getMsgType())){ + }else if(WxConstant.MARKDOWN.equalsIgnoreCase(robotMessage.getMsgType())){ WxMessageDto.WxRobotMarkdown wxRobotMarkdown = new WxMessageDto.WxRobotMarkdown(); wxRobotMarkdown.setContent(robotMessage.getContent()); wxRobotVo.setMarkdown(wxRobotMarkdown); @@ -51,8 +56,6 @@ public class WxMessageService implements IWxMessageService { public static void sendPost(String url, String param){ PrintWriter out = null; BufferedReader in = null; - JSONObject jsonObject = null; - String result = ""; try { URL realUrl = new URL(url); // 打开和URL之间的连接 @@ -61,18 +64,14 @@ public class WxMessageService implements IWxMessageService { conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流(设置请求编码为UTF-8) - out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), "UTF-8")); + out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8)); // 发送请求参数 out.print(param); // flush输出流的缓冲 out.flush(); // 获取请求返回数据(设置返回数据编码为UTF-8) in = new BufferedReader( - new InputStreamReader(conn.getInputStream(), "UTF-8")); - String line; - while ((line = in.readLine()) != null) { - result += line; - } + new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); } catch (IOException e) { e.printStackTrace(); } finally { diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/service/WxService.java b/wechatutil/src/main/java/com/ccsens/wechatutil/service/WxService.java index 68985fe2..1b8d8c9e 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/service/WxService.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/service/WxService.java @@ -61,8 +61,8 @@ public class WxService implements IWxService { } catch (IOException e) { throw new BaseException(e.getMessage()); } - if (null != wxUser.errcode) { - throw new BaseException(wxUser.errcode, wxUser.errmsg); + if (null != wxUser.getErrcode()) { + throw new BaseException(wxUser.getErrcode(), wxUser.getErrmsg()); } return wxUser; } diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/util/WxConstant.java b/wechatutil/src/main/java/com/ccsens/wechatutil/util/WxConstant.java index 600c7942..5c66b719 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/util/WxConstant.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/util/WxConstant.java @@ -11,6 +11,7 @@ public class WxConstant { /*** 默认小程序 */ public static final String ANYRING = "anyring"; + /*** 小程序登录路径 */ public static final String MINI_PROGRAM_LOGIN = "https://api.weixin.qq.com/sns/jscode2session?appid=%1$s&secret=%2$s&js_code=%3$s&grant_type=%4$s"; /*** 公众号获取accessToken */ @@ -32,6 +33,16 @@ public class WxConstant { /*** redis内存储AccessToken的Key */ public static final String ACCESS_TOKEN = "tall_wx_access_token_"; + /*** 机器人消息类型--文本 */ + public static final String TEXT = "text"; + /*** 机器人消息类型--markdown */ + public static final String MARKDOWN = "markdown"; + + + + + + /*** 小程序appId */ public static final Map APP_ID = new HashMap<>(); static {