有个H5支付的业务需要随机数字的键盘
参考了下文:https://blog.csdn.net/Mr_Smile2014/article/details/52473351
做了一些小修改:
在原有的基础上,增加了一些按键反馈的效果。
每个按键加上边框。
最终效果:
下面是所有代码:
1.HTML页面+JS
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" /> <meta http-equiv="Cache-Control" CONTENT="private,must-revalidate"> <link href="../media/css/bootstrap.min.css" rel="stylesheet" type="text/css"/> <!-- 引入js脚本文件 begin --> <!--[if lt IE 9]> <script src="http://cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="http://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script> <![endif]--> <title>H5付款</title> <style type="text/css"> .input-out { padding-top: 14px; } .btn-out { margin:30px 10px; } .btn-out button { width: 100%; background: #5CACEE; border: #5CACEE; color: #fff; height: 50px; border-radius: 3px; font-size: 18px; font-family: "Microsoft Yahei", "??????", SimHei, Tahoma, Arial, Helvetica, STHeiti; } .keyboard { background: #fff; } .keyboard table { width:100%; text-align:center; } .keyboard table td { padding: 15px; } .keyboard table a, .keyboard table a:hover, .keyboard table a:focus { color: #333; text-decoration: none; } .input-out label { color:#D2D1D1; font-weight: normal; font-size: 16px; } .bottom { color:#888888; margin-bottom: 15px; text-align:center; margin-top:100px; } .bottom a { color:#888888; } .bottom img { vertical-align: middle; width: 18px; } /*.inputText { font-size: 16px; border-left: medium none; border-right: medium none; border-top: medium none; border-bottom: 1px solid rgb(192,192,192); border-bottom: 1px solid #ddd; padding-left: 12px; width:100%; font-weight: normal; padding-top: 20px; height: 50px; }*/ input[type="password"],input[type="text"]{ font-size: 30px; border-left: medium none; border-right: medium none; border-top: medium none; /*border-bottom: 1px solid rgb(192,192,192);*/ border-bottom: 1px solid #ddd; padding-left: 12px; width:100%; font-weight: normal; padding-top: 10px; height: 40px; } </style> </head> <body> <div style="height: 20px;"> </div> <form action="../pay" method="post" id="from"> <div class="content"> <div> <label style="font-size: 20px;padding-left: 12px; ">账户</label> <input type="text" id="loginName" placeholder="" /> </div> <!--<div> <label style="font-size: 20px;padding-left: 12px; ">密码</label> <input type="password" id="pwd" placeholder="" /> </div> --><label style="font-size: 20px;padding-left: 12px; ">支付密码</label> <div id="tradePwd" class="input-out pass-label" style="border-bottom: 1px solid #ddd;padding-left: 12px;" random="2321321321" path="../" > <label id="pin" style="color: #8C8C8C;height: 20px;"></label> </div> </div> <div class="btn-out"> <button type="button" class="btn btn-default" ontouchstart="check();" id="pay">支付</button> </div> </form> <div class="bottom" id="bottom-out"> <!--<img src="<c:url value="/images/phone.png"/>" /> --><!--<span>客服电话:4000-xxx-xxx</span> --></div> <!-- jianpan--> <div class="keyboard" style="display:none;" id="keyboard"> <table class="table-bordered" id="key_table"> </table> </div> </body> <script src="../media/js/jquery-1.10.1.min.js" type="text/javascript"></script> <script src="../media/js/bootstrap.min.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready( function() { $("#pay").removeAttr("disabled"); $("input").click(function() { hideKey(); }); $("button").click(function() { hideKey(); }); $(".pass-label").click(function() { var rangdom = $(this).attr("random"); var path = $(this).attr("path"); pwdkey(this, rangdom, path); }); window.addEventListener( "onorientationchange" in window ? "orientationchange" : "resize", hengshuping, false); }); //横竖屏 function hengshuping() { if (window.orientation == 180 || window.orientation == 0) { $("div#keyboard td").each(function() { $(this).css("padding", "15px"); }); } if (window.orientation == 90 || window.orientation == -90) { $("div#keyboard td").each(function() { $(this).css("padding", "8px"); }); } window.scrollTo(0, $(".pass-label").offset().top); } //加载密码键盘 function pwdkey(obj, rangdom, path) { $('.keyboard').addClass("navbar-fixed-bottom"); $('.keyboard').css({ "z-index" : "9999" }); if (rangdom == null || rangdom == "") { alert("无法加载密码键盘,请刷新后重试!"); return false; } if ($("#pkey").val() == null || $("#pkey").val() == "undefined") { $(obj) .html( $(obj).html() + '<input type="hidden" name="pkey" id="pkey" />'); } //$("#pin").html("支付密码"); $("#pin").html(""); setCssNomal(); $("#pkey").val(""); $.ajax({ type : 'post', url : "../h5Pay/pkey", cache : false, async : false, data : { rangdom : rangdom }, success : function(data) { if (data == null || data == "" || data == "undefined" || data.length != 10) { alert("无法加载密码键盘,请刷新后重试!"); return false; } else { var key_table = $("#key_table"); key_table.html(""); var content = '<tr>'; for (var i = 0; i < 12; i++) { if ((i + 1) % 3 == 0 && i != 0 && i <= 5) { content = content + '<td class="table-bordered" style="width:33%;" key="' + data[i] + '"ontouchstart="ontouchreturn(this);" ontouchend="return ontouch(this);"><img src="' + '../h5Pay/getKey?key=' + data[i] + '&rangdom=' + rangdom + '" style="height:20px;" id="key_img"/></td></tr><tr>'; } else if (i <= 7) { content = content + '<td class="table-bordered" style="width:33%;" key="' + data[i] + '"ontouchstart="ontouchreturn(this);" ontouchend="return ontouch(this);"><img src="' + '../h5Pay/getKey?key=' + data[i] + '&rangdom=' + rangdom + '" style="height:20px;" id="key_img"/></td>'; } else if (i == 8) { content = content + '<td class="table-bordered" style="width:33%;" ontouchstart="ontouchreturn(this);" ontouchend="return deleteOne(this);"><img src="' + '../media/image/keys/delete.png" style="height:20px;" id="key_img"/></td>' + '</tr><tr>'; } else if (i < 11) { content = content + '<td class="table-bordered" style="width:33%;" key="' + data[i - 1] + '" ontouchstart="ontouchreturn(this);" ontouchend="return ontouch(this);"><img src="' + '../h5Pay/getKey?key=' + data[i - 1] + '&rangdom=' + rangdom + '" style="height:20px;" id="key_img"/></td>'; } else { content = content + '<td class="table-bordered" style="width:33%;" ontouchstart="ontouchreturn(this);" ontouchend="return _ok(this);"><img src="' + '../media/image/keys/ok.png" style="height:20px;" id="key_img"/></td>' + '</tr>'; } } key_table.html(content); setTimeout(function() { $("#keyboard").show(); }, 600); hengshuping(); } }, error : function() { alert("无法加载键盘,请刷新后重试!"); } }); } function ontouchreturn(obj) { $(obj).attr("bgcolor","#c5c5c5"); } //数字按钮映射input function ontouch(obj) { $(obj).removeAttr("bgcolor"); var pkey = $("#pkey").val(); var key = $(obj).attr("key"); if (pkey == "") { $("#pin").html(""); } var content = $("#pin").html(); if (content != "" && content.length >= 6) { return false; } if (pkey != "") { key = "," + key; } pkey = pkey + key; $("#pkey").val(pkey); $("#pin").append("●"); setCssKey(); } //删除按钮 function deleteOne(obj) { $(obj).removeAttr("bgcolor"); var pkey = $("#pkey").val() + ""; if (pkey == "") { return false; } var local = pkey.lastIndexOf(","); if (local == -1) { $("#pkey").val(""); $("#pin").html(""); //$("#pin").html("支付密码"); setCssNomal(); } else { pkey = pkey.substring(0, local - 1); var content = $("#pin").html(); content = content.substring(0, content.length - 1); $("#pkey").val(pkey); $("#pin").html(content); } } //确认按钮 function _ok(obj) { $(obj).removeAttr("bgcolor"); $("#key_table").html(""); $("#keyboard").hide(); } function showkey() { $("#keyboard").show(); } function hideKey() { $("#key_table").html(""); $("#keyboard").hide(); } function setCssKey() { $("#pin").css({ "font-size" : "18px", "color" : "#030303", "font-weight" : "normal", "letter-spacing" : "1px" }); } function setCssNomal() { $("#pin").css({ "font-size" : "16px", "color" : "#8C8C8C", "font-weight" : "normal" }); } //支付按钮 function check() { hideKey(); var pin=""; pin=$("#pkey").val(); if(pin==""||pin==undefined) { bool=false; alert("请输入支付密码"); return false; }else { var keys=pin.split(","); if(keys.length!=6) { alert("请输入6位支付密码"); return false; } } $.ajax({ type : 'post', url : "../h5Pay/pay", data : { random:"2321321321", pin:pin }, cache : false, success : function(data) { if(data.success) { alert(data.message); }else{ } }, error : function(){ alert("系统异常,请重试!"); } }); } </script> </html>
2.密码键盘的MODEL类
import java.io.Serializable; import java.util.List; import java.util.Map; public class PwdKeyDto implements Serializable{ private static final long serialVersionUID = 1L; private List<String> rundomKeys;// 随机Keys private Map<String, String> valueKeyMaps;// 密文和明文映射 private Map<String, String> imgKeyMaps;// 密文和明文映射 public PwdKeyDto() { super(); // TODO Auto-generated constructor stub } public List<String> getRundomKeys() { return rundomKeys; } public void setRundomKeys(List<String> rundomKeys) { this.rundomKeys = rundomKeys; } public PwdKeyDto(List<String> rundomKeys, Map<String, String> valueKeyMaps, Map<String, String> imgKeyMaps) { super(); this.rundomKeys = rundomKeys; this.valueKeyMaps = valueKeyMaps; this.imgKeyMaps = imgKeyMaps; } public Map<String, String> getValueKeyMaps() { return valueKeyMaps; } public void setValueKeyMaps(Map<String, String> valueKeyMaps) { this.valueKeyMaps = valueKeyMaps; } public Map<String, String> getImgKeyMaps() { return imgKeyMaps; } public void setImgKeyMaps(Map<String, String> imgKeyMaps) { this.imgKeyMaps = imgKeyMaps; } }
3.后台Action
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import com.sanwing.spi.model.PwdKeyDto; import com.sanwing.spi.utils.PwdKeyUtils; @Controller @RequestMapping("/h5Pay") public class H5Pay { private static final Logger logger = Logger.getLogger(H5Pay.class); @RequestMapping("/jumpH5Pay") public String jumpH5Pay(HttpServletRequest request,HttpServletResponse response){ return "/h5Pay"; } /** * * @Description: 获取密码键盘 * @param request * @param rangdom 随机字符串 * @return * */ @SuppressWarnings("unchecked") @ResponseBody @RequestMapping(value = "/pkey", method = RequestMethod.POST) public Object digitkeyboard(HttpServletRequest request, String rangdom) { try { if (isBlank(rangdom)) { return ""; } PwdKeyDto pwdkey = PwdKeyUtils.digitkeyboard(); HttpSession session = request.getSession(); session.setAttribute("pwdkey_" + rangdom, pwdkey); /*redisUtil.set("pwdkey_" + rangdom, pwdkey, redisUtil.getDigitkeyimeOut());*/ return pwdkey.getRundomKeys(); } catch (Exception e) { logger.error("[获取密码键盘digitkeyboard][error:{}",e); } return ""; } /** * 获取key图片 * * @throws Exception */ @RequestMapping(value = "/getKey", method = RequestMethod.GET) public void getKey(String key, String rangdom, HttpServletRequest request, HttpServletResponse response) throws Exception { HttpSession session = request.getSession(); PwdKeyDto pwdkey = (PwdKeyDto) session.getAttribute("pwdkey_" + rangdom); if (pwdkey == null || pwdkey.getImgKeyMaps() == null) { logger.error("获取图片[getKey]:未获取到对应的键盘的映射关系"); throw new Exception(); } Map<String, String> imagekeys = pwdkey.getImgKeyMaps(); String path = imagekeys.get(key); String rootPath = request.getSession().getServletContext() .getRealPath("/"); path = rootPath + path; if (path != null && path.length() != 0) { try { InputStream fis = new FileInputStream(new File(path)); BufferedInputStream bis = new BufferedInputStream(fis); OutputStream fos = response.getOutputStream(); BufferedOutputStream bos = new BufferedOutputStream(fos); String fileName = "image."; String[] strs = path.split("\\."); fileName = fileName + strs[strs.length - 1]; setFileDownloadHeader(request, response, fileName); int byteRead = 0; byte[] buffer = new byte[8192]; while ((byteRead = bis.read(buffer, 0, 8192)) != -1) { bos.write(buffer, 0, byteRead); } bos.flush(); fis.close(); bis.close(); fos.close(); bos.close(); } catch (Exception e) { logger.error("获取图片[path:" + path + "])失败:" + e.toString(), e); } } } /** * * @Description: 支付 * @param pin 交易密码密文 * @param random 随机码 * @return * */ @ResponseBody @RequestMapping(value = "/pay", method = RequestMethod.POST) public String pay(String pin,String random, HttpServletRequest request) { try { logger.info("[支付(pay)][params:pin=" + pin + ",random="+random+"]"); if (!isBlank(pin)) { StringBuffer sb = new StringBuffer(""); //PwdKeyDto pwdkey = (PwdKeyDto) redisUtil.get("pwdkey_" + random); HttpSession session = request.getSession(); PwdKeyDto pwdkey = (PwdKeyDto) session.getAttribute("pwdkey_" + random); if (pwdkey == null || pwdkey.getValueKeyMaps() == null) { return "密码键盘已失效,请重新输入密码"; } Map<String, String> valuekeys = pwdkey.getValueKeyMaps(); String[] pins = pin.split(","); if (pins.length != 6) { return "交易密码位数不对"; } for (String pinKey : pins) { String val = valuekeys.get(pinKey); if (isBlank(val)) { return "密码键盘已失效,请重新输入密码"; } sb.append(val); } //sb.toString()就是明文交易密码 System.out.println(sb.toString()); } return "成功"; } catch (Exception e) { logger.error("[支付(pay)][error:{}]",e); return "支付异常,请重试!"; } } public static void setFileDownloadHeader(HttpServletRequest request,HttpServletResponse response, String fileName) { try { String encodedfileName = null; String agent = request.getHeader("USER-AGENT"); if(null != agent && -1 != agent.indexOf("MSIE")){//IE encodedfileName = java.net.URLEncoder.encode(fileName,"UTF-8"); }else if(null != agent && -1 != agent.indexOf("Mozilla")){ encodedfileName = new String (fileName.getBytes("UTF-8"),"iso-8859-1"); }else{ encodedfileName = java.net.URLEncoder.encode(fileName,"UTF-8"); } response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedfileName + "\""); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } public static boolean isBlank(final CharSequence cs) { int strLen; if (cs == null || (strLen = cs.length()) == 0) { return true; } for (int i = 0; i < strLen; i++) { if (Character.isWhitespace(cs.charAt(i)) == false) { return false; } } return true; } }
4.密码键盘工具类
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import javax.servlet.http.HttpServletRequest; import com.sanwing.spi.model.PwdKeyDto; public class PwdKeyUtils { private final static Map<String, String> imagesValueMap; /** * * @Description: 获取密码键盘映射关系 * @param imagesValueMap * @return * */ static { imagesValueMap = new HashMap<String, String>(); imagesValueMap.put("0", "/media/image/keys/0.png"); imagesValueMap.put("1", "/media/image/keys/1.png"); imagesValueMap.put("2", "/media/image/keys/2.png"); imagesValueMap.put("3", "/media/image/keys/3.png"); imagesValueMap.put("4", "/media/image/keys/4.png"); imagesValueMap.put("5", "/media/image/keys/5.png"); imagesValueMap.put("6", "/media/image/keys/6.png"); imagesValueMap.put("7", "/media/image/keys/7.png"); imagesValueMap.put("8", "/media/image/keys/8.png"); imagesValueMap.put("9", "/media/image/keys/9.png"); } public static PwdKeyDto digitkeyboard() { List<String> rundomKeys = new ArrayList<String>();// 随机key映射 Map<String, String> valueKeys = new HashMap<String, String>();// 密文和明文映射 Map<String, String> imgKeyMaps = new HashMap<String, String>();// 密文和图片映射 List<String> keys = new ArrayList<String>(); for (int i = 0; i < 10; i++) { keys.add(i + ""); } for (int i = 0; i < 10; i++) { Random r = new Random(); int index = r.nextInt(keys.size()); String key = keys.get(index); keys.remove(index); String randomkey = randomKey(24); rundomKeys.add(randomkey); valueKeys.put(randomkey, key); imgKeyMaps.put(randomkey, imagesValueMap.get(key)); } PwdKeyDto dto = new PwdKeyDto(rundomKeys, valueKeys, imgKeyMaps); return dto; } /** * * @Description:获取动态key * @param num * key位数 * @return * */ public static String randomKey(int num) { StringBuffer sb = new StringBuffer(""); char[] chars = { '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' }; for (int i = 0; i < num; i++) { int id = (int) Math.ceil(Math.random() * 60); sb.append(chars[id]); } return sb.toString(); } /** * * @Description:解密pin * @param request * @param pin * @return * */ public static String decryptPinData(HttpServletRequest request, String ciphertextpin) throws Exception { if (!isBlank(ciphertextpin)) { Map<String, String> valuekeys = (Map<String, String>) request .getSession().getAttribute("valuekeys"); if (valuekeys == null || valuekeys.size() != 10) { throw new Exception(); } String[] ciphertextpins = ciphertextpin.split(","); StringBuffer sb = new StringBuffer(""); for (String ctpin : ciphertextpins) { sb.append(valuekeys.get(ctpin)); } } return null; } public static boolean isBlank(final CharSequence cs) { int strLen; if (cs == null || (strLen = cs.length()) == 0) { return true; } for (int i = 0; i < strLen; i++) { if (Character.isWhitespace(cs.charAt(i)) == false) { return false; } } return true; } }
最后,还有扣下来的按键图片。