当前位置: 首页>移动开发>正文

微信hd ios 微信HD 旧版

微信支付NATIVE版V2旧版

工具类

package com.xihui.nyy_cloud.utils;

import com.xihui.nyy_cloud.constant.ConstWeiChat;
import com.xihui.nyy_cloud.constant.WXPayConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
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 java.io.*;
import java.security.*;
import java.util.*;


public class WXPayUtil {

    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final Random RANDOM = new SecureRandom();

    /**
     * 统一下单 v2版 Native
     * @param mchId 商户号
     * @param body 商品描述
     * @param attach 附加数据(可有可无)
     * @param orderId 商家订单号
     * @param totalFee 总金额
     * @param ip 用户请求ip地址
     * @param notifyUrl 支付结果回调地址
     * @return 返回封装好后的数据,包括签名
     * @throws Exception
     */
    public static Map<String, String> genParamToMap(String mchId,String body,String attach,String orderId,String totalFee,String ip,String notifyUrl) throws Exception {
        Map<String, String> paraMap = new HashMap<>(16);
        paraMap.put("appid", ConstWeiChat.APPID);  //appid
        //附加数据,在查询API和支付通知中原样返回
        paraMap.put("attach", attach); 
        paraMap.put("body", "NYY-" + body);//商品描述
        paraMap.put("mch_id", mchId);
        paraMap.put("nonce_str", WXPayUtil.generateNonceStr()); //随机数
        //此路径是微信服务器调用支付结果通知路径
        paraMap.put("notify_url",notifyUrl);
        //订单号
        paraMap.put("out_trade_no", orderId);
        paraMap.put("spbill_create_ip", ip);//用户请求的ip
        paraMap.put("total_fee", totalFee);
        paraMap.put("trade_type", "NATIVE "); //Native的交易类型
        String sign = WXPayUtil.generateSignature(paraMap, ConfigUtil.API_KEY);
        paraMap.put("sign", sign);
        return paraMap;
    }

    /**
     * 退款
     * @param mchId
     * @param orderId
     * @param totalFee
     * @param notifyUrl
     * @return
     * @throws Exception
     */
    public static Map<String, String> genParamToMapRefund(String mchId,String orderId,String outRefundNo,String totalFee,String refundFee,String notifyUrl) throws Exception {
        Map<String, String> paraMap = new HashMap<>(16);
        paraMap.put("appid", ConstWeiChat.APPID);
        paraMap.put("mch_id", mchId);
        paraMap.put("nonce_str", WXPayUtil.generateNonceStr());
        //此路径是微信服务器调用支付结果通知路径
        paraMap.put("notify_url",notifyUrl);
        //订单号
        paraMap.put("out_trade_no", orderId);
        paraMap.put("out_refund_no",outRefundNo);
        paraMap.put("total_fee", totalFee);
        paraMap.put("refund_fee", refundFee);
        paraMap.put("refund_fee_type", "RMB");
        String sign = WXPayUtil.generateSignature(paraMap, ConfigUtil.API_KEY);
        paraMap.put("sign", sign);
        return paraMap;
    }


    /**
     * XML格式字符串转换为Map
     *
     * @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>();
            DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // 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;
        }

    }

    /**
     * 将Map转换为XML格式的字符串
     *
     * @param data Map类型数据
     * @return XML格式的字符串
     * @throws Exception
     */
    public static String mapToXml(Map<String, String> data) throws Exception {
        org.w3c.dom.Document document = WXPayXmlUtil.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字段的XML
     */
    public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
        return generateSignedXml(data, key, WXPayConstants.SignType.MD5);
    }

