|
作者:kongshanxuelin,来自原文地址 小程序最近火了,所以我也花点时间研究了下,同时自己也练练手做了一个小程序“商务工作记事册”,在微信小程序里可以被搜索到,接下去我讲讲我开发过程中遇到的一些坑吧,主要针对后台。
主要功能: - 支付接口调测:点击支持我们,可以直接捐赠;
- 提醒功能:主要使用客服通知以及微信小卡片通知;
- 自定义账本字段:主要就各种Event的Tag可以扩展字段;
- 文件上传下载:支持事件上传图片,录音文件;
首先要做到上面这些,后台必须要从小程序平台得到的信息如下图:
准备工作就绪,讲讲我开发这个小程序遇到的一些问题以及相应实现吧 前端问题- 高度不能用rpx,不然无法计算以及自适应,所以我后来改成了px,不知道有没有更好的方案;
- 首页应该要验证token合法性,要去服务器请求验证,这个过程是异步的,所以在载入首页之前应该有个splash过渡窗口引导,不然进入首页token非法的就将无法请求到数据,不知道有没有更好的方案;
- 排版布局使用的是weui,不知道还有没有更好的wxss可以使用;
- 由于异步调用多会乱,所以后来引入的Promise,好很多;
后台问题支付实现; 支付微信为了安全性,增加了数字签名,首先你要发起一个订单,得到订单号,有了这个订单号,就可以启动微信支付功能了,以下是代码: public JSONObject prePay() throws Exception{ UserBean ub = getWxUser(); if(ub!=null){ String appid = (StrUtil.formatNullStr(TagConst.globalMap.get("wx.appid"))); String appkey = (StrUtil.formatNullStr(TagConst.globalMap.get("wx.seckey"))); String mch_id = (StrUtil.formatNullStr(TagConst.globalMap.get("wx.mchid"))); String mchkey = (StrUtil.formatNullStr(TagConst.globalMap.get("wx.mchkey"))); String openId = ub.getUid(); String clientIP = HttpUtils.getIP(request); if(clientIP.indexOf(":")>0) clientIP = "127.0.0.1"; String fee = StrUtil.formatNullStr(request.getParameter("fee"),"1"); String body = StrUtil.formatNullStr(request.getParameter("body")); String atta = StrUtil.formatNullStr(request.getParameter("atta")); String nonce_str = UUIDHexGenerator.generate(); String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); String code = PayUtil.createCode(8); String out_trade_no = mch_id+today+code;//商户订单号 String spbill_create_ip = clientIP;//终端IP String trade_type = "JSAPI";//交易类型 String openid=openId;//用户标识 /**/ PayInfo paymentPo = new PayInfo(); paymentPo.setAppid(appid); paymentPo.setMch_id(mch_id); paymentPo.setNonce_str(nonce_str); paymentPo.setBody(body); paymentPo.setOut_trade_no(out_trade_no); paymentPo.setTotal_fee(fee); paymentPo.setSpbill_create_ip(spbill_create_ip); paymentPo.setNotify_url(URL_NOTIFY); paymentPo.setTrade_type(trade_type); paymentPo.setOpenid(openid); // 把请求参数打包成数组 Map<String, String> sParaTemp = new HashMap<String, String>(); sParaTemp.put("appid", paymentPo.getAppid()); sParaTemp.put("mch_id", paymentPo.getMch_id()); sParaTemp.put("nonce_str", paymentPo.getNonce_str()); sParaTemp.put("body", paymentPo.getBody()); sParaTemp.put("out_trade_no", paymentPo.getOut_trade_no()); sParaTemp.put("total_fee",paymentPo.getTotal_fee()); sParaTemp.put("spbill_create_ip", paymentPo.getSpbill_create_ip()); sParaTemp.put("notify_url",paymentPo.getNotify_url()); sParaTemp.put("trade_type", paymentPo.getTrade_type()); sParaTemp.put("openid", paymentPo.getOpenid()); // 除去数组中的空值和签名参数 Map<String, String> sPara = PayUtil.paraFilter(sParaTemp); String prestr = PayUtil.createLinkString(sPara); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串 String key = "&key="+mchkey; // 商户支付密钥 //MD5运算生成签名 String mysign = PayUtil.sign(prestr, key, "utf-8").toUpperCase(); paymentPo.setSign(mysign); String respXml = MessageUtil.messageToXML(paymentPo); respXml = respXml.replace("__", "_"); String url = ""; String param = respXml; String result =PayUtil.httpRequest(url, "POST", param); Map<String, String> map = new HashMap<String, String>(); InputStream in=new ByteArrayInputStream(result.getBytes()); // 读取输入流 SAXReader reader = new SAXReader(); Document document = reader.read(in); Element root = document.getRootElement(); List<Element> elementList = root.elements(); for (Element element : elementList) { map.put(element.getName(), element.getText()); } // 返回信息 String return_code = map.get("return_code"); String return_msg = map.get("return_msg"); JSONObject jsonObject=new JSONObject() ; if(return_code=="SUCCESS"||return_code.equals(return_code)){ String prepay_id = map.get("prepay_id"); jsonObject.put("nonceStr", nonce_str); jsonObject.put("package", "prepay_id="+prepay_id); Long timeStamp= System.currentTimeMillis()/1000; jsonObject.put("timeStamp", timeStamp+""); String stringSignTemp = "appId="+appid+"&nonceStr=" + nonce_str + "&package=prepay_id=" + prepay_id+ "&signType=MD5&timeStamp=" + timeStamp; String paySign=PayUtil.sign(stringSignTemp, "&key="+mchkey, "utf-8").toUpperCase(); jsonObject.put("paySign", paySign); return jsonObject; } } return null; }
提醒的实现; 这个主要首先获取accesstoken,然后根据这个token去发送消息,以下是代码; public static void sendMsgCard(String openId,String formId,String time,String title,String description ){ String temp_id = StrUtil.formatNullStr(TagConst.globalMap.get("wx.template.notify")); String dataKeyWorks = "{"; dataKeyWorks += "\"keyword1\":{\"color\":\"#173177\",\"value\":\""+time+"\"},"; dataKeyWorks += "\"keyword2\":{\"color\":\"#173177\",\"value\":\""+title+"\"},"; dataKeyWorks += "\"keyword3\":{\"color\":\"#173177\",\"value\":\""+description+"\"}"; dataKeyWorks += "}"; String json = "{"+ "\"touser\":\""+openId+"\","+ "\"template_id\":\""+temp_id+"\","+ "\"page\":\"pages/index/index\","+ "\"form_id\":\""+formId+"\","+ "\"data\":"+dataKeyWorks+""+ "}"; String url = "" + getMsgToken(); String result =PayUtil.httpRequest(url, "POST", json); } public static void sendMsgCard(String date,String openId,String formId,String time,String title,String description ){ String temp_id = StrUtil.formatNullStr(TagConst.globalMap.get("wx.template.notify")); String dataKeyWorks = "{"; dataKeyWorks += "\"keyword1\":{\"color\":\"#173177\",\"value\":\""+time+"\"},"; dataKeyWorks += "\"keyword2\":{\"color\":\"#173177\",\"value\":\""+title+"\"},"; dataKeyWorks += "\"keyword3\":{\"color\":\"#173177\",\"value\":\""+description+"\"}"; dataKeyWorks += "}"; String json = "{"+ "\"touser\":\""+openId+"\","+ "\"template_id\":\""+temp_id+"\","+ "\"page\":\"pages/index/index?dt="+date.split(" ")[0]+"\","+ "\"form_id\":\""+formId+"\","+ "\"data\":"+dataKeyWorks+""+ "}"; String url = "" + getMsgToken(); String result =PayUtil.httpRequest(url, "POST", json); } public static void sendMsg2User(String openId,String data){ String json = "{"+ "\"touser\":\""+openId+"\","+ "\"msgtype\":\"text\","+ "\"text\":"+ "{"+ "\"content\":\""+data+"\""+ "}"+ "}"; String url = "" + getMsgToken(); String result =PayUtil.httpRequest(url, "POST", json); } private static long lastTime = 0; private static String accessToken = ""; private static String getMsgToken(){ if(lastTime>0 && ((new Date().getTime()-lastTime)<6000*1000)){ return accessToken; } String url = ""+ StrUtil.formatNullStr(TagConst.globalMap.get("wx.appid"))+"&secret=" + StrUtil.formatNullStr(TagConst.globalMap.get("wx.seckey")); String retStr = PayUtil.httpRequest(url, "GET", null); JSONObject json = JSON.parseObject(retStr); if(json!=null){ lastTime = new Date().getTime(); accessToken = StrUtil.formatNullStr(json.get("access_token")); } return accessToken; }
智能客服的实现; 拦截用户发来的客服消息,这个需要在微信后台开启,开启后微信会给你客服号以及安全码,有了这两个后,等待微信验证你的URL通过,通过后你就可以拦截用户发来的消息了 //接入在线客服的token固定值 public String wx_getmsg() throws Exception{ String token = StrUtil.formatNullStr(TagConst.globalMap.get("wx.contact.token")); String contactKey = StrUtil.formatNullStr(TagConst.globalMap.get("wx.contact.seckey")); String signature = StrUtil.formatNullStr(request.getParameter("signature")); String timestamp = StrUtil.formatNullStr(request.getParameter("timestamp")); String nonce = StrUtil.formatNullStr(request.getParameter("nonce")); String echostr = StrUtil.formatNullStr(request.getParameter("echostr")); String _signature = SHA1.getSHA1(token, timestamp, nonce); if (!signature.equals(_signature)) { throw new AesException(AesException.ValidateSignatureError); } //发来消息的用户ID String openid = StrUtil.formatNullStr(request.getParameter("openid")); String encrypt_type=StrUtil.formatNullStr(request.getParameter("encrypt_type")); String msg_signature=StrUtil.formatNullStr(request.getParameter("msg_signature")); WXBizMsgCrypt pc = new WXBizMsgCrypt(token, contactKey, StrUtil.formatNullStr(TagConst.globalMap.get("wx.appid"))); InputStream is = request.getInputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream(10240); org.apache.commons.io.IOUtils.copy(is, out); String jsonStr = new String(out.toByteArray(), request.getCharacterEncoding()); String result2 = pc.decryptMsg(msg_signature, timestamp, nonce, jsonStr); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document document = db.parse(new InputSource(new ByteArrayInputStream(result2.getBytes("utf-8")))); Element root = document.getDocumentElement(); NodeList nodelist1 = root.getElementsByTagName("MsgType"); NodeList nodelist2 = root.getElementsByTagName("Content"); NodeList eventNodelist = root.getElementsByTagName("Event"); NodeList nodelist3 = root.getElementsByTagName("FromUserName"); String msgType = nodelist1.item(0).getTextContent(); String content = (nodelist2!=null && nodelist2.item(0)!=null) ? nodelist2.item(0).getTextContent() : ""; String fromOpnId = nodelist3.item(0).getTextContent(); String event = (eventNodelist!=null && eventNodelist.item(0)!=null) ? eventNodelist.item(0).getTextContent() : ""; if(msgType.equals("text")){ String data = "Sorry,我不能理解您的指令!"; //责任链模式处理消息 MsgHandler searchTaskHander = new SearchTasksMsgHandler(); MsgHandler robotMsgHandler = new RobotMsgHandler(); searchTaskHander.setSuccessor(robotMsgHandler); data = searchTaskHander.handleRequest(fromOpnId, data); WXTemplateMsgManager.sendMsg2User(fromOpnId,data); }else if(msgType.equals("event") && event.equals("user_enter_tempsession")){ String data = "为你提供搜索、天气等一系列个性化服务,现在开始可以向我提问."; WXTemplateMsgManager.sendMsg2User(fromOpnId,data); }else{ String data = "不支持的消息"; WXTemplateMsgManager.sendMsg2User(fromOpnId,data); } return echostr; }
|