java ssh框架整合微信条码支付(原刷卡支付)

大家好我是user_fangniu

不说概念了直接上代码,顺便说一下期间遇到的坑,由于maven依赖是直接引用远端库,HttpClient版本冲突就使用了spring内置的RestTemplate的POST方式来调起微信支付接口。将APPID、MCHID、SERCET改成自己公众号和微信支付的参数就正常可以使用了。有什么不懂得随时联系;QQ:97000937,备注来意

package com.zk.pos.pay.weixin;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.zk.pos.entity.PosTransactionCardNet;

public class PosWxPayUtils {
    
    private static Log log = LogFactory.getLog(PosWxPayUtils.class);
    
    public enum SignType {
        MD5, HMACSHA256
    }
    
    public static final String FIELD_SIGN = "sign";
    
    public static final String FIELD_SIGN_TYPE = "sign_type";
    
    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
    private static final String PAYUrl = "https://api.mch.weixin.qq.com/pay/micropay";
    
    private static final String PAYSNUrl = "https://api.mch.weixin.qq.com/pay/orderquery";
    
    private static final String APPID = "";
    
    private static final String MCHID = "";
    
    private static final String SERCET = "";

    private static final Random RANDOM = new SecureRandom();
    
    /**
     * 扫码支付
     * @throws Exception
     */
    public static boolean barCode(String code, String out_trade_no, String deviceSn, String price, PosTransactionCardNet posTransactionCardNet) throws Exception {
        // TODO 微信支付sdk带的xml转换器jdk1.8使用不兼容,硬拼接xml吧
            try {
                String nonceStr = generateNonceStr();
                String ipV4 = getLocalIpv4Address();
                price = changeBranch(price);
                
                StringBuffer strb1 = new StringBuffer();
                strb1.append("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?><xml>");
                SortedMap<String, String> packageParams = new TreeMap<String, String>();
                
                packageParams.put("appid", APPID);
                strb1.append("<appid>" + APPID + "</appid>");
                
                packageParams.put("mch_id", MCHID);
                strb1.append("<mch_id>" + MCHID + "</mch_id>");

                packageParams.put("attach", "卡充值");
                strb1.append("<attach>" + "卡充值" + "</attach>");
                
                packageParams.put("auth_code", code);
                strb1.append("<auth_code>" + code + "</auth_code>");
                
                packageParams.put("body", "付款码卡充值");
                strb1.append("<body>" + "付款码卡充值" + "</body>");
                
                packageParams.put("device_info", deviceSn);
                strb1.append("<device_info>" + deviceSn + "</device_info>");
                
                packageParams.put("nonce_str", nonceStr);
                strb1.append("<nonce_str>" + nonceStr + "</nonce_str>");
                
                packageParams.put("out_trade_no", out_trade_no);
                strb1.append("<out_trade_no>" + out_trade_no + "</out_trade_no>");
                
                packageParams.put("spbill_create_ip", ipV4);
                strb1.append("<spbill_create_ip>" + ipV4 + "</spbill_create_ip>");
                
                packageParams.put("total_fee", price);
                strb1.append("<total_fee>" + price + "</total_fee>");

                // 根据package数据生成预支付订单号的签名sign
                String sign = generateSignature(packageParams, SERCET);

                // 生成需要提交给统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder 的xml数据
                packageParams.put("sign", sign);
                strb1.append("<sign>" + sign + "</sign>");
                
                strb1.append("</xml>");
                String result = restTemplatePost(strb1.toString(), PAYUrl);
              
                Map resultMap = xmlToMap(result);
                /*System.out.println("map"+resultMap);*/
                
                //判断支付是否成功
                String return_code = resultMap.get("return_code").toString();
                String result_code = resultMap.get("result_code").toString();
                String err_code = resultMap.get("err_code").toString();
                String err_code_des = resultMap.get("err_code_des").toString();
                
                if("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)){
                    posTransactionCardNet.setTransactionId(resultMap.get("transaction_id").toString());
                    log.info("微信免密支付成功!");
                    /*System.out.println("微信免密支付成功!");*/
                    return true;
                } else if ("USERPAYING".equals(err_code)){
                    //采用轮询的方式去调取,
                    for(int i = 0; i < 4; i++){
                        Thread.sleep(3000);
                        //手动拼装需要查询订单的接口的参数xml
                        String nonceStr2 = generateNonceStr();
                        
                        StringBuffer strb2 = new StringBuffer();
                        strb2.append("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?><xml>");
                        strb2.append("<appid>" + APPID + "</appid>");
                        strb2.append("<mch_id>" + MCHID + "</mch_id>");
                        strb2.append("<nonce_str>" + nonceStr2 + "</nonce_str>");
                        //微信订单号和商户订单号2选1传,这里选择传入商户订单号
                        strb2.append("<out_trade_no>" + out_trade_no + "</out_trade_no>");
                        
                        SortedMap<String, String> packageParams2 = new TreeMap<String, String>();
                        packageParams2.put("appid", APPID);
                        packageParams2.put("mch_id", MCHID);
                        packageParams2.put("nonce_str", nonceStr2);
                        packageParams2.put("out_trade_no", out_trade_no);
                        
                        //验签
                        String sign2 = generateSignature(packageParams2, SERCET);
                        
                        packageParams2.put("sign", sign2);
                        strb2.append("<sign>" + sign2 + "</sign>");
                        
                        strb2.append("</xml>");
                        
                        String result2 = restTemplatePost(strb2.toString(), PAYSNUrl);
                        System.out.println("result2"+result2);
                        Map resultMap2 = xmlToMap(result2);
                        String trade_state = resultMap2.get("trade_state").toString();
                        
                        if("SUCCESS".equals(trade_state)){
                            /*System.out.println("微信免密支付成功!");*/
                            log.info("微信免密支付成功!");
                            return true;
                        }
                        log.info("正在支付" + out_trade_no);
                    }
                }
                /*System.out.println("微信支付失败,"+err_code_des);*/
                log.error("微信支付失败,"+err_code_des);
                return false;
            }catch(Exception e) {
                e.printStackTrace();
                return false;
            }
    }
    
