java实现微信公众号的支付技能
前沿,环境的配置:
1.首先,这个项目需要有对应的公众号,以及在公众平台上注册公众号支付的权限,也就是开通微信商户平台,
并且将商户平台和对用的公众号进行绑定
至此开发人员将获取到必要字段:
2.其次,开发人员需要登录微信商户平台,添加支付授权目录,
还需要在商户平台设置授权域名:xx.xxx.cn (注:这个域名相当于Java项目运行时的ip:端口号)
具体参考微信开发文档:微信开发文档
然后,我们开始代码开发部分:
第一步:微信授权功能,不详细讲解,不清楚可以看我的–>另一篇微信授权登录的文章
主要是为了获取微信用户的openid
我们还需要知道的静态参数有:
参数: //下面是支付需要的一些参数,设为静态,方便不同方法调用
private static String APPID = “wxxxxxxxxxxxxxxxxxxxx51”; //微信公众号id
private static String SECRET = “2xxxxxxxxxxxxxxxxxc”; //微信公众号密钥id
private static String MCHID = “1xxxxxxxxx”; //微信支付商户号
private static String KEY = “xxxxxxxxxxxxxxxxxxxxx”; //商户号对应的密钥
private static String UNURL = “https://api.mch.weixin.qq.com/pay/unifiedorder”; //微信统一下单链接
private static String TRADETYPE=“JSAPI”;//jsapi代表公众号支付
public static String NOTIFY_URL_DC = “https://xxx.com/projects/wx/home/weixin_notify”;//回调函数url
public static String CREATE_IP = “122.110.110.110”;//发起支付ip(微信白名单的ip)
第二步:获取prepay_id
//这个是统一下单接口调用方法
public Map<String,Object> weixin_pay(CarOrder carOrder) {
try {
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>(); //填装支付参数map
String out_trade_no = Order.getOrderNo(); //订单号
String openid = Order.getCustomerId(); //openid
String key =KEY ; // 商户号对应的密钥
String currTime = PayToolUtil.getCurrTime();//时间戳
String nonce_str = currTime.substring(8, currTime.length()) + PayToolUtil.buildRandom(4) + ""; //随机字符串
String trade_type = TRADETYPE; //微信调用接口的方式(js)
String money = Order.getTotalPrice(); //支付金额
double total_price = Double.parseDouble(money)*100; //转换成该币种的最小单位
int totalPrice = (int)total_price;
//接着就将这些需要传递给微信的参数进行装载,然后生成xml格式数据
packageParams.put("appid", APPID ); //微信公众号id
packageParams.put("mch_id", MCHID); // 商户号
packageParams.put("nonce_str", nonce_str);//随机字符串
packageParams.put("total_fee", String.valueOf(totalPrice)); //价格的单位为分
packageParams.put("spbill_create_ip", CREATE_IP); // 获取发起电脑 ip
packageParams.put("notify_url", NOTIFY_URL_DC); // 回调接口方法(后台)
packageParams.put("out_trade_no",out_trade_no); // 商户订单号
packageParams.put("body", "订单微信支付"); // 商品或支付单简要描述
packageParams.put("trade_type", trade_type);
packageParams.put("openid", openid);
packageParams.put("attach", out_trade_no);
String sign = this.createSign("UTF-8", packageParams, key);//根据商户的支付密钥和参数生成签名
packageParams.put("sign", sign); //将签名也放入到map集合中
//接着就是将这些参数全部转换成xml格式数据
String requestXML=this.toxml(packageParams);
//然后就可以想微信发送请求,获取prepare_id了
String resXml=this.sendRequest(UNURL,requestXML,null);
//然后将微信返回的xml数据转换成map格式进行获取
Map map = XMLUtil4jdom.doXMLParse(resXml);
最后将map中的数据添加key生成签名,返回到页面进行调取支付页面
SortedMap<Object,Object> returnMap = new TreeMap<Object, Object>();
if(returnMap !=null){
if(map.get("return_code").equals("SUCCESS")){ //返回成功
if(map.get("result_code").equals("SUCCESS")){
returnMap.put("package","prepay_id="+map.get("prepay_id"));
String timestamp = currTime.substring(0, 10);
returnMap.put("appId",WeixinConfig.APPID);
returnMap.put("timeStamp",timestamp);
returnMap.put("nonceStr",nonce_str);
returnMap.put("signType","MD5");
String paySign = this.createSign("UTF-8", returnMap, key);
returnMap.put("paySign",paySign);
resultMap.put("returnMap",returnMap);
resultMap.put("status",1);
}
}else{
resultMap.put("status",0);
resultMap.put("errorMsg","微信支付失败");
}
}
第三步:页面获取参数后调用微信支付,输密码付钱
data里面装载上文的entity,也就是需要里面的参数进行调用输密码支付页面
function doWxpay(data) {
if (data.code == 0) {
myTip.show("1", "success");
$('#msg').fadeIn(100, function () {
$.toast(data.msg, 'forbidden', 2000);
})
} else {
myTip.show("2", "success");
//支付
if (typeof WeixinJSBridge == "undefined") {
myTip.show("3", "success");
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady',
jsApiCall(data), false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady',
jsApiCall(data));
document.attachEvent('onWeixinJSBridgeReady',
jsApiCall(data));
}
} else {
myTip.show("4", "success");
jsApiCall(data);
}
}
}
function jsApiCall(data) {
myTip.show("5", "success");
WeixinJSBridge.invoke('getBrandWCPayRequest', {
"appId" : data.returnMap.appId, //公众号名称
"timeStamp" : data.returnMap.timeStamp, //时间戳,自1970年以来的秒数
"nonceStr" :data.returnMap.nonceStr, //随机串
"package" : data.returnMap.package,
"signType" : data.returnMap.signType, //微信签名方式
"paySign" :data.returnMap.paySign
}, function(res) {
if(res.err_msg == "get_brand_wcpay_request:ok"){ //支付成功
$.toast("支付成功",2000);
} else if(res.err_msg == "get_brand_wcpay_request:cancel"){ //支付取消
$.toast("支付取消",'forbidden', 2000);
}else if(res.err_msg == "get_brand_wcpay_request:fail"){ //支付失败
alert(JSON.stringify(res));
$.toast("支付失败",'forbidden', 2000);
}
});
}
以下是前文用到的两个工具方法
public static String toxml(SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.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 ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
} else {
sb.append("<" + k + ">" + v + "</" + k + ">");
}
}
sb.append("</xml>");
return sb.toString();
}
//将xml转换成map集合
public static Map<String,String> xmlToMap(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map<String, String> m = new HashMap<String, String>();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if(children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = XMLUtil4jdom.getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
//像微信携带参数发送请求
public static String sendRequest(String urlStr, String data, String contentType){
BufferedReader reader = null;
try {
URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(CONNECT_TIMEOUT);
if(contentType != null)
conn.setRequestProperty("content-type", contentType);
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
if(data == null)
data = "";
writer.write(data);
writer.flush();
writer.close();
reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
sb.append("\r\n");
}
return sb.toString();
} catch (IOException e) {
//logger.error("Error connecting to " + urlStr + ": " + e.getMessage());
} finally {
try {
if (reader != null)
reader.close();
} catch (IOException e) {
}
}
return null;
}
//创建签名的方法
public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
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=" + API_KEY);
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}