微信公眾號(hào)支付(二)實(shí)現(xiàn)統(tǒng)一下單接口
上一篇已經(jīng)獲取到了用戶的OpenId
這篇主要是調(diào)用微信公眾支付的統(tǒng)一下單API
API地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
看文檔,主要流程就是把20個(gè)左右的參數(shù)封裝為XML格式發(fā)送到微信給的接口地址,然后就可以獲取到返回的內(nèi)容了,如果成功里面就有支付所需要的預(yù)支付ID
請(qǐng)求參數(shù)就不解釋了。
其中,隨機(jī)字符串:我用的是UUID去中劃線
public static String create_nonce_str() {
return UUID.randomUUID().toString().replace("-","");
}
商戶訂單號(hào):每個(gè)訂單號(hào)只能使用一次,所以用的是系統(tǒng)的訂單號(hào)加的時(shí)間戳。
總金額:不能為
通知地址:微信支付成功或失敗回調(diào)給系統(tǒng)的地址
簽名:
import java.io.Serializable;
public class PayInfo implements Serializable{
private static final long serialVersionUID = L;
private String appid;
private String mch_id;
private String device_info;
private String nonce_str;
private String sign;
private String body;
private String attach;
private String out_trade_no;
private int total_fee;
private String spbill_create_ip;
private String notify_url;
private String trade_type;
private String openid;
//下面是get,set方法
}
/**
* 創(chuàng)建統(tǒng)一下單的xml的java對(duì)象
* @param bizOrder 系統(tǒng)中的業(yè)務(wù)單號(hào)
* @param ip 用戶的ip地址
* @param openId 用戶的openId
* @return
*/
public PayInfo createPayInfo(BizOrder bizOrder,String ip,String openId) {
PayInfo payInfo = new PayInfo();
payInfo.setAppid(Constants.appid);
payInfo.setDevice_info("WEB");
payInfo.setMch_id(Constants.mch_id);
payInfo.setNonce_str(CommonUtil.create_nonce_str().replace("-", ""));
payInfo.setBody("這里是某某白米飯的body");
payInfo.setAttach(bizOrder.getId());
payInfo.setOut_trade_no(bizOrder.getOrderCode().concat("A").concat(DateFormatUtils.format(new Date(), "MMddHHmmss")));
payInfo.setTotal_fee((int)bizOrder.getFeeAmount());
payInfo.setSpbill_create_ip(ip);
payInfo.setNotify_url(Constants.notify_url);
payInfo.setTrade_type("JSAPI");
payInfo.setOpenid(openId);
return payInfo;
}
獲取簽名:
/**
* 獲取簽名
* @param payInfo
* @return
* @throws Exception
*/
public String getSign(PayInfo payInfo) throws Exception {
String signTemp = "appid="+payInfo.getAppid()
+"&attach="+payInfo.getAttach()
+"&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="+Constants.key; //這個(gè)key注意
MessageDigest md = MessageDigest.getInstance("MD");
md.reset();
md.update(signTemp.getBytes("UTF-"));
String sign = CommonUtil.byteToStr(md.digest()).toUpperCase();
return sign;
}
注意:上面的Constants.key取值在商戶號(hào)API安全的API密鑰中。
一些工具方法:獲取ip地址,將字節(jié)數(shù)組轉(zhuǎn)換為十六進(jìn)制字符串,將字節(jié)轉(zhuǎn)換為十六進(jìn)制字符串
/**
* 將字節(jié)數(shù)組轉(zhuǎn)換為十六進(jìn)制字符串
*
* @param byteArray
* @return
*/
public static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = ; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 將字節(jié)轉(zhuǎn)換為十六進(jìn)制字符串
*
* @param btyes
* @return
*/
public static String byteToHexStr(byte bytes) {
char[] Digit = { '', '', '', '', '', '', '', '', '', '', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[];
tempArr[] = Digit[(bytes >>> ) & XF];
tempArr[] = Digit[bytes & XF];
String s = new String(tempArr);
return s;
}
/**
* 獲取ip地址
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
InetAddress addr = null;
try {
addr = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
return request.getRemoteAddr();
}
byte[] ipAddr = addr.getAddress();
String ipAddrStr = "";
for (int i = ; i < ipAddr.length; i++) {
if (i > ) {
ipAddrStr += ".";
}
ipAddrStr += ipAddr[i] & xFF;
}
return ipAddrStr;
}
這樣就獲取了簽名,把簽名與PayInfo中的其他數(shù)據(jù)轉(zhuǎn)成XML格式,當(dāng)做參數(shù)傳遞給統(tǒng)一下單地址。
PayInfo pi = pu.createPayInfo(bo,"...",""); String sign = pu.getSign(pi); pi.setSign(sign);
對(duì)象轉(zhuǎn)XML
/**
* 擴(kuò)展xstream使其支持CDATA
*/
private static XStream xstream = new XStream(new XppDriver() {
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
//增加CDATA標(biāo)記
boolean cdata = true;
@SuppressWarnings("rawtypes")
public void startNode(String name, Class clazz) {
super.startNode(name, clazz);
}
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
writer.write("<![CDATA[");
writer.write(text);
writer.write("]]>");
} else {
writer.write(text);
}
}
};
}
});
public static String payInfoToXML(PayInfo pi) {
xstream.alias("xml", pi.getClass());
return xstream.toXML(pi);
}
xml轉(zhuǎn)Map
@SuppressWarnings("unchecked")
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;
}
下面就是調(diào)用統(tǒng)一下單的URL了
log.info(MessageUtil.payInfoToXML(pi).replace("__", "_"));
Map<String, String> map = CommonUtil.httpsRequestToXML("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", MessageUtil.payInfoToXML(pi).replace("__", "_").replace("<![CDATA[", "").replace("]]>", ""));
log.info(map);
public static Map<String, String> httpsRequestToXML(String requestUrl, String requestMethod, String outputStr) {
Map<String, String> result = new HashMap<>();
try {
StringBuffer buffer = httpsRequest(requestUrl, requestMethod, outputStr);
result = MessageUtil.parseXml(buffer.toString());
} catch (ConnectException ce) {
log.error("連接超時(shí):"+ce.getMessage());
} catch (Exception e) {
log.error("https請(qǐng)求異常:"+ece.getMessage());
}
return result;
}
httpsRequest()這個(gè)方法在第一篇中
上面獲取到的Map如果成功的話,里面就會(huì)有
String return_code = map.get("return_code");
if(StringUtils.isNotBlank(return_code) && return_code.equals("SUCCESS")){
String return_msg = map.get("return_msg");
if(StringUtils.isNotBlank(return_msg) && !return_msg.equals("OK")) {
return "統(tǒng)一下單錯(cuò)誤!";
}
}else{
return "統(tǒng)一下單錯(cuò)誤!";
}
String prepay_Id = map.get("prepay_id");
這個(gè)prepay_id就是預(yù)支付的ID。后面支付需要它。
- 詳細(xì)介紹高性能Java緩存庫(kù)Caffeine
- java8中parallelStream性能測(cè)試及結(jié)果分析
- Java性能優(yōu)化之?dāng)?shù)據(jù)結(jié)構(gòu)實(shí)例代碼
- Java文件讀寫(xiě)IO/NIO及性能比較詳細(xì)代碼及總結(jié)
- 10種簡(jiǎn)單的Java性能優(yōu)化
- Java8中流的性能及流的幾個(gè)特性
- 淺談Java模板引擎性能對(duì)比
- golang、python、php、c++、c、java、Nodejs性能對(duì)比
- JAVA下單接口優(yōu)化實(shí)戰(zhàn)TPS性能提高10倍
相關(guān)文章
基于java Springboot實(shí)現(xiàn)教務(wù)管理系統(tǒng)詳解
這篇文章主要介紹了Java 實(shí)現(xiàn)簡(jiǎn)易教務(wù)管理系統(tǒng)的代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
使用Java獲取Json中的數(shù)據(jù)簡(jiǎn)單示例
開(kāi)發(fā)過(guò)程中經(jīng)常會(huì)遇到j(luò)son數(shù)據(jù)的處理,而單獨(dú)對(duì)json數(shù)據(jù)進(jìn)行增刪改并不方便,下面這篇文章主要給大家介紹了關(guān)于使用Java獲取Json中的數(shù)據(jù),文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04
VsCode搭建Java開(kāi)發(fā)環(huán)境的方法
這篇文章主要介紹了VsCode搭建Java開(kāi)發(fā)環(huán)境的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11
設(shè)計(jì)模式之原型模式_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了設(shè)計(jì)模式之原型模式,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08
關(guān)于Java的對(duì)象序列化流和反序列化流詳細(xì)解讀
這篇文章主要介紹了關(guān)于Java的對(duì)象序列化流和反序列化流,對(duì)象序列化:就是將對(duì)象保存到磁盤(pán)中,或者在網(wǎng)絡(luò)中傳輸對(duì)象,反之,自己序列還可以從文件中讀取回來(lái),重構(gòu)對(duì)象,對(duì)它進(jìn)行反序列化,需要的朋友可以參考下2023-05-05
Springboot啟動(dòng)報(bào)錯(cuò)時(shí)實(shí)現(xiàn)異常定位
這篇文章主要介紹了Springboot啟動(dòng)報(bào)錯(cuò)時(shí)實(shí)現(xiàn)異常定位,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
Java算法練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(1)
方法下面小編就為大家?guī)?lái)一篇Java算法的一道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-07-07

