微信公眾號支付詳細過程
背景
公司的公眾號有個保險專案需要用到微信支付,官網java文件寫的並不是很詳細。個人根據網上一些demo總結出來的微信支付功能,中間遇到過幾個坑,後續會詳細講解。話不多說,先上程式碼!
一.轉換MD5格式類MD5Util類
import java.security.MessageDigest; public class MD5Util { public final static String MD5(String s) { char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; try { byte[] btInput = s.getBytes(); MessageDigest mdInst = MessageDigest.getInstance("MD5"); mdInst.update(btInput); byte[] md = mdInst.digest(); int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } String md5Str = new String(str); return md5Str; } catch (Exception e) { e.printStackTrace(); return null; } } }
二.統一下單引數類UnifiedOrderRequest
public class UnifiedOrderRequest { private String appid;// 公眾賬號ID 是 String(32) wxd678efh567hg6787 微信支付分配的公眾賬號ID(企業號corpid即為此appId) private String mch_id;//商戶號 必填 String(32) 1230000109 微信支付分配的商戶號 private String device_info; //裝置號 否 String(32) 013467007045764 自定義引數,可以為終端裝置號(門店號或收銀裝置ID),PC網頁或公眾號內支付可以傳"WEB" private String nonce_str;//隨機字串 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 隨機字串,長度要求在32位以內。推薦隨機數生成演算法 private String sign;//簽名 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 通過簽名演算法計算得出的簽名值,詳見簽名生成演算法 private String sign_type;//簽名型別 sign_type 否 String(32) HMAC-SHA256 簽名型別,預設為MD5,支援HMAC-SHA256和MD5。 private String body;//商品描述 body 是 String(128) 騰訊充值中心-QQ會員充值 商品簡單描述,該欄位請按照規範傳遞,具體請見引數規定 private String detail;//商品詳情 detail 否 String(6000) 單品優惠欄位(暫未上線) private String attach;//附加資料 attach 否 String(127) 深圳分店 附加資料,在查詢API和支付通知中原樣返回,可作為自定義引數使用。 private String out_trade_no;//商戶訂單號 out_trade_no 是 String(32) 20150806125346 商戶系統內部訂單號,要求32個字元內,只能是數字、大小寫字母_-|*@ ,且在同一個商戶號下唯一。詳見商戶訂單號 private String fee_type;//標價幣種 fee_type 否 String(16) CNY 符合ISO 4217標準的三位字母程式碼,預設人民幣:CNY,詳細列表請參見貨幣型別 private String total_fee;//標價金額 total_fee 是 Int 88 訂單總金額,單位為分,詳見支付金額 private String spbill_create_ip;//終端IP spbill_create_ip 是 String(16) 123.12.12.123 APP和網頁支付提交使用者端ip,Native支付填呼叫微信支付API的機器IP。 private String time_start;//交易起始時間 time_start 否 String(14) 20091225091010 訂單生成時間,格式為yyyyMMddHHmmss,如2009年12月25日9點10分10秒錶示為20091225091010。其他詳見時間規則 private String time_expire;//交易結束時間 time_expire 否 String(14) 20091227091010 訂單失效時間,格式為yyyyMMddHHmmss,如2009年12月27日9點10分10秒錶示為20091227091010。其他詳見時間規則 注意:最短失效時間間隔必須大於5分鐘 private String goods_tag;//訂單優惠標記 goods_tag 否 String(32) WXG 訂單優惠標記,使用代金券或立減優惠功能時需要的引數,說明詳見代金券或立減優惠 private String notify_url;//通知地址 notify_url 是 String(256) http://www.weixin.qq.com/wxpay/pay.php 非同步接收微信支付結果通知的回撥地址,通知url必須為外網可訪問的url,不能攜帶引數。 private String trade_type;//交易型別 trade_type 是 String(16) JSAPI 取值如下:JSAPI,NATIVE,APP等,說明詳見引數規定 private String product_id;//商品ID product_id 否 String(32) 12235413214070356458058 trade_type=NATIVE時(即掃碼支付),此引數必傳。此引數為二維碼中包含的商品ID,商戶自行定義。 private String limit_pay;//指定支付方式 limit_pay 否 String(32) no_credit 上傳此引數no_credit--可限制使用者不能使用信用卡支付 private String openid;//使用者標識 openid 否 String(128) oUpF8uMuAJO_M2pxb1Q9zNjWeS6o trade_type=JSAPI時(即公眾號支付),此引數必傳,此引數為微信使用者在商戶對應appid下的唯一標識。openid如何獲取,可參考【獲取openid】。企業號請使用【企業號OAuth2.0介面】獲取企業號內成員userid,再呼叫【企業號userid轉openid介面】進行轉換 //以下省略get和set方法 }
三.統一下單返回引數類UnifiedOrderRespose
public class UnifiedOrderRespose { private String return_code; //返回狀態碼 private String return_msg; //返回資訊 private String appid; //公眾賬號ID private 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; //二維碼連結 //....以下省略set和get方法 }
四.將返回結果轉換為json類WXAuthUtil,該類部分微信引數,其中MCH_ID、APPID、APPSECRET在微信公眾號設定,KEY在微信商戶號上設定,KEY用於後面生成簽名。
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
public class WXAuthUtil {
public static final String MCH_ID="";//商戶號
public static final String APPID="";//
public static final String APPSECRET ="";
public static final String KEY="";
public static JSONObject doGetJson(String url) throws ClientProtocolException, IOException {
JSONObject jsonObject =null;
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet =new HttpGet(url);
HttpResponse response = client.execute(httpGet);
HttpEntity entity =response.getEntity();
if(entity!=null)
{
//把返回的結果轉換為JSON物件
String result =EntityUtils.toString(entity, "UTF-8");
jsonObject =JSON.parseObject(result);
}
return jsonObject;
}
}
五.簽名方式類WXPayConstants,包含微信返回結果定義的引數
public class WXPayConstants {
public enum SignType {
MD5, HMACSHA256
}
public static final String FAIL = "FAIL";
public static final String SUCCESS = "SUCCESS";
public static final String HMACSHA256 = "HMAC-SHA256";
public static final String MD5 = "MD5";
public static final String FIELD_SIGN = "sign";
public static final String FIELD_SIGN_TYPE = "sign_type";
}
六.請求連線類HttpKit,統一下單、查詢訂單用到
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.Response;
/**
* https 請求 微信為https的請求
*
* @author andy
* @date 2015-10-9 下午2:40:19
*/
public class HttpKit {
private static final String DEFAULT_CHARSET = "UTF-8";
/**
* @return 返回型別:
* @throws IOException
* @throws UnsupportedEncodingException
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @description 功能描述: get 請求
*/
public static String get(String url, Map<String, String> params, Map<String, String> headers) throws IOException, ExecutionException, InterruptedException {
AsyncHttpClient http = new AsyncHttpClient();
AsyncHttpClient.BoundRequestBuilder builder = http.prepareGet(url);
builder.setBodyEncoding(DEFAULT_CHARSET);
if (params != null && !params.isEmpty()) {
Set<String> keys = params.keySet();
for (String key : keys) {
//builder.addQueryParame(key, params.get(key));
builder.addQueryParam(key, params.get(key));
}
}
if (headers != null && !headers.isEmpty()) {
Set<String> keys = headers.keySet();
for (String key : keys) {
builder.addHeader(key, params.get(key));
}
}
Future<Response> f = builder.execute();
String body = f.get().getResponseBody(DEFAULT_CHARSET);
http.close();
return body;
}
/**
* @return 返回型別:
* @throws IOException
* @throws UnsupportedEncodingException
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @description 功能描述: get 請求
*/
public static String get(String url) throws KeyManagementException, NoSuchAlgorithmException, NoSuchProviderException, UnsupportedEncodingException, IOException, ExecutionException, InterruptedException {
return get(url, null);
}
/**
* @return 返回型別:
* @throws IOException
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnsupportedEncodingException
* @description 功能描述: get 請求
*/
public static String get(String url, Map<String, String> params) throws KeyManagementException, NoSuchAlgorithmException, NoSuchProviderException, UnsupportedEncodingException, IOException, ExecutionException, InterruptedException {
return get(url, params, null);
}
/**
* @return 返回型別:
* @throws IOException
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @description 功能描述: POST 請求
*/
public static String post(String url, Map<String, String> params) throws IOException, ExecutionException, InterruptedException {
AsyncHttpClient http = new AsyncHttpClient();
AsyncHttpClient.BoundRequestBuilder builder = http.preparePost(url);
builder.setBodyEncoding(DEFAULT_CHARSET);
if (params != null && !params.isEmpty()) {
Set<String> keys = params.keySet();
for (String key : keys) {
//builder.addParameter(key, params.get(key));
builder.addQueryParam(key, params.get(key));
}
}
Future<Response> f = builder.execute();
String body = f.get().getResponseBody(DEFAULT_CHARSET);
http.close();
return body;
}
public static String post(String url, String s) throws IOException, ExecutionException, InterruptedException {
AsyncHttpClient http = new AsyncHttpClient();
AsyncHttpClient.BoundRequestBuilder builder = http.preparePost(url);
builder.setBodyEncoding(DEFAULT_CHARSET);
builder.setBody(s);
Future<Response> f = builder.execute();
String body = f.get().getResponseBody(DEFAULT_CHARSET);
http.close();
return body;
}
}
七.支付工具類
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.security.MessageDigest;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import com.rybsj.entity.PaymentOrder;
import com.rybsj.tools.WXPayConstants.SignType;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 支付工具類
* @author lh
*/
public class WXPayUtil {
public static Logger log=LoggerFactory.getLogger(WXPayUtil.class);
/**
* 生成訂單物件資訊
* @param orderId 訂單號
* @param appId 微信appId
* @param mch_id 微信分配的商戶ID
* @param body 支付介紹主體
* @param price 支付價格(放大100倍)
* @param spbill_create_ip 終端IP
* @param notify_url 非同步直接結果通知介面地址
* @param noncestr
* @return
*/
public static Map<String,Object> createOrderInfo(Map<String, String> requestMap) {
//生成訂單物件
UnifiedOrderRequest unifiedOrderRequest = new UnifiedOrderRequest();
unifiedOrderRequest.setAppid(requestMap.get("appId"));//公眾賬號ID
unifiedOrderRequest.setBody(requestMap.get("body"));//商品描述
unifiedOrderRequest.setMch_id(requestMap.get("mch_id"));//商戶號
unifiedOrderRequest.setNonce_str(requestMap.get("nonce_str"));//隨機字串
unifiedOrderRequest.setNotify_url(requestMap.get("notify_url"));//通知地址
unifiedOrderRequest.setOpenid(requestMap.get("userWeixinOpenId"));
unifiedOrderRequest.setDetail(requestMap.get("detail"));//詳情
unifiedOrderRequest.setOut_trade_no(requestMap.get("out_trade_no"));//商戶訂單號
unifiedOrderRequest.setSpbill_create_ip(requestMap.get("spbill_create_ip"));//終端IP
unifiedOrderRequest.setTotal_fee(requestMap.get("payMoney")); //金額需要擴大100倍:1代表支付時是0.01
unifiedOrderRequest.setTrade_type("JSAPI");//JSAPI--公眾號支付、NATIVE--原生掃碼支付、APP--app支付
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", unifiedOrderRequest.getAppid());
packageParams.put("body", unifiedOrderRequest.getBody());
packageParams.put("mch_id", unifiedOrderRequest.getMch_id());
packageParams.put("nonce_str", unifiedOrderRequest.getNonce_str());
packageParams.put("notify_url", unifiedOrderRequest.getNotify_url());
packageParams.put("openid", unifiedOrderRequest.getOpenid());
packageParams.put("detail", unifiedOrderRequest.getDetail());
packageParams.put("out_trade_no", unifiedOrderRequest.getOut_trade_no());
packageParams.put("spbill_create_ip", unifiedOrderRequest.getSpbill_create_ip());
packageParams.put("total_fee", unifiedOrderRequest.getTotal_fee());
packageParams.put("trade_type", unifiedOrderRequest.getTrade_type());
try {
unifiedOrderRequest.setSign(generateSignature(packageParams,"你的密匙"));//簽名 ,此處第二個引數填寫祕鑰
} catch (Exception e) {
e.printStackTrace();
}
//將訂單物件轉為xml格式
xstream.alias("xml", UnifiedOrderRequest.class);//根元素名需要是xml
System.out.println("封裝好的統一下單請求資料:"+xstream.toXML(unifiedOrderRequest).replace("__", "_"));
Map<String,Object> responseMap = new HashMap<String,Object>();
responseMap.put("orderInfo_toString", xstream.toXML(unifiedOrderRequest).replace("__", "_"));
responseMap.put("unifiedOrderRequest",unifiedOrderRequest);
return responseMap;
}
/**
* 生成簽名
* @param appid_value
* @param mch_id_value
* @param productId
* @param nonce_str_value
* @param trade_type
* @param notify_url
* @param spbill_create_ip
* @param total_fee
* @param out_trade_no
* @return
*/
private static String createSign(UnifiedOrderRequest unifiedOrderRequest) {
//根據規則建立可排序的map集合
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", unifiedOrderRequest.getAppid());
packageParams.put("body", unifiedOrderRequest.getBody());
packageParams.put("mch_id", unifiedOrderRequest.getMch_id());
packageParams.put("nonce_str", unifiedOrderRequest.getNonce_str());
packageParams.put("notify_url", unifiedOrderRequest.getNotify_url());
packageParams.put("out_trade_no", unifiedOrderRequest.getOut_trade_no());
packageParams.put("spbill_create_ip", unifiedOrderRequest.getSpbill_create_ip());
packageParams.put("trade_type", unifiedOrderRequest.getTrade_type());
packageParams.put("total_fee", unifiedOrderRequest.getTotal_fee());
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 + "&");
}
}
//第二步拼接key,key設定路徑:微信商戶平臺(pay.weixin.qq.com)-->賬戶設定-->API安全-->金鑰設定
sb.append("key="+"你的密匙");
String sign = MD5Util.MD5(sb.toString()).toUpperCase();//MD5加密
log.error("方式一生成的簽名="+sign);
return sign;
}
private static XStream xstream = new XStream(new XppDriver() {
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
// 對所有xml節點的轉換都增加CDATA標記
boolean cdata = true;
String NodeName = "";
@SuppressWarnings("unchecked")
public void startNode(String name, Class clazz) {
NodeName = name;
super.startNode(name, clazz);
}
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
if(!NodeName.equals("detail")){
writer.write(text);
}else{
writer.write("<![CDATA[");
writer.write(text);
writer.write("]]>");
}
} else {
writer.write(text);
}
}
};
}
});
//xml解析
public static SortedMap<String, String> doXMLParseWithSorted(String strxml) throws Exception {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
SortedMap<String,String> m = new TreeMap<String,String>();
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 = getChildrenText(children);
}
m.put(k, v);
}
//關閉流
in.close();
return m;
}
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(getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
/**
* 調統一下單API
* @param orderInfo
* @return
*/
public static UnifiedOrderRespose httpOrder(String orderInfo) {
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
try {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
//加入資料
conn.setRequestMethod("POST");
conn.setDoOutput(true);
BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
buffOutStr.write(orderInfo.getBytes("UTF-8"));
buffOutStr.flush();
buffOutStr.close();
//獲取輸入流
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
String line = null;
StringBuffer sb = new StringBuffer();
while((line = reader.readLine())!= null){
sb.append(line);
}
//將請求返回的內容通過xStream轉換為UnifiedOrderRespose物件
xstream.alias("xml", UnifiedOrderRespose.class);
UnifiedOrderRespose unifiedOrderRespose = (UnifiedOrderRespose)xstream.fromXML(sb.toString());
return unifiedOrderRespose;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* XML格式字串轉換為Map
*
* @param strXML XML字串
* @return XML資料轉換後的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception ex) {
// do nothing
}
return data;
} catch (Exception ex) {
WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
throw ex;
}
}
/**
* 將Map轉換為XML格式的字串
*
* @param data Map型別資料
* @return XML格式的字串
* @throws Exception
*/
public static String mapToXml(Map<String, String> data) throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document document = documentBuilder.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key: data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
value = value.trim();
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
try {
writer.close();
}
catch (Exception ex) {
}
return output;
}
/**
* 生成帶有 sign 的 XML 格式字串
*
* @param data Map型別資料
* @param key API金鑰
* @return 含有sign欄位的XML
*/
public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
return generateSignedXml(data, key, SignType.MD5);
}
/**
* 生成帶有 sign 的 XML 格式字串
*
* @param data Map型別資料
* @param key API金鑰
* @param signType 簽名型別
* @return 含有sign欄位的XML
*/
public static String generateSignedXml(final Map<String, String> data, String key, SignType signType) throws Exception {
String sign = generateSignature(data, key, signType);
data.put(WXPayConstants.FIELD_SIGN, sign);
return mapToXml(data);
}
/**
* 判斷簽名是否正確
*
* @param xmlStr XML格式資料
* @param key API金鑰
* @return 簽名是否正確
* @throws Exception
*/
public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
Map<String, String> data = xmlToMap(xmlStr);
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key).equals(sign);
}
/**
* 判斷簽名是否正確,必須包含sign欄位,否則返回false。使用MD5簽名。
*
* @param data Map型別資料
* @param key API金鑰
* @return 簽名是否正確
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
return isSignatureValid(data, key, SignType.MD5);
}
/**
* 判斷簽名是否正確,必須包含sign欄位,否則返回false。
*
* @param data Map型別資料
* @param key API金鑰
* @param signType 簽名方式
* @return 簽名是否正確
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType) throws Exception {
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key, signType).equals(sign);
}
/**
* 生成簽名
*
* @param data 待簽名資料
* @param key API金鑰
* @return 簽名
*/
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
return generateSignature(data, key, SignType.MD5);
}
/**
* 生成簽名. 注意,若含有sign_type欄位,必須和signType引數保持一致。
*
* @param data 待簽名資料
* @param key API金鑰
* @param signType 簽名方式
* @return 簽名
*/
public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(WXPayConstants.FIELD_SIGN)) {
continue;
}
if (data.get(k)!=null&&data.get(k).trim().length() > 0) // 引數值為空,則不參與簽名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
if (SignType.MD5.equals(signType)) {
return MD5(sb.toString()).toUpperCase();
}
else if (SignType.HMACSHA256.equals(signType)) {
return HMACSHA256(sb.toString(), key);
}
else {
log.error("獲取簽名失敗,失敗原因:"+String.format("Invalid sign_type: %s", signType));
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* 獲取隨機字串 Nonce Str
* @return String 隨機字串
*/
public static String generateNonceStr() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
/**
* Map轉xml資料
*/
public static String GetMapToXML(Map<String,String> param){
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
for (Map.Entry<String,String> entry : param.entrySet()) {
sb.append("<"+ entry.getKey() +">");
sb.append(entry.getValue());
sb.append("</"+ entry.getKey() +">");
}
sb.append("</xml>");
return sb.toString();
}
/**
* 生成 MD5
* @param data 待處理資料
* @return MD5結果
*/
public static String MD5(String data) throws Exception {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 生成 HMACSHA256
* @param data 待處理資料
* @param key 金鑰
* @return 加密結果
* @throws Exception
*/
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 日誌
* @return
*/
public static Logger getLogger() {
Logger logger = LoggerFactory.getLogger("wxpay java sdk");
return logger;
}
/**
* 獲取當前時間戳,單位秒
* @return
*/
public static long getCurrentTimestamp() {
return System.currentTimeMillis()/1000;
}
/**
* 獲取當前時間戳,單位毫秒
* @return
*/
public static long getCurrentTimestampMs() {
return System.currentTimeMillis();
}
/**
* 生成 uuid, 即用來標識一筆單,也用做 nonce_str
* @return
*/
public static String generateUUID() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
/**
* 支付簽名
* @param timestamp
* @param noncestr
* @param packages
* @return
* @throws UnsupportedEncodingException
*/
public static String paySign(String timestamp, String noncestr,String packages,String appId){
Map<String, String> paras = new HashMap<String, String>();
paras.put("appid", appId);
paras.put("timestamp", timestamp);
paras.put("noncestr", noncestr);
paras.put("package", packages);
paras.put("signType", "MD5");
StringBuffer sb = new StringBuffer();
Set es = paras.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 + "&");
}
}
String sign = MD5Util.MD5(sb.toString()).toUpperCase();//MD5加密
return sign;
}
/**
* map轉物件
* @param map
* @return
*/
public static PaymentOrder getPaymentOrders(Map<String,String> map) {
PaymentOrder payOrder=new PaymentOrder();
payOrder.setAppid(map.get("appid"));
payOrder.setOpenid(map.get("openid"));
payOrder.setOrdetaileid(Integer.parseInt(map.get("out_trade_no")));
payOrder.setTotal_fee(Integer.parseInt(map.get("total_fee")));
payOrder.setTrade_type(map.get("trade_type"));
//payOrder.setTime_start(map.get("time_start"));
//payOrder.setTime_expire(map.get("time_expire"));
return payOrder;
}
/**
* 物件轉map
*/
public static Map<String,String> getMaptoOrder(PaymentOrder porder){
Map<String,String> map=new HashMap<String,String>();
map.put("appId", porder.getAppid());
map.put("timeStamp", porder.getTimestamp());
map.put("nonceStr", porder.getNonce_str());
map.put("signType", porder.getSignType());
map.put("package", porder.getPackages());
map.put("paySign", porder.getPaySign());
return map;
}
/**
* description: 解析微信通知xml
*
* @param xml
* @return
* @author ex_yangxiaoyi
* @see
*/
public static Map parseXmlToList(String xml) {
Map retMap = new HashMap();
try {
StringReader read = new StringReader(xml);
// 建立新的輸入源SAX 解析器將使用 InputSource 物件來確定如何讀取 XML 輸入
InputSource source = new InputSource(read);
// 建立一個新的SAXBuilder
SAXBuilder sb = new SAXBuilder();//
// 通過輸入源構造一個Document
Document doc = (Document) sb.build(source);
Element root = doc.getRootElement();// 指向根節點
List<Element> es = root.getChildren();
if (es != null && es.size() != 0) {
for (Element element : es) {
retMap.put(element.getName(), element.getValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return retMap;
}
}
以上為工具類,後面講支付流程。首先需要使用者授權獲取openid,注意不要認為官方文件必填項顯示此引數為否就不用openid了,後面解釋有說明當trade_type=JSAPI(即公眾號支付)時此引數必填。
首先獲取使用者許可權,需要設定設定回撥域名(即redirect_uri的域名),該域名需為80埠,且需要下載一個txt檔案到該域名下,具體操作如下圖:
此外獲取access_token還要配置IP白名單,如下圖:找到開發下面的基本配置,點選IP白名單的配置,多個ip可用回車鍵隔開
配置完後就可以通過微信授權獲取openid了。看了很多demo微信授權連結都是直接寫在後臺的,本人試了下寫在前臺也能用。其中appid改成自己公眾號的appid,redirect_uri就是剛才設定的授權回撥域名,getWXUserInformation是對應後臺的獲取openid的方法,此方法也可獲取微信頭像和名字等資訊。state=123是授權時帶過去的引數,可以隨便修改。scope=snsapi_userinfo時表示授權時會彈出授權頁面需要使用者同意,此時可以獲取使用者其他資訊,當scope=snsapi_base時表示授權不會彈出授權頁面,此時只能獲取openid。
window.location="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx。。。。。&redirect_uri=。。。/getWXUserInformation&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect";
下面附上後臺獲取openid業務層類Access_tokenAction
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.apache.http.client.ClientProtocolException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.alibaba.fastjson.JSONObject;
import com.rybsj.service.UserService;
import com.rybsj.tools.WXAuthUtil;
@Controller
public class Access_tokenAction {
@Resource(name="userService")
private UserService userimpl;
private static Logger logger=LoggerFactory.getLogger(Access_tokenAction.class);
/**
* 獲取微信使用者資訊
* @throws IOException
* @throws ClientProtocolException
*/
@RequestMapping("/getWXUserInformation")
public String getWXUserInformation(HttpServletRequest request) throws ClientProtocolException, IOException {
/*
* start 獲取微信使用者基本資訊
*/
String code=request.getParameter("code");
System.out.println(code);
logger.info("code="+code);
//第二步:通過code換取網頁授權access_token
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+WXAuthUtil.APPID
+ "&secret="+WXAuthUtil.APPSECRET
+ "&code="+code
+ "&grant_type=authorization_code";
System.out.println("url:"+url);
logger.info("url="+url);
JSONObject jsonObject = WXAuthUtil.doGetJson(url);
System.out.println(jsonObject.toString());
logger.info("獲取token返回資訊為:"+jsonObject.toString());
/*
{ "access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
*/
String openid = jsonObject.getString("openid");
String access_token = jsonObject.getString("access_token");
String refresh_token = jsonObject.getString("refresh_token");
//第五步驗證access_token是否失效;暫時不需要
String chickUrl="https://api.weixin.qq.com/sns/auth?access_token="+access_token+"&openid="+openid;
JSONObject chickuserInfo = WXAuthUtil.doGetJson(chickUrl);
System.out.println(chickuserInfo.toString());
logger.info("返回驗證資訊:"+chickuserInfo.toString());
if(!"0".equals(chickuserInfo.getString("errcode"))){
// 第三步:重新整理access_token(如果需要)-----暫時沒有使用,參考文件https://mp.weixin.qq.com/wiki,
String refreshTokenUrl="https://api.weixin.qq.com/sns/oauth2/refresh_token?appid="+openid+"&grant_type=refresh_token&refresh_token="+refresh_token;
JSONObject refreshInfo = WXAuthUtil.doGetJson(chickUrl);
/*
* { "access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE" }
*/
System.out.println(refreshInfo.toString());
access_token=refreshInfo.getString("access_token");
}
// 第四步:拉取使用者資訊(需scope為 snsapi_userinfo)
String infoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token="+access_token
+ "&openid="+openid
+ "&lang=zh_CN";
System.out.println("infoUrl:"+infoUrl);
JSONObject userInfo = WXAuthUtil.doGetJson(infoUrl);
logger.info("使用者資訊為:"+userInfo.toString());
/*
{ "openid":" OPENID",
" nickname": NICKNAME,
"sex":"1",
"province":"PROVINCE"
"city":"CITY",
"country":"COUNTRY",
"headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
"privilege":[ "PRIVILEGE1" "PRIVILEGE2" ],
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
*/
System.out.println("JSON-----"+userInfo.toString());
System.out.println("名字-----"+userInfo.getString("nickname"));
System.out.println("頭像-----"+userInfo.getString("headimgurl"));
//request.getSession().setAttribute("openid", openid);
Map<String, String> map=new HashMap<String,String>();
map.put("openid", openid);
map.put("nickname", userInfo.getString("nickname"));
map.put("headimgurl", userInfo.getString("headimgurl"));
request.getSession().setAttribute("wxUserMap", map);
/*
* end 獲取微信使用者基本資訊
*/
//獲取到使用者資訊後就可以進行重定向,走自己的業務邏輯了。。。。。。
//接來的邏輯就是你係統邏輯了,請自由發揮
return "operatingVan";
}
}
獲取openid後就可以微信下單了。首先設定支付授權目錄,該目錄以前是在微信平臺設定的 現在改在商戶平臺設定了
先附上前端程式碼orderPayment.jsp和後臺對應下單方法getPaymentOrder,此頁面首先通過pay()方法從後臺的獲取getPaymentOrder方法獲取微信支付的引數appId,timeStamp,nonceStr,signType,package,paySign.然後呼叫onBridgeReady()方法實現微信支付,當用戶輸入密碼後點擊立即支付會跳轉到商戶後臺的getPaymentResult()方法內,此處跳轉可在getPaymentOrder()方法的下單引數notify_url中設定
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<base href="<%=basePath%>">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>微信支付</title>
<script>
setRem();
window.addEventListener('orientationchange', setRem);
window.addEventListener('resize', setRem);
function setRem() {
var html = document.querySelector('html');
var width = html.getBoundingClientRect().width;
html.style.fontSize = width / 16 + 'px';
}
</script>
<link rel="stylesheet" href="css/common.css" />
<link rel="stylesheet" type="text/css" href="css/payment.css" />
</head>
<body>
<!--訂單需支付-->
<div class="payment-box">
<p>訂單需支付</p>
<ul>
<li>
<span class="left">首期款</span>
<span class="right">¥<span class="decime" >${order.initialcost}</span></span>
</li>
<li>
<span class="left">服務費</span>
<span class="right">¥<span class="decime" >${order.servicecost}</span></span>
</li>
<li>
<span class="left">合計</span>
<span class="right amount">¥<span class="decime">${sum }</span></span>
</li>
</ul>
</div>
<div class="height20"></div>
<!--是否需要開發票-->
<div class="invoice">
<div class="left">是否需要開發票</div>
<div class="right"><img src="images/Off.png" id="onOff" class="active"></div>
</div>
<div class="address">
<ul>
<li>
<div class="info">
發票資訊
</div>
<div class="info-input">
<input type="text" placeholder="請輸入">
<i><img src="images/icon-right.png"></i>
</div>
</li>
<li>
<div class="second-info">
<span>收貨地址</span>
<input type="text" name="" placeholder="我是一個收件地址,也可能是其它"/>
<i><img src="images/icon-right.png"></i>
</div>
</li>
</ul>
</div>
<div class="height20"></div>
<!--支付方式-->
<div class="payment-style">
<ul>
<li class="payments">
<div class="left">
<img src="images/select-false.png" class="weixin-img"><span>微信支付</span>
</div>
<div class="right">
<img src="images/icon-right.png">
</div>
</li>
<li class="payments">
<div class="left">
<img src="images/select-false.png" class="transfer-img"><span>轉賬支付</span>
</div>
<div class="right">
<img src="images/icon-down.png" class="iconDow">
</div>
</li>
<div class="child-info">
<ul>
<li>
<div class="left">賬戶名</div>
<div class="right">aaa</div>
</li>
<li>
<div class="left">開戶行</div>
<div class="right">中國銀行</div>
</li>
<li>
<div class="left">賬號</div>
<div class="right">0000 1111 222 3333 4444</div>
</li>
</ul>
</div>
<li class="payments">
<div class="left">
<img src="images/select-false.png" class="qr-img"><span>二維碼支付</span>
</div>
<div class="right">
<img src="images/icon-down.png" class="iconDow">
</div>
</li>
<div class="last-info">
<img src="images/ma.png">
</div>
</ul>
</div>
<input type="hidden" id="order" value="${order.ordetaileid }" />
<script type="text/javascript" src="js/jquery.js" ></script>
<script type="text/javascript" src="js/carinformation.js" ></script>
<script>
var appId,timeStamp,nonceStr,pg,signType,paySign;
var ordetaileid=$("#order").val();
var initialcost=parseFloat($(".decime").eq(0).html());
var servicecost=parseFloat($(".decime").eq(1).html());
var sum=parseFloat($(".decime").eq(2).html());
$(".decime").eq(0).html(formatCurrency(initialcost));
$(".decime").eq(1).html(formatCurrency(servicecost));
$(".decime").eq(2).html(formatCurrency(sum));
(function(){
//開關事件
$("#onOff").on("touchend",function(){
if($(this).hasClass("active")){
$(this).attr("src","images/On.png");
$(this).removeClass("active");
}else{
$(this).attr("src","images/Off.png");
$(this).addClass("active");
}
$(".address").stop().slideToggle();
})
//支付事件
$(".payments").on("touchend",function(){
var judgeClass = $(this).find(".left img").attr("class");
$(this).find(".left img").attr("src","images/select-true.png").end().siblings("li").find(".left img").attr("src","images/select-false.png");
if(judgeClass == "weixin-img"){
$(".last-info").stop().slideUp();
$(".child-info").stop().slideUp();
$(this).find(".right img").attr("src","images/icon-right.png");
//alert("微信支付");
//微信支付
pay();
}else if(judgeClass == "transfer-img"){//轉賬支付
if($(".child-info").is(":visible")){
$(".child-info").stop().slideUp();
$(this).find(".left img").attr("src","images/select-false.png");
$(this).find(".right .iconDow").attr("src","images/icon-down.png");
}else{
$(".child-info").stop().slideDown();
$(this).find(".right .iconDow").attr("src","images/icon-up.png").end().siblings("li").find(".right .iconDow").attr("src","images/icon-down.png");
}
$(".last-info").stop().slideUp();
}else if(judgeClass == "qr-img"){//二維碼支付
if($(".last-info").is(":visible")){
$(".last-info").stop().slideUp();
$(this).find(".left img").attr("src","images/select-false.png");
$(this).find(".right .iconDow").attr("src","images/icon-down.png");
}else{
$(".last-info").stop().slideDown();
$(this).find(".right .iconDow").attr("src","images/icon-up.png").end().siblings("li").find(".right .iconDow").attr("src","images/icon-down.png");
}
$(".child-info").stop().slideUp();
}
})
})();
function pay() {
$.ajax({
url:"getPaymentOrder",
dataType:"json",
async:false,
data:"ordetaileid="+ordetaileid+"&money="+sum,
success:function(data){
if(data==1){
alert("該訂單已支付");//呼叫支付查詢介面
$.ajax({
url:"findWXOrder",
dataType:"json",
async:false,
data:"ordetaileid="+ordetaileid,
success:function(v){
if(v==2){
window.location="userSignList.html";//支付成功,跳轉至簽約介面
return;
}else{
return;
}
}
})
return;
}
appId=data.appId;
timeStamp=data.timeStamp;
nonceStr=data.nonceStr;
pg=data.package;
signType=data.signType;
paySign=data.paySign;
}
})
if (typeof WeixinJSBridge == "undefined"){//非微信瀏覽器
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{//微信內部瀏覽器
onBridgeReady();
}
window.event.returnValue = false;
return false;
}
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":appId, //公眾號名稱,由商戶傳入
"timeStamp":timeStamp, //時間戳,自1970年以來的秒數
"nonceStr":nonceStr, //隨機串
"package":pg,
"signType":signType, //微信簽名方式:
"paySign":paySign //微信簽名
},
function(res){
//支付驗證簽名失敗!
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
alert("支付成功");
window.location="userSignList.html";
}// 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在使用者支付成功後返回 ok,但並不保證它絕對可靠。
}
);
}
</script>
</body>
</html>
後臺業務類PaymentOrderAction,此處注意兩次簽名(下單和獲取prepay_id後再次簽名)都要用MD5加密,簽名用到的key是商戶後臺的金鑰,本人之前用的是APPSECRET生成訂單一直失敗。getPaymentResult()此方法獲取支付結果並可操作自己的業務邏輯,然後將返回結果傳到前臺。若支付成功,前臺res.err_msg == "get_brand_wcpay_request:ok"。同時也可呼叫查詢訂單介面路徑findWXOrder和關閉訂單介面路徑wxCloseorder
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSONObject;
import com.rybsj.entity.PaymentOrder;
import com.rybsj.service.PaymentOrderService;
import com.rybsj.tools.HttpKit;
import com.rybsj.tools.WXAuthUtil;
import com.rybsj.tools.WXPayUtil;
@Controller
public class PaymentOrderAction extends BaseAction{
@Resource(name="paymentOrderService")
private PaymentOrderService paymentImpl;
/**
* 點選微信支付彈出支付頁面並獲取支付引數
* @param request
* @param money
* @return
* @throws Exception
*/
@RequestMapping("/getPaymentOrder")
@ResponseBody
public String getPaymentOrder(HttpServletRequest request,int ordetaileid,String sum) throws Exception {
Map<String,String> wxUserMap = (Map<String,String>)request.getSession().getAttribute("wxUserMap");
String openId=wxUserMap.get("openid");
PaymentOrder order=paymentImpl.getPaymentOrderOrdetaileid(ordetaileid);
Map<String, String> payMap=null;
//String openId="oW-bM0tabXXrrNzfjeq4jNlahGJQ";
//double sum2=mul(Double.parseDouble(sum),100);//金額轉分
//System.out.println(sum2);
if(order==null||order.getStatus()==3) {//該訂單不存在或失效
Map<String, String> paraMap = new HashMap<String, String>();
paraMap.put("appid", WXAuthUtil.APPID);
paraMap.put("body", "測試購買支付");
paraMap.put("mch_id", WXAuthUtil.MCH_ID);//商戶id
paraMap.put("nonce_str", WXPayUtil.generateNonceStr());
paraMap.put("notify_url", "---------/getPaymentResult");// 此路徑是微信伺服器呼叫支付結果通知路徑
paraMap.put("openid", openId);
paraMap.put("out_trade_no", ordetaileid+"");
//paraMap.put("spbill_create_ip", "123.12.12.123");
paraMap.put("spbill_create_ip", "192.168.1.123");
paraMap.put("total_fee", "1"); //金額
paraMap.put("trade_type", "JSAPI"); //交易型別(公眾號支付)
System.out.println(paraMap.toString());
String sign =WXPayUtil.generateSignature(paraMap, WXAuthUtil.KEY);
paraMap.put("sign", sign);
// 統一下單 https://api.mch.weixin.qq.com/pay/unifiedorder
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String xml = WXPayUtil.GetMapToXML(paraMap);
String xmlStr = HttpKit.post(url, xml);
System.out.println("xml="+xmlStr);
// 微信預付訂單id
String prepay_id = "";
Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
if ("SUCCESS".equals(map.get("return_code"))&&"SUCCESS".equals(map.get("result_code"))) {
//Map<String, String> map = doXMLParse(xmlStr);
prepay_id = map.get("prepay_id");
} else {
System.out.println("呼叫微信支付出錯,返回狀態碼:"+map.get("return_code")+",返回資訊:"+map.get("return_msg"));
return JSONObject.toJSONString(2);//呼叫微信支付出錯
}
//保險系統訂單入庫
PaymentOrder payOrder=WXPayUtil.getPaymentOrders(paraMap);
payOrder.setPrepay_id(prepay_id);
payOrder.setTime_start(getTimesMiao());
payOrder.setStatus(1);
if(order==null)paymentImpl.addPaymentOrder(payOrder);
else if(order.getStatus()==3) paymentImpl.updatePaymentOrder(payOrder);
//int pid=payOrder.getPid();//商戶預付訂單id
//
String timeStamp = WXPayUtil.getCurrentTimestamp()+"";//獲取當前時間戳(時分秒)
String nonceStr=WXPayUtil.generateNonceStr();
payMap = new HashMap<String, String>();
payMap.put("appId", paraMap.get("appid"));
payMap.put("timeStamp", timeStamp);
payMap.put("nonceStr", nonceStr);
payMap.put("signType", "MD5");
payMap.put("package", "prepay_id=" + prepay_id);
String paySign = WXPayUtil.generateSignature(payMap, WXAuthUtil.KEY);
System.out.println(paySign);
payMap.put("paySign", paySign);
//新增商戶系統訂單傳到前臺的資料appid,timestamp,noncestr,signType,package
payOrder.setNonce_str(nonceStr);
payOrder.setTimestamp(timeStamp);
payOrder.setSignType("MD5");
payOrder.setPackages(payMap.get("package"));
payOrder.setPaySign(paySign);
paymentImpl.updatePaymentOrder(payOrder);
}else if(order.getStatus()==1){//訂單未支付,
payMap = WXPayUtil.getMaptoOrder(order);
}else {
//該訂單已支付,但由於各種原因未跳轉至指定頁面
return JSONObject.toJSONString(1);//訂單已支付
}
String str=JSONObject.toJSONString(payMap);
System.out.println(str);
return str;
}
/**
* 支付結果通知頁面
* @throws Exception
*/
@RequestMapping("/getPaymentResult")
@ResponseBody
public String getPaymentResult(HttpServletRequest request,HttpServletResponse response) throws Exception {
String out_trade_no=null;
String return_code =null;
String result_code=null;
Map<String, Object> resultMap=null;
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");
System.out.println(resultStr);
//logger.info("支付成功的回撥:"+resultStr);
resultMap = WXPayUtil.parseXmlToList(resultStr);
request.setAttribute("out_trade_no", out_trade_no);
//通知微信.非同步確認成功.必寫.不然微信會一直通知後臺.八次之後就認為交易失敗了.
//response.getWriter().write(RequestHandler.setXML("SUCCESS", ""));
} catch (Exception e) {
//logger.error("微信回撥接口出現錯誤:",e);
try {
//response.getWriter().write(RequestHandler.setXML("FAIL", "error"));
} catch (Exception e1) {
e1.printStackTrace();
}
}
PaymentOrder paymentOrder=new PaymentOrder();
Map<String,String> return_data = new HashMap<String,String>();
return_code=(String)resultMap.get("return_code");
out_trade_no=(String)resultMap.get("out_trade_no");
if(return_code.equals("SUCCESS")){
//支付成功的業務邏輯,如果支付成功,則修改商戶訂單的狀態,
paymentOrder=paymentImpl.getPaymentOrderOrdetaileid(Integer.parseInt(out_trade_no));
if(paymentOrder==null) {
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "訂單不存在");
return WXPayUtil.mapToXml(return_data);
}else {
result_code=(String)resultMap.get("result_code");
if(result_code.equals("SUCCESS")) {
if(paymentOrder.getStatus()==2) {
return_data.put("return_code", "SUCCESS");
return_data.put("return_msg", "OK");
return WXPayUtil.mapToXml(return_data);
}else {
//String sign = resultMap.get(