微信公衆號支付 js api java版本

        提及來.微信支付真是一堆坑. 竟然官網都沒有java版本的完整代碼. 就算是php版本的.還都有錯誤.且先後各類版本.各類文檔一大堆....不停的誤導開發人員.javascript

        花了一天半時間.總算實現了微信公衆號支付.和pc端的微信掃碼支付.其餘不說了.直接給思路
php

        本人作的是微信V3版本的微信支付.微信的官方文檔中.提供的demo 只有一些工具類.這些類仍是頗有做用的.java

        https://mp.weixin.qq.com/paymch/readtemplate?t=mp/business/course3_tmpl&lang=zh_CN  能夠在這個鏈接中找到相應的java類.jquery

        這裏必定要注意.在官網填寫的受權目錄必定要寫到三級目錄.如:
git

個人回調地址是:http://111.111.111.111:1111/control/weixinPay_notify web

那麼,官網填寫都受權目錄就是:http://111.111.111.111:1111/control/api

我試過.受權目錄寫到2級.是沒用的.此處差評,第一個坑.數組


  首先,定義各類微信支付所須要的參數
緩存

GzhConfig.java服務器

public static String APPID = "XXXXXXXXXXXX";
//受理商ID,身份標識
public static String MCHID = "XXXXXXXXXXXXxx";
//商戶支付密鑰Key。審覈經過後,在微信發送的郵件中查看
public static String KEY = "XXXXXXXXXXXXXXXXX";
//JSAPI接口中獲取openid,審覈後在公衆平臺開啓開發模式後可查看
public static String APPSECRET = "xxxxxxxxxxxxxx";
//重定向地址
public static String REDIRECT_URL = "http://XXXXXXXXXXXXXXXXXXX/callWeiXinPay";
//異步回調地址
public static String NOTIFY_URL = "http://XXXXXXXXXXXXXXXXXXXXXX/weixinPay_notify";
//web回調地址
public static String WEB_NOTIFY_URL = "http://XXXXXXXXXXXXXXXXXXXXXXXXX/weixinPay_notify";

而後.就是正式的開始代碼了:

1.使用Oauth2.0受權.進行頁面跳轉,獲取code .(code關係着後面獲取openid.)

https://open.weixin.qq.com/connect/oauth2/authorize?appid=123456789&redirect_uri=http://111.111.111.111:1111/control/orderPay&response_type=code&scope=snsapi_base&state=456123456#wechat_redirect

此處.appid 這個在微信官網能夠獲取. 重定向地址. 就是獲取code 後.跳轉指向你的地址.這裏能夠是你的訂單結算頁面.response_type=code和scope=snsapi_base 都是固定格式.   state 是傳入傳出.這個參數用戶自定義爲任何均可以,好比說訂單id. 而後會和code 一塊兒傳遞到你的重定向地址,如我上面寫的重定向地址就是 orderPay連接.

