zoukankan      html  css  js  c++  java
  • 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(); }
  • 相关阅读:
    高级特性(4)- 数据库编程
    UVA Jin Ge Jin Qu hao 12563
    UVA 116 Unidirectional TSP
    HDU 2224 The shortest path
    poj 2677 Tour
    【算法学习】双调欧几里得旅行商问题(动态规划)
    南洋理工大学 ACM 在线评测系统 矩形嵌套
    UVA The Tower of Babylon
    uva A Spy in the Metro(洛谷 P2583 地铁间谍)
    洛谷 P1095 守望者的逃离
  • 原文地址:https://www.cnblogs.com/pxblog/p/10542917.html
Copyright © 2011-2022 走看看