微信公眾號支付開發 --Java
阿新 • • 發佈:2019-01-08
/** * 付款頁面 * @param request * @param response * @return * @throws Exception */ @RequestMapping("/callback") public String pay(HttpServletRequest request, HttpServletResponse response) throws Exception { LogUtils.trace("-----------------------/pay/callback/--------------------------------"); /** * 獲得code和state欄位 */ String code = request.getParameter("code"); String state = request.getParameter("state"); int total = (int)(Float.parseFloat(state)*100); /** * 獲取openId */ WxOauthAccessToken oauthAccessToken = PayUtil.getOauthAccessToken(code); String openId = oauthAccessToken.getOpenId(); /** * 執行統一下單介面,初始化各引數 */ Date now = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); String outTradeNo = "NO" + dateFormat.format( now ); String body = "測試"; String nonceStr = StringUtil.generateRandomString(16); // String spBillCreateIP = ReqUtils.getRealIp(request); String spBillCreateIP = "127.0.0.1"; //初始化傳入引數 JsApiReqData jsApiReqData = new JsApiReqData(body,nonceStr,outTradeNo,total,spBillCreateIP, openId); String info = MobiMessage.JsApiReqData2xml(jsApiReqData).replaceAll("__", "_"); LogUtils.trace(info); String url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; StringBuffer sb = JsApiReqUtils.httpsRequest(url, "POST", info); if (!"".equals(sb.toString())) { /** * 得到預支付ID,即prepay_id */ Map<String, String> getMap = MobiMessage.parseXml(new String(sb.toString().getBytes(), "utf-8")); LogUtils.trace(getMap); String prepay_id = getMap.get("prepay_id"); LogUtils.trace(prepay_id); // 生成支付簽名,這個簽名給微信支付的呼叫使用 Integer timeStamp = DateUtils.getCurrentTimestamp(); JsApiToJsData jsApiToJsData = new JsApiToJsData(timeStamp, nonceStr,prepay_id); String redirectUrl = "/#pay?appId=APPID&timeStamp=TIMESTAMP&nonceStr=NONCESTR&prepay_id=PREPAYID&signType=MD5&paySign=SIGN"; redirectUrl = redirectUrl.replace("APPID", jsApiToJsData.getAppid()) .replace("TIMESTAMP", jsApiToJsData.getTimeStamp() + "") .replace("NONCESTR", jsApiToJsData.getNonce_str()) .replace("PREPAYID", jsApiToJsData.getPrepay_id()) .replace("SIGN", jsApiToJsData.getSign()); LogUtils.trace(redirectUrl); return "redirect:" + redirectUrl ; } else { LogUtils.trace("統一下單失敗!"); return ""; } }
/** * 請求公眾號支付API需要提交的資料 * @author Created by fenghui. * @date Created on 2016/09/06/9:28 **/ public class JsApiReqData { //每個欄位具體的意思請檢視API文件 private String appid = ""; private String mch_id = ""; private String body = ""; private String nonce_str = ""; private String sign = ""; private String notify_url = ""; private String out_trade_no = ""; private String spbill_create_ip = ""; private Integer total_fee = 0; private String trade_type = ""; private String openid = ""; /** * @param body 要支付的商品的描述資訊,使用者會在支付成功頁面裡看到這個資訊 * @param nonceStr 隨機字串,不長於32 位 * @param outTradeNo 商戶系統內部的訂單號,32個字元內可包含字母, 確保在商戶系統唯一 * @param totalFee 訂單總金額,單位為“分”,只能整數 * @param spBillCreateIP 訂單生成的機器IP * @param openid 使用者標識,trade_type=JSAPI,此引數必傳,使用者在商戶appid下的唯一標識。 */ public JsApiReqData(String body,String nonceStr,String outTradeNo,Integer totalFee,String spBillCreateIP, String openid){ //微信分配的公眾號ID(開通公眾號之後可以獲取到) setAppid(WxConfigure.AppId); //微信支付分配的商戶號ID(開通公眾號的微信支付功能之後可以獲取到) setMch_id(WxConfigure.Mch_id); //接收微信支付非同步通知後的回撥地址 setNotify_url(WxConfigure.Notify_url); //交易型別,取值如下:JSAPI,NATIVE,APP, setTrade_type(WxConfigure.Trade_type); //要支付的商品的描述資訊,使用者會在支付成功頁面裡看到這個資訊 setBody(body); //商戶系統內部的訂單號,32個字元內可包含字母, 確保在商戶系統唯一 setOut_trade_no(outTradeNo); //訂單總金額,單位為“分”,只能整數 setTotal_fee(totalFee); //訂單生成的機器IP setSpbill_create_ip(spBillCreateIP); //隨機字串,不長於32 位 setNonce_str(nonceStr); //使用者標識,trade_type=JSAPI,此引數必傳,使用者在商戶appid下的唯一標識。 setOpenid(openid); //根據API給的簽名規則進行簽名 SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); parameters.put("appid", appid); parameters.put("mch_id", mch_id); parameters.put("nonce_str", nonce_str); parameters.put("body", body); parameters.put("out_trade_no", out_trade_no); parameters.put("notify_url", notify_url); parameters.put("total_fee", total_fee); parameters.put("spbill_create_ip", spbill_create_ip); parameters.put("trade_type", trade_type); parameters.put("openid", openid); String sign = DictionarySort.createSign(parameters); //根據給的簽名規則進行簽名 setSign(sign);//把簽名資料設定到Sign這個屬性中 }
類StringUtil主要用於產生隨機字串。public class JsApiToJsData { //每個欄位具體的意思請檢視API文件 private String appid = ""; private Integer timeStamp = 0; private String nonce_str = ""; private String sign = ""; private String prepay_id = ""; private String signType = ""; /** * @param nonceStr 隨機字串,不長於32 位 */ public JsApiToJsData(Integer timeStamp,String nonceStr,String prepay_id){ //微信分配的公眾號ID(開通公眾號之後可以獲取到) setAppid(WxConfigure.AppId); setTimeStamp(timeStamp); setNonce_str(nonceStr); setPrepay_id(prepay_id); setSignType("MD5"); //根據API給的簽名規則進行簽名 SortedMap<Object,Object> parameters = new TreeMap<Object,Object>(); parameters.put("appId", appid); parameters.put("timeStamp", timeStamp); parameters.put("nonceStr", nonceStr); parameters.put("package", "prepay_id=" + prepay_id); parameters.put("signType",signType); //根據給的簽名規則進行簽名 String sign = DictionarySort.createSign(parameters); setSign(sign);//把簽名資料設定到Sign這個屬性中 }
public class StringUtil {
public static String generateRandomString() {
return generateRandomString(16);
}
public static String generateRandomString(int length) {
String seekChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
int seekLength = seekChars.length();
String str = "";
for (int i = 0; i < length; i++) {
str += seekChars.charAt((int) (Math.random() * seekLength));
}
return str;
}
public static String generateRandomNumber() {
return generateRandomNumber(6);
}
public static String generateRandomNumber(int length) {
String seekChars = "0123456789";
int seekLength = seekChars.length();
String str = "";
for (int i = 0; i < length; i++) {
str += seekChars.charAt((int) (Math.random() * seekLength));
}
return str;
}
}
類JsApiReqUtils用於獲得網頁支付提交使用者端ip和觸發http請求。public class JsApiReqUtils {
//獲取真實IP
public static String getRealIp(HttpServletRequest request) {
if (request == null) {
LogUtils.error("getRealIp request null");
return "0.0.0.0";
}
String realIp = request.getHeader("X-Real-IP");
LogUtils.trace("realIp:" + realIp);
if (realIp != null && realIp.length() > 0) {
return realIp;
}
realIp = request.getHeader("X-Forwarded-For");
LogUtils.trace("realIp2:" + realIp);
if (realIp != null && realIp.length() > 0) {
return realIp;
}
return request.getRemoteAddr();
}
public static StringBuffer httpsRequest(String requestUrl, String requestMethod, String output)
throws Exception {
HttpURLConnection conn = (HttpURLConnection) new URL(requestUrl).openConnection();
//加入資料
conn.setRequestMethod(requestMethod);
conn.setDoOutput(true);
BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
buffOutStr.write(output.getBytes("utf-8"));
buffOutStr.flush();
buffOutStr.close();
//獲取輸入流
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
StringBuffer sb = new StringBuffer();
while((line = reader.readLine())!= null){
sb.append(line);
}
return sb;
}
}
MobiMessage類主要用於把json格式的資料轉換為微信需要的xml格式的資料,並把返回值從xml格式解析為map格式。
public class MobiMessage {
public static Map<String,String> xml2map(HttpServletRequest request) throws IOException, DocumentException {
Map<String,String> map = new HashMap<String, String>();
SAXReader reader = new SAXReader();
InputStream inputStream = request.getInputStream();
Document document = reader.read(inputStream);
Element root = document.getRootElement();
List<Element> list = root.elements();
for(Element e:list){
map.put(e.getName(), e.getText());
}
inputStream.close();
return map;
}
//訂單轉換成xml
public static String JsApiReqData2xml(JsApiReqData jsApiReqData){
/*XStream xStream = new XStream();
xStream.alias("xml",productInfo.getClass());
return xStream.toXML(productInfo);*/
MobiMessage.xstream.alias("xml",jsApiReqData.getClass());
return MobiMessage.xstream.toXML(jsApiReqData);
}
public static String RefundReqData2xml(RefundReqData refundReqData){
/*XStream xStream = new XStream();
xStream.alias("xml",productInfo.getClass());
return xStream.toXML(productInfo);*/
MobiMessage.xstream.alias("xml",refundReqData.getClass());
return MobiMessage.xstream.toXML(refundReqData);
}
public static String class2xml(Object object){
return "";
}
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;
}
//擴充套件xstream,使其支援CDATA塊
private static XStream xstream = new XStream(new XppDriver() {
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
// 對所有xml節點的轉換都增加CDATA標記
boolean cdata = true;
//@SuppressWarnings("unchecked")
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);
}
}
};
}
});
}
DateUtils類主要用於獲得當前的時間戳。
public class DateUtils {
public static int minTimestamp = 0; //最小的時間戳
public static int maxTimestamp = 1999999999;//最大的時間戳
public static int offset = 0;
/**
* 按照yyyy-MM-dd HH:mm:ss的格式,日期轉字串
*
* @param date
* @return yyyy-MM-dd HH:mm:ss
*/
public static String date2Str(Date date) {
return date2Str(date, "yyyy-MM-dd HH:mm:ss");
}
/**
* 按照引數format的格式,日期轉字串
*
* @param date
* @param format
* @return
*/
public static String date2Str(Date date, String format) {
if (date != null) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(date);
} else {
return "";
}
}
public static int getCurrentTimestamp() {
return (int) (System.currentTimeMillis() / 1000 + offset);
}
public static boolean setCurrentTimestamp(int timestamp) {
int systemTimestamp = (int) (System.currentTimeMillis() / 1000);
offset = timestamp - systemTimestamp;
return true;
}
}