2.在重定向到頁面(http://111.111.111.111:1111/control/orderPay)的時候中間執行java方法(如獲取openid 如執行微信統一下單接口,獲取預支付ID.).處理各類參數.下面貼具體代碼作說明.

GzhService.java


String code = request.getParameter("code");
String state = request.getParameter("state");
Debug.log("code-======"+code+"===========state======"+state);
String noncestr = Sha1Util.getNonceStr();//生成隨機字符串
String timestamp = Sha1Util.getTimeStamp();//生成1970年到如今的秒數.
//state 能夠傳遞你的訂單號.而後根據訂單號 查詢付款金額.我就不詳細寫了.
                                
String out_trade_no = state;//訂單號
GenericValue orderHeader = delegator.findOne("OrderHeader", UtilMisc.toMap("orderId", out_trade_no),false);
String total_fee = String.valueOf(orderHeader.getBigDecimal("grandTotal").doubleValue()*100);
String order_price = total_fee.substring(0, total_fee.indexOf("."));//訂單金額
//微信金額 以分爲單位.這是第二坑.若是不注意.頁面的報錯.你基本看不出來.由於他提示系統升級,正在維護.扯淡.....
String product_name=out_trade_no;//訂單名稱
//獲取jsapi_ticket.此參數是爲了生成 js api  加載時候的簽名用.必須.jsapi_ticket只會存在7200秒.而且有時間限制,(好像一年還只能調用兩萬次.因此必定要緩存.)這是第三坑.
//能夠在java中模擬url請求.就能獲取access_token 而後根據access_token 取得 jsapi_ticket,但必定要緩存起來..這個代碼.我只提供獲取.緩存大家本身處理.
//SendGET方法我會在後面放出
String tokenParam = "grant_type=client_credential&appid="+GzhConfig.APPID+"&secret="+GzhConfig.APPSECRET;
String tokenJsonStr = SendGET("https://api.weixin.qq.com/cgi-bin/token", tokenParam);
Map tokenMap = JSON.parseObject(tokenJsonStr);
//獲取access_token
String access_token = (String)tokenMap.get("access_token");
String ticketParam = "access_token="+access_token+"&type=jsapi";
String ticketJsonStr = SendGET("https://api.weixin.qq.com/cgi-bin/ticket/getticket", ticketParam);
Map ticketMap = JSON.parseObject(ticketJsonStr);
//獲取jsapi_ticket
String ticket = (String)ticketMap.get("ticket");
 //下面就到了獲取openid,這個表明用戶id.
//獲取openID
String openParam = "appid="+GzhConfig.APPID+"&secret="+GzhConfig.APPSECRET+"&code="+code+"&grant_type=authorization_code";
String openJsonStr = SendGET("https://api.weixin.qq.com/sns/oauth2/access_token", openParam);
Map openMap = JSON.parseObject(openJsonStr);
String openid = (String) openMap.get("openid");
RequestHandler reqHandler = new RequestHandler(request, response);
 //初始化     RequestHandler  類能夠在微信的文檔中找到.還有相關工具類    
reqHandler.init();
reqHandler.init(GzhConfig.APPID, GzhConfig.APPSECRET, GzhConfig.KEY, "");
//執行統一下單接口 得到預支付id
reqHandler.setParameter("appid",GzhConfig.APPID);
reqHandler.setParameter("mch_id", GzhConfig.MCHID);                //商戶號
reqHandler.setParameter("nonce_str", noncestr);            //隨機字符串
reqHandler.setParameter("body", product_name);                        //商品描述(必填.若是不填.也會提示系統升級.正在維護我艹.)
reqHandler.setParameter("out_trade_no", out_trade_no);        //商家訂單號
reqHandler.setParameter("total_fee", order_price);                    //商品金額,以分爲單位
reqHandler.setParameter("spbill_create_ip",request.getRemoteAddr());   //用戶的公網ip  IpAddressUtil.getIpAddr(request)
//下面的notify_url是用戶支付成功後爲微信調用的action  異步回調.
reqHandler.setParameter("notify_url", GzhConfig.NOTIFY_URL);
reqHandler.setParameter("trade_type", "JSAPI");
//------------須要進行用戶受權獲取用戶openid-------------
reqHandler.setParameter("openid", openid);   //這個必填.
//這裏只是在組裝數據.還沒到執行到統一下單接口.由於統一下單接口的數據傳遞格式是xml的.因此才須要組裝.
String requestUrl = reqHandler.getRequestURL();
                                requestUrl 例子:
 /*
<xml><appid>wx2421b1c4370ec43b</appid><attach>支付測試</attach><body>JSAPI支付測試</body><mch_id>10000100</mch_id><nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str><notify_url>http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php</notify_url><openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid><out_trade_no>1415659990</out_trade_no><spbill_create_ip>14.23.150.211</spbill_create_ip><total_fee>1</total_fee><trade_type>JSAPI</trade_type><sign>0CB01533B8C1EF103065174F50BCA001</sign></xml>
*/
                                
Debug.log("requestUrl==================="+requestUrl);
//統一下單接口提交  xml格式
URL orderUrl = new URL("https://api.mch.weixin.qq.com/pay/unifiedorder");
HttpURLConnection conn = (HttpURLConnection) orderUrl.openConnection();
conn.setConnectTimeout(30000); // 設置鏈接主機超時(單位:毫秒)
conn.setReadTimeout(30000); // 設置從主機讀取數據超時(單位:毫秒)
conn.setDoOutput(true); // post請求參數要放在http正文內,顧設置成true,默認是false
conn.setDoInput(true); // 設置是否從httpUrlConnection讀入,默認狀況下是true
conn.setUseCaches(false); // Post 請求不能使用緩存
// 設定傳送的內容類型是可序列化的java對象(若是不設此項,在傳送序列化對象時,當WEB服務默認的不是這種類型時可能拋java.io.EOFException)
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
conn.setRequestMethod("POST");// 設定請求的方法爲"POST",默認是GET
conn.setRequestProperty("Content-Length",requestUrl.length()+"");
String encode = "utf-8";
OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream(), encode);
out.write(requestUrl.toString());
out.flush();
out.close();
String result = getOut(conn);
Debug.log("result=========返回的xml============="+result);
Map<String, String> orderMap = XMLUtil.doXMLParse(result);
Debug.log("orderMap==========================="+orderMap);
//獲得的預支付id
String prepay_id = orderMap.get("prepay_id");
SortedMap<String,String> params = new TreeMap<String,String>();
params.put("appId", GzhConfig.APPID);
params.put("timeStamp",timestamp);
params.put("nonceStr", noncestr);
params.put("package", "prepay_id="+prepay_id);
params.put("signType", "MD5");
        