    /**
     * 生成带有 sign 的 XML 格式字符串
     *
     * @param data     Map类型数据
     * @param key      API密钥
     * @param signType 签名类型
     * @return 含有sign字段的XML
     */
    public static String generateSignedXml(final Map<String, String> data, String key, WXPayConstants.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);
    }

    /**
     * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
     *
     * @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, WXPayConstants.SignType.MD5);
    }

    /**
     * 判断签名是否正确,必须包含sign字段,否则返回false。
     *
     * @param data     Map类型数据
     * @param key      API密钥
     * @param signType 签名方式
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(Map<String, String> data, String key, WXPayConstants.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, WXPayConstants.SignType.MD5);
    }

    /**
     * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
     *
     * @param data     待签名数据
     * @param key      API密钥
     * @param signType 签名方式
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key, WXPayConstants.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 (WXPayConstants.SignType.MD5.equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        } else if (WXPayConstants.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() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }


    /**
     * 生成 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();
    }

    /**
     * 读取流中字符串
     *
     * @param in   输入流
     * @param type 编码
     * @return
     */
    public static String inputStream2String(InputStream in, String type) {
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(in, type);
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }
        BufferedReader br = new BufferedReader(reader);
        StringBuilder sb = new StringBuilder();
        String line = "";
        try {
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    /**
     * 加载证书
     * @param path
     * @throws IOException
     */
    private void initCert(String path) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {

    }
}
package com.xihui.nyy_cloud.utils;

import org.w3c.dom.Document;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

/**
 * 2018/7/3
 */
public final class WXPayXmlUtil {
    public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);

        return documentBuilderFactory.newDocumentBuilder();
    }

    public static Document newDocument() throws ParserConfigurationException {
        return newDocumentBuilder().newDocument();
    }
}
public class ConfigUtil {
    /*
     * 服务号相关信息
     */
    public final static String APPID = "";//服务号的应用号
    public final static String MCH_ID = "";//商户号
    public final static String API_KEY = "";//API密钥
    public final static String SIGN_TYPE = "MD5";//签名加密方式
    /**
     * 证书地址
     */
    public static String certLocalPath = "";
    /**
     * 统一下单-h5
     */
    public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    /**
     * 查询订单
     */
    public final static String ORDER_QUERY = "https://api.mch.weixin.qq.com/pay/orderquery";

    /**
     * 统一下单Body字段App前缀
     */
    public final static String BODY_NAME = "";
}
package com.xihui.nyy_cloud.utils;

import javax.servlet.http.HttpServletRequest;

public class CommonUtil {
    public static String toIpAddr(HttpServletRequest request) {
        String ip = null;

        //X-Forwarded-For:Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //Proxy-Client-IP:apache 服务代理
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //WL-Proxy-Client-IP:weblogic 服务代理
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //HTTP_CLIENT_IP:有些代理服务器
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //X-Real-IP:nginx服务代理
            ipAddresses = request.getHeader("X-Real-IP");
        }

        //有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
        if (ipAddresses != null && ipAddresses.length() != 0) {
            ip = ipAddresses.split(",")[0];
        }

        //还是不能获取到,最后再通过request.getRemoteAddr();获取
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ip = request.getRemoteAddr();
        }
        return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
    }
}
package com.xihui.nyy_cloud.utils;

import com.xihui.nyy_cloud.exception.CustomException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
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.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

/**
 * @author bing.li
 * @date 2020/9/12  9:23
 */
public class HttpCilentUtil {

    /**
     * post发送请求,xml
     *
     * @param url
     * @param data
     * @return
     */
    public static String postForobject(String url, String data) {
        HttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(url);
        try {
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(1000).setConnectTimeout(3000).build();
            httpPost.setConfig(requestConfig);
            StringEntity postEntity = new StringEntity(data, "UTF-8");
            httpPost.addHeader("Content-Type", "text/xml");
            httpPost.setEntity(postEntity);
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            return EntityUtils.toString(httpEntity, "UTF-8");
        } catch (ClientProtocolException e) {
            e.printStackTrace();
            throw new CustomException("网络异常,请检查重试");
        } catch (IOException e) {
            e.printStackTrace();
            throw new CustomException("网络异常,请稍后重试");
        }catch (Exception e){
            e.printStackTrace();
            throw new CustomException("网络异常,请稍后重试");
        } finally {

        }
    }

