微信公眾號(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文件讀寫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)文章
關(guān)于java入門與java開發(fā)環(huán)境配置詳細(xì)教程
這篇文章主要介紹了關(guān)于java入門與java開發(fā)環(huán)境配置詳細(xì)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03詳解SpringBoot Redis自適應(yīng)配置(Cluster Standalone Sentinel)
這篇文章主要介紹了詳解SpringBoot Redis自適應(yīng)配置(Cluster Standalone Sentinel),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07Java構(gòu)造方法實(shí)例詳解(動(dòng)力節(jié)點(diǎn)java學(xué)院整理)
其實(shí)java構(gòu)造方法很簡(jiǎn)單,下面通過示例給大家分享java構(gòu)造方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-04-04Java學(xué)習(xí)之Lambda表達(dá)式的使用詳解
Lambda表達(dá)式是Java SE 8中一個(gè)重要的新特性,允許通過表達(dá)式來代替功能接口。本文將通過一些簡(jiǎn)單的示例和大家講講Lamda表達(dá)式的使用,感興趣的可以了解一下2022-12-12