Java实现微信小程序相关接口

订阅通知

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

你可能感兴趣的:(java,微信小程序,开发语言)