您的位置:首页 > 移动开发 > 微信开发

微信APP支付-Java后台实现

2017-02-21 11:33 676 查看
转自:http://blog.csdn.net/walle167/article/details/50957503


1 注册微信开放者平台

开发者平台地址:https://open.weixin.qq.com/


2 成为开发者




3 上传APP




4 开通微信支付功能




5 设置商户号信息

APP 支付能力开通后,微信会给你一个商户号,用户和密码等信息。需要验证商户信息,还需要设置一个加密的密钥字段,这里就不一一细说了。


6 开发接口

微信APP支付接口,是很好调试的,(不像微信公众平台,需要80端口),可以直接在本地就可以进行调试。 具体业务就不细说,直接看代码就懂了。

1 基础信息配置

package com.qx.client.common.pay.weichart.config;

import java.util.Properties;

import com.tom.util.properties.PropertiesUtil;
import com.tom.util.system.RSystemConfig;

public class WeiChartConfig {

/**
* 预支付请求地址
*/
public static final String  PrepayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";

/**
* 查询订单地址
*/
public static final String  OrderUrl = "https://api.mch.weixin.qq.com/pay/orderquery";

/**
* 关闭订单地址
*/
public static final String  CloseOrderUrl = "https://api.mch.weixin.qq.com/pay/closeorder";

/**
* 申请退款地址
*/
public static final String  RefundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";

/**
* 查询退款地址
*/
public static final String  RefundQueryUrl = "https://api.mch.weixin.qq.com/pay/refundquery";

/**
* 下载账单地址
*/
public static final String  DownloadBillUrl = "https://api.mch.weixin.qq.com/pay/downloadbill";

/**
* 商户APPID
*/
public static final String  AppId = "wxabcdefghjjsdfsd";

/**
* 商户账户 获取支付能力后,从邮件中得到
*/
public static final String  MchId = "13000000000001";

/**
* 商户秘钥  32位,在微信商户平台中设置
*/
public static final String  AppSercret = "qx12345645778679789";

/**
* 服务器异步通知页面路径
*/
public static String notify_url = getProperties().getProperty("notify_url");

/**
* 页面跳转同步通知页面路径
*/
public static String return_url = getProperties().getProperty("return_url");

/**
* 退款通知地址
*/
public static String refund_notify_url = getProperties().getProperty("refund_notify_url");

/**
* 退款需要证书文件,证书文件的地址
*/
public static String refund_file_path = getProperties().getProperty("refund_file_path");

/**
* 商品名称
*/
public static String subject =  getProperties().getProperty("subject");

/**
* 商品描述
*/
public static String body = getProperties().getProperty("body");

private static  Properties properties;

public static synchronized Properties getProperties(){
if(properties == null){
String path = System.getenv(RSystemConfig.KEY_WEB_HOME_CONF) + "/weichart.properties";
properties = PropertiesUtil.getInstance().getProperties(path);
}
return properties;
}

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

2 http /https请求的工具类


其中有需要证书的,也有不需要证书的。 

证书是在需要退款接口的时候需要使用,直接把证书放在服务器上,然后传路径
package com.qx.client.common.pay.weichart.util.httpClient;

import
20c75
java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyStore;
import java.util.Map;

import javax.net.ssl.SSLContext;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class HttpClientUtil{

public static String post(String url,
Map<String, String> headMap,
Map<String, String> params){
try{
HttpClient httpclient = new HttpClient();
httpclient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");
PostMethod httpPost = new PostMethod(url);
if(null != headMap){
for(String key : headMap.keySet()){
httpPost.setRequestHeader(key, headMap.get(key));
}
}

if(null != params){
for(String pkey : params.keySet()){
httpPost.addParameter(pkey, params.get(pkey));
}
}
httpclient.executeMethod(httpPost);

BufferedReader reader = new BufferedReader(new InputStreamReader(httpPost.getResponseBodyAsStream()));
StringBuffer stringBuffer = new StringBuffer();
String str = "";
while((str = reader.readLine()) != null){
stringBuffer.append(str);
}
reader.close();
return stringBuffer.toString();
}catch(Exception e){
e.printStackTrace();
}
return null;
}

public static String postHttplient(String url,
String xmlInfo){
try{
HttpClient httpclient = new HttpClient();
httpclient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");
PostMethod httpPost = new PostMethod(url);
httpPost.setRequestEntity(new StringRequestEntity(xmlInfo));
httpclient.executeMethod(httpPost);

BufferedReader reader = new BufferedReader(new InputStreamReader(httpPost.getResponseBodyAsStream()));
StringBuffer stringBuffer = new StringBuffer();
String str = "";
while((str = reader.readLine()) != null){
stringBuffer.append(str);
}
reader.close();
return stringBuffer.toString();
}catch(Exception e){
e.printStackTrace();
}
return null;
}

/**
* 需要加密执行的
* @param url
* @param xmlInfo
* @return
* @throws Exception
*/
public static String postHttplientNeedSSL(String url,
String xmlInfo,
String cretPath,
String mrchId)
throws Exception{
//选择初始化密钥文件格式
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//得到密钥文件流
FileInputStream instream = new FileInputStream(new File(cretPath));
try{
//用商户的ID 来解读文件
keyStore.load(instream, mrchId.toCharArray());
}finally{
instream.close();
}
//用商户的ID 来加载
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mrchId.toCharArray()).build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
//用最新的httpclient 加载密钥
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
StringBuffer ret = new StringBuffer();
try{
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new StringEntity(xmlInfo));
CloseableHttpResponse response = httpclient.execute(httpPost);
try{
HttpEntity entity = response.getEntity();
if(entity != null){
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
String text;
while((text = bufferedReader.readLine()) != null){
ret.append(text);
}
}
EntityUtils.consume(entity);
}finally{
response.close();
}
}finally{
httpclient.close();
}
return ret.toString();
}

