一:需求
满足公司在网页上达到直接微信扫码支付的需求
二:API官方文档
参考链接:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
三:微信支付的过程(用户-商家-微信服务器)
四:代码实现
实体类: 参考链接:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
1.统一下单请求参数
/** * 统一下单请求参数(必填) */ @XmlRootElement <span>此处加注解是为了后台xmljie public class UnifiedOrderRequest { private String appid; //ID private String mch_id; //商户号 private String nonce_str; //随机字符串 private String sign; //数字签名 private String body; //商品描述 private String out_trade_no; //商户订单 private String total_fee; //总金额 private String spbill_create_ip; //终端Ip private String notify_url; //通知地址 private String trade_type; //交易类型 public UnifiedOrderRequest(){}; public UnifiedOrderRequest(String appid, String mch_id, String nonce_str, String sign, String body, String out_trade_no, String total_fee, String spbill_create_ip, String notify_url, String trade_type) { super(); this.appid = appid; this.mch_id = mch_id; this.nonce_str = nonce_str; this.sign = sign; this.body = body; this.out_trade_no = out_trade_no; this.total_fee = total_fee; this.spbill_create_ip = spbill_create_ip; this.notify_url = notify_url; this.trade_type = trade_type; } }
统一下单参数(非必须)3.订单返回参数/** * 统一下单请求参数(非必填) */ @XmlRootElement <span>此处加注解是为了xml解析</span> public class UnifiedOrderRequestExt extends UnifiedOrderRequest{ private String device_info; //设备号 private String detail; //商品详情 private String attach; //附加数据 private String fee_type; //货币类型 private String time_start; //交易起始时间 private String time_expire; //交易结束时间 private String goods_tag; //商品标记 private String product_id; //商品ID private String limit_pay; //指定支付方式 private String openid; //用户标志 public UnifiedOrderRequestExt(){ super(); } public UnifiedOrderRequestExt(String appid, String mch_id, String nonce_str, String sign, String body, String out_trade_no, String total_fee, String spbill_create_ip, String notify_url, String trade_type, String device_info, String detail, String attach, String fee_type, String time_start, String time_expire, String goods_tag, String product_id, String limit_pay, String openid) { super(appid, mch_id, nonce_str, sign, body, out_trade_no, total_fee, spbill_create_ip, notify_url, trade_type); this.device_info = device_info; this.detail = detail; this.attach = attach; this.fee_type = fee_type; this.time_start = time_start; this.time_expire = time_expire; this.goods_tag = goods_tag; this.product_id = product_id; this.limit_pay = limit_pay; this.openid = openid; } }
/** * 统一下单返回参数 * */
@XmlAccessorType(XmlAccessType.FIELD)@XmlRootElement(name = "UnifiedOrderRespose")@XmlType(propOrder={"return_code","return_msg","appid","mch_id","device_info","nonce_str","sign","result_code","err_code","err_code_des","trade_type","prepay_id","code_url"})public class UnifiedOrderRespose {private String return_code; //返回状态码private String return_msg; //返回信息private String appid; //公众账号IDprivate String mch_id; //商户号private String device_info; //设备号private String nonce_str; //随机字符串private String sign; //数字签名private String result_code; //业务结果private String err_code; //错误代码private String err_code_des; // 错误代码描述private String trade_type; //交易类型private String prepay_id; //预支付交易会话标致private String code_url; //二维码链接public UnifiedOrderRespose(){}public UnifiedOrderRespose(String return_code, String return_msg,String appid, String mch_id, String device_info, String nonce_str,String sign, String result_code, String err_code,String err_code_des, String trade_type, String prepay_id,String code_url) {super();this.return_code = return_code;this.return_msg = return_msg;this.appid = appid;this.mch_id = mch_id;this.device_info = device_info;this.nonce_str = nonce_str;this.sign = sign;this.result_code = result_code;this.err_code = err_code;this.err_code_des = err_code_des;this.trade_type = trade_type;this.prepay_id = prepay_id;this.code_url = code_url;}<span>此处加注解是为了xml解析</span>
二:xml解析(二种解析类型,已封装,可直接使用)
四:现在实体类和转换方法都已经贴出。现在就是后台接口的编写了import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class XMLObjectConvertUtil { /** * 将xml文件转换为Map集合 * @param String类型的xml * @return map集合 */ public static Map<String, String> getMap(String xml) throws Exception{ try{ Map<String, String> maps = new HashMap<String, String>(); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputStream inputStream = new ByteArrayInputStream(xml.getBytes("utf-8")); Document document = documentBuilder.parse(inputStream); document.getDocumentElement().normalize(); //获得所有的节点 NodeList nodeList = document.getDocumentElement().getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); //如果为标记节点 如 <username> if(node.getNodeType() == node.ELEMENT_NODE){ Element e = (Element) node; //将节点名称与节点内容放进map集合中 maps.put(e.getNodeName(),e.getTextContent()); } } return maps; }catch(Exception e){ e.printStackTrace(); } return null; } /** * 将Map集合转换成String类型的xml * @param maps * @return * @throws Exception */ public String getXml(Map<String, String> maps) throws Exception{ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document document = documentBuilder.newDocument(); //创建根目录 Element root = document.createElement("xml"); //添加根目录进文档 document.appendChild(root); //遍历所有的key for(String key:maps.keySet()){ //获得key所对应的value String value= maps.get(key); if(value == null){ value =""; } //去掉空格 value = value.trim(); //创建子节点 Element filed = document.createElement(key); //将key所对应的value放入node中 filed.appendChild(document.createTextNode(value)); //将子节点放入根节点中 root.appendChild(filed); } TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer =transformerFactory.newTransformer(); DOMSource domSource = new DOMSource(document); transformer.setOutputProperty(OutputKeys.ENCODING,"utf-8"); transformer.setOutputProperty(OutputKeys.INDENT,"yes"); StringWriter sw = new StringWriter(); StreamResult sr = new StreamResult(sw); transformer.transform(domSource, sr); String output = sw.getBuffer().toString(); return output; } /** * 将java对象转换成String类型的xml * @param obj * @return * @throws Exception */ public String getXml(Object obj) throws Exception{ StringWriter sw = new StringWriter(); JAXBContext context = JAXBContext.newInstance(obj.getClass()); Marshaller mar = context.createMarshaller(); //统一编码 mar.setProperty(mar.JAXB_ENCODING, "utf-8"); mar.setProperty(mar.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); mar.marshal(obj, sw); return sw.toString(); } }
package com.util; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.StringReader; import java.io.StringWriter; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.UUID; import javax.servlet.http.HttpServletResponse; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.struts2.ServletActionContext; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import com.bean.UnifiedOrderRequest; import com.bean.UnifiedOrderRespose; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.common.BitMatrix; import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable; public class MainAction { String orderId; HttpServletResponse response; String msg; /** * 创建二维码 */ public void createCode(){ //创建订单 String orderInfo = createOrderInfo(orderId); if(orderInfo!=null){ //得到统一下单 API String codeUrl = httpOrder(orderInfo); if(codeUrl!=null){ //利用 返回的预支付交易链接(codeUrl) 生成扫描的二维码 try { int width =200; //宽200 int height = 200; //高200 String format = "jpg"; //图片格式 //开始创建二维码 Hashtable hs = new Hashtable(); //设置编码格式 hs.put(EncodeHintType.CHARACTER_SET, "utf-8"); //设置二维码 BitMatrix bit = new MultiFormatWriter().encode(codeUrl, BarcodeFormat.QR_CODE, width, height); OutputStream out = null; out = ServletActionContext.getResponse().getOutputStream(); MatrixToImageWriter.writeToStream(bit,format,out); out.flush(); out.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * 调统一下单API */ private String httpOrder(String orderInfo) { //微信支付统一接口 String url ="https://api.mch.weixin.qq.com/pay/unifiedorder"; try { //连接微信统一支付url HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); //加入数据 conn.setRequestMethod("POST"); //打开传输输出流 conn.setDoOutput(true); //获取输出流 BufferedOutputStream buffer = new BufferedOutputStream(conn.getOutputStream()); buffer.write(orderInfo.getBytes()); buffer.flush(); buffer.close(); //获取输入流 BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); //接受数据 String line = null; StringBuffer sb = new StringBuffer(); //将输入流中的信息放在sb中 while((line=reader.readLine())!=null){ sb.append(line); }; //解析xml成对象 JAXBContext context = JAXBContext.newInstance(UnifiedOrderRespose.class); //反序列化成对象 Unmarshaller unmarshaller = context.createUnmarshaller(); //读取数据 StringReader sr = new StringReader(sb.toString()); //根据微信Demo 将xml转换成Map Map<String, String> ResponseMap = new HashMap<String, String>(); //创建文档类型 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); //将获取到的xml放入输入流中 InputStream in = new ByteArrayInputStream(sb.toString().getBytes("utf-8")); Document doc; doc = db.parse(in); NodeList nl = doc.getDocumentElement().getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if(node.getNodeType() == node.ELEMENT_NODE){ Element e = (Element) node; ResponseMap.put(e.getNodeName(), e.getTextContent()); } } /* 转换成UnifiedOrderRespose对象 UnifiedOrderRespose unRespose = (UnifiedOrderRespose) unmarshaller.unmarshal(new File("C:/Users/Administrator/Workspaces/MyEclipse 10/weixinpay/WebRoot/xmls/Result.xml")); UnifiedOrderRespose unRespose = (UnifiedOrderRespose) unmarshaller.unmarshal(sr); System.out.println(unRespose.getReturn_code()+"|"+unRespose.getReturn_msg()); unRespose.setReturn_code("SUCCESS"); unRespose.setReturn_msg("SUCCESS"); unRespose.setCode_url("www.baidu.com");*/ //如果请求成功返回 ,则返回支付链接 if(null!=ResponseMap && "SUCCESS".equals(ResponseMap.get("return_code")) && "SUCCESS".equals(ResponseMap.get("return_msg"))){ return ResponseMap.get("code_url"); }else{ return null; } }catch (SAXException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ParserConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (JAXBException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } /** * 生成订单,返回xml文件 */ private String createOrderInfo(String orderId) { UnifiedOrderRequest un = new UnifiedOrderRequest(); un.setAppid("xxxxxxxxxxxxx");//公众账号ID un.setMch_id("xxxxxxxxx");//商户号 un.setNonce_str(UUID.randomUUID().toString().substring(0,30));//随机字符串 <span style="color:#ff0000;"><strong>说明2(见文末)</strong></span> un.setBody("xxxxxx");//商品描述 un.setOut_trade_no(orderId);//商户订单号 un.setTotal_fee("10000"); //金额需要扩大100倍:1代表支付时是0.01 un.setSpbill_create_ip("xxxxxxxxxxxxx");//终端IP un.setNotify_url("xxxxxxxxxxxxxx");//支付成功后,回调的地址 un.setSign(createSign(un));//签名 un.setTrade_type("NATIVE");//JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付 try { //将订单对象转为xml格式 StringWriter sw = new StringWriter(); JAXBContext context = JAXBContext.newInstance(un.getClass()); Marshaller mar = context.createMarshaller(); mar.setProperty(mar.JAXB_ENCODING, "utf-8"); mar.setProperty(mar.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); mar.marshal(un, sw); /*将xml转换为对象 JAXBContext context = JAXBContext.newInstance(UnifiedOrderRequest.class); Unmarshaller unmarshaller = context.createUnmarshaller(); UnifiedOrderRequest unifiedOrderRequest = (UnifiedOrderRequest)unmarshaller.unmarshal(new StringReader("xml文件")); */ return sw.toString(); } catch (JAXBException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } /** * 生成数字签名 */ private String createSign(UnifiedOrderRequest un) { //创建可排序的Map集合 SortedMap<String, String> packageParms = new TreeMap<String, String>(); packageParms.put("appid", un.getAppid()); packageParms.put("body", un.getBody()); packageParms.put("mch_id", un.getMch_id()); packageParms.put("nonce_str", un.getNonce_str()); packageParms.put("notify_url", un.getNotify_url()); packageParms.put("out_trade_no", un.getOut_trade_no()); packageParms.put("spbill_create_ip", un.getSpbill_create_ip()); packageParms.put("trade_type", un.getTrade_type()); packageParms.put("total_fee", un.getTotal_fee()); StringBuffer sb = new StringBuffer(); //按照字典排序 Set se = packageParms.entrySet(); //迭代器 Iterator iterator = se.iterator(); while(iterator.hasNext()){ Map.Entry entry = (Entry) iterator.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); //为空不参与签名,参数名区分大小写 if(null!=v && !"".equals(v) && !"key".equals(k) && !"sign".equals(k)){ sb.append(k+ "="+v+"&"); } } /*拼接 key,设置路径:微信商户平台(pay.weixin.com)->账户设置->API安全-->秘钥设置 */ sb.append("key="+"xxxxxxxxxxx"); MD5Utile md5= new MD5Utile(); String sign = md5.getMD5(sb.toString(),"utf-8"); return sign; } public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } }
看到这里,包括二维码的制作,与用户,服务器的交互都已完成。如有不懂,可以私信!