$(document).ready(function(){ var url = location.href.split('#')[0]; $.get(rootPath + "/wechat/member/user/getWechatConfig?url=" + url,"",function(data){ wx.config({ debug: false, appId: data.appId, nonceStr: data.nonceStr, signature: data.signature, timestamp: parseInt(data.timestamp), 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' ] }); }); document.querySelector('#wxpay').onclick = function () { $.get(rootPath + "/wechat/member/user/getWechatPayConfig","",function(data){ if(data != null){ wx.chooseWXPay({ timestamp: data.timestamp, nonceStr: data.nonceStr, package: data.pkg, signType: 'MD5', paySign: data.paySign, success: function (res) { // 支付成功后的回调函数 alert(res.err_msg); } }); }else{ alert("支付异常"); } }); }; wx.error(function (res) { alert(res.errMsg); }); });
获取页签参考调用微信Js-SDK上传、预览图片,下面是后台获取支付签名等参数:
public static WechatPayConfig getWechatPayConfig(String openId) throws Exception { WechatPayConfig config = new WechatPayConfig(); config.setAppId(APPID); String timestamp = Long.toString(System.currentTimeMillis() / 1000); config.setTimestamp(timestamp); String nonceStr = UUID.randomUUID().toString().replaceAll("-", ""); config.setNonceStr(nonceStr); PayInfo payInfo = new PayInfo(); payInfo.setAppid(APPID); payInfo.setMch_id(WEIXIN_MCH_ID);// 商户号 payInfo.setDevice_info("WEB");// 设备号 payInfo.setNonce_str(nonceStr);// 随机字符串 payInfo.setBody("这是商品描述");// 商品描述 payInfo.setOut_trade_no(nonceStr);// 商户订单号 payInfo.setTotal_fee("1");// 支付金额 payInfo.setSpbill_create_ip(InetAddress.getLocalHost().getHostAddress());// 终端IP payInfo.setNotify_url(PAY_CALL_BACK);// 接收微信支付异步通知回调地址 payInfo.setTrade_type("JSAPI");// 交易类型 payInfo.setOpenid(openId);// 用户标识 payInfo.setSign(getSign(payInfo));// 签名 logger.info(JsonMapper.toJsonString(payInfo)); String paramesValue = MessageUtil.payInfoToXML(payInfo) .replace("__", "_").replace("<![CDATA[", "").replace("]]>", ""); Map<String, String> map = httpsRequestToXML(paramesValue); if (!map.get("return_code").equals("SUCCESS")) { logger.info("调用微信统一下单接口失败:" + map.get("return_msg")); return null; } logger.info("调用微信统一下单接口成功:"); logger.info(map.toString()); String pkg = "prepay_id=" + map.get("prepay_id"); config.setPkg(pkg); String str = "appId=" + APPID + "&nonceStr=" + nonceStr + "&package=" + config.getPkg() + "&signType=MD5" + "&timeStamp=" + timestamp + "&key=" + WEIXIN_KEY; logger.info("str:" + str); String paySign = MD5Util.MD5Encode(str, "UTF-8").toUpperCase(); logger.info("paySign:" + paySign); config.setPaySign(paySign); return config; }
public static String getSign(PayInfo payInfo) throws Exception { String signTemp = "appid=" + payInfo.getAppid() + "&body=" + payInfo.getBody() + "&device_info=" + payInfo.getDevice_info() + "&mch_id=" + payInfo.getMch_id() + "&nonce_str=" + payInfo.getNonce_str() + "¬ify_url=" + payInfo.getNotify_url() + "&openid=" + payInfo.getOpenid() + "&out_trade_no=" + payInfo.getOut_trade_no() + "&spbill_create_ip=" + payInfo.getSpbill_create_ip() + "&total_fee=" + payInfo.getTotal_fee() + "&trade_type=" + payInfo.getTrade_type() + "&key=" + WEIXIN_KEY; // 商户号API安全的API密钥 String sign = MD5Util.MD5Encode(signTemp, "UTF-8").toUpperCase(); return sign; }
public static String payInfoToXML(PayInfo pi) { XStream xstream = new XStream(); xstream.alias("xml", pi.getClass()); return xstream.toXML(pi); }
public static Map<String, String> httpsRequestToXML(String outputStr) { Map<String, String> result = new HashMap<>(); try { StringBuffer buffer = httpsRequest(WeixinUtil.WEIXIN_UNIFIED_ORDER, "POST", outputStr); result = MessageUtil.parseXml(buffer.toString()); } catch (ConnectException ce) { logger.error("连接超时:" + ce.getMessage()); } catch (Exception e) { logger.error("https请求异常:" + e.getMessage()); } return result; } public static StringBuffer httpsRequest(String requestUrl, String requestMethod, String output) throws Exception { StringBuffer buffer = new StringBuffer(); try { URL url = new URL(requestUrl); HttpsURLConnection connection = (HttpsURLConnection) url .openConnection(); connection.setConnectTimeout(30000); // 设置连接主机超时(单位:毫秒) connection.setReadTimeout(30000); // 设置从主机读取数据超时(单位:毫秒) connection.setDoOutput(true); // post请求参数要放在http正文内,顾设置成true,默认是false connection.setDoInput(true); // 设置是否从httpUrlConnection读入,默认情况下是true connection.setUseCaches(false); // Post 请求不能使用缓存 // 设定传送的内容类型是可序列化的java对象(如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException) connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.setRequestMethod(requestMethod);// 设定请求的方法为"POST",默认是GET connection.setRequestProperty("Content-Length", requestUrl.length() + ""); String encode = "utf-8"; if (null != output) { OutputStream outputStream = connection.getOutputStream(); outputStream.write(output.getBytes("UTF-8")); outputStream.flush(); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader( inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader( inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; connection.disconnect(); } catch (Exception e) { logger.info("post请求调用统一下单接口异常" + e); } return buffer; } public static Map<String, String> parseXml(String xml) throws Exception { Map<String, String> map = new HashMap<String, String>(); Document document = DocumentHelper.parseText(xml); Element root = document.getRootElement(); List<Element> elementList = root.elements(); for (Element e : elementList) map.put(e.getName(), e.getText()); return map; }
import java.security.MessageDigest; public class MD5Util { private static String byteArrayToHexString(byte b[]) { StringBuffer resultSb = new StringBuffer(); for (int i = 0; i < b.length; i++) resultSb.append(byteToHexString(b[i])); return resultSb.toString(); } private static String byteToHexString(byte b) { int n = b; if (n < 0) n += 256; int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; } public static String MD5Encode(String origin, String charsetname) { String resultString = null; try { resultString = new String(origin); MessageDigest md = MessageDigest.getInstance("MD5"); if (charsetname == null || "".equals(charsetname)) resultString = byteArrayToHexString(md.digest(resultString .getBytes())); else resultString = byteArrayToHexString(md.digest(resultString .getBytes(charsetname))); } catch (Exception exception) { } return resultString; } private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; }
步骤:
1.获取页签,只有成功获取了页签,才能调微信Js-SDK下的API
2.调统一支付也叫统一下单接口,出入都是一个xml,成功会返回:
{
result_code=SUCCESS,
sign=3E5062C1CCBBF020628280A9E6CDE9E9,
mch_id=10022311,
prepay_id=wx201511170958368f617c0d5f0988190603,
return_msg=OK,
appid=wx3d407f02f5d3dd63,
nonce_str=Fl2ViAVbdwQdZSQz,
return_code=SUCCESS,
device_info=WEB,
trade_type=JSAPI
}
其中prepay_id=wx201511181358442e0bc083190380094951就是package的值
3.最后用appId、nonceStr、package、signType、timeStamp、key 6个参数得到paySign的值