微信开发入门
- - 行业应用 - ITeye博客【做微信平台开发需要以下步骤,wx.zip示例可以参考,修改配置即可】. 1.申请一个公众号(订阅号或者服务号). 2.需要有自己的服务器(建议使用花生壳做内网映射). * 检查是否是微信发送的请求. * @param signature 签名. * @param timestamp 时间戳. * @param nonce 随机数.
【做微信平台开发需要以下步骤,wx.zip示例可以参考,修改配置即可】
1.申请一个公众号(订阅号或者服务号)
2.需要有自己的服务器(建议使用花生壳做内网映射)
3.配置微信服务器
4.编写后台代码
*微信入口
package com.wx.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.code.util.WeixinHanlder; import com.code.util.WeixinHanlder.TextMsg; @WebServlet("/weixin_service") public class WxServlet extends HttpServlet{ private static final long serialVersionUID = 1L; /** * 验证微信信息 */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String signature,timestamp,nonce,echostr; signature = req.getParameter("signature"); timestamp = req.getParameter("timestamp"); nonce = req.getParameter("nonce"); echostr = req.getParameter("echostr"); try { if(WeixinHanlder.checkSgin(signature, timestamp, nonce)){ ServletOutputStream out = resp.getOutputStream(); out.print(echostr); out.close(); }else{ System.out.println("error invoke method failure "); } } catch (Exception e) { e.printStackTrace(); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletInputStream in = req.getInputStream(); try { Object obj = WeixinHanlder.XML.getMessage(in); System.out.println(obj); if(obj instanceof WeixinHanlder.TextMsg){ TextMsg msg = (TextMsg)obj; System.out.println(msg.getContent()); } } catch (Exception e) { e.printStackTrace(); } } }
* 微信加密工具类
package com.code.util; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.MessageFormat; import java.util.Arrays; import java.util.Formatter; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.UUID; import javax.servlet.ServletContext; import net.sf.json.JSONObject; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * 微信开发工具类 * @author LGF * */ public class WeixinHanlder { public static Integer CHANGE_TOKEN_TIME = 7000; public static String TOKEN = "weixin"; public static String OPENID = "gh_d97c8ce11b95"; public static String APPID = "wx1d80f7014b33404b"; public static String APPSECRET = "7eb7a1f207e905c41b7c7bb56221e3df"; public static String URL_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx1d80f7014b33404b&secret=7eb7a1f207e905c41b7c7bb56221e3df"; public static String URL_JS_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi"; static{ Properties props = new Properties(); try { props.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("weixin.properties")); OPENID = props.get("open_id").toString(); APPID = props.get("app_id").toString(); APPSECRET = props.get("app_secret").toString(); TOKEN = props.get("token").toString(); URL_ACCESS_TOKEN = props.get("url_access_token").toString(); URL_JS_TICKET = props.get("url_js_ticket").toString(); CHANGE_TOKEN_TIME = Integer.parseInt(props.get("change_token_time").toString()); } catch (IOException e) { e.printStackTrace(); } } /** * 检查是否是微信发送的请求 * @param signature 签名 * @param timestamp 时间戳 * @param nonce 随机数 * @return * @throws Exception */ public static boolean checkSgin(String signature,String timestamp,String nonce) throws Exception{ String[] strs = new String[]{timestamp,nonce,TOKEN}; Arrays.sort(strs); StringBuffer sb = new StringBuffer(); for (String string : strs) { sb.append(string); } String sha1 = SecurityHanlder.sha1(sb.toString()); if(sha1.equals(signature)){ return true; } return false; } /** * 获取系统默认时间 * @return */ public static Long getTime(){ return System.currentTimeMillis(); } /** * 关键字检索,默认逗号分隔 * @param source * @param kws * @return */ public static boolean keywordCheck(String source,String kws,String splitor){ if(source!=null){ String[] split = kws.split(splitor); for (Object kw : split) { if(source.indexOf(kw.toString())!=-1||(kw!=null&&kw.toString().indexOf(source)!=-1)){ return true; } } } return false; } /** * 关键字检索,默认逗号分隔 * @param source * @param kws * @return */ public static boolean keywordCheck(String source,String kws){ return keywordCheck(source,kws,","); } /** * 读取字符串 * @param input * @return * @throws IOException */ public static String readString(InputStream input) throws IOException{ BufferedInputStream bis = new BufferedInputStream(input); byte[] bs = new byte[1024]; StringBuffer sb = new StringBuffer(); while(bis.read(bs)!=-1){ sb.append(new String(bs)); } input.close(); bis.close(); return sb.toString(); } /** * JS 签名验证 * @param jsapi_ticket * @param url * @return */ public static Map<String, String> sign(String jsapi_ticket, String url) { Map<String, String> ret = new HashMap<String, String>(); String nonce_str = create_nonce_str(); String timestamp = create_timestamp(); String signature = ""; String string1; //注意这里参数名必须全部小写,且必须有序 //注意这里参数名必须全部小写,且必须有序 string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "×tamp=" + timestamp + "&url=" + url; System.out.println("************"+string1); try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.toString().getBytes("UTF-8")); signature = byteToHex(crypt.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } ret.put("url", url); ret.put("jsapi_ticket", jsapi_ticket); ret.put("nonceStr", nonce_str); ret.put("timestamp", timestamp); ret.put("signature", signature); return ret; } /** * 转换成十六进制 * @param hash * @return */ private static String byteToHex(final byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", b); } String result = formatter.toString(); formatter.close(); return result; } /** * 随机字符串 * @return */ private static String create_nonce_str() { return UUID.randomUUID().toString(); } /** * 随机时间 * @return */ private static String create_timestamp() { return Long.toString(System.currentTimeMillis() / 1000); } /** * XML 文件处理,获取XML文件 * @author LGF * */ public static class XML { public static String XML = "xml"; public static String TOUSERNAME = "ToUserName"; public static String FROMUSERNAME = "FromUserName"; public static String CREATETIME = "CreateTime"; public static String MSGTYPE = "MsgType"; public static String CONTENT = "Content"; public static String VIDEO = "video"; public static String MEDIAID = "MediaId"; public static String TITLE = "Title"; public static String DESCRIPTION = "Description"; public static String MUSICURL = "MusicURL"; public static String HQMUSICURL = "HQMusicUrl"; public static String THUMBMEDIAID = "ThumbMediaId"; public static String ARTICLES = "Articles"; public static String ITEM = "item"; public static String PICURL = "PicUrl"; public static String URL = "URL"; //消息类型 public static String MSGTYPE_TEXT = "text"; public static String MSGTYPE_IMAGE = "image"; public static String MSGTYPE_VIDO = "video"; public static String MSGTYPE_VOICE = "voice"; /** * 传入输入流获取Document对象 * @param input * @return * @throws Exception */ public static Document getDocuemnt(InputStream input) throws Exception{ return new SAXReader().read(input); } /** * XML输入流获取一个对象 * @param <T> * @param input * @return * @throws Exception */ @SuppressWarnings("unchecked") public static <T> T getMessage(InputStream input,Class<T> clazz) throws Exception{ Document d = getDocuemnt(input); Element root = d.getRootElement(); String type = root.element(MSGTYPE).getText(); String to = root.element(TOUSERNAME).getText(); String from = root.element(FROMUSERNAME).getText(); String time = root.element(CREATETIME).getText(); if(MSGTYPE_TEXT.equals(type)){ String content = root.element(CONTENT).getText(); return (T) new TextMsg(to,from,time==null||time.isEmpty()?null:Long.parseLong(time),type,content); }else if(MSGTYPE_IMAGE.equals(type)){ }else if(MSGTYPE_VIDO.equals(type)){ }else if(MSGTYPE_VOICE.equals(type)){ } return (T) d.asXML(); } /** * XML输入流获取一个对象 * @param input * @return * @throws Exception */ public static Object getMessage(InputStream input) throws Exception{ return getMessage(input,Object.class); } /** * 回复文本消息 * @param to * @param from * @param time * @param type * @param content * @return */ public static String getSendTextMessage(String to,Long time,String content){ Element r = getBaseMessageElement(to,time); r.addElement(CONTENT).addCDATA(content); return r.getDocument().asXML(); } /** * 设置基本回复消息 * @param to * @param from * @param time * @param type * @param content * @return */ private static Element getBaseMessageElement(String to,Long time){ Document d = DocumentHelper.createDocument(); Element r = d.addElement(XML); r.addElement(TOUSERNAME).addCDATA(to); r.addElement(FROMUSERNAME).addCDATA(WeixinHanlder.OPENID); r.addElement(CREATETIME).addCDATA(time.toString()); r.addElement(MSGTYPE).addCDATA(MSGTYPE_TEXT); return r; } /** * 回复文本消息 * @param to * @param content * @return */ public static String getSendTextMessage(String to,String content){ return getSendTextMessage(to,WeixinHanlder.getTime(),content); } } /** * 微信消息父类实体 * @author LGF * */ public static class Msg{ private String toUserName; private String fromUserName; private Long createTime; private String msgType; public Msg(String toUserName, String fromUserName, Long createTime, String msgType) { this.toUserName = toUserName; this.fromUserName = fromUserName; this.createTime = createTime; this.msgType = msgType; } public Msg() { } public String getToUserName() { return toUserName; } public void setToUserName(String toUserName) { this.toUserName = toUserName; } public String getFromUserName() { return fromUserName; } public void setFromUserName(String fromUserName) { this.fromUserName = fromUserName; } public Long getCreateTime() { return createTime; } public void setCreateTime(Long createTime) { this.createTime = createTime; } public String getMsgType() { return msgType; } public void setMsgType(String msgType) { this.msgType = msgType; } } /** * 文本消息实体类 * @author LGF * */ public static class TextMsg extends Msg { private String content; public TextMsg() { } public TextMsg(String toUserName, String fromUserName, Long createTime, String msgType, String content) { super(toUserName,fromUserName,createTime,msgType); this.content = content; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } /** * 获取微信 access_token 线程 * @author LGF * */ public static class ThreadAccessToken implements Runnable{ private ServletContext servletContext; //实例化后启动线程 public ThreadAccessToken(ServletContext servletContext) { this.servletContext = servletContext; servletContext.setAttribute("wx_openId", OPENID); servletContext.setAttribute("wx_appId", APPID); servletContext.setAttribute("wx_appSecret", APPSECRET); setToken(); new Thread(this).start(); } @Override public void run() { while(true){ try { //两小时刷新一次签名 Thread.sleep(60*1000); setToken(); } catch (InterruptedException e) { e.printStackTrace(); new ThreadAccessToken(servletContext); } } } /** * 设置 access_token */ private synchronized void setToken(){ try { InputStream input = URLHanlder.getInputStream(URL_ACCESS_TOKEN); JSONObject json = JSONObject.fromObject(readString(input)); String wx_access_token = json.get(Const.ACCESS_TOKEN).toString(); servletContext.setAttribute("wx_access_token", wx_access_token); System.out.println("get access access_token success " + wx_access_token); InputStream input1 = URLHanlder.getInputStream(MessageFormat.format(URL_JS_TICKET, wx_access_token)); JSONObject json1 = JSONObject.fromObject(readString(input1)); String wx_ticket = json1.get(Const.TICKET).toString(); servletContext.setAttribute("wx_ticket", wx_ticket); System.out.println("get access ticket success " + wx_ticket); } catch (Exception e) { e.printStackTrace(); } } } } /** * 常量 * @author LGF * */ interface Const{ public static String ACCESS_TOKEN = "access_token"; public static String TICKET = "ticket"; }
* JS安全域名验证
package com.wx.controller; import java.io.IOException; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.code.util.JSONHanlder; import com.code.util.WeixinHanlder; @WebServlet("/weixin_sign") public class WXSign extends HttpServlet{ private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); resp.setCharacterEncoding("UTF-8"); String url = req.getParameter("url"); String jsapi_ticket = req.getServletContext().getAttribute("wx_ticket").toString(); String appid = req.getServletContext().getAttribute("wx_appId").toString(); Map<String, String> sign = WeixinHanlder.sign(jsapi_ticket, url); sign.put("appId", appid); String string = JSONHanlder.getObjectAsString(sign); ServletOutputStream out = resp.getOutputStream(); out.print(string); System.out.println(sign); out.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
* JSON 工具类
package com.code.util; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Iterator; import java.util.List; import net.sf.json.JSONObject; import net.sf.json.JsonConfig; import net.sf.json.processors.JsonValueProcessor; public class JSONHanlder { /** * 获取JSON数据 * @param key * @param source * @return */ public static String getVal(String key,String json){ if(key!=null&&key.trim().isEmpty()==false&&key.indexOf(".")!=-1&&json!=null&&json.trim().isEmpty()==false){ String[] ks = key.split("[.]"); List<String> list = Arrays.asList(ks); return getVal(json,list.iterator()); }else if(key!=null&&key.trim().isEmpty()==false){ return JSONObject.fromObject(json).get(key).toString(); } return null; } /** * 将对象转换成 JSON * @param obj * @return */ public static String getObjectAsString(Object obj){ return getObjectAsString(null,null,obj); } /** * 将对象转换成 JSON * @param obj * @return */ public static String getObjectAsString(Object ... obj){ return getObjectAsString(null,null,obj); } /** * 将对象转换成 JSON * @param parrent 时间格式 * @param excludes 不包含的字段 * @param obj * @return */ public static String getObjectAsString(String parrent,String excludes,Object... obj){ if(obj==null||obj.length==0){ return "{}"; } if(obj.length==1){ return JSONObject.fromObject(obj[0],Config.getInstance(parrent,excludes)).toString(); } Data data = new Data(); data.getRecords().addAll(Arrays.asList(obj)); return JSONObject.fromObject(data,Config.getInstance(parrent,excludes)).toString(); } /** * 将对象转换成 JSON * @param excludes * @param obj * @return */ public static String getObjectAsString(String excludes,Object... obj){ return getObjectAsString(null,excludes,obj); } /** * 将对象转换成 JSON * @param parrent * @param obj * @return */ public static String getObjectAsString(Object parrent,Object... obj){ if(parrent!=null){ return getObjectAsString(parrent.toString(),null,obj); } return getObjectAsString(null,null,obj); } /** * 获取JSON数据 * @param source * @param ks * @return */ private static String getVal(Object source,Iterator<String> ks){ if(ks.hasNext()){ source = JSONObject.fromObject(source).get(ks.next()); if(source==null){ return null; } return getVal(source,ks); } return source.toString(); } /** * JSON 配置 * @author LGF * */ public static class Config extends JsonConfig implements JsonValueProcessor { /** * 日期格式化 */ private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); /** * 默认实例 */ private static Config config = new Config(); /** * 默认构造方法 */ private Config() { this.registerJsonValueProcessor(Date.class, this); } /** * 构造方法 * @param parrent 日期格式化 * @param excludes 不包含的字段 */ private Config(String parrent,String excludes) { if(parrent!=null){ sdf = new SimpleDateFormat(parrent); } if(excludes!=null){ this.setExcludes(excludes.split(",")); } this.registerJsonValueProcessor(Date.class, this); } @Override public Object processArrayValue(Object date, JsonConfig cfg) { if(date instanceof Date){ return sdf.format(date); } return null; } @Override public Object processObjectValue(String name, Object date, JsonConfig cfg) { if(date instanceof Date){ return sdf.format(date); } return null; } /** * 获取默认实例 * @return */ public static Config getDefaultInstance(){ return config; } /** * 日期格式化实例 * @param parrent * @return */ public static Config getInstance(String parrent,String excludes){ return new Config(parrent,excludes); } } /** * JSON 数据储存实体 * @author LGF * */ public static class Data{ private List<Object> records = new ArrayList<Object>(); public List<Object> getRecords() { return records; } public void setRecords(List<Object> records) { this.records = records; } } }
* 加密工具类
package com.code.util; import java.security.MessageDigest; import java.util.Formatter; import java.util.UUID; public class SecurityHanlder { private static final String uuid = "4c7a37a8-b8d7-442f-b9df-31ec4bcacebc"; /** * 获取 md5 加密字符串 * @param str * @return * @throws Exception */ public static String md5(String str) throws Exception { return getSign(str,"MD5"); } /** * 获取 md5 加密字符串 * 规则 str+uuid * @param str * @return * @throws Exception */ public static String md5x(String str) throws Exception { return getSign(str+uuid,"MD5"); } /** * 获取 sha1 加密字符串 * @param str * @return * @throws Exception */ public static String sha1(String str) throws Exception { return getSign(str,"SHA-1"); } /** * 获取 sha1 加密字符串 * 二次加密 * 规则 str+uuid * @param str * @return * @throws Exception */ public static String sha1x(String str) throws Exception { return getSign(str+uuid,"SHA-1"); } /** * 获取 sha1进行加密的字符后再进行 md5 加密字符串 * 二次加密 * @param str * @return * @throws Exception */ public static String md5_sha1(String str) throws Exception { return getSign(getSign(str,"MD5"),"SHA-1"); } /** * 获取 md5 进行加密的字符后再进行 sha1加密字符串 * @param str * @return * @throws Exception */ public static String sha1_md5(String str) throws Exception { return getSign(getSign(str,"SHA-1"),"MD5"); } /** * 创建随机字符 * @return */ public static String createNonceStr() { return UUID.randomUUID().toString(); } /** * 获取加密签名 * @param str 字符 * @param type 加密类型 * @return * @throws Exception */ public static String getSign(String str, String type) throws Exception { MessageDigest crypt = MessageDigest.getInstance(type); crypt.reset(); crypt.update(str.getBytes("UTF-8")); return str = byteToHex(crypt.digest()); } /** * 创建时间戳 * @return */ public static String createTimestamp() { return Long.toString(System.currentTimeMillis() / 1000); } /** * 字节转换 16 进制 * @param hash * @return */ private static String byteToHex(final byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", b); } String result = formatter.toString(); formatter.close(); return result; } }
* URL 工具类
package com.code.util; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.Map; public class URLHanlder { /** * 发送 HTTP 请求获取输入流 * @param str * @param params * @return */ public static InputStream getInputStream(String str,Map<String,String> params){ URL url = null; try { if(params!=null&¶ms.size()!=0){ StringBuffer sb = new StringBuffer(); int index = 0; if(str.indexOf("?")!=-1){ sb.append("&"); }else{ sb.append("?"); } for (String s : params.keySet()) { sb.append(s); sb.append("="); sb.append(params.get(s)); if(params.size()-1!=index){ sb.append("&"); } index++; } str+=sb.toString(); } url = new URL(str); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); return conn.getInputStream(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 发送 HTTP 请求获取输入流 * @param str * @return */ public static InputStream getInputStream(String str){ return getInputStream(str,null); } }
* 微信监听类(获取 token)
package com.code.util; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class WXListener implements ServletContextListener { public WXListener() { System.out.println("start com.code.util.WXListener.WXListener()"); } public void contextInitialized(ServletContextEvent e) { ServletContext servletContext = e.getServletContext(); new WeixinHanlder.ThreadAccessToken(servletContext); } public void contextDestroyed(ServletContextEvent e) { System.out.println("destory com.code.util.WXListener.WXListener()"); } }
* 微信 properties 配置
open_id=gh_08d0a8f58b1fsssgf app_id=wxe0faf281eed31f38899 app_secret=dcc59f91d76ea35a05a56fdc0bf16aa7dfh token=weixin url_access_token=https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wxe0faf281eed31f38&secret=dcc59f91d76ea35a05a56fdc0bf16aa7 url_js_ticket=https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi change_token_time=7200
* web.xml 配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>wx</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <listener> <listener-class>com.code.util.WXListener</listener-class> </listener> </web-app>
* index.jsp 示例
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0"> <title>index</title> </head> <body> <input type="submit" id="onMenuShareAppMessage" value="submit"> <br/> <input type="file" /> <br/> <input type="button" value="reload" onclick="location.reload(true)"> </body> <script type="text/javascript" src="js/jquery-1.9.0.min.js"></script> <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> <script type="text/javascript"> $(function() { $.ajax({ type : "GET", url : "weixin_sign", async: false, data:{url:location.href.split("#")[0]}, dataType: "JSON", success : function(msg) { console.debug(msg); var params = { //url,jsapi_ticket,nonceStr,timestamp,signature debug: true, appId:msg.appId, timestamp:msg.timestamp, nonceStr:msg.nonceStr, signature:msg.signature, jsApiList: ['checkJsApi', 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'hideMenuItems', 'showMenuItems', 'hideAllNonBaseMenuItem', 'showAllNonBaseMenuItem', 'translateVoice', 'startRecord', 'stopRecord', 'onRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'uploadVoice', 'downloadVoice', 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'getNetworkType', 'openLocation', 'getLocation', 'hideOptionMenu', 'showOptionMenu', 'closeWindow', 'scanQRCode', 'chooseWXPay', 'openProductSpecificView', 'addCard', 'chooseCard', 'openCard'] } wx.config(params); } }); }); wx.ready(function(){ $("#onMenuShareAppMessage").click(function(){ wx.openLocation({ latitude: 23.099994, longitude: 113.324520, name: 'TIT 创意园', address: '广州市海珠区新港中路 397 号', scale: 14, infoUrl: 'http://weixin.qq.com' }); }); }); wx.error(function(res){ alert("not success"); }); </script> </html>