public static String postHtpps(String urlStr,
String xmlInfo){
try{
URL url = new URL(urlStr);
URLConnection con = url.openConnection();
con.setDoOutput(true);
con.setRequestProperty("Pragma:", "no-cache");
con.setRequestProperty("Cache-Control", "no-cache");
con.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream(), "utf-8");
out.write(xmlInfo);
out.flush();
out.close();
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
StringBuffer lines = new StringBuffer();
String line = "";
for(line = br.readLine(); line != null; line = br.readLine()){
lines.append(line);
}
return lines.toString();
}catch(MalformedURLException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
return null;
}

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

4 调用API接口


其中包含 XML生成,和解析XML,请求参数字典排序,拼接密钥,MD5加密
package com.qx.client.common.pay.weichart.util;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.mobj.com.encoding.RMd5;
import org.mobj.com.xml.FXmlDocument;
import org.mobj.com.xml.FXmlNode;
import org.mobj.com.xml.FXmlNodes;

import com.qx.client.common.pay.weichart.config.WeiChartConfig;
import com.qx.client.common.pay.weichart.util.httpClient.HttpClientUtil;

public class WeiChartUtil{

/**
* 返回状态码
*/
public static final String ReturnCode = "return_code";

/**
* 返回信息
*/
public static final String ReturnMsg = "return_msg";

/**
* 业务结果
*/
public static final String ResultCode = "result_code";

/**
* 预支付交易会话标识
*/
public static final String PrepayId = "prepay_id";
/**
* 得到微信预付单的返回ID
* @param orderId  商户自己的订单号
* @param totalFee  总金额  (分)
* @return
*/
public static Map<String, String> getPreyId(String orderId,
String totalFee){
Map<String, String> reqMap = new HashMap<String, String>();
reqMap.put("appid", WeiChartConfig.AppId);
reqMap.put("mch_id", WeiChartConfig.MchId);
reqMap.put("nonce_str", getRandomString());
reqMap.put("body", WeiChartConfig.body);
//reqMap.put("detail", WeiChartConfig.subject); //非必填
//reqMap.put("attach", "附加数据"); //非必填
reqMap.put("out_trade_no", orderId); //商户系统内部的订单号,
reqMap.put("total_fee", totalFee); //订单总金额,单位为分
reqMap.put("spbill_create_ip", getHostIp()); //用户端实际ip
// reqMap.put("time_start", "172.16.40.18"); //交易起始时间 非必填
// reqMap.put("time_expire", "172.16.40.18"); //交易结束时间  非必填
// reqMap.put("goods_tag", "172.16.40.18"); //商品标记 非必填
reqMap.put("notify_url", WeiChartConfig.notify_url); //通知地址
reqMap.put("trade_type", "APP"); //交易类型
//reqMap.put("limit_pay", "no_credit"); //指定支付方式,no_credit 指定不能使用信用卡支  非必填
reqMap.put("sign", getSign(reqMap));

String reqStr = creatXml(reqMap);
String retStr = HttpClientUtil.postHttplient(WeiChartConfig.PrepayUrl, reqStr);
return getInfoByXml(retStr);
}

/**
* 关闭订单
* @param orderId  商户自己的订单号
* @return
*/
public static Map<String, String> closeOrder(String orderId){
Map<String, String> reqMap = new HashMap<String, String>();
reqMap.put("appid", WeiChartConfig.AppId);
reqMap.put("mch_id", WeiChartConfig.MchId);
reqMap.put("nonce_str", getRandomString());
reqMap.put("out_trade_no", orderId); //商户系统内部的订单号,
reqMap.put("sign", getSign(reqMap));

String reqStr = creatXml(reqMap);
String retStr = HttpClientUtil.postHttplient(WeiChartConfig.CloseOrderUrl, reqStr);
return getInfoByXml(retStr);
}

/**
* 查询订单
* @param orderId 商户自己的订单号
* @return
*/
public static String getOrder(String orderId){
Map<String, String> reqMap = new HashMap<String, String>();
reqMap.put("appid", WeiChartConfig.AppId);
reqMap.put("mch_id", WeiChartConfig.MchId);
reqMap.put("nonce_str", getRandomString());
reqMap.put("out_trade_no", orderId); //商户系统内部的订单号,
reqMap.put("sign", getSign(reqMap));

String reqStr = creatXml(reqMap);
String retStr = HttpClientUtil.postHttplient(WeiChartConfig.OrderUrl, reqStr);
return retStr;
}

/**
* 退款
* @param orderId  商户订单号
* @param refundId  退款单号
* @param totralFee 总金额(分)
* @param refundFee 退款金额(分)
* @param opUserId 操作员ID
* @return
*/
public static Map<String, String> refundWei(String orderId,String refundId,String totralFee,String refundFee,String opUserId){
Map<String, String> reqMap = new HashMap<String, String>();
reqMap.put("appid", WeiChartConfig.AppId);
reqMap.put("mch_id", WeiChartConfig.MchId);
reqMap.put("nonce_str", getRandomString());
reqMap.put("out_trade_no", orderId); //商户系统内部的订单号,
reqMap.put("out_refund_no", refundId); //商户退款单号
reqMap.put("total_fee", totralFee); //总金额
reqMap.put("refund_fee", refundFee); //退款金额
reqMap.put("op_user_id", opUserId); //操作员
reqMap.put("sign", getSign(reqMap));

String reqStr = creatXml(reqMap);
String retStr = "";
try{
retStr = HttpClientUtil.postHttplientNeedSSL(WeiChartConfig.RefundUrl, reqStr, WeiChartConfig.refund_file_path, WeiChartConfig.MchId);
}catch(Exception e){
e.printStackTrace();
return null;
}
return getInfoByXml(retStr);
}

/**
* 退款查询
* @param refundId  退款单号
* @return
*/
public static Map<String, String> getRefundWeiInfo(String refundId){
Map<String, String> reqMap = new HashMap<String, String>();
reqMap.put("appid", WeiChartConfig.AppId);
reqMap.put("mch_id", WeiChartConfig.MchId);
reqMap.put("nonce_str", getRandomString());
reqMap.put("out_refund_no", refundId); //商户退款单号
reqMap.put("sign", getSign(reqMap));

String reqStr = creatXml(reqMap);
String retStr = HttpClientUtil.postHttplient(WeiChartConfig.RefundQueryUrl, reqStr);
return getInfoByXml(retStr);
}
/**
* 传入map  生成头为XML的xml字符串,例:<xml><key>123</key></xml>
* @param reqMap
* @return
*/
public static String creatXml(Map<String, String> reqMap){
Set<String> set = reqMap.keySet();
FXmlNode rootXml = new FXmlNode();
rootXml.setName("xml");
for(String key : set){
rootXml.createNode(key, reqMap.get(key));
}
return rootXml.xml().toString();
}

/**
* 得到加密值
* @param map
* @return
*/
public static String getSign(Map<String, String> map){
String[] keys = map.keySet().toArray(new String[0]);
Arrays.sort(keys);
StringBuffer reqStr = new StringBuffer();
for(String key : keys){
String v = map.get(key);
if(v != null && !v.equals("")){
reqStr.append(key).append("=").append(v).append("&");
}
}
reqStr.append("key").append("=").append(WeiChartConfig.AppSercret);

//MD5加密
return RMd5.encode(reqStr.toString()).toUpperCase();
}

/**
* 得到10 位的时间戳
* 如果在JAVA上转换为时间要在后面补上三个0
* @return
*/
public static String getTenTimes(){
String t = new Date().getTime()+"";
t = t.substring(0, t.length()-3);
return t;
}

/**
* 得到随机字符串
* @param length
* @return
*/
public static String getRandomString(){
int length = 32;
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();

for(int i = 0; i < length; ++i){
int number = random.nextInt(62);//[0,62)
sb.append(str.charAt(number));
}
return sb.toString();
}

/**
* 得到本地机器的IP
* @return
*/
private static String getHostIp(){
String ip = "";
try{
ip = InetAddress.getLocalHost().getHostAddress();
}catch(UnknownHostException e){
e.printStackTrace();
}
return ip;
}

/**
* 将XML转换为Map 验证加密算法 然后返回
* @param xml
* @return
*/
public static Map<String, String>  getInfoByXml(String xml){
try{
FXmlDocument xdoc = new FXmlDocument();
FXmlNode nodeRoot = xdoc.formatStringToXml(xml);
FXmlNodes allNodes =  nodeRoot.allNodes();
Map<String, String> map = new HashMap<String, String>();
for(FXmlNode fXmlNode : allNodes){
map.put(fXmlNode.name(), fXmlNode.text());
}
//对返回结果做校验.去除sign 字段再去加密
String retSign = map.get("sign");
map.remove("sign");
String rightSing = getSign(map);
if(rightSing.equals(retSign)){
return map;
}
}catch(Exception e){
return null;
}
return null;
}

/**
* 将金额转换成分
* @param fee 元格式的
* @return 分
*/
public static String changeToFen(Double fee){
String priceStr = "";
if(fee != null){
int p = (int)(fee * 100); //价格变为分
priceStr = Integer.toString(p);
}
return priceStr;
}

}
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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