//生成支付簽名,這個簽名 給 微信支付的調用使用
String paySign =  reqHandler.createSign(params);        
request.setAttribute("paySign", paySign);
request.setAttribute("appId", GzhConfig.APPID);
request.setAttribute("timeStamp", timestamp);        //時間戳
request.setAttribute("nonceStr", noncestr);            //隨機字符串
request.setAttribute("signType", "MD5");        //加密格式
request.setAttribute("out_trade_no", out_trade_no);          //訂單號
request.setAttribute("package", "prepay_id="+prepay_id);//預支付id ,就這樣的格式.
String url = "http://xxxxxxxxxx/control/wxPayment";
String signValue = "jsapi_ticket="+ticket+"&noncestr="+noncestr+"&timestamp="+timestamp+"&url="+url;
Debug.log("url====="+signValue);
//這個簽名.主要是給加載微信js使用.別和上面的搞混了.
String signature = Sha1Util.getSha1((signValue));
request.setAttribute("signature", signature);

                


//此頁面的一些其餘方法

public static String getOut(HttpURLConnection conn) throws IOException{
        if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
            return null;
        }
        // 獲取響應內容體
        BufferedReader in = new BufferedReader(new InputStreamReader(
                conn.getInputStream(), "UTF-8"));
        String line = "";
        StringBuffer strBuf = new StringBuffer();
        while ((line = in.readLine()) != null) {
            strBuf.append(line).append("\n");
        }
        in.close();
        return  strBuf.toString().trim();
}

public static String SendGET(String url,String param){
   String result="";//訪問返回結果
   BufferedReader read=null;//讀取訪問結果
    
   try {
    //建立url
    URL realurl=new URL(url+"?"+param);
    //打開鏈接
    URLConnection connection=realurl.openConnection();
     // 設置通用的請求屬性
             connection.setRequestProperty("accept", "*/*");
             connection.setRequestProperty("connection", "Keep-Alive");
             connection.setRequestProperty("user-agent",
                     "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
             //創建鏈接
             connection.connect();
          // 獲取全部響應頭字段
//             Map<String, List<String>> map = connection.getHeaderFields();
             // 遍歷全部的響應頭字段,獲取到cookies等
//             for (String key : map.keySet()) {
//                 System.out.println(key + "--->" + map.get(key));
//             }
             // 定義 BufferedReader輸入流來讀取URL的響應
             read = new BufferedReader(new InputStreamReader(
                     connection.getInputStream(),"UTF-8"));
             String line;//循環讀取
             while ((line = read.readLine()) != null) {
                 result += line;
             }
   } catch (IOException e) {
    e.printStackTrace();
   }finally{
    if(read!=null){//關閉流
     try {
      read.close();
     } catch (IOException e) {
      e.printStackTrace();
     }
    }
   }
     
   return result; 
 }

            

            其餘相關類的方法:

            


/*

'============================================================================

'api說明:

'createSHA1Sign建立簽名SHA1

'getSha1()Sha1簽名

'============================================================================

'*/

public class Sha1Util {
public static String getNonceStr() {
Random random = new Random();
return MD5Util.MD5Encode(String.valueOf(random.nextInt(10000)), "UTF-8");
}
public static String getTimeStamp() {
return String.valueOf(System.currentTimeMillis() / 1000);
}
   //建立簽名SHA1
public static String createSHA1Sign(SortedMap<String, String> signParams) throws Exception {
StringBuffer sb = new StringBuffer();
Set es = signParams.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();
sb.append(k + "=" + v + "&");
//要採用URLENCODER的原始值!
}
String params = sb.substring(0, sb.lastIndexOf("&"));
System.out.println("sha1 sb:" + params);
return getSha1(params);
}
//Sha1簽名
public static String getSha1(String str) {
if (str == null || str.length() == 0) {
return null;
}
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f' };
try {
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(str.getBytes("GBK"));
byte[] md = mdTemp.digest();
int j = md.length;
char buf[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return new String(buf);
} catch (Exception e) {
return null;
}
}
}



/**

 * xml工具類

 * @author  miklchen

 *

 */

public class XMLUtil {
/**
 * 解析xml,返回第一級元素鍵值對。若是第一級元素有子節點,則此節點的值是子節點的xml數據。
 * @param  strxml
 * @return 
 * @throws  JDOMException
 * @throws  IOException
 */
public static Map<String,String> doXMLParse(String strxml) throws JDOMException, IOException {
if(null == strxml || "".equals(strxml)) {
return null;
}
Map<String,String> m = new HashMap<String,String>();
InputStream in = HttpClientUtil.String2Inputstream(strxml);
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);
}
//關閉流
in.close();
return m;
}
/**
 * 獲取子結點的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();
}
/**
 * 獲取xml編碼字符集
 * @param strxml
 * @return
 * @throws IOException 
 * @throws JDOMException 
 */