    public static String getLocalIpv4Address() throws SocketException {
        Enumeration allNetInterfaces = null;
        try {
            allNetInterfaces = NetworkInterface.getNetworkInterfaces();
        } catch (java.net.SocketException e) {
            e.printStackTrace();
        }
        InetAddress ip = null;
        while (allNetInterfaces.hasMoreElements())
        {
            NetworkInterface netInterface = (NetworkInterface) allNetInterfaces
                    .nextElement();
            Enumeration addresses = netInterface.getInetAddresses();
            while (addresses.hasMoreElements())
            {
                ip = (InetAddress) addresses.nextElement();
                if (ip != null && ip instanceof Inet4Address)
                {
                    if(ip.getHostAddress().equals("127.0.0.1")){
                        continue;
                    }
                    /*System.out.println("/u672c/u673a/u7684IP = " + ip.getHostAddress());*/
                    return ip.getHostAddress();
                }
            }
        }
        System.out.println("网络无连接!");
        return null;
    }
    
    /**
     * 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 = 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) {
            throw ex;
        }
    }
    
    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();
    }
    
    
    /**
     * 生成签名
     *
     * @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(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));
        }
    }
    
    /**
     * 生成 MD5
     *
     * @param data 待处理数据
     * @return MD5结果
     */
    public static String MD5(String data) throws Exception {
        java.security.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();
    }
    
    /**
     * 获取随机字符串 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);
    }
    
    /**
     * spring内置的RestTemplate去请求微信客户端
     * @return
     */
    public static String restTemplatePost(String str, String url) {
        //这里更改成spring内置的RestTemplate去请求微信客户端
        RestTemplate restTemplate = new RestTemplate();
        
        //指定字符编码为UTF-8,原编码为ISO-8859-1,这个必须加
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        
        HttpHeaders headers = new HttpHeaders();
        
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
        
        HttpEntity<String> formEntity = new HttpEntity<String>(str, headers);

        String result = restTemplate.postForObject(url, formEntity, String.class);
        return result;
    }
    
    /**
     * 将元为单位的转换为分 替换小数点,支持以逗号区分的金额
     *
     * @param amount
     * @return
     */
    public static String changeBranch(String amount) {
        String currency = amount.replaceAll("\\$|\\¥|\\,", ""); // 处理包含, ¥
        // 或者$的金额
        int index = currency.indexOf(".");
        int length = currency.length();
        Long amLong;
        if (index == -1) {
            amLong = Long.valueOf(currency + "00");
        } else if (length - index >= 3) {
            amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));
        } else if (length - index == 2) {
            amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);
        } else {
            amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");
        }
        return amLong.toString();
    }
 
}
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值