**因工作需要,需要对接微信和支付宝扫码支付功能。研究后发现微信的native支付第二种方式及支付宝支付当面付模式满足业务需求,查了网上资料和自己对接官方文档最终实现需求,现将实现过程及代码做一简单记录。

微信native:
业务流程:

1.商户后台生成订单; 2.调用【统一下单接口】发送预支付请求,生成预支付订单返回qrcodeURL并展示; 3.调用【订单查询接口】轮询订单状态 4.根据返回查询结果进行业务操作: ** 1. 支付成功。(记录数据库) ** 2. 支付不成功: **** 1. 超时:记录支付失败(记录数据库) **** 2. 未超时:继续轮询查询接口

配置类PayConfigUtil:
1
2
3
4
5
6
7
8
9
10
//微信支付参数
public class PayConfigUtil {
public static final String APP_ID = "";//微信开发平台应用ID
public static final String MCH_ID = "";//商户号(商户号ID)
public static final String API_KEY = "";//API key(商户号里面的)
public static final String NOTIFY_URL = "";//回调地址
public static final String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信统一下单接口
public static final String SELECT_URL = "https://api.mch.weixin.qq.com/pay/orderquery";//查询订单接口

}
微信支付常用方法 PayCommonUtil:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/**
* 微信支付常用方法
*/
public class PayCommonUtil {

//private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger("mydebug");
static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger("mydebug");
/**
* 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
* @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 = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();
return tenpaySign.equals(mysign);
}

/**
* @author
* @date 2016-4-22
* @Description:sign签名
* @param characterEncoding
* 编码格式
* @param
*
* @return
*/
public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
StringBuilder sb = new StringBuilder();
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=").append(API_KEY);
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}

/**
* @author
* @date 2016-4-22
* @Description:将请求参数转换为xml格式的string
* @param parameters
* 请求参数
* @return
*/
public static String getRequestXml(SortedMap<Object, Object> parameters) {
StringBuilder sb = new StringBuilder();
sb.append("<xml>");
Set es = 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();
}

/**
* 取出一个指定长度大小的随机正整数.
*
* @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));
}


//发起订购 返回二维码url
public static String wxPay(String ip,String body,int order_price,String userId,String out_trade_no) throws Exception {
// 账号信息
String appid = PayConfigUtil.APP_ID; // appid
String mch_id = PayConfigUtil.MCH_ID; // 商户号
String key = PayConfigUtil.API_KEY; // key
String currTime = PayCommonUtil.getCurrTime();
String strTime = currTime.substring(8, currTime.length());
String strRandom = PayCommonUtil.buildRandom(4) + "";
String nonce_str = strTime + strRandom;
String device_info = userId;//设备id 可自定义为userID
String notify_url = PayConfigUtil.NOTIFY_URL;
String trade_type = "NATIVE"; //交易类型
SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
packageParams.put("appid", appid);
packageParams.put("mch_id", mch_id);
packageParams.put("device_info", device_info);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", body);
packageParams.put("product_id", "joymusic_"+order_price);
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("total_fee", String.valueOf(order_price));
packageParams.put("spbill_create_ip", ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
//生成签名
String sign = PayCommonUtil.createSign("UTF-8", packageParams,key);
packageParams.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(packageParams);
String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);
Map map = XMLUtil.doXMLParse(resXml);
return (String) map.get("code_url");
}


/**
* 查询订单
* @param orderFormName 订单名称 out_trade_no: 商户订单;transaction_id: 微信支付订单
* @param orderId
* @return
* @throws Exception
*/
public static Map selectOrder(String orderFormName,String orderId) throws Exception {
// 账号信息
String appid = PayConfigUtil.APP_ID; // appid
String mch_id = PayConfigUtil.MCH_ID; // 商户号
String key = PayConfigUtil.API_KEY; // key
String currTime = PayCommonUtil.getCurrTime();
String strTime = currTime.substring(8, currTime.length());
String strRandom = PayCommonUtil.buildRandom(4) + "";
String nonce_str = strTime + strRandom;//随机数
SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
packageParams.put("appid", appid);
packageParams.put("mch_id", mch_id);
packageParams.put("nonce_str", nonce_str);
packageParams.put(orderFormName, orderId);
//生成签名
String sign = PayCommonUtil.createSign("UTF-8", packageParams,key);
packageParams.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(packageParams);
String resXml = HttpUtil.postData(PayConfigUtil.SELECT_URL, requestXML);
Map map = XMLUtil.doXMLParse(resXml);
return map;
}


/**
* 获取当前时间 yyyyMMddHHmmss
*
* @return String
*/
public static String getCurrTime() {
Date now = new Date();
SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
return outFormat.format(now);
}

/**
* 支付回调方法
* @param request
* @param response
* @throws Exception
*/
public static HashMap<String,String> wxPayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
//读取参数
InputStream inputStream ;
StringBuilder sb = new StringBuilder();
inputStream = request.getInputStream();
String s ;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null){
sb.append(s);
}
in.close();
inputStream.close();

//解析xml成map
Map<String, String> m = new HashMap<String, String>();
m = XMLUtil.doXMLParse(sb.toString());

