java實(shí)現(xiàn)微信H5支付方法詳解
前面做了app微信支付的回調(diào)處理,現(xiàn)在需要做微信公眾號(hào)的支付,花了一天多時(shí)間,終于折騰出來了!鑒于的微信官方?jīng)]有提供Java版的demo,所以全靠自己按照同樣的文檔敲敲敲,所以記錄下來,以供自己及后來人參考,不足之處,還請(qǐng)指正。
首先,我們貼出調(diào)用支付接口的H5頁面,當(dāng)然,在這個(gè)頁面之前,還需要做很多其他的操作,我們一步一步的來。
的官方文檔給了兩個(gè)不同的支付接口,在微信公眾平臺(tái)開發(fā)中文檔的“微信JS-SDK說明文檔”中,給出的支付方式是下面被屏蔽的那一部分,而在商戶平臺(tái)的“H5調(diào)起支付API”中,又給了一份不同的接口,即下面未屏蔽正常使用的接口。關(guān)于的微信提供了兩個(gè)不同的支付接口,網(wǎng)上搜索結(jié)果也是眾說紛紛,所以,只有自己試了。當(dāng)然,為了簡單,我直接試了下面這一種,然后奇跡般的成功了。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>微信網(wǎng)頁支付</title>
<!-- <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> -->
<!-- <script type="text/javascript" src=" https://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> -->
<script type="text/javascript">
/* wx.config({
debug: true, // 開啟調(diào)試模式,調(diào)用的所有api的返回值會(huì)在客戶端alert出來,若要查看傳入的參數(shù),可以在pc端打開,參數(shù)信息會(huì)通過log打出,僅在pc端時(shí)才會(huì)打印。
appId: appid, // 必填,公眾號(hào)的唯一標(biāo)識(shí)
timestamp: timestamp, // 必填,生成簽名的時(shí)間戳
nonceStr: nonceStr, // 必填,生成簽名的隨機(jī)串
signature: '',// 必填,簽名,見附錄1
jsApiList: [chooseWXPay] // 必填,需要使用的JS接口列表,所有JS接口列表見附錄2
}); */
// config信息驗(yàn)證后會(huì)執(zhí)行ready方法,所有接口調(diào)用都必須在config接口獲得結(jié)果之后,config是一個(gè)客戶端的異步操作
//所以如果需要在頁面加載時(shí)就調(diào)用相關(guān)接口,則須把相關(guān)接口放在ready函數(shù)中調(diào)用來確保正確執(zhí)行
//wx.ready(function(){
//參數(shù)是后臺(tái)傳過來的,簽名加密,隨機(jī)數(shù),時(shí)間戳等全部后臺(tái)處理好
var appId="${appId}";
var timeStamp="${timeStamp}";
var nonceStr="${nonceStr}";
var prepay_id="${prepay_id}";//之前參數(shù)名叫package,對(duì)應(yīng)api接口,因?yàn)閜ackage是關(guān)鍵字,被坑了一次
var sign="${paySign}";
//支付接口
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId" : appId, //公眾號(hào)名稱,由商戶傳入
"timeStamp" : timeStamp, //時(shí)間戳,自1970年以來的秒數(shù) (java需要處理成10位才行,又一坑)
"nonceStr" : nonceStr, //隨機(jī)串
"package" : prepay_id, //拼裝好的預(yù)支付標(biāo)示
"signType" : "MD5",//微信簽名方式
"paySign" : sign //微信簽名
},
function(res){
//使用以下方式判斷前端返回,微信團(tuán)隊(duì)鄭重提示:res.err_msg將在用戶支付成功后返回 ok,但并不保證它絕對(duì)可靠。
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
alert("支付成功");
}else{
alert("支付失敗");
}
}
);
}
if (typeof(WeixinJSBridge) == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
//});
//究竟哪個(gè)是支付接口
/* wx.chooseWXPay({
timestamp: timestamp, // 支付簽名時(shí)間戳,注意微信jssdk中的所有使用timestamp字段均為小寫。但最新版的支付后臺(tái)生成簽名使用的timeStamp字段名需大寫其中的S字符
nonceStr: nonceStr, // 支付簽名隨機(jī)串,不長于 32 位
package: 'prepay_id', // 統(tǒng)一支付接口返回的prepay_id參數(shù)值,提交格式如:prepay_id=***)
signType: 'MD5', // 簽名方式,默認(rèn)為'SHA1',使用新版支付需傳入'MD5'
paySign: sign, // 支付簽名
success: function (res) {
// 支付成功后的回調(diào)函數(shù)
//使用以下方式判斷前端返回,微信團(tuán)隊(duì)鄭重提示:res.err_msg將在用戶支付成功后返回 ok,但并不保證它絕對(duì)可靠。
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
}
}
}); */
</script>
</head>
<body>
</body>
</html>
上面h5頁面中,支付接口所需的參數(shù)都是由后臺(tái)傳過來的,除此之外,在進(jìn)行上面一步之前,我們還需要獲取一個(gè)預(yù)支付標(biāo)識(shí),下面貼上后臺(tái)傳參,及獲取預(yù)支付標(biāo)識(shí)和參數(shù)加密等方法(獲取預(yù)支付標(biāo)識(shí)之前需要網(wǎng)頁授權(quán)獲取用戶openid,鑒于這個(gè)比較簡單,所以不貼代碼了)
首先是后臺(tái)參數(shù)封裝并對(duì)其簽名(關(guān)鍵部分代碼):
if(payway.equals("1")){
System.out.println("----------支付寶支付-------------");
request.setAttribute("WIDout_trade_no", WIDout_trade_no);//訂單號(hào)
request.setAttribute("WIDsubject", WIDsubject);//訂單名稱
request.setAttribute("WIDtotal_fee", WIDtotal_fee);//付款金額
request.setAttribute("WIDshow_url", WIDshow_url);//商品鏈接
request.setAttribute("WIDbody", "");//商品描述,可空
return "alipayapi";
}else if(payway.equals("2")){
System.out.println("----------微信支付-------------");
//1、通過網(wǎng)頁授權(quán)接口,獲取到的openid
String openid=request.getSession().getAttribute("openid")+"";
//處理價(jià)格單位為:分(請(qǐng)自行處理)
WIDtotal_fee="1";
String preid=getPrepayid(WIDout_trade_no, WIDtotal_fee, openid);//獲取預(yù)支付標(biāo)示
System.out.println("預(yù)支付標(biāo)示:----------------"+preid);
//APPID
String appId=Common.appid;
request.setAttribute("appId", appId);
//時(shí)間戳
String timeStamp=(System.currentTimeMillis()/1000)+"";
request.setAttribute("timeStamp", timeStamp);
//隨機(jī)字符串
String nonceStr=Common.randString(16).toString();
request.setAttribute("nonceStr", nonceStr);
//預(yù)支付標(biāo)識(shí)
request.setAttribute("prepay_id", "prepay_id="+preid);
//加密方式
request.setAttribute("signType", "MD5");
//組裝map用于生成sign
Map<String, String> map=new HashMap<String, String>();
map.put("appId", appId);
map.put("timeStamp", timeStamp);
map.put("nonceStr", nonceStr);
map.put("package", "prepay_id="+preid);
map.put("signType", "MD5");
request.setAttribute("paySign", Common.sign(map, Common.MchSecret));//簽名
return "weixinpay";
}else {
return "error";
}
接下是獲取預(yù)支付標(biāo)識(shí)的方法getPrepayid:
/**
* 微信統(tǒng)一下單接口,獲取預(yù)支付標(biāo)示prepay_id
* @param out_trade_no1 商戶訂單號(hào)
* @param total_fee1 訂單金額(單位:分)
* @param openid1 網(wǎng)頁授權(quán)取到的openid
* @return
*/
@ResponseBody
public String getPrepayid(String out_trade_no1,String total_fee1,String openid1){
String result = "";
String appid = Common.appid;
String mch_id = Common.mch_id;
String nonce_str = Common.randString(16);//生成隨機(jī)數(shù),可直接用系統(tǒng)提供的方法
String body = "E光學(xué)-商品訂單";
String out_trade_no = out_trade_no1;
String total_fee = total_fee1;
String spbill_create_ip = "xxx.xxx.38.47";//用戶端ip,這里隨意輸入的
String notify_url = "http://www.xxxxxxx.cn/egxwx/wxpay_notify_url.jsp";//支付回調(diào)地址
String trade_type = "JSAPI";
String openid = openid1;
HashMap<String, String> map = new HashMap<String, String>();
map.put("appid", appid);
map.put("mch_id", mch_id);
map.put("attach", "支付測試");
map.put("device_info", "WEB");
map.put("nonce_str", nonce_str);
map.put("body", body);
map.put("out_trade_no", out_trade_no);
map.put("total_fee", total_fee);
map.put("spbill_create_ip", spbill_create_ip);
map.put("trade_type", trade_type);
map.put("notify_url", notify_url);
map.put("openid", openid);
String sign = Common.sign(map, Common.MchSecret);//參數(shù)加密
System.out.println("sign秘鑰:-----------"+sign);
map.put("sign", sign);
//組裝xml
String content=Common.MapToXml(map);
//System.out.println(content);
String PostResult=HttpClient.HttpsPost("https://api.mch.weixin.qq.com/pay/unifiedorder", content);
JSONObject jsonObject=XmlUtil.XmlToJson(PostResult);//返回的的結(jié)果
if(jsonObject.getString("return_code").equals("SUCCESS")&&jsonObject.getString("result_code").equals("SUCCESS")){
result=jsonObject.get("prepay_id")+"";//這就是預(yù)支付id
}
return result;
}
接下是簽名的方法(MD5加密是調(diào)用微信一個(gè)jar里面的,你也可以自己寫一個(gè),網(wǎng)上很多參考):
/**
* 生成簽名sign
* 第一步:非空參數(shù)值的參數(shù)按照參數(shù)名ASCII碼從小到大排序,按照鍵值對(duì)的形式。生成字符串StringA
* stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
* 第二部:拼接API密鑰,這里的秘鑰是微信商戶平臺(tái)的秘鑰,是自己設(shè)置的,不是公眾號(hào)的秘鑰
* stringSignTemp="stringA&key=192006250b4c09247ec02edce69f6a2d"
* 第三部:MD5加密
* sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7"
*
* @param map 不包含空字符串的map
* @return
*/
public static String sign(Map<String, String> map,String key) {
//排序
String sort=sortParameters(map);
//拼接API秘鑰
sort=sort+"&key="+key;
//System.out.println(sort);
//MD5加密
String sign=MD5.MD5Encode(sort).toUpperCase();
return sign;
}
/**
* 對(duì)參數(shù)列表進(jìn)行排序,并拼接key=value&key=value形式
* @param map
* @return
*/
private static String sortParameters(Map<String, String> map) {
Set<String> keys = map.keySet();
List<String> paramsBuf = new ArrayList<String>();
for (String k : keys) {
paramsBuf.add((k + "=" + getParamString(map, k)));
}
// 對(duì)參數(shù)排序
Collections.sort(paramsBuf);
String result="";
int count=paramsBuf.size();
for(int i=0;i<count;i++){
if(i<(count-1)){
result+=paramsBuf.get(i)+"&";
}else {
result+=paramsBuf.get(i);
}
}
return result;
}
/**
* 返回key的值
* @param map
* @param key
* @return
*/
private static String getParamString(Map map, String key) {
String buf = "";
if (map.get(key) instanceof String[]) {
buf = ((String[]) map.get(key))[0];
} else {
buf = (String) map.get(key);
}
return buf;
}
/**
* 字符串列表從大到小排序
* @param data
* @return
*/
private static List<String> sort(List<String> data) {
Collections.sort(data, new Comparator<String>() {
public int compare(String obj1, String obj2) {
return obj1.compareTo(obj2);
}
});
return data;
}
Map轉(zhuǎn)XML的方法:
/**
* Map轉(zhuǎn)Xml
* @param arr
* @return
*/
public static String MapToXml(Map<String, String> arr) {
String xml = "<xml>";
Iterator<Entry<String, String>> iter = arr.entrySet().iterator();
while (iter.hasNext()) {
Entry<String, String> entry = iter.next();
String key = entry.getKey();
String val = entry.getValue();
if (IsNumeric(val)) {
xml += "<" + key + ">" + val + "</" + key + ">";
} else
xml += "<" + key + "><![CDATA[" + val + "]]></" + key + ">";
}
xml += "</xml>";
return xml;
}
private static boolean IsNumeric(String str) {
if (str.matches("\\d *")) {
return true;
} else {
return false;
}
}
以上就是java實(shí)現(xiàn)微信H5支付的主要代碼了,大部分都有注釋,也沒有什么好解釋的了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
運(yùn)用Spring?Aop+注解實(shí)現(xiàn)日志記錄
我們都知道Spring框架的兩大特性分別是 IOC (控制反轉(zhuǎn))和 AOP (面向切面),這個(gè)是每一個(gè)Spring學(xué)習(xí)視頻里面一開始都會(huì)提到的,這里,如果我們使用Aop來記錄日志,那是再好不過了,感興趣的朋友跟隨小編一起學(xué)習(xí)下Spring?Aop注解實(shí)現(xiàn)日志記錄的過程吧2022-01-01
基于Spring p標(biāo)簽和c標(biāo)簽注入方式
這篇文章主要介紹了Spring p標(biāo)簽和c標(biāo)簽注入方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
Spring?Boot集成RabbitMQ以及隊(duì)列模式操作
RabbitMQ是實(shí)現(xiàn)AMQP(高級(jí)消息隊(duì)列協(xié)議)的消息中間件的一種,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot集成RabbitMQ以及隊(duì)列模式操作的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04
教你在 IntelliJ IDEA 中使用 VIM插件的詳細(xì)教程
這篇文章主要介紹了在 IntelliJ IDEA 中使用 VIM的教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
SpringBoot集成yitter-idgenerator(雪花漂移)分布式Id自增的實(shí)現(xiàn)
本文主要介紹了SpringBoot集成yitter-idgenerator(雪花漂移)分布式Id自增的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
Java中小球碰撞并使用按鈕控制數(shù)量實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于Java中小球碰撞并使用按鈕控制數(shù)量的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-12-12