public static String getXMLEncoding(String strxml) throws JDOMException, IOException {
InputStream in = HttpClientUtil.String2Inputstream(strxml);
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
in.close();
return (String)doc.getProperty("encoding");
}
}

  

   

/**
 * Http客戶端工具類<br/>
 * 這是內部調用類,請不要在外部調用。
 * @author miklchen
 *
 */
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;


public class HttpClientUtil {
	
	public static final String SunX509 = "SunX509";
	public static final String JKS = "JKS";
	public static final String PKCS12 = "PKCS12";
	public static final String TLS = "TLS";
	
	/**
	 * get HttpURLConnection
	 * @param strUrl url地址
	 * @return HttpURLConnection
	 * @throws IOException
	 */
	public static HttpURLConnection getHttpURLConnection(String strUrl)
			throws IOException {
		URL url = new URL(strUrl);
		HttpURLConnection httpURLConnection = (HttpURLConnection) url
				.openConnection();
		return httpURLConnection;
	}
	
	/**
	 * get HttpsURLConnection
	 * @param strUrl url地址
	 * @return HttpsURLConnection
	 * @throws IOException
	 */
	public static HttpsURLConnection getHttpsURLConnection(String strUrl)
			throws IOException {
		URL url = new URL(strUrl);
		HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url
				.openConnection();
		return httpsURLConnection;
	}
	
	/**
	 * 獲取不帶查詢串的url
	 * @param strUrl
	 * @return String
	 */
	public static String getURL(String strUrl) {

		if(null != strUrl) {
			int indexOf = strUrl.indexOf("?");
			if(-1 != indexOf) {
				return strUrl.substring(0, indexOf);
			} 
			
			return strUrl;
		}
		
		return strUrl;
		
	}
	
	/**
	 * 獲取查詢串
	 * @param strUrl
	 * @return String
	 */
	public static String getQueryString(String strUrl) {
		
		if(null != strUrl) {
			int indexOf = strUrl.indexOf("?");
			if(-1 != indexOf) {
				return strUrl.substring(indexOf+1, strUrl.length());
			} 
			
			return "";
		}
		
		return strUrl;
	}
	
	/**
	 * 查詢字符串轉換成Map<br/>
	 * name1=key1&name2=key2&...
	 * @param queryString
	 * @return
	 */
	public static Map queryString2Map(String queryString) {
		if(null == queryString || "".equals(queryString)) {
			return null;
		}
		
		Map m = new HashMap();
		String[] strArray = queryString.split("&");
		for(int index = 0; index < strArray.length; index++) {
			String pair = strArray[index];
			HttpClientUtil.putMapByPair(pair, m);
		}
		
		return m;
		
	}
	
	/**
	 * 把鍵值添加至Map<br/>
	 * pair:name=value
	 * @param pair name=value
	 * @param m
	 */
	public static void putMapByPair(String pair, Map m) {
		
		if(null == pair || "".equals(pair)) {
			return;
		}
		
		int indexOf = pair.indexOf("=");
		if(-1 != indexOf) {
			String k = pair.substring(0, indexOf);
			String v = pair.substring(indexOf+1, pair.length());
			if(null != k && !"".equals(k)) {
				m.put(k, v);
			}
		} else {
			m.put(pair, "");
		}
	}
	
	/**
	 * BufferedReader轉換成String<br/>
	 * 注意:流關閉須要自行處理
	 * @param reader
	 * @return String
	 * @throws IOException
	 */
	public static String bufferedReader2String(BufferedReader reader) throws IOException {
		StringBuffer buf = new StringBuffer();
		String line = null;
		while( (line = reader.readLine()) != null) {
			buf.append(line);
			buf.append("\r\n");
		}
				
		return buf.toString();
	}
	