    /**
     * 加载证书发送post请求,退款
     */
    public static String postForobject(String url, String data,String path) {
        HttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(url);
        try {
            //拼接证书的路径
            path = path + ConfigUtil.certLocalPath;
            KeyStore keyStore = KeyStore.getInstance("PKCS12");

            //加载本地的证书进行https加密传输
            FileInputStream instream = new FileInputStream(new File(path));
            try {
                keyStore.load(instream, ConfigUtil.MCH_ID.toCharArray()); //加载证书密码,默认为商户ID
            } catch (CertificateException e) {
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } finally {
                instream.close();
            }

            // Trust own CA and all self-signed certs
            SSLContext sslcontext = SSLContexts.custom()
                    .loadKeyMaterial(keyStore, ConfigUtil.MCH_ID.toCharArray())  //加载证书密码,默认为商户ID
                    .build();
            // Allow TLSv1 protocol only
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslcontext,
                    new String[]{"TLSv1"},
                    null,
                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

            httpClient = HttpClients.custom()
                    .setSSLSocketFactory(sslsf)
                    .build();
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(1000).setConnectTimeout(3000).build();
            httpPost.setConfig(requestConfig);
            StringEntity postEntity = new StringEntity(data, "UTF-8");
            httpPost.addHeader("Content-Type", "text/xml");
            httpPost.setEntity(postEntity);
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            return EntityUtils.toString(httpEntity, "UTF-8");
        } catch (ClientProtocolException e) {
            e.printStackTrace();
            throw new CustomException("网络异常,请检查重试");
        } catch (IOException e) {
            e.printStackTrace();
            throw new CustomException("网络异常,请稍后重试");
        }catch (Exception e){
            e.printStackTrace();
            throw new CustomException("网络异常,请稍后重试");
        } finally {

        }
    }
}

常量

/**
 * 常量
 */
public class WXPayConstants {

    public enum SignType {
        MD5, HMACSHA256
    }



    public static final String FAIL = "FAIL";
    public static final String SUCCESS = "SUCCESS";
    public static final String HMACSHA256 = "HMAC-SHA256";
    public static final String MD5 = "MD5";

    public static final String FIELD_SIGN = "sign";
    public static final String FIELD_SIGN_TYPE = "sign_type";

}

微信支付示例

支付代码

