最近做微信公众号开发,用到了网页中调起微信支付
的功能,微信公众平台提供的DEMO是基于PHP的,没有Java的,到网上查阅资料也都是V2.x
的,而现在微信支付已经更新到了V3.x
版本,经过几天的探索,终于成功实现,将实现过程总结下。
获取签名证书,初始化JS-API
在网页中JS调起微信支付需用到微信JS-SDK,首先得初始化JS-API,后台将签名证书
传给前端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| public static AccessToken getAccessToken(String appId, String appSecret) { AccessToken accessToken = null; String requestUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; requestUrl = requestUrl.replace("APPID", appId); requestUrl = requestUrl.replace("APPSECRET", appSecret); String result = CommonUtil.httpRequest(requestUrl); JSONObject jsonObject = JSONObject.fromObject(result); if (null != jsonObject) { try { accessToken = new AccessToken(); accessToken.setAccess_token(jsonObject.getString("access_token")); accessToken.setExpires_in(jsonObject.getInt("expires_in")); } catch (Exception e) { accessToken = null; int errorCode = jsonObject.getInt("errcode"); String errorMsg = jsonObject.getString("errmsg"); } } return accessToken; }
public static String getJsapiTicket(String accessToken) { String jsapiTicket = null; String requestUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi"; requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken); String result = CommonUtil.httpRequest(requestUrl); JSONObject jsonObject = JSONObject.fromObject(result); if (null != jsonObject) { try { jsapiTicket = jsonObject.getString("ticket"); } catch (Exception e) { int errorCode = jsonObject.getInt("errcode"); String errorMsg = jsonObject.getString("errmsg"); } } return jsapiTicket; }
|
access_token和jsapiTicket需控制调用频率
,参考微信公众号调用access_token接口频率限制解决(Java)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| 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 string1; String signature = ""; string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "×tamp=" + timestamp + "&url=" + url; try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes("UTF-8")); signature = byteToHex(crypt.digest()); } catch (NoSuchAlgorithmException e) { return null; } catch (UnsupportedEncodingException e) { return null; } ret.put("jsapi_ticket", jsapi_ticket); ret.put("nonceStr", nonce_str); ret.put("timestamp", timestamp); ret.put("signature", signature); return ret; }
|
获取微信支付配置参数
微信支付参数需要的有3个:a.微信分配的公众账号ID(appid);b.微信支付分配的商户号(mch_id/PartnerId);c.商户密钥(PartnerKey)
调用统一下单接口,生成预支付订单号
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
public String submitXmlGetPrepayId() { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); HttpPost httpPost = new HttpPost(unifiedorder); String xml = getPackage(); StringEntity entity; String result = null; try { entity = new StringEntity(xml, "utf-8"); httpPost.setEntity(entity); HttpResponse httpResponse; httpResponse = closeableHttpClient.execute(httpPost); HttpEntity httpEntity = httpResponse.getEntity(); if (httpEntity != null) { result = result.replaceAll("<![CDATA[|]]>", ""); String prepay_id = Jsoup.parse(result).select("prepay_id").html(); this.prepay_id = prepay_id; if (prepay_id != null) { return prepay_id; } } closeableHttpClient.close(); } catch (Exception e) { e.printStackTrace(); } return prepay_id; }
|
生成带支付签名的订单凭据并返回
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public class WXPay { public static String createPackageValue(String appid, String appKey, String prepay_id) { SortedMap<String, String> nativeObj = new TreeMap<String, String>(); nativeObj.put("appId", appid); nativeObj.put("timeStamp", OrderUtil.GetTimestamp()); Random random = new Random(); String randomStr = MD5.GetMD5String(String.valueOf(random.nextInt(10000))); nativeObj.put("nonceStr", MD5Util.MD5Encode(randomStr, "utf-8").toLowerCase()); nativeObj.put("package", "prepay_id=" +prepay_id); nativeObj.put("signType", "MD5"); nativeObj.put("paySign", createSign(nativeObj, appKey)); System.out.println(JSONObject.fromObject(nativeObj).toString()); return JSONObject.fromObject(nativeObj).toString(); }
@SuppressWarnings("rawtypes") public static String createSign(SortedMap<String, String> packageParams, String AppKey) { StringBuffer sb = new StringBuffer(); Set es = packageParams.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + AppKey); String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase(); return sign; } }
|
调起微信支付(发起微信支付请求)
用户确认金额并输入支付密码
向用户展示支付结果
参考
JS-SDK-API
微信支付PDF文档
这篇文章。