	/**
	 * 處理輸出<br/>
	 * 注意:流關閉須要自行處理
	 * @param out
	 * @param data
	 * @param len
	 * @throws IOException
	 */
	public static void doOutput(OutputStream out, byte[] data, int len) 
			throws IOException {
		int dataLen = data.length;
		int off = 0;
		while(off < dataLen) {
			if(len >= dataLen) {
				out.write(data, off, dataLen);
			} else {
				out.write(data, off, len);
			}
			
			//刷新緩衝區
			out.flush();
			
			off += len;
			
			dataLen -= len;
		}
		
	}
	
	/**
	 * 獲取SSLContext
	 * @param trustFile 
	 * @param trustPasswd
	 * @param keyFile
	 * @param keyPasswd
	 * @return
	 * @throws NoSuchAlgorithmException 
	 * @throws KeyStoreException 
	 * @throws IOException 
	 * @throws CertificateException 
	 * @throws UnrecoverableKeyException 
	 * @throws KeyManagementException 
	 */
	public static SSLContext getSSLContext(
			FileInputStream trustFileInputStream, String trustPasswd,
			FileInputStream keyFileInputStream, String keyPasswd)
			throws NoSuchAlgorithmException, KeyStoreException,
			CertificateException, IOException, UnrecoverableKeyException,
			KeyManagementException {

		// ca
		TrustManagerFactory tmf = TrustManagerFactory.getInstance(HttpClientUtil.SunX509);
		KeyStore trustKeyStore = KeyStore.getInstance(HttpClientUtil.JKS);
		trustKeyStore.load(trustFileInputStream, HttpClientUtil
				.str2CharArray(trustPasswd));
		tmf.init(trustKeyStore);

		final char[] kp = HttpClientUtil.str2CharArray(keyPasswd);
		KeyManagerFactory kmf = KeyManagerFactory.getInstance(HttpClientUtil.SunX509);
		KeyStore ks = KeyStore.getInstance(HttpClientUtil.PKCS12);
		ks.load(keyFileInputStream, kp);
		kmf.init(ks, kp);

		SecureRandom rand = new SecureRandom();
		SSLContext ctx = SSLContext.getInstance(HttpClientUtil.TLS);
		ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), rand);

		return ctx;
	}
	
	/**
	 * 獲取CA證書信息
	 * @param cafile CA證書文件
	 * @return Certificate
	 * @throws CertificateException
	 * @throws IOException
	 */
	public static Certificate getCertificate(File cafile)
			throws CertificateException, IOException {
		CertificateFactory cf = CertificateFactory.getInstance("X.509");
		FileInputStream in = new FileInputStream(cafile);
		Certificate cert = cf.generateCertificate(in);
		in.close();
		return cert;
	}
	
	/**
	 * 字符串轉換成char數組
	 * @param str
	 * @return char[]
	 */
	public static char[] str2CharArray(String str) {
		if(null == str) return null;
		
		return str.toCharArray();
	}
	
	/**
	 * 存儲ca證書成JKS格式
	 * @param cert
	 * @param alias
	 * @param password
	 * @param out
	 * @throws KeyStoreException
	 * @throws NoSuchAlgorithmException
	 * @throws CertificateException
	 * @throws IOException
	 */
	public static void storeCACert(Certificate cert, String alias,
			String password, OutputStream out) throws KeyStoreException,
			NoSuchAlgorithmException, CertificateException, IOException {
		KeyStore ks = KeyStore.getInstance("JKS");

		ks.load(null, null);

		ks.setCertificateEntry(alias, cert);

		// store keystore
		ks.store(out, HttpClientUtil.str2CharArray(password));

	}
	
	public static InputStream String2Inputstream(String str) {
		return new ByteArrayInputStream(str.getBytes());
	}

}

 

 RequestHandler.java

  

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/*
 '微信支付服務器簽名支付請求請求類
 '============================================================================
 'api說明:
 'init(app_id, app_secret, partner_key, app_key);
 '初始化函數,默認給一些參數賦值,如cmdno,date等。
 'setKey(key_)'設置商戶密鑰
 'getLasterrCode(),獲取最後錯誤號
 'GetToken();獲取Token
 'getTokenReal();Token過時後實時獲取Token
 'createMd5Sign(signParams);生成Md5簽名
 'genPackage(packageParams);獲取package包
 'createSHA1Sign(signParams);建立簽名SHA1
 'sendPrepay(packageParams);提交預支付
 'getDebugInfo(),獲取debug信息
 '============================================================================
 '*/