public Map<String, String> wxPrePay(HttpServletRequest request, HttpServletResponse response, Integer price, String orderNum) {
        //创建接收返回值参数和发送统一下单参数的map
        Map<String, String> resultMap = new HashMap<>();
        Map<String, String> param = new HashMap<>();

        //判断金额是否小于0
        if (price <= 0) {
            resultMap.put("message", "付款金额小于0");
            resultMap.put("code", "500");
            return resultMap;
        }

        //微信支付结果回调地址
        String url = ConstWeiChat.HUI_DIAO_URL + ConstUrl.PRE_ORDER_PATH + ConstWeiChat.WEI_XIN_HUI_DIAO_URL;

        //填充参数并计算签名
        try {
            param = WXPayUtil.genParamToMap(ConfigUtil.MCH_ID, "body参数", "", orderNum, price.toString(), CommonUtil.toIpAddr(request), url);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //请求参数由map转换为XML格式字符串
        String paramXML = null;
        try {
            paramXML = WXPayUtil.mapToXml(param);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //调用统一下单接口,并接收返回数据
        String result = HttpCilentUtil.postForobject(ConfigUtil.UNIFIED_ORDER_URL, paramXML);

        System.err.println("返回报文:" + result);

        //返回数据转换为map类型
        try {
            Map<String, String> map = WXPayUtil.xmlToMap(result);
            //判断返回的结果数据是否成功
            if (!"SUCCESS".equals(map.get("return_code"))) {
                resultMap.put("code", "501");
                resultMap.put("message", map.get("return_msg"));
                return resultMap;
            }
            //当return_code的返回值为SUCCESS时才会有这个字段数据
            if (!"SUCCESS".equals(map.get("result_code"))) {
                resultMap.put("code", "502");
                resultMap.put("message", map.get("err_code_des"));
                return resultMap;
            }

            //当return_code和result_code都为SUCCESS时获取返回的响应数据
            resultMap.put("code", "200");
            resultMap.put("message", map.get("code_url"));
            resultMap.put("orderNum", orderNum);
            System.err.println("Native支付返回的调起网页地址:" + map.get("code_url"));
        } catch (Exception e) {
            e.printStackTrace();
        }

        return resultMap;
    }

微信回调支付状态

public void wxNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            //读取参数
            InputStream inputStream;
            StringBuffer sb = new StringBuffer();
            inputStream = request.getInputStream(); //获取请求的输入流
            String s;
            //读取输入流并放到缓冲流中
            BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            while ((s = in.readLine()) != null) { //读取一行信息并不等于null时放入到sb中
                sb.append(s);
            }
            in.close(); //关闭缓冲流
            inputStream.close(); //关闭输入流
            //解析xml成map
            Map<String, String> m = new HashMap<String, String>();
            m = XMLUtil.doXMLParse(sb.toString());
            for (Object keyValue : m.keySet()) {
                System.out.println(keyValue + "=" + m.get(keyValue));
            }
            //过滤空 设置  过滤空值是为了校验签名,签名计算空值是不参与生成签名的
            Map<String, String> packageParams = new HashMap<>();
            Iterator it = m.keySet().iterator();
            while (it.hasNext()) {
                String parameter = (String) it.next();
                String parameterValue = m.get(parameter);
                String v = "";
                if (null != parameterValue) {
                    v = parameterValue.trim();
                }
                packageParams.put(parameter, v);
            }
            //判断签名是否正确
            String resXml = "";
            try {
                if (WXPayUtil.isSignatureValid(packageParams, ConfigUtil.API_KEY)) {
                    if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
                        // 这里是支付成功
                        //执行自己的业务逻辑\
                        final String out_trade_no = (String) packageParams.get("out_trade_no"); //商户订单号
                        //查询订单信息
                        TOrder tOrder = tOrderMapper.selectOne(new QueryWrapper<TOrder>().eq("order_num", out_trade_no));
                        if (tOrder == null) {
                            log.info("支付失败,错误信息:" + "订单编号在数据库中不存在");
                            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[参数错误]]></return_msg>" + "</xml> ";
                        }
                        Integer totalFee = tOrder.getFkPrice();

                        //根据订单号主动请求微信支付查询订单接口
                        Map<String, String> map = orderQuery(out_trade_no);
                        //return_code和result_code都为SUCCESS标识时才算成功
                        if ("SUCCESS".equals(map.get("return_code"))) {
                            String outTradeNo = map.get("out_trade_no");
                            String total_fee = map.get("total_fee");
                            String mchId = map.get("mch_id");
                            String attachs = map.get("attach");
                            if ("SUCCESS".equals(map.get("result_code"))) {
                                //判断商户号是否一致、统一下单编码是否一致、付款金额是否一致
                                if (!ConfigUtil.MCH_ID.equals(mchId) || !out_trade_no.equals(outTradeNo) || !totalFee.toString().equals(total_fee)) {
                                    log.info("支付失败,错误信息:" + "商家号或统一下单编号或付款金额不一致");
                                    resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[参数错误]]></return_msg>" + "</xml> ";
                                } else {
                                    //判断当前订单状态是否为代付款状态,如果为代付款状态,则修改状态
                                    if (0 == tOrder.getStatus()) {
                                        //修改订单状态
                                        tOrderMapper.changeOrderStatus(outTradeNo, 1, new Date());
                                        resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                                    } else {
                                        resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                                        log.info("订单已处理");
                                    }

                                }
                            }
                        } else {
                            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[通知签名验证失败]]></return_msg>" + "</xml> ";
                            log.info("查询订单失败,{}", map.get("return_msg"));
                        }

                    } else {
                        log.info("支付失败,错误信息:" + packageParams.get("err_code"));
                        resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";

                    }
                } else {
                    resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[通知签名验证失败]]></return_msg>" + "</xml> ";
                    log.info("通知签名验证失败");
                }
            } catch (Exception e) {
                log.error("微信支付回调异常{}", e.getMessage());
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[未知异常]]></return_msg>" + "</xml> ";
            }

            //------------------------------
            //处理业务完毕
            //------------------------------
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            out.write(resXml.getBytes());
            out.flush();
            out.close();
        } catch (Exception e) {
            log.error("微信支付回调异常{}", e.getMessage());
            throw new CustomException("解析微信回调报文异常");
        }
    }

