当前位置:首页 » SEO知识

微信小程序开发我遇到的一些坑和建议

2017-04-14 08:22 本站整理 浏览(104)

小程序最近火了,所以我也花点时间研究了下,同时自己也练练手做了一个小程序“商务工作记事册”,在微信小程序里可以被搜索到,接下去我讲讲我开发过程中遇到的一些坑吧,主要针对后台。

 


 

 

 

主要功能:

 

  1. 支付接口调测:点击支持我们,可以直接捐赠;
  2. 提醒功能:主要使用客服通知以及微信小卡片通知;
  3. 自定义账本字段:主要就各种Event的Tag可以扩展字段;
  4. 文件上传下载:支持事件上传图片,录音文件;

首先要做到上面这些,后台必须要从小程序平台得到的信息如下图:


 
 准备工作就绪,讲讲我开发这个小程序遇到的一些问题以及相应实现吧

 

  • 前端问题
  1. 高度不能用rpx,不然无法计算以及自适应,所以我后来改成了px,不知道有没有更好的方案;
  2. 首页应该要验证token合法性,要去服务器请求验证,这个过程是异步的,所以在载入首页之前应该有个splash过渡窗口引导,不然进入首页token非法的就将无法请求到数据,不知道有没有更好的方案;
  3. 排版布局使用的是weui,不知道还有没有更好的wxss可以使用;
  4. 由于异步调用多会乱,所以后来引入的Promise,好很多;
  • 后台问题
  1. 支付实现;

支付微信为了安全性,增加了数字签名,首先你要发起一个订单,得到订单号,有了这个订单号,就可以启动微信支付功能了,以下是代码:

	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 = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	        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;
	}

 

 

  1. 提醒的实现;

这个主要首先获取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 = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=" + 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 = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=" + 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 = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + 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 = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+
	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;
	}

 

  1. 智能客服的实现;

拦截用户发来的客服消息,这个需要在微信后台开启,开启后微信会给你客服号以及安全码,有了这两个后,等待微信验证你的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;
	}