• JAVA微信支付——PC二维码支付代码(WeChatPay.java 才是调用类)


    微信官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html


    MD5Util.java
    package weixin;
    
    import java.security.MessageDigest;
    
    
    public class MD5Util {
    
    	private static String byteArrayToHexString(byte b[]) {
    		StringBuffer resultSb = new StringBuffer();
    		for (int i = 0; i < b.length; i++)
    			resultSb.append(byteToHexString(b[i]));
    
    		return resultSb.toString();
    	}
    
    	private static String byteToHexString(byte b) {
    		int n = b;
    		if (n < 0)
    			n += 256;
    		int d1 = n / 16;
    		int d2 = n % 16;
    		return hexDigits[d1] + hexDigits[d2];
    	}
    
    	public static String MD5Encode(String origin, String charsetname) {
    		String resultString = null;
    		try {
    			resultString = new String(origin);
    			MessageDigest md = MessageDigest.getInstance("MD5");
    			if (charsetname == null || "".equals(charsetname))
    				resultString = byteArrayToHexString(md.digest(resultString
    						.getBytes()));
    			else
    				resultString = byteArrayToHexString(md.digest(resultString
    						.getBytes(charsetname)));
    		} catch (Exception exception) {
    		}
    		return resultString;
    	}
    
    	private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
    			"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
    
    	
    
        
    }
    

      

    RandomUtil.java
    package weixin;
    
    import java.util.Random;
    
    public class RandomUtil {
    	private static char ch[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
    	      'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
    	      'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
    	      'x', 'y', 'z', '0', '1' };//最后又重复两个0和1,因为需要凑足数组长度为64
    
    	  private static Random random = new Random();
    
    	  //生成指定长度的随机字符串
    	  public static synchronized String createRandomString(int length) {
    	    if (length > 0) {
    	      int index = 0;
    	      char[] temp = new char[length];
    	      int num = random.nextInt();
    	      for (int i = 0; i < length % 5; i++) {
    	        temp[index++] = ch[num & 63];//取后面六位,记得对应的二进制是以补码形式存在的。
    	        num >>= 6;//63的二进制为:111111
    	        // 为什么要右移6位?因为数组里面一共有64个有效字符。为什么要除5取余?因为一个int型要用4个字节表示,也就是32位。
    	      }
    	      for (int i = 0; i < length / 5; i++) {
    	        num = random.nextInt();
    	        for (int j = 0; j < 5; j++) {
    	          temp[index++] = ch[num & 63];
    	          num >>= 6;
    	        }
    	      }
    	      return new String(temp, 0, length);
    	    }
    	    else if (length == 0) {
    	      return "";
    	    }
    	    else {
    	      throw new IllegalArgumentException();
    	    }
    	  }
    
    
    	  
    }
    

      

    SignUtil.java
    package weixin;
    
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.*;
    
    
    public class SignUtil {
    	 /** 
         * 微信支付签名算法sign 
         * @param parameters
         * @return 
         */  
        @SuppressWarnings("unchecked")  
        public static String createSign(SortedMap<Object,Object> parameters,String key){  
            StringBuffer sb = new StringBuffer();  
            Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)  
            Iterator it = es.iterator();  
            while(it.hasNext()) {  
                Map.Entry entry = (Map.Entry)it.next();  
                String k = (String)entry.getKey();  
                Object v = entry.getValue();  
                if(null != v && !"".equals(v)   
                        && !"sign".equals(k) && !"key".equals(k)) {  
                    sb.append(k + "=" + v + "&");  
                }  
            }  
            sb.append("key=" + key);  
            String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();  
            return sign;  
        } 
        /**
         * 与接口配置信息中的 token 要一致,这里赋予什么值,在接口配置信息中的Token就要填写什么值,
         * 两边保持一致即可,建议用项目名称、公司名称缩写等,我在这里用的是项目名称weixinface
         */
        private static String token = "HR9QhjKMCoUQlwd";
         
        /**
         * 验证签名
         * @param signature
         * @param timestamp
         * @param nonce
         * @return
         */
        public static boolean checkSignature(String signature, String timestamp, String nonce){
            String[] arr = new String[]{token, timestamp, nonce};
            // 将 token, timestamp, nonce 三个参数进行字典排序
            Arrays.sort(arr);
            StringBuilder content = new StringBuilder();
            for(int i = 0; i < arr.length; i++){
                content.append(arr[i]);
            }
            MessageDigest md = null;
            String tmpStr = null;
             
            try {
                md = MessageDigest.getInstance("SHA-1");
                // 将三个参数字符串拼接成一个字符串进行 shal 加密
                byte[] digest = md.digest(content.toString().getBytes());
                tmpStr = byteToStr(digest);
            } catch (NoSuchAlgorithmException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            content = null;
            // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
            return tmpStr != null ? tmpStr.equals(signature.toUpperCase()): false;
        }
         
        /**
         * 将字节数组转换为十六进制字符串
         * @param digest
         * @return
         */
        private static String byteToStr(byte[] digest) {
            // TODO Auto-generated method stub
            String strDigest = "";
            for(int i = 0; i < digest.length; i++){
                strDigest += byteToHexStr(digest[i]);
            }
            return strDigest;
        }
         
        /**
         * 将字节转换为十六进制字符串
         * @param b
         * @return
         */
        private static String byteToHexStr(byte b) {
            // TODO Auto-generated method stub
            char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
            char[] tempArr = new char[2];
            tempArr[0] = Digit[(b >>> 4) & 0X0F];
            tempArr[1] = Digit[b & 0X0F];
             
            String s = new String(tempArr);
            return s;
        }
    }
    

      

    XmlPostUtil.java
    package weixin;
    
    
    import java.io.ByteArrayOutputStream;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    public class XmlPostUtil {
        public static byte[] sendXmlRequest(String path, String params) throws Exception {
            URL url = new URL(path);
            System.out.println("发送xml:" + params);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");// 提交模式
            conn.setDoOutput(true);// 是否输入参数
            conn.setRequestProperty("Pragma:", "no-cache");
            conn.setRequestProperty("Cache-Control", "no-cache");
            conn.setRequestProperty("Content-Type", "text/xml");
            byte[] bypes = params.toString().getBytes("UTF-8");
            conn.getOutputStream().write(bypes);// 输入参数
            InputStream inStream = conn.getInputStream();
            return readInputStream(inStream);
        }
    
        public static byte[] readInputStream(InputStream inStream) throws Exception {
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outStream.write(buffer, 0, len);
            }
            byte[] data = outStream.toByteArray();//网页的二进制数据
            outStream.close();
            inStream.close();
            System.out.println(new String(data, "utf-8"));
            return data;
        }
    
    }
    

      

    XMLUtil.java
    package weixin;
    
    import org.jdom.Document;
    import org.jdom.Element;
    import org.jdom.JDOMException;
    import org.jdom.input.SAXBuilder;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.*;
    
    /**
     *xml工具类
     *
     */
    public class XMLUtil {
    
    	/**
    	 * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
    	 * @param strxml
    	 * @return
    	 * @throws JDOMException
    	 * @throws IOException
    	 */
    	public static Map doXMLParse(String strxml) throws JDOMException, IOException {
    		if(null == strxml || "".equals(strxml)) {
    			return null;
    		}
    		
    		Map m = new HashMap();
    		InputStream in = String2Inputstream(strxml);
    		SAXBuilder builder = new SAXBuilder();
            /**********************修复部分内容*********************/
    
    		String FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
    		builder.setFeature(FEATURE, true);
    		 
    		FEATURE = "http://xml.org/sax/features/external-general-entities";
    		builder.setFeature(FEATURE, false);
    		 
    		FEATURE = "http://xml.org/sax/features/external-parameter-entities";
    		builder.setFeature(FEATURE, false);
    		 
    		FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
    		builder.setFeature(FEATURE, false);
    		
    		Document doc = builder.build(in);
    		Element root = doc.getRootElement();
    		List list = root.getChildren();
    		Iterator it = list.iterator();
    		while(it.hasNext()) {
    			Element e = (Element) it.next();
    			String k = e.getName();
    			String v = "";
    			List children = e.getChildren();
    			if(children.isEmpty()) {
    				v = e.getTextNormalize();
    			} else {
    				v = XMLUtil.getChildrenText(children);
    			}
    			
    			m.put(k, v);
    		}
    		
    
    		in.close();
    		
    		return m;
    	}
    	
    	/**
    	 * 获取子结点的xml
    	 * @param children
    	 * @return String
    	 */
    	public static String getChildrenText(List children) {
    		StringBuffer sb = new StringBuffer();
    		if(!children.isEmpty()) {
    			Iterator it = children.iterator();
    			while(it.hasNext()) {
    				Element e = (Element) it.next();
    				String name = e.getName();
    				String value = e.getTextNormalize();
    				List list = e.getChildren();
    				sb.append("<" + name + ">");
    				if(!list.isEmpty()) {
    					sb.append(XMLUtil.getChildrenText(list));
    				}
    				sb.append(value);
    				sb.append("</" + name + ">");
    			}
    		}
    		
    		return sb.toString();
    	}
    	
    
    	public static InputStream String2Inputstream(String str) {
    		return new ByteArrayInputStream(str.getBytes());
    	}
    
        public static String mapToXml(SortedMap<Object, Object> sortedMap){
            StringBuffer sb = new StringBuffer("<xml>");
            Iterator iterator = sortedMap.keySet().iterator();
            while (iterator.hasNext()) {
                Object key = (String) iterator.next();
                Object value = sortedMap.get(key);
                sb.append("<"+key+">");
                sb.append("<![CDATA["+value+"]]>");
                sb.append("</"+key+">");
            }
            sb.append("</xml>");
            return sb.toString();
        }
    
    }
    

      

    PaymentConfig.java
    package weixin;
    
    public class PaymentConfig {
    	/*******微信支付参数*********/
    	//公众账号ID
    	public static final String appid="";
    	//密钥
    	public static final String appKey="";
    	//商户号
    	public static final String mch_id="";
    	//接口地址
    	public static final String pay_url="https://api.mch.weixin.qq.com/pay/unifiedorder";
    	//支付返回地址
    	public static final String wxRetrun="";
    	//交易场景信息 具体参照微信官方文档不同支付类型的写法
    	public static final String scene_info = "{"h5_info": {"type":"Wap","wap_url": "https://pay.qq.com","wap_name": "微信支付"}} ";
    	
    	public static final int ENROLL_PRICE = 200;
    	
    }
    

       

    WeChatPay.java
    package weixin;
    
    import com.payment.util.RandomUtil;
    import com.payment.util.SignUtil;
    import com.payment.util.XMLUtil;
    import com.payment.util.XmlPostUtil;
    import org.jdom.JDOMException;
    
    import java.io.IOException;
    import java.util.Map;
    import java.util.SortedMap;
    import java.util.TreeMap;
    
    public class WeChatPay {
    
        /**
         * 二维码支付
         * @param orderNo
         * @param money
         * @param body
         * @param ip
         * @return
         */
        public Map getPaymentMapCode(String orderNo,int money,String body,String ip,String wxReturn){
            //一次签名
            SortedMap<Object, Object> paramMap = new TreeMap<Object, Object>();
            paramMap.put("appid",PaymentConfig.appid);//公众号ID
            paramMap.put("mch_id",PaymentConfig.mch_id);//商户号
            paramMap.put("nonce_str", RandomUtil.createRandomString(32));//32位随机字符串
            paramMap.put("body",body);//商品描述
            paramMap.put("out_trade_no",orderNo);//商户订单号
            paramMap.put("total_fee",String.valueOf(money));//设置交易金额 金额为分
            //paramMap.put("total_fee",1);//设置交易金额 金额为分
            paramMap.put("spbill_create_ip",ip);//客户机IP
            paramMap.put("notify_url",wxReturn);//通知地址
            paramMap.put("trade_type","NATIVE");//支付方式 原生扫码
            paramMap.put("product_id", "shangpingid"); //自行定义
            paramMap.put("sign", SignUtil.createSign(paramMap, PaymentConfig.appKey));
            String rXml = "";
            String prepayid="";
            try {
                rXml = new String(XmlPostUtil.sendXmlRequest(PaymentConfig.pay_url, XMLUtil.mapToXml(paramMap)));
                prepayid = (String) XMLUtil.doXMLParse(rXml).get("prepay_id");//得到预支付id
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //二次签名
            SortedMap<Object, Object> paramMap1 = new TreeMap<Object, Object>();
            paramMap1.put("appId", PaymentConfig.appid);
            paramMap1.put("timeStamp", System.currentTimeMillis());
            paramMap1.put("package", "prepay_id="+prepayid);
            paramMap1.put("signType", "MD5");
            paramMap1.put("nonceStr", RandomUtil.createRandomString(32));
            paramMap1.put("paySign", SignUtil.createSign(paramMap1, PaymentConfig.appKey));
    
            try {
                Map map = XMLUtil.doXMLParse(rXml);
                System.out.println("return_code:"+map.get("return_code"));
                System.out.println("code_url:"+map.get("code_url"));
                return map;
            } catch (JDOMException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return paramMap1;
    
        }
    
        //通知微信正确接收
        public static String getSuccessXml() {
            String xml = "<xml>" +
                    "<return_code><![CDATA[SUCCESS]]></return_code>" +
                    "<return_msg><![CDATA[OK]]></return_msg>" +
                    "</xml>";
            return xml;
        }
    
    }


     

    调用类方法,需要自行完善逻辑代码

    public void weixinPay(HttpServletRequest request){
            //请求IP地址
            String ip = request.getRemoteAddr();
            //发起支付
            WeChatPay weChatPay = new WeChatPay();
            //wxReturn 为微信异步回调地址,这里可以根据自己的方式获取
            String wxReturn = PropertyUtils.getPropertyValue(new File(realPathResolver.get(CONFIG)), WEIXIN_NOTICE_URL);
    
            /**
             * 调用微信支付
             * order.getOrderNo() 订单号
             *  price 订单价格精度转换后的价格   order.getPrice()  订单价格,因为微信是分为单位 所以这里要乘以100   关于支付精度转换,可以查看 https://www.cnblogs.com/pxblog/p/13186037.html
             */

         int price=0.01; Map map
    = weChatPay.getPaymentMapCode(order.getOrderNo(),price, "微信支付", ip, wxReturn); String return_code = String.valueOf(map.get("return_code")); if (return_code.equals("SUCCESS")) { //微信调用成功
            //code_url是支付的链接
            request.getSession().setAttribute("code_url", map.get("code_url") + ""); 
            //跳转到支付页面
            } else {
                //微信支付调取失败!
            }
        }
    QRCodeUtil.java
    package weixin;
    
    import com.google.zxing.BarcodeFormat;
    import com.google.zxing.EncodeHintType;
    import com.google.zxing.MultiFormatWriter;
    import com.google.zxing.common.BitMatrix;
    import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
    
    import javax.imageio.ImageIO;
    import java.awt.*;
    import java.awt.geom.RoundRectangle2D;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.util.Hashtable;
    
    public class QRCodeUtil {
    
        private static final String CHARSET = "utf-8";
        private static final String FORMAT_NAME = "JPG";
        // 二维码尺寸
        private static final int QRCODE_SIZE = 300;
        // LOGO宽度
        private static final int WIDTH = 60;
        // LOGO高度
        private static final int HEIGHT = 60;
    
        private static BufferedImage createImage(String content, String imgPath,
                                                 boolean needCompress) throws Exception {
            Hashtable hints = new Hashtable();
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
            hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
            hints.put(EncodeHintType.MARGIN, 1);
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content,
                    BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);
            int width = bitMatrix.getWidth();
            int height = bitMatrix.getHeight();
            BufferedImage image = new BufferedImage(width, height,
                    BufferedImage.TYPE_INT_RGB);
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000
                            : 0xFFFFFFFF);
                }
            }
            if (imgPath == null || "".equals(imgPath)) {
                return image;
            }
            // 插入图片
            QRCodeUtil.insertImage(image, imgPath, needCompress);
            return image;
        }
    
    
        private static void insertImage(BufferedImage source, String imgPath,
                                        boolean needCompress) throws Exception {
            File file = new File(imgPath);
            if (!file.exists()) {
                System.err.println(""+imgPath+"   该文件不存在!");
                return;
            }
            Image src = ImageIO.read(new File(imgPath));
            int width = src.getWidth(null);
            int height = src.getHeight(null);
            if (needCompress) { // 压缩LOGO
                if (width > WIDTH) {
                    width = WIDTH;
                }
                if (height > HEIGHT) {
                    height = HEIGHT;
                }
                Image image = src.getScaledInstance(width, height,
                        Image.SCALE_SMOOTH);
                BufferedImage tag = new BufferedImage(width, height,
                        BufferedImage.TYPE_INT_RGB);
                Graphics g = tag.getGraphics();
                g.drawImage(image, 0, 0, null); // 绘制缩小后的图
                g.dispose();
                src = image;
            }
            // 插入LOGO
            Graphics2D graph = source.createGraphics();
            int x = (QRCODE_SIZE - width) / 2;
            int y = (QRCODE_SIZE - height) / 2;
            graph.drawImage(src, x, y, width, height, null);
            Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
            graph.setStroke(new BasicStroke(3f));
            graph.draw(shape);
            graph.dispose();
        }
    
        //获取生成二维码的图片流
        public static ByteArrayOutputStream encodeIO(String content,String imgPath,Boolean needCompress) throws Exception {
            BufferedImage image = QRCodeUtil.createImage(content, imgPath,
                    needCompress);
            //创建储存图片二进制流的输出流
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            //将二进制数据写入ByteArrayOutputStream
            ImageIO.write(image, "jpg", baos);
            return baos;
        }
    }

    生成二维码请求

      @RequestMapping(value = "/get_qr_code")
        public void getQrCode(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //从session中获取前面放在code_url地址
    String content = request.getSession().getAttribute("code_url") + ""; System.out.printf(content); //二维码图片中间logo String imgPath = ""; Boolean needCompress = true; //通过调用我们的写的工具类,拿到图片流 ByteArrayOutputStream out = QRCodeUtil.encodeIO(content, imgPath, needCompress); //定义返回参数 response.setCharacterEncoding("UTF-8"); response.setContentType("image/jpeg;charset=UTF-8"); response.setContentLength(out.size()); ServletOutputStream outputStream = response.getOutputStream(); outputStream.write(out.toByteArray()); outputStream.flush(); outputStream.close(); }

    支付页面代码、显示二维码,由于微信支付没有同步支付通知,所以需要在这个页面上写轮询方法,查询自己数据库订单,判断是否已经支付

    <img src="/get_qr_code"><br/>

    轮询js

    //2秒轮询一次
    setInterval("get_pay_status()",2000);
    
    //轮询订单支付状态
        function get_pay_status() {
            $.ajax({
                url:'/get_order_status',
                data:{
                    orderNo:'订单编号'
                },
                success:function (data) {
                    if (data.code==1000){
                        //如果后台返回支付成功,则跳转到支付成功页面
                        window.location.href="}/to_weixin_return_url.jspx?orderNo=订单编号";
                    }
                }
            })
        }

    微信异步回调类,需要自行完善逻辑代码

     /**
         * 微信支付通知
         *
         * @return
         */
        @ResponseBody
        @RequestMapping(value = "/wechart_notice")
        public String wechartNotice(HttpServletRequest request, HttpServletResponse response, ModelMap model) {
            String result = "";
            try {
                InputStream inStream = request.getInputStream();
                ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = inStream.read(buffer)) != -1) {
                    outSteam.write(buffer, 0, len);
                }
                outSteam.close();
                inStream.close();
                result = new String(outSteam.toByteArray(), "utf-8");
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //判断返回报文是否为空
            if (StringUtils.isNotBlank(result)) {
                try {
                    Map<String, String> map = XMLUtil.doXMLParse(result);
                    //获取商家订单编号 对应orderNo
                    String orderNo = map.get("out_trade_no");
    //获取微信支付订单号 String transaction_id = map.get("transaction_id"); Order order = orderMng.findByOrderNo(orderNo); //判断支付是否成功 if ("SUCCESS".equals(map.get("result_code"))) { //支付成功,这里之所以加了一个判断,是因为这个回调可能会有多次,所以我们只有当订单状态时未支付的情况下,才执行下面操作 if (!Constants.ORDER_SUCCESS.equals(order.getStatus())) { //当微信支付成功后,把订单支付状态改为已支付 order.setStatus(Constants.ORDER_SUCCESS); } //处理业务逻辑 } else { //支付失败 order.setStatus(Constants.ORDER_FAIL); }
    //更新数据库订单状态 orderMng.update(order); }
    catch (JDOMException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } WeChatPay weChatPay = new WeChatPay(); return weChatPay.getSuccessXml(); }
  • 相关阅读:
    python之路 --- python文件模式
    实现购物车功能 --- 文件操作版
    python之路 --- python模块初认识&数据类型
    web自动化之selenium
    python之路 --- python基础
    Ios App破解之路二 JJ斗地主
    记一次 CocoaPod 的使用过程
    Mac 电脑查看 pkg包的安装路径
    使用vscode 开发go项目的最新姿势. go版本1.14.2
    IOS App破解之路一 拿到appstore上的ipa
  • 原文地址:https://www.cnblogs.com/pxblog/p/10542917.html
Copyright © 2020-2023  润新知