主动请求查询微信支付状态

public void weixinUpdateOrderStatus(String orderNum) {
        //根据订单号查询订单是否存在
        TOrder tOrder = tOrderMapper.selectOne(new QueryWrapper<TOrder>().eq("order_num", orderNum));
        if (tOrder == null) {
            throw new CustomException("订单不存在,请刷新页面");
        }
        Integer totalFee = tOrder.getFkPrice();
        //根据订单号主动请求微信支付查询订单接口
        Map<String, String> map = orderQuery(orderNum);

        if ("SUCCESS".equals(map.get("return_code"))) {
            String outTradeNo = map.get("out_trade_no");
            String total_fee = map.get("total_fee");
            String mchId = map.get("mch_id");
            if ("SUCCESS".equals(map.get("result_code"))) {
                //判断商户号是否一致、统一下单编码是否一致、付款金额是否一致
                if (!ConfigUtil.MCH_ID.equals(mchId) || !orderNum.equals(outTradeNo) || !totalFee.toString().equals(total_fee)) {
                    throw new CustomException("商户号、下单编码、付款金额不一致,不做修改");
                } else {
                    //判断当前订单状态是否为代付款状态,如果为代付款状态,则修改状态
                    if ("SUCCESS".equals(map.get("trade_state"))) {
                        if (0 == tOrder.getStatus()) {
                            //修改订单状态
                            tOrderMapper.changeOrderStatus(outTradeNo, 1, new Date());
                        }
                    }
                }
            }
        } else {
            throw new CustomException("查询微信订单失败{ " + map.get("return_msg") + "}");
        }
    }

退款实例

退款需要证书

退款代码

