微信支付後臺介面開發(掃碼版)
阿新 • • 發佈:2018-11-21
一:需求
滿足公司在網頁上達到直接微信掃碼支付的需求
二:API官方文件
參考連結:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
三:微信支付的過程(使用者-商家-微信伺服器)
四:程式碼實現
實體類: 參考連結:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
1.統一下單請求引數
/** * 統一下單請求引數(必填) */ @XmlRootElement <span>此處加註解是為了後臺xmljie public class UnifiedOrderRequest { private String appid; //ID private String mch_id; //商戶號 private String nonce_str; //隨機字串 private String sign; //數字簽名 private String body; //商品描述 private String out_trade_no; //商戶訂單 private String total_fee; //總金額 private String spbill_create_ip; //終端Ip private String notify_url; //通知地址 private String trade_type; //交易型別 public UnifiedOrderRequest(){}; public UnifiedOrderRequest(String appid, String mch_id, String nonce_str, String sign, String body, String out_trade_no, String total_fee, String spbill_create_ip, String notify_url, String trade_type) { super(); this.appid = appid; this.mch_id = mch_id; this.nonce_str = nonce_str; this.sign = sign; this.body = body; this.out_trade_no = out_trade_no; this.total_fee = total_fee; this.spbill_create_ip = spbill_create_ip; this.notify_url = notify_url; this.trade_type = trade_type; } }
統一下單引數(非必須)
3.訂單返回引數/** * 統一下單請求引數(非必填) */ @XmlRootElement <span>此處加註解是為了xml解析</span> public class UnifiedOrderRequestExt extends UnifiedOrderRequest{ private String device_info; //裝置號 private String detail; //商品詳情 private String attach; //附加資料 private String fee_type; //貨幣型別 private String time_start; //交易起始時間 private String time_expire; //交易結束時間 private String goods_tag; //商品標記 private String product_id; //商品ID private String limit_pay; //指定支付方式 private String openid; //使用者標誌 public UnifiedOrderRequestExt(){ super(); } public UnifiedOrderRequestExt(String appid, String mch_id, String nonce_str, String sign, String body, String out_trade_no, String total_fee, String spbill_create_ip, String notify_url, String trade_type, String device_info, String detail, String attach, String fee_type, String time_start, String time_expire, String goods_tag, String product_id, String limit_pay, String openid) { super(appid, mch_id, nonce_str, sign, body, out_trade_no, total_fee, spbill_create_ip, notify_url, trade_type); this.device_info = device_info; this.detail = detail; this.attach = attach; this.fee_type = fee_type; this.time_start = time_start; this.time_expire = time_expire; this.goods_tag = goods_tag; this.product_id = product_id; this.limit_pay = limit_pay; this.openid = openid; } }
/**
* 統一下單返回引數
*
*/
<span>此處加註解是為了xml解析</span>
@XmlAccessorType(XmlAccessType.FIELD)@XmlRootElement(name = "UnifiedOrderRespose")@XmlType(propOrder={"return_code","return_msg","appid","mch_id","device_info","nonce_str","sign","result_code","err_code","err_code_des","trade_type","prepay_id","code_url"})public class UnifiedOrderRespose {private String return_code; //返回狀態碼private String return_msg; //返回資訊private String appid; //公眾賬號IDprivate String mch_id; //商戶號private String device_info; //裝置號private String nonce_str; //隨機字串private String sign; //數字簽名private String result_code; //業務結果private String err_code; //錯誤程式碼private String err_code_des; // 錯誤程式碼描述private String trade_type; //交易型別private String prepay_id; //預支付交易會話標緻private String code_url; //二維碼連結public UnifiedOrderRespose(){}public UnifiedOrderRespose(String return_code, String return_msg,String appid, String mch_id, String device_info, String nonce_str,String sign, String result_code, String err_code,String err_code_des, String trade_type, String prepay_id,String code_url) {super();this.return_code = return_code;this.return_msg = return_msg;this.appid = appid;this.mch_id = mch_id;this.device_info = device_info;this.nonce_str = nonce_str;this.sign = sign;this.result_code = result_code;this.err_code = err_code;this.err_code_des = err_code_des;this.trade_type = trade_type;this.prepay_id = prepay_id;this.code_url = code_url;}
二:xml解析(二種解析型別,已封裝,可直接使用)
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class XMLObjectConvertUtil {
/**
* 將xml檔案轉換為Map集合
* @param String型別的xml
* @return map集合
*/
public static Map<String, String> getMap(String xml) throws Exception{
try{
Map<String, String> maps = new HashMap<String, String>();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputStream inputStream = new ByteArrayInputStream(xml.getBytes("utf-8"));
Document document = documentBuilder.parse(inputStream);
document.getDocumentElement().normalize();
//獲得所有的節點
NodeList nodeList = document.getDocumentElement().getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
//如果為標記節點 如 <username>
if(node.getNodeType() == node.ELEMENT_NODE){
Element e = (Element) node;
//將節點名稱與節點內容放進map集合中
maps.put(e.getNodeName(),e.getTextContent());
}
}
return maps;
}catch(Exception e){
e.printStackTrace();
}
return null;
}
/**
* 將Map集合轉換成String型別的xml
* @param maps
* @return
* @throws Exception
*/
public String getXml(Map<String, String> maps) throws Exception{
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.newDocument();
//建立根目錄
Element root = document.createElement("xml");
//新增根目錄進文件
document.appendChild(root);
//遍歷所有的key
for(String key:maps.keySet()){
//獲得key所對應的value
String value= maps.get(key);
if(value == null){
value ="";
}
//去掉空格
value = value.trim();
//建立子節點
Element filed = document.createElement(key);
//將key所對應的value放入node中
filed.appendChild(document.createTextNode(value));
//將子節點放入根節點中
root.appendChild(filed);
}
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer =transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING,"utf-8");
transformer.setOutputProperty(OutputKeys.INDENT,"yes");
StringWriter sw = new StringWriter();
StreamResult sr = new StreamResult(sw);
transformer.transform(domSource, sr);
String output = sw.getBuffer().toString();
return output;
}
/**
* 將java物件轉換成String型別的xml
* @param obj
* @return
* @throws Exception
*/
public String getXml(Object obj) throws Exception{
StringWriter sw = new StringWriter();
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller mar = context.createMarshaller();
//統一編碼
mar.setProperty(mar.JAXB_ENCODING, "utf-8");
mar.setProperty(mar.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
mar.marshal(obj, sw);
return sw.toString();
}
}
四:現在實體類和轉換方法都已經貼出。現在就是後臺介面的編寫了
package com.util;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.struts2.ServletActionContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.bean.UnifiedOrderRequest;
import com.bean.UnifiedOrderRespose;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;
public class MainAction {
String orderId;
HttpServletResponse response;
String msg;
/**
* 建立二維碼
*/
public void createCode(){
//建立訂單
String orderInfo = createOrderInfo(orderId);
if(orderInfo!=null){
//得到統一下單 API
String codeUrl = httpOrder(orderInfo);
if(codeUrl!=null){
//利用 返回的預支付交易連結(codeUrl) 生成掃描的二維碼
try {
int width =200; //寬200
int height = 200; //高200
String format = "jpg"; //圖片格式
//開始建立二維碼
Hashtable hs = new Hashtable();
//設定編碼格式
hs.put(EncodeHintType.CHARACTER_SET, "utf-8");
//設定二維碼
BitMatrix bit = new MultiFormatWriter().encode(codeUrl, BarcodeFormat.QR_CODE, width, height);
OutputStream out = null;
out = ServletActionContext.getResponse().getOutputStream();
MatrixToImageWriter.writeToStream(bit,format,out);
out.flush();
out.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 調統一下單API
*/
private String httpOrder(String orderInfo) {
//微信支付統一介面
String url ="https://api.mch.weixin.qq.com/pay/unifiedorder";
try {
//連線微信統一支付url
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
//加入資料
conn.setRequestMethod("POST");
//開啟傳輸輸出流
conn.setDoOutput(true);
//獲取輸出流
BufferedOutputStream buffer = new BufferedOutputStream(conn.getOutputStream());
buffer.write(orderInfo.getBytes());
buffer.flush();
buffer.close();
//獲取輸入流
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
//接受資料
String line = null;
StringBuffer sb = new StringBuffer();
//將輸入流中的資訊放在sb中
while((line=reader.readLine())!=null){
sb.append(line);
};
//解析xml成物件
JAXBContext context = JAXBContext.newInstance(UnifiedOrderRespose.class);
//反序列化成物件
Unmarshaller unmarshaller = context.createUnmarshaller();
//讀取資料
StringReader sr = new StringReader(sb.toString());
//根據微信Demo 將xml轉換成Map
Map<String, String> ResponseMap = new HashMap<String, String>();
//建立文件型別
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
//將獲取到的xml放入輸入流中
InputStream in = new ByteArrayInputStream(sb.toString().getBytes("utf-8"));
Document doc;
doc = db.parse(in);
NodeList nl = doc.getDocumentElement().getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if(node.getNodeType() == node.ELEMENT_NODE){
Element e = (Element) node;
ResponseMap.put(e.getNodeName(), e.getTextContent());
}
}
/* 轉換成UnifiedOrderRespose物件
UnifiedOrderRespose unRespose = (UnifiedOrderRespose) unmarshaller.unmarshal(new File("C:/Users/Administrator/Workspaces/MyEclipse 10/weixinpay/WebRoot/xmls/Result.xml"));
UnifiedOrderRespose unRespose = (UnifiedOrderRespose) unmarshaller.unmarshal(sr);
System.out.println(unRespose.getReturn_code()+"|"+unRespose.getReturn_msg());
unRespose.setReturn_code("SUCCESS");
unRespose.setReturn_msg("SUCCESS");
unRespose.setCode_url("www.baidu.com");*/
//如果請求成功返回 ,則返回支付連結
if(null!=ResponseMap
&& "SUCCESS".equals(ResponseMap.get("return_code"))
&& "SUCCESS".equals(ResponseMap.get("return_msg"))){
return ResponseMap.get("code_url");
}else{
return null;
}
}catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* 生成訂單,返回xml檔案
*/
private String createOrderInfo(String orderId) {
UnifiedOrderRequest un = new UnifiedOrderRequest();
un.setAppid("xxxxxxxxxxxxx");//公眾賬號ID
un.setMch_id("xxxxxxxxx");//商戶號
un.setNonce_str(UUID.randomUUID().toString().substring(0,30));//隨機字串 <span style="color:#ff0000;"><strong>說明2(見文末)</strong></span>
un.setBody("xxxxxx");//商品描述
un.setOut_trade_no(orderId);//商戶訂單號
un.setTotal_fee("10000"); //金額需要擴大100倍:1代表支付時是0.01
un.setSpbill_create_ip("xxxxxxxxxxxxx");//終端IP
un.setNotify_url("xxxxxxxxxxxxxx");//支付成功後,回撥的地址
un.setSign(createSign(un));//簽名
un.setTrade_type("NATIVE");//JSAPI--公眾號支付、NATIVE--原生掃碼支付、APP--app支付
try {
//將訂單物件轉為xml格式
StringWriter sw = new StringWriter();
JAXBContext context = JAXBContext.newInstance(un.getClass());
Marshaller mar = context.createMarshaller();
mar.setProperty(mar.JAXB_ENCODING, "utf-8");
mar.setProperty(mar.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
mar.marshal(un, sw);
/*將xml轉換為物件
JAXBContext context = JAXBContext.newInstance(UnifiedOrderRequest.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
UnifiedOrderRequest unifiedOrderRequest = (UnifiedOrderRequest)unmarshaller.unmarshal(new StringReader("xml檔案"));
*/
return sw.toString();
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* 生成數字簽名
*/
private String createSign(UnifiedOrderRequest un) {
//建立可排序的Map集合
SortedMap<String, String> packageParms = new TreeMap<String, String>();
packageParms.put("appid", un.getAppid());
packageParms.put("body", un.getBody());
packageParms.put("mch_id", un.getMch_id());
packageParms.put("nonce_str", un.getNonce_str());
packageParms.put("notify_url", un.getNotify_url());
packageParms.put("out_trade_no", un.getOut_trade_no());
packageParms.put("spbill_create_ip", un.getSpbill_create_ip());
packageParms.put("trade_type", un.getTrade_type());
packageParms.put("total_fee", un.getTotal_fee());
StringBuffer sb = new StringBuffer();
//按照字典排序
Set se = packageParms.entrySet();
//迭代器
Iterator iterator = se.iterator();
while(iterator.hasNext()){
Map.Entry entry = (Entry) iterator.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
//為空不參與簽名,引數名區分大小寫
if(null!=v && !"".equals(v)
&& !"key".equals(k) && !"sign".equals(k)){
sb.append(k+ "="+v+"&");
}
}
/*拼接 key,設定路徑:微信商戶平臺(pay.weixin.com)->賬戶設定->API安全-->祕鑰設定 */
sb.append("key="+"xxxxxxxxxxx");
MD5Utile md5= new MD5Utile();
String sign = md5.getMD5(sb.toString(),"utf-8");
return sign;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
}
看到這裡,包括二維碼的製作,與使用者,伺服器的互動都已完成。如有不懂,可以私信!