订阅通知
JSONObject body=new JSONObject();
body.set("touser","用户openId");
body.set("template_id","订阅模板id");
JSONObject json1=new JSONObject();
json1.set("appid","小程序appid");
json1.set("pagepath","index?foo=bar");
body.set("miniprogram",json1);
JSONObject json=new JSONObject();
//参数thing2
json.set("thing2",new JSONObject().set("value", "标题"));
//参数thing4
json.set("thing4",new JSONObject().set("value","内容"));
body.set("data",json);
String accessToken = "小程序accessToken";
String post = cn.hutool.http.HttpUtil.post("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken, body.toString());
获取用户openid
//1、向微信服务器 使用登录凭证 code 获取 session_key 和 openid
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + "小程序appid" +
"&secret=" + "小程序secret" +
"&js_code=" + "小程序获取到的用户code" +
"&grant_type=authorization_code";
System.out.println(url);
RestTemplate restTemplate = restTemplateConfig.restTemplateRemote();
ResponseEntity<String> res = restTemplate.getForEntity(url, String.class);
JSONObject jsonObject = JSONObject.parseObject(res.getBody());
RestTemplateConfig类
```java
package com.yhyx.mallxdfzx.config;
import com.google.common.collect.Lists;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.List;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static org.apache.commons.collections.CollectionUtils.isEmpty;
/**
* @Description: RestTemplate配置类
* @ClassName RestTemplateConfig
*/
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean("restTemplateRemote")
public RestTemplate restTemplateRemote() {
return new RestTemplate();
}
/***************** 添加连接池属性支持的RestTemplate ******************/
private static final Logger LOGGER = LoggerFactory.getLogger(RestTemplateConfig.class);
/**
* 每个主机路由最大连接数,默认50个
*/
@Value("${httpclient.defaultMaxPerRoute:50}")
private int defaultMaxPerRoute;
/**
* 全局最大连接数,默认100个
*/
@Value("${httpclient.maxTotal:100}")
private int maxTotal;
/**
* 每个连接最大数量,默认10个
*/
@Value("${httpclient.maxConnTotal:10}")
private int maxConnTotal;
/**
* 每个路由最大连接,默认10个
*/
@Value("${httpclient.maxConnPerRoute:10}")
private int maxConnPerRoute;
/**
* 连接请求超时时间,默认60秒
*/
@Value("${httpclient.connectionRequestTimeout:60000}")
private int connectionRequestTimeout;
/**
* 连接超时时间,默认10秒
*/
@Value("${httpclient.connectionTimeout:10000}")
private int connectionTimeout;
/**
* SOCKET超时时间,默认10秒
*/
@Value("${httpclient.socketTimeout:10000}")
private int socketTimeout;
/**
* 当需要对参数进行编码的时候,使用的编码格式,默认为UTF-8
*/
@Value("${httpclient.uriEncode:UTF-8}")
private String uriEncode;
/**
* {@code HTTP}重试请求次数限制
*/
@Value("${httpclient.retryCount:3}")
private Integer retryCount;
/**
* {@code HTTP}读取响应超时时间
*/
@Value("${httpclient.readTimeout:60000}")
private Integer readTimeout;
/**
* 创建收{@code HTTP}连接池管理的{@code Spring RestTemplate}实例
*
* @param clientHttpRequestFactory
* @return
*/
@Bean("PoolingRestTemplate")
public RestTemplate poolingRestTemplate(
@Qualifier("PoolingClientHttpRequestFactory") ClientHttpRequestFactory clientHttpRequestFactory
) {
return createRestTemplate(clientHttpRequestFactory);
}
/**
* 创建一个{@code RestTemplate}实例,分别给内部和外部区分是否需要负载均衡的{@code RestTemplate Bean}使用
*
* @param clientHttpRequestFactory
* @return
*/
private RestTemplate createRestTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
RestTemplate poolingRestTemplate = new RestTemplate(clientHttpRequestFactory);
// 设置消息转换器,避免乱码
List<HttpMessageConverter<?>> messageConverterList = poolingRestTemplate.getMessageConverters();
if (!isEmpty(messageConverterList)) {
messageConverterList.stream().filter(StringHttpMessageConverter.class::isInstance)
.forEach(converter -> {
((StringHttpMessageConverter) converter).setDefaultCharset(Charset.forName(uriEncode));
((StringHttpMessageConverter) converter).setWriteAcceptCharset(true);
((StringHttpMessageConverter) converter).setSupportedMediaTypes(Lists.newArrayList(MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.APPLICATION_JSON));
});
}
return poolingRestTemplate;
}
/**
* 创建{@code ClientHttpRequestFactory}
*
* @param httpClient
* @return
*/
@Bean("PoolingClientHttpRequestFactory")
public ClientHttpRequestFactory clientHttpRequestFactory(@Qualifier("PoolingHttpClient") HttpClient httpClient) {
HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
httpComponentsClientHttpRequestFactory.setReadTimeout(readTimeout);
return httpComponentsClientHttpRequestFactory;
}
/**
* 创建{@code HttpClient}
*
* @return
*/
@Bean("PoolingHttpClient")
public HttpClient httpClient() {
// 创建注册器
Registry<ConnectionSocketFactory> protocolRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("HTTP", PlainConnectionSocketFactory.getSocketFactory())
.register("HTTPS", SSLConnectionSocketFactory.getSocketFactory())
.build();
// 创建HTTP连接池管理器
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(protocolRegistry);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
poolingHttpClientConnectionManager.setMaxTotal(maxTotal);
// 创建请求配置
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(connectionRequestTimeout)
.setConnectTimeout(connectionTimeout)
.setSocketTimeout(socketTimeout)
.build();
// 创建ClosableHttpClient对象
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
return httpClientBuilder
.setMaxConnTotal(maxConnTotal)
.setMaxConnPerRoute(maxConnPerRoute)
.setDefaultRequestConfig(requestConfig)
.setRetryHandler(httpRequestRetryHandler())
.build();
}
/**
* 重试处理器
*
* @return
*/
@Bean("HttpRequestRetryHandler")
public HttpRequestRetryHandler httpRequestRetryHandler() {
LOGGER.info("创建HttpRequest连接重试管理器");
return this::handleRetryException;
}
private Boolean handleRetryException(IOException e, int i, HttpContext httpContext) {
if (i > retryCount) {
// 重试超过3次,放弃请求
LOGGER.error("retry has more than 3 time, give up request");
return FALSE;
}
if (e instanceof NoHttpResponseException) {
// 服务器没有响应,可能是服务器断开了连接,应该重试
LOGGER.error("receive no response from server, retry");
return TRUE;
}
if (e instanceof SSLHandshakeException) {
// SSL握手异常
LOGGER.error("SSL hand shake exception");
return FALSE;
}
if (e instanceof InterruptedIOException) {
// 超时
LOGGER.error("InterruptedIOException");
return FALSE;
}
if (e instanceof UnknownHostException) {
// 服务器不可达
LOGGER.error("server host unknown");
return FALSE;
}
if (e instanceof SSLException) {
LOGGER.error("SSLException");
return FALSE;
}
HttpClientContext context = HttpClientContext.adapt(httpContext);
HttpRequest request = context.getRequest();
if (!(request instanceof HttpEntityEnclosingRequest)) {
// 如果请求不是关闭连接的请求
return TRUE;
}
return FALSE;
}
}
获取用户手机号
http://t.csdn.cn/jFJY3
使用到的两个工具类(gradle导入)
```java
implementation group: 'com.alibaba', name: 'fastjson', version: '1.2.68'
implementation group: 'cn.hutool', name: 'hutool-all', version: '5.4.0'
微信支付
public String payInquiryOrder(PayVo prescriptionPayVo) {
try {
String selfkey =sysConfig.getPrivatekey().trim();//商户v2秘钥
String resultStr = null;
String str = null;
String errorString = null;
String errorCode = null;
String resXML = null;
String resXml = null;
String nonce_str = WXPayUtil.generateNonceStr();
JSONArray json = null;
Map map = null;
SortedMap<Object, Object> orderMap = new TreeMap<Object, Object>();
/*----- 1.生成预支付订单需要的的package数据-----*/
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
packageParams.put("appid","公众号appid");
packageParams.put("mch_id", "商户号");
packageParams.put("nonce_str", nonce_str);
packageParams.put("notify_url", sysConfig.getIqpaysuccesscallback());
packageParams.put("attach", prescriptionPayVo.getAttach());
packageParams.put("nonce_str", nonce_str);
packageParams.put("openid", prescriptionPayVo.getOpenid());
packageParams.put("out_trade_no", prescriptionPayVo.getOut_trade_no());
packageParams.put("spbill_create_ip", WXPayUtil.localip());
packageParams.put("total_fee",com.cn.ih.java.main.utils.Utils.getFee(prescriptionPayVo.getTotal()));
packageParams.put("trade_type", "JSAPI");
packageParams.put("fee_type", "CNY");
packageParams.put("body", prescriptionPayVo.getDescription());
packageParams.put("sign_type", "MD5");
/*----2.根据package生成签名sign---- */
String sign = PayForUtil.createSign("UTF-8", packageParams, selfkey);
packageParams.put("sign", sign);
logger.info("###signWX" + sign);
String requestXML = PayForUtil.getRequestXml(packageParams);
logger.info("###requestXML" + requestXML);
resXml = WxHttpUtil.postData("https://api.mch.weixin.qq.com/pay/unifiedorder",requestXML);
logger.info("###resXml" + resXml);
if (null == resXml || "".equals(resXml)) {
errorString = "接口异常!返回数据为空,请检查接口是否可用;接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder" ;
resXml = " " + "fail"
+ "" + errorString + " ";
logger.info("###" + errorString);
orderMap.put("ResultCode", "-1");
orderMap.put("ErrorMsg", "执行失败。");
json = JSONArray.fromObject(orderMap);
} else {
try {
map = XMLUtil.doXMLParse(resXml);
String return_code = (String) map.get("return_code");
System.out.println(return_code);
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonceStr = String.valueOf(System.currentTimeMillis());
orderMap.put("appId", sysConfig.getWxappid());
// 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
orderMap.put("timeStamp", timestamp);
orderMap.put("nonceStr", nonceStr);
logger.info("###nonceStr" + nonceStr);
orderMap.put("package", "prepay_id=" + map.get("prepay_id"));
orderMap.put("signType", "MD5");
String sign1 = PayForUtil.createSign("UTF-8", orderMap, selfkey);
orderMap.put("paySign", sign1);
orderMap.put("resCode", return_code);
logger.info("###sign" + map.get("sign"));
json = JSONArray.fromObject(orderMap);
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
logger.info("###调用JSAPI预支付下单接口需要返回的参数" + json.toString());
return json.toString();
}catch(Exception e) {
e.printStackTrace();
return super.errorResult(e.getMessage());
}
}
PayVo类
/**
* @author qingshi
* @date 2022/9/5 15:51
* info:
*/
public class PayVo {
private String description;//商品描述
private String out_trade_no;//商户订单号
private String time_expire;//
private String attach;//附加数据
private String notify_url;//回调
private String openid;//用户标识
private String total;//总金额
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public String getTime_expire() {
return time_expire;
}
public void setTime_expire(String time_expire) {
this.time_expire = time_expire;
}
public String getAttach() {
return attach;
}
public void setAttach(String attach) {
this.attach = attach;
}
public String getNotify_url() {
return notify_url;
}
public void setNotify_url(String notify_url) {
this.notify_url = notify_url;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getTotal() {
return total;
}
public void setTotal(String total) {
this.total = total;
}
}
getFee方法
public static String getFee(String fee) {
System.out.println(Float.valueOf(fee).floatValue());
// System.out.println( String.valueOf(+ Float.parseFloat(fee)) );
Float a =Float.valueOf(fee).floatValue()*100;
System.out.println(a);
fee = String.valueOf(a);//浮点变量a转换为字符串str
// System.out.println(str);
//先把小数点后的0截取掉
int idx = fee.lastIndexOf(".");//查找小数点的位置
System.out.println(idx);
String strNum = fee.substring(0,idx);//截取从字符串开始到小数点位置的字符串,就是整数部分
System.out.println(strNum);
//将截取后的金额转换为整数
int num = Integer.valueOf(strNum);//把整数部分通过Integer.valueof方法转换为数字
//工行金额以分为单位,将金额* 100
// num = num * 100;
System.out.println(num);
return num+"";
}
WXPayUtil类
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.*;
import java.security.MessageDigest;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.cn.ih.java.main.utils.WXPayConstants.SignType;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WXPayUtil {
/**
* XML????????????Map
*@author Lyp
* @param strXML XML?????
* @return XML?????????Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(),element.getTagName());
}
}
try {
stream.close();
} catch (Exception ex) {
// do nothing
}
return data;
} catch (Exception ex) {
WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
throw ex;
}
}
/**
* ??ap?????ML?????????
*
* @param data Map??????
* @return XML?????????
* @throws Exception
*/
public static String mapToXml(Map<String, String> data) throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document document = documentBuilder.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key: data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
value = value.trim();
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
try {
writer.close();
}
catch (Exception ex) {
}
return output;
}
/**
* ?????? sign ?? XML ????????
*
* @param data Map??????
* @param key API???
* @return ???sign?????ML
*/
public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
return generateSignedXml(data, key, SignType.MD5);
}
/**
* ?????? sign ?? XML ????????
*
* @param data Map??????
* @param key API???
* @param signType ??????
* @return ???sign?????ML
*/
public static String generateSignedXml(final Map<String, String> data, String key, SignType signType) throws Exception {
String sign = generateSignature(data, key, signType);
data.put(WXPayConstants.FIELD_SIGN, sign);
return mapToXml(data);
}
/**
* ????????????
*
* @param xmlStr XML??????
* @param key API???
* @return ?????????
* @throws Exception
*/
public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
Map<String, String> data = xmlToMap(xmlStr);
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key).equals(sign);
}
/**
* ????????????????????ign???????????alse?????D5?????
*
* @param data Map??????
* @param key API???
* @return ?????????
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
return isSignatureValid(data, key, SignType.MD5);
}
/**
* ????????????????????ign???????????alse??
*
* @param data Map??????
* @param key API???
* @param signType ??????
* @return ?????????
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType) throws Exception {
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key, signType).equals(sign);
}
/**
* ??????
*
* @param data ????????
* @param key API???
* @return ???
*/
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
return generateSignature(data, key, SignType.MD5);
}
/**
* ??????. ?????????sign_type?????????signType????????????
*
* @param data ????????
* @param key API???
* @param signType ??????
* @return ???
*/
public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(WXPayConstants.FIELD_SIGN)) {
continue;
}
if (data.get(k).trim().length() > 0) // ??????????????????
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
if (SignType.MD5.equals(signType)) {
return MD5(sb.toString()).toUpperCase();
}
else if (SignType.HMACSHA256.equals(signType)) {
return HMACSHA256(sb.toString(), key);
}
else {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* ??????????? Nonce Str
*
* @return String ????????
*/
public static String generateNonceStr() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
/**
* ??? MD5
*
* @param data ????????
* @return MD5???
*/
public static String MD5(String data) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* ??? HMACSHA256
* @param data ????????
* @param key ???
* @return ??????
* @throws Exception
*/
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* ???
* @return
*/
public static Logger getLogger() {
Logger logger = LoggerFactory.getLogger("wxpay java sdk");
return logger;
}
/**
* ?????????????????
* @return
*/
public static long getCurrentTimestamp() {
return System.currentTimeMillis()/1000;
}
/**
* ??????????????????
* @return
*/
public static long getCurrentTimestampMs() {
return System.currentTimeMillis();
}
/**
* ??? uuid?? ?????????????????? nonce_str
* @return
*/
public static String generateUUID() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
/**
* ??????ip???
* @return
*/
public static String localip(){
String ip=null;
Enumeration allNetInterfaces;
try {
allNetInterfaces=NetworkInterface.getNetworkInterfaces();
while(allNetInterfaces.hasMoreElements()){
NetworkInterface netInterface=(NetworkInterface)allNetInterfaces.nextElement();
List<InterfaceAddress> InterfaceAddress=netInterface.getInterfaceAddresses();
for(InterfaceAddress add:InterfaceAddress){
InetAddress Ip=add.getAddress();
if(Ip!=null&&Ip instanceof Inet4Address){
ip=Ip.getHostAddress();
}
}
}
} catch (SocketException e) {
System.out.println("??????ip???????????");
e.printStackTrace();
}
return ip;
}
}
支付成功回调接口
@RequestMapping(value="/iqPaySucessCallMethod")
public void iqPaySucessCallMethod(HttpServletRequest req, HttpServletResponse response){
logger.info("========== 开始处理支付回调通知 ==========");
String resultStr = null;
String str = null;
InputStream inputStream;
StringBuffer sb = new StringBuffer();
try {
inputStream = req.getInputStream();
String s;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null) {
sb.append(s);
}
in.close();
inputStream.close();
// 解析xml成map
Map<String, String> m = new HashMap<String, String>();
m = XMLUtil.doXMLParse(sb.toString());
// 过滤空设置TreeMap,扫码
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
Iterator<String> it = m.keySet().iterator();
while (it.hasNext()) {
String parameter = it.next();
String parameterValue = m.get(parameter);
String v = "";
if (null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
// 微信支付的api密钥
String key = sysConfig.getPrivatekey();
logger.info("微信支付返回回来的参数:" + packageParams);
String return_code = (String) packageParams.get("return_code");
String result_code = (String) packageParams.get("result_code");
// 判断签名是否正确
if (PayForUtil.isTenpaySign("UTF-8", packageParams, key)) {
// -------------------------------
// 处理业务开始
// --------------------------
String resXml = "";
if (StrKit.notBlank(return_code) && StrKit.notBlank(result_code)
&& return_code.equalsIgnoreCase("SUCCESS") && result_code.equalsIgnoreCase("SUCCESS")) {
// 支付成功
// 执行自己的业务逻辑
// 声明日志插入结果对象
String outTradeNo = ((String) packageParams.get("out_trade_no"));
logger.info("#########################开始校验订单号:"+outTradeNo+"是否存在" );
if(!CollectionUtils.isEmpty(inquiryOrderPayList)) {
//非第一次回调
// 执行自己业务逻辑结束
logger.info("给微信回调返回成功");
// 通知微信异步成功不然会一直通知后台八次之后交易失败
resXml = "" + " "
+ " " + " ";
}else {
//第一次回调
//执行业务
logger.info("###流水表插入开始");
//获取微信回调返回的参数
packageParams.get("appid");
packageParams.get("mch_id");
packageParams.get("trade_type");
packageParams.get("trade_state");
packageParams.get("trade_state_desc");
packageParams.get("out_trade_no");
packageParams.get("transaction_id");
packageParams.get("bank_type");
packageParams.get("attach"));
packageParams.get("time_end"));
packageParams.get("openid"));
packageParams.get("fee_type"));
packageParams.get("fee_type"));
Float.valueOf(packageParams.get("total_fee").toString());
Float.valueOf(packageParams.get("total_fee").toString());
logger.info("###流水表插入结束");
// 执行自己业务逻辑结束
logger.info("给微信回调返回成功");
// 通知微信异步成功不然会一直通知后台八次之后交易失败
resXml = "" + " "
+ " " + " ";
logger.info("###该订单" + outTradeNo + "在数据库里的流水状态:" + inquiryOrderPay.toString());
}
} else {
logger.info("支付失败,错误信息:" + packageParams.get("err_code"));
resXml = "" + " "
+ " " + " ";
}
// ------------------------------
// 处理业务完毕
// ------------------------------
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
} else {
logger.info("通知签名验证失败");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
logger.info("==========结束处理支付回调通知==========");
}
微信退款
WXPay引入的包
//implementation group: 'com.github.wxpay',name: 'wxpay-sdk',version: '0.0.3'
@Transactional
@Override
public String refundInquiryOrder(String transaction_id) {
try {
logger.info("#############################################执行微信支付退款接口refundPrescription方法开始");
logger.info("==========================微信退款开始!!========================");
Map<String,String> data = new HashMap<String,String>();
int pay = (int) inquiryOrderPay.getTotalFee();
String out_refund_no = UUIDHexGenerator.createTradeNo();
data.put("out_refund_no" , out_refund_no);
data.put("transaction_id" , transaction_id);
data.put("total_fee" , String.valueOf(pay));
data.put("refund_fee" , String.valueOf(pay));
PayConfig config = new PayConfig();
WXPay wxpay = new WXPay(config);
data.put("appid" , config.getAppID());
data.put("mch_id" , config.getMchID());
data.put("nonce_str" , com.github.wxpay.sdk.WXPayUtil.generateNonceStr());
data.put("sign" , MD5Util.getSign(data));
Map<String,String> resp = wxpay.refund(data);//获取微信退款返回的结果
logger.info("微信返回信息:\n" + resp);
String return_code = resp.get("return_code"); //返回状态码
String return_msg = resp.get("return_msg"); //返回信息
if("SUCCESS".equals(return_code)){//响应成功
String result_code = resp.get("result_code"); //业务结果
String err_code_des = resp.get("err_code_des"); //错误代码描述
if("SUCCESS".equals(result_code)){//退款成功
// 执行自己业务逻辑
}else{//退款失败
}
}else{//响应失败
}
logger.info("#####################################################################执行退款回调结束,退款表type: "+saveAndFlush.getType());
resMap.put("timestamp", TimeHelper.getCurrentTime());
String results = JsonHelper.parserMap(resMap);
return results;
} catch (Exception e) {
e.printStackTrace();
return super.errorResult(e.getMessage());
}
}
MD5Util类
import com.cn.ih.java.main.config.PayConfig;
import com.github.wxpay.sdk.WXPayConstants;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
/**
* @ClassName MD5Util
* @Author ttaurus
* @Date Create in 2020/1/21 13:56
*/
public class MD5Util{
public static String getSign(Map<String,String> data) throws Exception{
PayConfig config = new PayConfig();
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for(String k : keyArray){
if(k.equals(WXPayConstants.FIELD_SIGN)){
continue;
}
if(data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(config.getKey());
MessageDigest md = null;
try{
md = MessageDigest.getInstance("MD5");
}catch(NoSuchAlgorithmException e){
e.printStackTrace();
}
byte[] array = new byte[0];
array = md.digest(sb.toString().getBytes(StandardCharsets.UTF_8));
StringBuilder sb2 = new StringBuilder();
for(byte item : array){
sb2.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1 , 3));
}
System.out.println("签名为:"+sb2);
return sb2.toString().toUpperCase();
}
}
PayConfig类
package com.cn.ih.java.main.config;
import com.cn.ih.java.main.utils.WxPayConstant;
import com.github.wxpay.sdk.WXPayConfig;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
/**
* @author qingshi
* @date 2022/10/29 16:13
* info:
*/
public class PayConfig implements WXPayConfig {
private byte[] certData;
/**
* 微信退款所需要的配置! 退款只需要证书即可。
* @throws Exception
*/
public PayConfig() throws Exception {
//部署服务器用到的路径 绝对路径。
//String certPath = "/usr/local/app/tomcat-dev/webapps/shop/WEB-INF/classes/apiclient_cert.p12";//从微信商户平台下载的安全证书存放的目录
//本地用到的路径 相对路径
// String certPath = "E:\\JavaProject\\msgservice\\src\\main\\resources\\apiclient_cert.p12";
File file = new File(WxPayConstant.getPrivateppath());
InputStream certStream = new FileInputStream(file);
this.certData = new byte[(int) file.length()];
certStream.read(this.certData);
certStream.close();
}
@Override
public String getAppID() {
return WxPayConstant.getWxappid(); //appid
}
public String getAPPSECRET(){
return WxPayConstant.getAppsecret(); //appSecret
}
@Override
public String getMchID() {
return WxPayConstant.getMchid(); //商户号id
}
@Override
public String getKey() {
return WxPayConstant.getPrivatekey(); //支付API密钥
}
// public String getNOTIFY_URL(){
// /*
// 支付回调URL 必须在https下访问。这一步无法在本地实现 必须设置在服务器上
// 此url只是作为一个样例。
// */
// return "https://www.testtest.design21/dev/user/pay/notify";
// }
@Override
public InputStream getCertStream() {
ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
return certBis;
}
@Override
public int getHttpConnectTimeoutMs() {
return 8000;
}
@Override
public int getHttpReadTimeoutMs() {
return 10000;
}
}
WxPayConstant类
在public class WxPayConstant {
public static String privateppath;
public static String getPrivateppath() {
return privateppath;
}
public static void setPrivateppath(String privateppath) {
WxPayConstant.privateppath = privateppath;
}
}