public Map<String, String> weiXinRefund(HttpServletRequest request, HttpServletResponse response, Long id) {
        Map<String, String> resultMap = new HashMap<>();
        Map<String, String> parameters = new HashMap<>();
        //根据订单id查询订单信息
        TOrderLord tOrderLord = tOrderLordMapper.selectById(id);
        //根据当时统一下单的微信编码,获取所有下单金额
        List<TOrderLord> tOrderLords = tOrderLordMapper.selectList(new QueryWrapper<TOrderLord>().eq("weixin_code", tOrderLord.getWeixinCode()));
        Integer totalMoney = 0;
        for (TOrderLord orderLord : tOrderLords) {
            totalMoney += orderLord.getFkPrice();
        }
        //设置回调地址
//        String url = request.getRequestURI().toString();
//        String domain = url.substring(0, url.length() - 13);
        //生产环境
        String notify_url = ConstWeiChat.HUI_DIAO_URL + ConstUrl.PRE_WEI_XIN_PATH + ConstWeiChat.WEI_XIN_REFUND_URL; //回调地址
        //生成退款订单号
        String outRefundNo = ConstOrder.ORDER_REFUND_PRE + DateUtil.getNowTimeyyyyMMddHHmmssSSS_CN();
        try {
            parameters = WXPayUtil.genParamToMapRefund(ConfigUtil.MCH_ID, tOrderLord.getWeixinCode(), outRefundNo, totalMoney.toString(), tOrderLord.getFkPrice().toString(), notify_url);
        } catch (Exception e) {
            e.printStackTrace();
        }

        String requestXML = null;
        try {
            requestXML = WXPayUtil.mapToXml(parameters);
        } catch (Exception e) {
            log.error("退款Map参数,转换为XML格式异常{}", e.getMessage());
            e.printStackTrace();
        }
        System.out.println(requestXML);
        String result = HttpCilentUtil.postForobject(ConfigUtil.REFUND_URL, requestXML,"");
        System.out.println("发送退款,返回结果数据:"+result);
        try {
            //解析退款返回信息
            resultMap = WXPayUtil.xmlToMap(result);
            if (resultMap.get("return_code").equals("FAIL")) {
                throw new CustomException(resultMap.get("return_msg"));
            }
            if (resultMap.get("result_code").equals("FAIL")) {
                throw new CustomException("请求成功,但提交退款业务失败,失败原因:" + resultMap.get("err_code_des"));
            }

            //退款申请发送成功后,修改订单退款状态为退款中状态
            tOrderService.orderRefund(id, outRefundNo);
        } catch (Exception e) {
            log.error("异常:{}" + e.getMessage());
            throw new CustomException("异常:{"+e.getMessage()+"}");
        }
        return resultMap;
    }

退款回调