public class RequestHandler {
	/** Token獲取網關地址地址 */
	private String tokenUrl;
	/** 預支付網關url地址 */
	private String gateUrl;
	/** 查詢支付通知網關URL */
	private String notifyUrl;
	/** 商戶參數 */
	private String appid;
	private String appkey;
	private String partnerkey;
	private String appsecret;
	private String key;
	/** 請求的參數 */
	private SortedMap parameters;
	/** Token */
	private String Token;
	private String charset;
	/** debug信息 */
	private String debugInfo;
	private String last_errcode;

	private HttpServletRequest request;

	private HttpServletResponse response;

	/**
	 * 初始構造函數。
	 * 
	 * @return
	 */
	public RequestHandler(HttpServletRequest request,
			HttpServletResponse response) {
		this.last_errcode = "0";
		this.request = request;
		this.response = response;
		this.charset = "GBK";
		this.parameters = new TreeMap();
		// 驗證notify支付訂單網關
		notifyUrl = "https://gw.tenpay.com/gateway/simpleverifynotifyid.xml";
		
	}

	/**
	 * 初始化函數。
	 */
	public void init(String app_id, String app_secret, String app_key,
			String partner_key) {
		this.last_errcode = "0";
		this.Token = "token_";
		this.debugInfo = "";
		this.appkey = app_key;
		this.appid = app_id;
		this.partnerkey = partner_key;
		this.appsecret = app_secret;
	}

	public void init() {
	}

	/**
	 * 獲取最後錯誤號
	 */
	public String getLasterrCode() {
		return last_errcode;
	}

	/**
	 *獲取入口地址,不包含參數值
	 */
	public String getGateUrl() {
		return gateUrl;
	}

	/**
	 * 獲取參數值
	 * 
	 * @param parameter
	 *            參數名稱
	 * @return String
	 */
	public String getParameter(String parameter) {
		String s = (String) this.parameters.get(parameter);
		return (null == s) ? "" : s;
	}
	
	/**

     * 設置參數值

     * @param parameter 參數名稱

     * @param parameterValue 參數值

     */

    public void setParameter(String parameter, String parameterValue) {

        String v = "";

        if(null != parameterValue) {

            v = parameterValue.trim();

        }
        this.parameters.put(parameter, v);

    }
	
	 //設置密鑰
	
	public void setKey(String key) {
		this.partnerkey = key;
	}
	//設置微信密鑰
	public void  setAppKey(String key){
		this.appkey = key;
	}
	
	// 特殊字符處理
	public String UrlEncode(String src) throws UnsupportedEncodingException {
		return URLEncoder.encode(src, this.charset).replace("+", "%20");
	}

	// 獲取package的簽名包
	public String genPackage(SortedMap<String, String> packageParams)
			throws UnsupportedEncodingException {
		String sign = createSign(packageParams);

		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();
			sb.append(k + "=" + UrlEncode(v) + "&");
		}

		// 去掉最後一個&
		String packageValue = sb.append("sign=" + sign).toString();
		System.out.println("packageValue=" + packageValue);
		return packageValue;
	}

	/**
	 * 建立md5摘要,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。
	 */
	public String createSign(SortedMap<String, String> packageParams) {
		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 (null != v && !"".equals(v) && !"sign".equals(k)
					&& !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.append("key=" + GzhConfig.KEY);
		System.out.println("md5 sb:" + sb);
		String sign = MD5Util.MD5Encode(sb.toString(), this.charset)
				.toUpperCase();

		return sign;

	}
	/**
	 * 建立package簽名
	 */
	public boolean createMd5Sign(String signParams) {
		StringBuffer sb = new StringBuffer();
		Set es = this.parameters.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 + "&");
			}
		}

		// 算出摘要
		String enc = TenpayUtil.getCharacterEncoding(this.request,
				this.response);
		String sign = MD5Util.MD5Encode(sb.toString(), enc).toLowerCase();

		String tenpaySign = this.getParameter("sign").toLowerCase();

		// debug信息
		this.setDebugInfo(sb.toString() + " => sign:" + sign + " tenpaySign:"
				+ tenpaySign);

		return tenpaySign.equals(sign);
	}

	public String getRequestURL() throws UnsupportedEncodingException {        

        this.createSign();        

        StringBuffer sb = new StringBuffer();

        sb.append("<xml>");

        String enc = TenpayUtil.getCharacterEncoding(this.request, this.response);

        Set es = this.parameters.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 ("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();

    }
	
	protected void createSign() {

        StringBuffer sb = new StringBuffer();

        Set es = this.parameters.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(null != v && !"".equals(v)

                    && !"sign".equals(k) && !"key".equals(k)) {

                sb.append(k + "=" + v + "&");

            }

        }

        sb.append("key=" + GzhConfig.KEY); //本身的API密鑰      

        String enc = TenpayUtil.getCharacterEncoding(this.request, this.response);

        String sign = MD5Util.MD5Encode(sb.toString(), enc).toUpperCase();

        this.setParameter("sign", sign);              

    }
	
    //輸出XML
	   public String parseXML() {
		   StringBuffer sb = new StringBuffer();
	       sb.append("<xml>");
	       Set es = this.parameters.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(null != v && !"".equals(v) && !"appkey".equals(k)) {
					
					sb.append("<" + k +">" + getParameter(k) + "</" + k + ">\n");
				}
			}
	       sb.append("</xml>");
			return sb.toString();
		}

	/**
	 * 設置debug信息
	 */
	protected void setDebugInfo(String debugInfo) {
		this.debugInfo = debugInfo;
	}
	public void setPartnerkey(String partnerkey) {
		this.partnerkey = partnerkey;
	}
	public String getDebugInfo() {
		return debugInfo;
	}
	public String getKey() {
		return key;
	}
	
	public static String setXML(String return_code, String return_msg) {
        return "<xml><return_code><![CDATA[" + return_code
                + "]]></return_code><return_msg><![CDATA[" + return_msg
                + "]]></return_msg></xml>";
	}
}

  