//过滤空 设置 TreeMap
SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
Iterator it = m.keySet().iterator();
while (it.hasNext()) {
String parameter = (String) it.next();
String parameterValue = m.get(parameter);
String v = "";
if(null != parameterValue) v = parameterValue.trim();
packageParams.put(parameter, v);
}
// 账号信息
String key = PayConfigUtil.API_KEY; // key
HashMap<String, String> resultMap = new HashMap<>();
//判断签名是否正确
if(PayCommonUtil.isTenpaySign("UTF-8", packageParams,key)) {
String resXml = "";
if("SUCCESS".equals((String) packageParams.get("result_code"))){
String mch_id = (String) packageParams.get("mch_id");
String openid = (String) packageParams.get("openid");//用户标识
String device_info = (String) packageParams.get("device_info");//设备id
String is_subscribe = (String) packageParams.get("is_subscribe");//是否关注公众号
String out_trade_no = (String) packageParams.get("out_trade_no");//商户订单号
String transaction_id = (String) packageParams.get("transaction_id");//微信支付订单号
String total_fee = (String) packageParams.get("total_fee");//订单金额
String cash_fee = (String) packageParams.get("cash_fee");//支付金额(分)
String time_end = (String) packageParams.get("time_end");//订单完成时间
resultMap.put("result","SUCCESS");
resultMap.put("openid",openid);
resultMap.put("userId",device_info);
resultMap.put("myOrderNumber",out_trade_no);
resultMap.put("wxOderNumber",transaction_id);
resultMap.put("total_fee",total_fee);
resultMap.put("orderFee",cash_fee);
resultMap.put("orderTime",time_end);
//通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
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();
} else {
resultMap.put("result","FAIL");//支付失败
logger.info("支付失败,错误信息:" + packageParams.get("err_code"));
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";

BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
}
} else{
resultMap.put("result","FAIL");//支付失败
}
return resultMap;
}

}
其他工具类:
1. xml工具类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class XMLUtil {
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
* @param strxml
* @return
* @throws
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws IOException, JDOMException {
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);
}
//关闭流
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();
}
}
2. http支付请求工具类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class HttpUtil {
//private static final Log logger = LogFactory.getLog("org.apache.catalina.tribes.MESSAGES" );
static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger("error");

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);
}

//发起post请求
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.info("Error connecting to " + urlStr + ": " + e.getMessage());
} finally {
try {
if (reader != null)
reader.close();
} catch (IOException e) {
}
}
return null;
}
}
3. md5加密:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* md5加密
*/
public class MD5Util {

private final static char[] hexArray = "0123456789ABCDEF".toCharArray();

public static String byteArrToHex(byte... bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}


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 = byteArrToHex(md.digest(resultString.getBytes()));
else
resultString = byteArrToHex(md.digest(resultString.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
}

}
调用及实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//支付
String ip = "192.168.0.1";//发起支付ip
String body = "XX订购";//订单标题
String orderPrice = "1";//支付金额(分)
String userId = "test010101";//设备id 可当做用户id
String out_trade_no = userId.substring(0,5)+"_"+ PayCommonUtil.getCurrTime() + PayCommonUtil.buildRandom(4);//商户订单id
//数据库记录发起订购记录(自定义)
setInitPay(ip, userId,orderPrice,body,"86",songId,platform,out_trade_no,payType);
//调用支付方法 获取二维码URL
String WXQRcode = PayCommonUtil.wxPay(ip, body, orderPrice, userId, out_trade_no);

//回调

因域名问题,支付回调接口地址配置不上!所以采用了轮询订单状态查询方法判断订单状态
//查询订单
String isLast = request.getParameter("isLast");//最后一次
String out_trade_no = request.getParameter("outTrade");//商户订单id
String isKeep = "0";//继续查询 1 ,不继续: 0
String orderResult = "1";//支付结果 : 0 :成功,1:未成功
Map orderSelectMap = null;
String payType = request.getParameter("payType");
orderSelectMap = PayCommonUtil.selectOrder("out_trade_no", out_trade_no);
String return_code = orderSelectMap.get("return_code").toString();
String result_code = orderSelectMap.get("result_code").toString();
String trade_state = orderSelectMap.get("trade_state").toString();
if ("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)){
String myOrderNumber = orderSelectMap.get("out_trade_no").toString();
//订单状态 :0: 用户支付中 1: 支付成功 2: 转入退款 3:未支付 4:已关闭 5: 已撤销 6:支付失败
if ("SUCCESS".equals(trade_state)){//更新数据库
updateInfoPay(userId,myOrderNumber,"1");
orderResult = "0";
}else if("USERPAYING".equals(trade_state) ||"NOTPAY".equals(trade_state)){//当返回未支付或者 正在支付中时
if ("1".equals(isLast)) {
updateInfoPay(userId,myOrderNumber,"3");
}else {
isKeep = "1";
}
}else if("REFUND".equals(trade_state)){
updateInfoPay(userId,myOrderNumber,"2");
}else if("CLOSED".equals(trade_state)){
updateInfoPay(userId,myOrderNumber,"4");
}else if("REVOKED".equals(trade_state)){
updateInfoPay(userId,myOrderNumber,"5");
}else if("PAYERROR".equals(trade_state)){
updateInfoPay(userId,myOrderNumber,"6");
}
}
JSONObject jsonStr = new JSONObject();
jsonStr.put("isKeep", isKeep);
jsonStr.put("result", orderResult);
//写入流返回
out.print(jsonStr);
out.flush();
out.close();