public void weiXinAutoRefundMessage(HttpServletRequest request, HttpServletResponse response) {
        try {
            //读取参数
            InputStream inputStream;
            StringBuffer sb = new StringBuffer();
            inputStream = request.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());
            for (Object keyValue : m.keySet()) {
                System.out.println(keyValue + "=" + m.get(keyValue));
            }
            //过滤空 设置
            Map<String, String> packageParams = new HashMap<>();
            Iterator it = m.keySet().iterator();
            while (it.hasNext()) {
                String parameter = (String) it.next();
                String parameterValue = m.get(parameter);
                String v = "";
                if (null != parameterValue) {
                    v = parameterValue.trim();
                }
                packageParams.put(parameter, v);
            }
            String resXml = "";
            try {
                if (WXPayUtil.isSignatureValid(packageParams,ConfigUtil.API_KEY)) {
                    //退款成功通知
                    if ("SUCCESS".equals(packageParams.get("return_code"))) {
                        //用Base64编码解码ciphertext字段
                        byte[] decode = Base64.getDecoder().decode(packageParams.get("req_info"));
                        String str = new String(decode, "UTF-8");
                        String key = Md5Util.MD532(ConfigUtil.API_KEY);
                        //用key对res进行解密
                        String rest = AESUtil.decryptData(str, key);
                        //解密后的xml格式转换为map格式
                        Map<String, String> resMap = WXPayUtil.xmlToMap(rest);
                        //根据退款编码查询该订单退款金额与当前通知退款金额是否相符
                        final TOrderLord refundOrderInfo = tOrderLordMapper.getRefundCodeOrderInfo(resMap.get("out_refund_no"));
                        //判断退款金额与订单付款金额是否相等
                        if(!refundOrderInfo.getFkPrice().equals(resMap.get("settlement_refund_fee"))){
                            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[退款金额与实际退款金额不符]]></return_msg>" + "</xml> ";
                            log.info("退款金额与实际退款金额不符");
                        }
                        //根据退款编码查询退款订单信息
                        Map<String, String> map = getWeixinRefundInfo(resMap.get("out_refund_no"));
                        //return_code和result_code都为SUCCESS标识时才算成功
                        if ("SUCCESS".equals(map.get("return_code"))) {
                            String outRefundNo = map.get("out_refund_no_0");
                            String refundFee = map.get("refund_fee_0");
                            String mchId = map.get("mch_id");
                            if ("SUCCESS".equals(map.get("result_code"))) {
                                //判断商户号是否一致、退款编码是否一致、统一下单编码是否一致、付款金额是否一致
                                if (!ConfigUtil.MCH_ID.equals(mchId) || !resMap.get("out_trade_no").equals(refundOrderInfo.getWeixinCode()) || !refundOrderInfo.getWeixinRefund().equals(outRefundNo) || !refundOrderInfo.getFkPrice().toString().equals(refundFee)) {
                                    log.info("退款失败,错误信息:" + "参数错误");
                                    resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[参数错误]]></return_msg>" + "</xml> ";
                                } else {
                                    if(!"PROCESSING".equals(map.get("refund_status_0"))) { //退款状态为正在处理
                                        //修改之前先查看当前订单是否已退款成功或失败
                                        if (refundOrderInfo.getRefunded() == 1) {
                                            Integer refund = 0;
                                            if ("SUCCESS".equals(map.get("refund_status_0"))) { //退款状态为成功
                                                refund = 2;
                                            }
                                            if ("REFUNDCLOSE".equals(map.get("refund_status_0")) || "CHANGE".equals(map.get("refund_status_0"))) { //退款状态为关闭或异常都作为失败处理
                                                refund = 3;
                                            }
                                            //修改订单状态
                                            tOrderLordMapper.changeRefundOrder(outRefundNo, refund);
                                            if(refund == 2){
                                                //生成退款记录
                                                final TOrderLord tOrderLord = tOrderLordMapper.selectOne(new QueryWrapper<TOrderLord>()
                                                        .eq("weixin_refund", resMap.get("out_refund_no")).last("limit 1"));

                                                final List<TOrder> orderList = tOrderMapper.selectList(new QueryWrapper<TOrder>().eq("order_lord_id", tOrderLord.getId()));
                                                final TUser tUser = tUserMapper.selectById(tOrderLord.getUserId());
                                                orderList.forEach(order->{
                                                    //生成商家收钱记录表信息
                                                    TBussinessMoneyRecords tBussinessMoneyRecords = new TBussinessMoneyRecords();
                                                    tBussinessMoneyRecords.setShopId(tOrderLord.getShopId())
                                                            .setUserImg(tUser.getHeadPhoto())
                                                            .setUserName(tUser.getUserName())
                                                            .setGoodsImg(order.getHeadImg())
                                                            .setGoodsName(order.getGoodsName())
                                                            .setGoodsPrice(order.getPrice())
                                                            .setGoodsNum(order.getNum())
                                                            .setTotalPrice(order.getTotalMoney())
                                                            .setHasPlus(false);
                                                    if(Const.CONST_STATUS_ZERO == tBussinessMoneyRecordsMapper.insert(tBussinessMoneyRecords)){
                                                        log.error("WeixinServiceImpl,生成商家收钱记录错误,记录信息为:"+tBussinessMoneyRecords.toString());
                                                        throw new CustomException("生成退款收钱记录错误,请刷新重试");
                                                    }

                                                });
                                                TBussinessMoneyRecords tBussinessMoneyRecords = new TBussinessMoneyRecords();
                                                tBussinessMoneyRecords.setShopId(tOrderLord.getShopId())
                                                        .setUserImg(tUser.getHeadPhoto())
                                                        .setUserName(tUser.getUserName())
                                                        .setGoodsImg("")
                                                        .setGoodsName("运费")
                                                        .setGoodsPrice(tOrderLord.getFreight())
                                                        .setGoodsNum(1)
                                                        .setTotalPrice(tOrderLord.getFreight())
                                                        .setHasPlus(false);
                                                if(Const.CONST_STATUS_ZERO == tBussinessMoneyRecordsMapper.insert(tBussinessMoneyRecords)){
                                                    log.error("WeixinServiceImpl,生成商家运费收钱记录错误,记录信息为:"+tBussinessMoneyRecords.toString());
                                                    throw new CustomException("生成退款收钱记录错误,请刷新重试");
                                                }



                                            }
                                            resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                                        } else {
                                            resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                                            log.info("退款订单已处理");

                                        }
                                    } else {
                                        resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[退款状态错误]]></return_msg>" + "</xml> ";
                                    }
                                }
                            }
                        } else {
                            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[通知签名验证失败]]></return_msg>" + "</xml> ";
                            log.info("查询退款订单失败,{}", map.get("return_msg"));
                        }

                    } else {
                        log.info("退款失败,错误信息:" + packageParams.get("return_msg"));
                        resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";

                    }
                } else {
                    resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[通知签名验证失败]]></return_msg>" + "</xml> ";
                    log.info("通知签名验证失败");
                }
            } catch (Exception e) {
                log.error("微信退款回调异常{}", e.getMessage());
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[未知异常]]></return_msg>" + "</xml> ";
            }

            //------------------------------
            //处理业务完毕
            //------------------------------
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            out.write(resXml.getBytes());
            out.flush();
            out.close();
        } catch (Exception e){
            log.error("微信退款回调异常{}", e.getMessage());
            throw new CustomException("解析微信回调报文异常");
        }
    }

