簡介:微信小程序支付這里的坑還是有的舔株,所以提醒各位在編寫的一定要注意=嗔椤1グ丁!
1.首先呢徽千,你需要準(zhǔn)備**openid苫费,appid**,還有申請微信支付后要設(shè)置一個**32位的密鑰**双抽,需要先生成一個**sign**黍衙,得到**prepay_id**,然后再得到一個**paySign**荠诬,總之就是很墨跡琅翻,下面獻上我的controller
//微信下單支付
@ResponseBody
@RequestMapping("doOrder")
public void doOrder(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
//得到openid
String openid = request.getParameter("openid");
int fee = 0;
//得到小程序傳過來的價格,注意這里的價格必須為整數(shù)柑贞,1代表1分方椎,所以傳過來的值必須*100;
if (null != request.getParameter("price")) {
fee = Integer.parseInt(request.getParameter("price").toString());
}
System.out.println(request.getParameter("price"));
System.out.println(fee);
//訂單編號
String did = request.getParameter("did");
//訂單標(biāo)題
String title = request.getParameter("title");
//時間戳
String times = System.currentTimeMillis() + "";
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
packageParams.put("appid", "wxa**********2e2");
packageParams.put("mch_id", "1486425722");
packageParams.put("nonce_str", times);//時間戳
packageParams.put("body", title);//支付主體
packageParams.put("out_trade_no", did);//編號
packageParams.put("total_fee", fee);//價格
// packageParams.put("spbill_create_ip", getIp2(request));這里之前加了ip钧嘶,但是總是獲取sign失敗棠众,原因不明,之后就注釋掉了
packageParams.put("notify_url", base+"/notify");//支付回調(diào)接口有决,用于支付成功后處理業(yè)務(wù)邏輯闸拿,小程序端支付success不能保證100%回調(diào)成功,建議采用后端異步回調(diào)處理方式书幕,回調(diào)方法在最后
packageParams.put("trade_type", "JSAPI");//這個api有新荤,固定的
packageParams.put("openid", openid);//openid
//獲取sign
String sign = PayCommonUtil.createSign("UTF-8", packageParams, "x********************************4");//最后這個是自己設(shè)置的32位密鑰
packageParams.put("sign", sign);
//轉(zhuǎn)成XML
String requestXML = PayCommonUtil.getRequestXml(packageParams);
System.out.println(requestXML);
//得到含有prepay_id的XML
String resXml = HttpUtil.postData("https://api.mch.weixin.qq.com/pay/unifiedorder", requestXML);
System.out.println(resXml);
//解析XML存入Map
Map map = XMLUtil.doXMLParse(resXml);
System.out.println(map);
// String return_code = (String) map.get("return_code");
//得到prepay_id
String prepay_id = (String) map.get("prepay_id");
SortedMap<Object, Object> packageP = new TreeMap<Object, Object>();
packageP.put("appId", "wxa**********2e2");//!Lɑ恪苛骨!注意,這里是appId,上面是appid苟呐,真懷疑寫這個東西的人痒芝。。牵素。
packageP.put("nonceStr", times);//時間戳
packageP.put("package", "prepay_id=" + prepay_id);//必須把package寫成 "prepay_id="+prepay_id這種形式
packageP.put("signType", "MD5");//paySign加密
packageP.put("timeStamp", (System.currentTimeMillis() / 1000) + "");
//得到paySign
String paySign = PayCommonUtil.createSign("UTF-8", packageP, "x********************************4");
packageP.put("paySign", paySign);
//將packageP數(shù)據(jù)返回給小程序
Gson gson = new Gson();
String json = gson.toJson(packageP);
PrintWriter pw = response.getWriter();
System.out.println(json);
pw.write(json);
pw.close();
}
```
2.下面是需要用到的工具類
(1).生成sign以及得到sign后生成XML工具類PayCommonUtil
public class PayCommonUtil {
/**
? ? * 是否簽名正確,規(guī)則是:按參數(shù)名稱a-z排序,遇到空值的參數(shù)不參加簽名严衬。
? ? * @return boolean
? ? */?
? ? public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {?
? ? ? ? StringBuffer sb = new StringBuffer();?
? ? ? ? Set es = packageParams.entrySet();?
? ? ? ? Iterator it = es.iterator();?
? ? ? ? while(it.hasNext()) {?
? ? ? ? ? ? Map.Entry entry = (Map.Entry)it.next();?
? ? ? ? ? ? String k = (String)entry.getKey();?
? ? ? ? ? ? String v = (String)entry.getValue();?
? ? ? ? ? ? if(!"sign".equals(k) && null != v && !"".equals(v)) {?
? ? ? ? ? ? ? ? sb.append(k + "=" + v + "&");?
? ? ? ? ? ? }?
? ? ? ? }?
? ? ? ? sb.append("key=" + API_KEY);?
? ? ? ? //算出摘要?
? ? ? ? String mysign = MD5.MD5Encode(sb.toString(), characterEncoding).toLowerCase();?
? ? ? ? String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();?
? ? ? ? //System.out.println(tenpaySign + "? ? " + mysign);?
? ? ? ? return tenpaySign.equals(mysign);?
? ? }?
? ? /**
? ? * @author
? ? * @Description:sign簽名
? ? * @param characterEncoding
? ? *? ? ? ? ? ? 編碼格式
? ? * @param parameters
? ? *? ? ? ? ? ? 請求參數(shù)
? ? * @return
? ? */?
? ? public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {?
? ? ? ? StringBuffer sb = new StringBuffer();?
? ? ? ? Set es = packageParams.entrySet();?
? ? ? ? Iterator it = es.iterator();?
? ? ? ? while (it.hasNext()) {?
? ? ? ? ? ? Map.Entry entry = (Map.Entry) it.next();?
? ? ? ? ? ? String k = entry.getKey().toString();?
? ? ? ? ? ? String v = entry.getValue().toString();?
? ? ? ? ? ? if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {?
? ? ? ? ? ? ? ? sb.append(k + "=" + v + "&");?
? ? ? ? ? ? }?
? ? ? ? }?
? ? ? ? sb.append("key=" + API_KEY);?
? ? ? ? String sign = MD5.MD5Encode(sb.toString(), characterEncoding).toUpperCase();?
? ? ? ? return sign;?
? ? }?
? ? /**
? ? * @author
? ? * @Description:將請求參數(shù)轉(zhuǎn)換為xml格式的string
? ? * @param parameters
? ? *? ? ? ? ? ? 請求參數(shù)
? ? * @return
? ? */?
? ? public static String getRequestXml(SortedMap<Object, Object> parameters) {?
? ? ? ? StringBuffer sb = new StringBuffer();?
? ? ? ? sb.append("<xml>");?
? ? ? ? Set es = parameters.entrySet();?
? ? ? ? Iterator it = es.iterator();?
? ? ? ? while (it.hasNext()) {?
? ? ? ? ? ? Map.Entry entry = (Map.Entry) it.next();?
? ? ? ? ? ? String k = entry.getKey().toString();?
? ? ? ? ? ? String v = entry.getValue().toString();?
? ? ? ? ? ? if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {?
? ? ? ? ? ? ? ? sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");?
? ? ? ? ? ? } else {?
? ? ? ? ? ? ? ? sb.append("<" + k + ">" + v + "</" + k + ">");?
? ? ? ? ? ? }?
? ? ? ? }?
? ? ? ? sb.append("</xml>");?
? ? ? ? return sb.toString();?
? ? }?
? ? /**
? ? * 取出一個指定長度大小的隨機正整數(shù).
? ? *?
? ? * @param length
? ? *? ? ? ? ? ? int 設(shè)定所取出隨機數(shù)的長度。length小于11
? ? * @return int 返回生成的隨機數(shù)笆呆。
? ? */?
? ? public static int buildRandom(int length) {?
? ? ? ? int num = 1;?
? ? ? ? double random = Math.random();?
? ? ? ? if (random < 0.1) {?
? ? ? ? ? ? random = random + 0.1;?
? ? ? ? }?
? ? ? ? for (int i = 0; i < length; i++) {?
? ? ? ? ? ? num = num * 10;?
? ? ? ? }?
? ? ? ? return (int) ((random * num));?
? ? }?
? ? /**
? ? * 獲取當(dāng)前時間 yyyyMMddHHmmss
? ? *?
? ? * @return String
? ? */?
? ? public static String getCurrTime() {?
? ? ? ? Date now = new Date();?
? ? ? ? SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");?
? ? ? ? String s = outFormat.format(now);?
? ? ? ? return s;?
? ? }?
}
```
(2).訪問官方接口得到含有prepay_id的XML工具類HttpUtil
public class HttpUtil {
//private static final Log logger = Logs.get();?
? ? private final static int CONNECT_TIMEOUT = 5000; // in milliseconds?
? ? private final static String DEFAULT_ENCODING = "UTF-8";?
? ? public static String postData(String urlStr, String data){?
? ? ? ? return postData(urlStr, data, null);?
? ? }?
? ? public static String postData(String urlStr, String data, String contentType){?
? ? ? ? BufferedReader reader = null;?
? ? ? ? try {?
? ? ? ? ? ? URL url = new URL(urlStr);?
? ? ? ? ? ? URLConnection conn = url.openConnection();?
? ? ? ? ? ? conn.setDoOutput(true);?
? ? ? ? ? ? conn.setConnectTimeout(CONNECT_TIMEOUT);?
? ? ? ? ? ? conn.setReadTimeout(CONNECT_TIMEOUT);?
? ? ? ? ? ? if(contentType != null)?
? ? ? ? ? ? ? ? conn.setRequestProperty("content-type", contentType);?
? ? ? ? ? ? OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);?
? ? ? ? ? ? if(data == null)?
? ? ? ? ? ? ? ? data = "";?
? ? ? ? ? ? writer.write(data);?
? ? ? ? ? ? writer.flush();?
? ? ? ? ? ? writer.close();? ?
? ? ? ? ? ? reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));?
? ? ? ? ? ? StringBuilder sb = new StringBuilder();?
? ? ? ? ? ? String line = null;?
? ? ? ? ? ? while ((line = reader.readLine()) != null) {?
? ? ? ? ? ? ? ? sb.append(line);?
? ? ? ? ? ? ? ? sb.append("\r\n");?
? ? ? ? ? ? }?
? ? ? ? ? ? return sb.toString();?
? ? ? ? } catch (IOException e) {?
? ? ? ? ? ? //logger.error("Error connecting to " + urlStr + ": " + e.getMessage());?
? ? ? ? } finally {?
? ? ? ? ? ? try {?
? ? ? ? ? ? ? ? if (reader != null)?
? ? ? ? ? ? ? ? ? ? reader.close();?
? ? ? ? ? ? } catch (IOException e) {?
? ? ? ? ? ? }?
? ? ? ? }?
? ? ? ? return null;?
? ? }?
}
```
(3).解析XML工具類
public class XMLUtil {
public static Map doXMLParse(String strxml) throws JDOMException, IOException {?
? ? ? ? strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");?
? ? ? ? if(null == strxml || "".equals(strxml)) {?
? ? ? ? ? ? return null;?
? ? ? ? }?
? ? ? ? Map m = new HashMap();?
? ? ? ? InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));?
? ? ? ? SAXBuilder builder = new SAXBuilder();?
? ? ? ? Document doc = builder.build(in);?
? ? ? ? Element root = doc.getRootElement();?
? ? ? ? List list = root.getChildren();?
? ? ? ? Iterator it = list.iterator();?
? ? ? ? while(it.hasNext()) {?
? ? ? ? ? ? Element e = (Element) it.next();?
? ? ? ? ? ? String k = e.getName();?
? ? ? ? ? ? String v = "";?
? ? ? ? ? ? List children = e.getChildren();?
? ? ? ? ? ? if(children.isEmpty()) {?
? ? ? ? ? ? ? ? v = e.getTextNormalize();?
? ? ? ? ? ? } else {?
? ? ? ? ? ? ? ? v = XMLUtil.getChildrenText(children);?
? ? ? ? ? ? }?
? ? ? ? ? ? m.put(k, v);?
? ? ? ? }?
? ? ? ? //關(guān)閉流?
? ? ? ? in.close();?
? ? ? ? return m;?
? ? }?
? ? /**
? ? * 獲取子結(jié)點的xml
? ? * @param children
? ? * @return String
? ? */?
? ? public static String getChildrenText(List children) {?
? ? ? ? StringBuffer sb = new StringBuffer();?
? ? ? ? if(!children.isEmpty()) {?
? ? ? ? ? ? Iterator it = children.iterator();?
? ? ? ? ? ? while(it.hasNext()) {?
? ? ? ? ? ? ? ? Element e = (Element) it.next();?
? ? ? ? ? ? ? ? String name = e.getName();?
? ? ? ? ? ? ? ? String value = e.getTextNormalize();?
? ? ? ? ? ? ? ? List list = e.getChildren();?
? ? ? ? ? ? ? ? sb.append("<" + name + ">");?
? ? ? ? ? ? ? ? if(!list.isEmpty()) {?
? ? ? ? ? ? ? ? ? ? sb.append(XMLUtil.getChildrenText(list));?
? ? ? ? ? ? ? ? }?
? ? ? ? ? ? ? ? sb.append(value);?
? ? ? ? ? ? ? ? sb.append("</" + name + ">");?
? ? ? ? ? ? }?
? ? ? ? }?
? ? ? ? return sb.toString();?
? ? }?
}
```
(4).MD5加密工具類
public class MD5 {
private static String byteArrayToHexString(byte b[]) {
? ? ? ? StringBuffer resultSb = new StringBuffer();
? ? ? ? for (int i = 0; i < b.length; i++)
? ? ? ? ? ? resultSb.append(byteToHexString(b[i]));
? ? ? ? return resultSb.toString();
? ? }
? ? private static String byteToHexString(byte b) {
? ? ? ? int n = b;
? ? ? ? if (n < 0)
? ? ? ? ? ? n += 256;
? ? ? ? int d1 = n / 16;
? ? ? ? int d2 = n % 16;
? ? ? ? return hexDigits[d1] + hexDigits[d2];
? ? }
? ? public static String MD5Encode(String origin, String charsetname) {
? ? ? ? String resultString = null;
? ? ? ? try {
? ? ? ? ? ? resultString = new String(origin);
? ? ? ? ? ? MessageDigest md = MessageDigest.getInstance("MD5");
? ? ? ? ? ? if (charsetname == null || "".equals(charsetname))
? ? ? ? ? ? ? ? resultString = byteArrayToHexString(md.digest(resultString
? ? ? ? ? ? ? ? ? ? ? ? .getBytes()));
? ? ? ? ? ? else
? ? ? ? ? ? ? ? resultString = byteArrayToHexString(md.digest(resultString
? ? ? ? ? ? ? ? ? ? ? ? .getBytes(charsetname)));
? ? ? ? } catch (Exception exception) {
? ? ? ? }
? ? ? ? return resultString;
? ? }
? ? private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
? ? ? ? "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}
```
3.小程序支付函數(shù)
gopay: function () {
? ? var that = this
? ? wx.request({
? ? ? url: app.baseurl + 'doOrder',
? ? ? data: {
? ? ? ? 'openid': wx.getStorageSync('openids'),
? ? ? ? 'title': that.data.title,
? ? ? ? 'did': that.data.did,
? ? ? ? 'price': that.data.price*100
? ? ? },
? ? ? method: 'POST',
? ? ? header: {
? ? ? ? "content-type": 'application/x-www-form-urlencoded'
? ? ? },
? ? ? success: function (res) {
? ? ? ? console.log(res.data)
? ? ? ? console.log(res.data.timeStamp)
? ? ? ? console.log(res.data.nonceStr)
? ? ? ? console.log(res.data.package)
? ? ? ? console.log(res.data.paySign)
? ? ? ? wx.requestPayment({
? ? ? ? ? timeStamp: res.data.timeStamp,
? ? ? ? ? nonceStr: res.data.nonceStr,
? ? ? ? ? package: res.data.package,
? ? ? ? ? signType: res.data.signType,
? ? ? ? ? paySign: res.data.paySign,
? ? ? ? ? success: function (res) {
? ? ? ? ? console.log('支付調(diào)用成功',res)
? ? ? ? ? },
? ? ? ? ? fail: function (res) {
? ? ? ? ? ? console.log(res)
? ? ? ? ? }
? ? ? ? })
? ? ? }
? ? })
? }
```
4请琳、支付成功回調(diào)
/**
? ? * 此函數(shù)會被執(zhí)行多次,如果支付狀態(tài)已經(jīng)修改為已支付腰奋,則下次再調(diào)的時候判斷是否已經(jīng)支付单起,如果已經(jīng)支付了,則什么也執(zhí)行
? ? * @param request
? ? * @param response
? ? * @return
? ? * @throws IOException
? ? * @throws JDOMException
? ? */
? ? @RequestMapping(value = "notify")
? ? @ResponseBody
? ? public String notify(HttpServletRequest request, HttpServletResponse response) throws IOException {
? ? String resXml = "";
? ? ? ? System.out.println("微信支付回調(diào)");
//resultxml中含用戶訂單號等信息劣坊,解析后用于處理訂單
? ? ? ? Map<String, String> params = PayCommonUtil.doXMLParse(resultxml);
? ? ? ? if (!PayCommonUtil.isTenpaySign(params)) {
? ? ? ? ? ? // 支付失敗
? ? ? ? ? ? resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
? ? ? ? ? ? ? ? ? ? + "<return_msg><![CDATA[報文為空]]></return_msg>" + "</xml> ";
? ? ? ? } else {
? ? ? ? ? ? System.out.println("==========付款成功==========");
? ? ? ? ? ? // ------------------------------
? ? ? ? ? ? // 處理業(yè)務(wù)開始
? ? ? ? ? ? // ------------------------------
? ? ? ? ? ? // 此處處理訂單狀態(tài)嘀倒,結(jié)合自己的訂單數(shù)據(jù)完成訂單狀態(tài)的更新
? ? ? ? ? ? // ------------------------------
//通知微信.異步確認成功.必寫.不然會一直通知后臺.八次之后就認為交易失敗了
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
? ? ? ? ? ? ? ? ? ? + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
? ? ? ? }
? ? ? ? BufferedOutputStream out = new BufferedOutputStream(
? ? ? ? ? ? ? ? response.getOutputStream());
? ? ? ? out.write(resXml.getBytes());
? ? ? ? out.flush();
? ? ? ? out.close();
? ? }
```
結(jié)束啦,小程序支付的java后臺就這些局冰。