TenpayUtil

   

import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class TenpayUtil {
	
	private static Object Server;
	private static String QRfromGoogle;

	/**
	 * 把對象轉換成字符串
	 * @param obj
	 * @return String 轉換成字符串,若對象爲null,則返回空字符串.
	 */
	public static String toString(Object obj) {
		if(obj == null)
			return "";
		
		return obj.toString();
	}
	
	/**
	 * 把對象轉換爲int數值.
	 * 
	 * @param obj
	 *            包含數字的對象.
	 * @return int 轉換後的數值,對不能轉換的對象返回0。
	 */
	public static int toInt(Object obj) {
		int a = 0;
		try {
			if (obj != null)
				a = Integer.parseInt(obj.toString());
		} catch (Exception e) {

		}
		return a;
	}
	
	/**
	 * 獲取當前時間 yyyyMMddHHmmss
	 * @return String
	 */ 
	public static String getCurrTime() {
		Date now = new Date();
		SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
		String s = outFormat.format(now);
		return s;
	}
	
	/**
	 * 獲取當前日期 yyyyMMdd
	 * @param date
	 * @return String
	 */
	public static String formatDate(Date date) {
		SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
		String strDate = formatter.format(date);
		return strDate;
	}
	
	/**
	 * 取出一個指定長度大小的隨機正整數.
	 * 
	 * @param length
	 *            int 設定所取出隨機數的長度。length小於11
	 * @return int 返回生成的隨機數。
	 */
	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));
	}
	
	/**
	 * 獲取編碼字符集
	 * @param request
	 * @param response
	 * @return String
	 */

	public static String getCharacterEncoding(HttpServletRequest request,
			HttpServletResponse response) {
		
		if(null == request || null == response) {
			return "gbk";
		}
		
		String enc = request.getCharacterEncoding();
		if(null == enc || "".equals(enc)) {
			enc = response.getCharacterEncoding();
		}
		
		if(null == enc || "".equals(enc)) {
			enc = "gbk";
		}
		
		return enc;
	}
	
	public  static String URLencode(String content){
		
		String URLencode;
		
		URLencode= replace(Server.equals(content), "+", "%20");
		
		return URLencode;
	}
	private static String replace(boolean equals, String string, String string2) {
		
		return null;
	}

	/**
	 * 獲取unix時間,從1970-01-01 00:00:00開始的秒數
	 * @param date
	 * @return long
	 */
	public static long getUnixTime(Date date) {
		if( null == date ) {
			return 0;
		}
		
		return date.getTime()/1000;
	}
	
	 public static String QRfromGoogle(String chl)
	    {
	        int widhtHeight = 300;
	        String EC_level = "L";
	        int margin = 0;
	        String QRfromGoogle;
	        chl = URLencode(chl);
	        
	        QRfromGoogle = "http://chart.apis.google.com/chart?chs=" + widhtHeight + "x" + widhtHeight + "&cht=qr&chld=" + EC_level + "|" + margin + "&chl=" + chl;
	       
	        return QRfromGoogle;
	    }

	/**
	 * 時間轉換成字符串
	 * @param date 時間
	 * @param formatType 格式化類型
	 * @return String
	 */
	public static String date2String(Date date, String formatType) {
		SimpleDateFormat sdf = new SimpleDateFormat(formatType);
		return sdf.format(date);
	}
	
}
MD5Util
public class MD5Util {
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" };
}