主动查询退款状态

public void changeWeixinRefundOrder(Long id) {
        //根据订单id查询订单信息
        TOrderLord orderLord = tOrderLordMapper.selectById(id);

        if(orderLord.getRefunded() == 1){ //退款状态为1时,表示还在退款中,查看微信订单退款状态
            //根据退款编码查询订单退款状态
            Map<String, String> map = getWeixinRefundInfo(orderLord.getWeixinRefund());
            System.out.println(map);
            if ("SUCCESS".equals(map.get("return_code"))) {
                String outRefundNo = map.get("out_refund_no_0");
                String refundFee = map.get("refund_fee_0");
                String mchId = map.get("mch_id");
                if ("SUCCESS".equals(map.get("result_code"))) {
                    //判断商户号是否一致、退款编码是否一致、统一下单编码是否一致、付款金额是否一致
                    System.err.println("ConfigUtil.MCH_ID" + ConfigUtil.MCH_ID);
                    System.err.println("mchId" + mchId);
                    System.err.println("map.get(\"out_trade_no\")" + map.get("out_trade_no"));
                    System.err.println("orderLord.getWeixinCode()" + orderLord.getWeixinCode());
                    System.err.println("map.get(\"out_refund_no_0\")" + map.get("out_refund_no_0"));
                    System.err.println("outRefundNo" + outRefundNo);
                    System.err.println("orderLord.getFkPrice()" + orderLord.getFkPrice());
                    System.err.println("refundFee" + refundFee);
                    if (!ConfigUtil.MCH_ID.equals(mchId) || !map.get("out_trade_no").equals(orderLord.getWeixinCode()) || !orderLord.getWeixinRefund().equals(outRefundNo) || !orderLord.getFkPrice().toString().equals(refundFee)) {
                        log.info("失败,错误信息:" + "参数错误");
                    } else {
                        if(!"PROCESSING".equals(map.get("refund_status_0"))) { //退款状态为正在处理
                            //修改之前先查看当前订单是否已退款成功或失败
                            if (orderLord.getRefunded() == 1) {
                                Integer refund = 1;
                                if ("SUCCESS".equals(map.get("refund_status_0"))) { //退款状态为成功
                                    refund = 2;
                                }
                                if ("REFUNDCLOSE".equals(map.get("refund_status_0")) || "CHANGE".equals(map.get("refund_status_0"))) { //退款状态为关闭或异常都作为失败处理
                                    refund = 3;
                                }
                                //修改订单状态
                                tOrderLordMapper.changeRefundOrder(outRefundNo, refund);
                            }
                        }
                    }
                }
            } else {
                log.info("查询退款订单失败,{}", map.get("return_msg"));
            }
        }

    }



https://www.xamrdz.com/mobile/47z1959895.html

相关文章: