Browse Source

20210917

tall3
zy_Java 4 years ago
parent
commit
2ffd119ef0
  1. 6
      wechatutil/pom.xml
  2. 19
      wechatutil/src/main/java/com/ccsens/wechatutil/api/WxController.java
  3. 2
      wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/WechatCode.java
  4. 2
      wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/MiniProgramUser.java
  5. 9
      wechatutil/src/main/java/com/ccsens/wechatutil/bean/po/WxOauth2UserInfo.java
  6. 14
      wechatutil/src/main/java/com/ccsens/wechatutil/exception/PayException.java
  7. 67
      wechatutil/src/main/java/com/ccsens/wechatutil/payutil/AesUtil.java
  8. 132
      wechatutil/src/main/java/com/ccsens/wechatutil/payutil/HttpsUtil.java
  9. 163
      wechatutil/src/main/java/com/ccsens/wechatutil/payutil/JacksonUtil.java
  10. 151
      wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxMessageUtil.java
  11. 322
      wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxPayDirectUtils.java
  12. 687
      wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxPayProviderUtils.java
  13. 64
      wechatutil/src/main/java/com/ccsens/wechatutil/payutil/WxTokenUtil.java
  14. 8
      wechatutil/src/main/java/com/ccsens/wechatutil/service/IWxMessageService.java
  15. 21
      wechatutil/src/main/java/com/ccsens/wechatutil/service/WxMessageService.java
  16. 4
      wechatutil/src/main/java/com/ccsens/wechatutil/service/WxService.java
  17. 11
      wechatutil/src/main/java/com/ccsens/wechatutil/util/WxConstant.java

6
wechatutil/pom.xml

@ -28,6 +28,12 @@
<version>1.0-SNAPSHOT</version>
</dependency>
<!--微信v3支付-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.2.2</version>
</dependency>
</dependencies>
<build>

19
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();
}
}

2
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;

2
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;
}

9
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<String> privilegeList;
@JsonProperty("unionid")
private String unionId;
@JsonProperty("language")
private String language;
}

14
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;
}
}

67
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);
}
}
}

132
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 请求方式GETPOST
* @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;
}
}

163
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> T jsonToBean(String json, Class<T> clazz, boolean isSet) throws IOException {
T t = null;
if (!isSet) {
t = getDefaultObjectMapper().readValue(json, clazz);
}
else {
t = getDefaultObjectMapper().readValue(json, new TypeReference<T>() {
});
}
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 <T> List<T> jsonToBean(String json, Class<T> clazz, Class<?> setClazz) throws IOException {
List<T> tList = null;
ObjectMapper mapper = getDefaultObjectMapper();
JavaType javaType = getCollectionType(mapper,setClazz,clazz);
tList = getDefaultObjectMapper().readValue(json, javaType);
return tList;
}
public static <T> String beanToJson(T bean) throws JsonProcessingException {
String json = null;
json = getDefaultObjectMapper().writeValueAsString(bean);
return json;
}
public static <T> 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<String,String> jsonToMap(String json) throws IOException {
Map<String,String> map = null;
ObjectMapper mapper = getDefaultObjectMapper();
map = getDefaultObjectMapper().readValue(json,Map.class);
return map;
}
public static String mapToJson(Map<String,String> map) throws JsonProcessingException {
String json = null;
json = getDefaultObjectMapper().writeValueAsString(map);
return json;
}
public static <T> T mapToBean(Map<String,String> map,Class<T> clazz) {
try {
return jsonToBean(mapToJson(map),clazz,false);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//------------------------------------ Xml and Bean ----------------------------0
public static <T> T xmlToBean(InputStream xmlInStream, Class<T> clazz) throws IOException {
T t = null;
t = getDefaultXmlMapper().readValue(xmlInStream, clazz);
return t;
}
public static <T> String beanToXml(T bean) throws JsonProcessingException {
String xmlString = null;
xmlString = getDefaultXmlMapper().writeValueAsString(bean);
return xmlString;
}
//这个方法可能有点问题,必须配合其他jar包才能使用
// <dependency>
// <groupId>org.codehaus.woodstox</groupId>
// <artifactId>woodstox-core-asl</artifactId>
// <version>4.4.1</version>
// </dependency>
// public static <T> 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 <T> 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<String,Object> xmlToMap(InputStream xmlInStream) throws IOException {
Map<String,Object> map = getDefaultXmlMapper().readValue(xmlInStream, Map.class);
return map;
}
public static String mapToXml(Map<String,String> map,String root) throws JsonProcessingException {
String xmlString = null;
xmlString = getDefaultXmlMapper().writer().with(SerializationFeature.WRAP_ROOT_VALUE)
.withRootName(root).writeValueAsString(map);
return xmlString;
}
}

151
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<T> {
private String touser;
private String template_id;
private String page;
private T data;
}
@Getter
@Setter
public static class ReceiveSubscriBuyData {
public ReceiveSubscriBuyData(List<String> 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<String> 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<String> params) throws Exception {
// String ret = null;
// String url = String.format(URL_SEND_SUBSCRIBE_MESSAGE, WxTokenUtil.getToken());
// switch (notifyType) {
// //订单支付成功通知
// case Buy: {
// SubscribeEntity<ReceiveSubscriBuyData> 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<ReceiveSubscriOrderSendData> 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");
// }
}

322
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+TIMEZONEYYYY-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("-", "");
}
}

687
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;
}
/**
* 解析支付完毕微信通知参数
* <p>
* 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<String, Object> payToUsrWx(
String partner_trade_no, String openid, int total_fee, String desc, String spbill_create_ip)
throws Exception {
//1.构造请求字符串
Map<String, Object> reqMap = new HashMap();
reqMap.put("mch_appid", SUBAPPID);
reqMap.put("mchid", SUBMCHID);
//reqMap.put("device_info",null); //不能添加空字段,Jackson会生成闭包xml <device_info/>导致签名失败
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<String, Object> 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<String, Object> sParaTemp) throws FileNotFoundException {
// 除去数组中的空值和签名参数
Map<String, String> 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<String, String> paraFilter(Map<String, Object> sArray) {
Map<String, String> 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<String, String> params) {
List<String> 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<String, String> paraFilterEmpty(Map<String, Object> sArray) {
Map<String, String> 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;
}
}

64
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失败");
}
}
}

8
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;
}

21
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 {

4
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;
}

11
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<String, String> APP_ID = new HashMap<>();
static {

Loading…
Cancel
Save