下面纔到了頁面代碼:jsp 頁面

    

<script type="text/javascript" src="/xxxx/jquery-1.6.2.min.js"></script>
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script language="javascript">
//加載
wx.config({
    debug: true, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。
    appId: '${StringUtil.wrapString(requestAttributes.appId)!}', // 必填,公衆號的惟一標識
    timestamp: ${StringUtil.wrapString(requestAttributes.timeStamp)?default(0)!}, // 必填,生成簽名的時間戳
    nonceStr: '${StringUtil.wrapString(requestAttributes.nonceStr)!}', // 必填,生成簽名的隨機串
    signature: '${StringUtil.wrapString(requestAttributes.signature)!}',// 必填,簽名,見附錄1
    jsApiList: [
    'checkJsApi',
            'chooseWXPay'] // 必填,須要使用的JS接口列表,全部JS接口列表見附錄2
});
wx.ready(function(){
//支付
wx.chooseWXPay({
    timestamp: ${StringUtil.wrapString(requestAttributes.timeStamp)?default(0)!}, // 支付簽名時間戳,注意微信jssdk中的全部使用timestamp字段均爲小寫。但最新版的支付後臺生成簽名使用的timeStamp字段名需大寫其中的S字符
    nonceStr: '${StringUtil.wrapString(requestAttributes.nonceStr)!}', // 支付簽名隨機串,不長於 32 位
    package: '${StringUtil.wrapString(requestAttributes.package)!}', // 統一支付接口返回的prepay_id參數值,提交格式如:prepay_id=***)
    signType: '${StringUtil.wrapString(requestAttributes.signType)!}', // 簽名方式,默認爲'SHA1',使用新版支付需傳入'MD5'
    paySign: '${StringUtil.wrapString(requestAttributes.paySign)!}', // 支付簽名
    success: function (res) {
        // 支付成功後的回調函數
        WeixinJSBridge.log(res.err_msg);
        //alert("支付接口:"+res.err_code + res.err_desc + res.err_msg);
        if(!res.err_msg){
                    //支付完後.跳轉到成功頁面.
        location.href="orderconfirm?orderId=${StringUtil.wrapString(requestAttributes.out_trade_no)!}";
        }
    }
});
    // config信息驗證後會執行ready方法,全部接口調用都必須在config接口得到結果以後,config是一個客戶端的異步操做,因此若是須要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則能夠直接調用,不須要放在ready函數中。
});
wx.error(function(res){
    // config信息驗證失敗會執行error函數,如簽名過時致使驗證失敗,具體錯誤信息能夠打開config的debug模式查看,也能夠在返回的res參數中查看,對於SPA能夠在這裏更新簽名。
});
wx.checkJsApi({
    jsApiList: ['chooseWXPay'], // 須要檢測的JS接口列表,全部JS接口列表見附錄2,
    success: function(res) {
    //alert("檢測接口:"+res.err_msg);
    }
    });
</script>





下面是後臺異步回調代碼:

/**
 * 異步返回
 */
@SuppressWarnings("deprecation")
public static String weixinNotify(HttpServletRequest request, HttpServletResponse response){
        try {
        
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
    outSteam.write(buffer, 0, len);
}
outSteam.close();
inStream.close();
String resultStr  = new String(outSteam.toByteArray(),"utf-8");
Map<String, String> resultMap = XMLUtil.doXMLParse(resultStr);
String result_code = resultMap.get("result_code");
String is_subscribe = resultMap.get("is_subscribe");
String out_trade_no = resultMap.get("out_trade_no");
String transaction_id = resultMap.get("transaction_id");
String sign = resultMap.get("sign");
String time_end = resultMap.get("time_end");
String bank_type = resultMap.get("bank_type");
String return_code = resultMap.get("return_code");
//簽名驗證
GenericValue userLogin =delegator.findOne("UserLogin", UtilMisc.toMap("userLoginId","admin"),false);
if(return_code.equals("SUCCESS")){
    //此處就是你的邏輯代碼
        }
 request.setAttribute("out_trade_no", out_trade_no);
//通知微信.異步確認成功.必寫.否則會一直通知後臺.八次以後就認爲交易失敗了.
response.getWriter().write(RequestHandler.setXML("SUCCESS", ""));
} catch (UnsupportedEncodingException e) { 
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
} 
return "success";
}


        代碼中,刪除了一些和公司相關的代碼.因此若是直接複製進去.確定是要作大量修改的.見諒.

相關文章
相關標籤/搜索