在微信支付的调试过程中,发现了一个困扰了很长时间的BUG,或者说是一个问题。 

就是微信请求预支付的时候如果传了中文,就是body中给的是中文,就会报body不是UTF-8格式。如果强行对字段进行编码,又会报 加密错误。

但是这不是最主要的让人困扰的地方,最让我烦恼的是,我用本地的JDK调试的时候,它是OK 的。 但是用TOMCAT 部署的时候 却一直都不行

在网上有很多的说法,有的说,对body 进行编码转换UTF-8,有的说对整个请求的XML,进行编码。

还有的说编码格式用统一的。iso900…等等,巴拉巴拉的。。 

反正我都不行。

最后在大神的帮助下,慢慢梳理,对发送请求的post方法上面想办法。 

然后就是下面的 这句关键
public static String postHtpps(String urlStr,
String xmlInfo){
try{
URL url = new URL(urlStr);
URLConnection con = url.openConnection();
con.setDoOutput(true);
con.setRequestProperty("Pragma:", "no-cache");
con.setRequestProperty("Cache-Control", "no-cache");
con.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
//在输入流里面进行转码,是最重要的
OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream(), "utf-8");
out.write(xmlInfo);
out.flush();
out.close();
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
StringBuffer lines = new StringBuffer();
String line = "";
for(line = br.readLine(); line != null; line = br.readLine()){
lines.append(line);
}
return lines.toString();
}catch(MalformedURLException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
return null;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: