1、先在微信开放平台申请开发应用,微信开放平台会生成APP的唯一标识APPID。由于需要保证支付安全,需要在开放平台绑定商户应用包名和应用签名,设置好后才能正常发起支付。
2、注册APPID (这个可以放在项目的application里)
商户APP工程中引入微信JAR包,调用API前,需要先向微信注册您的APPID,代码如下:
1
2
3
|
final IWXAPI msgApi = WXAPIFactory.createWXAPI(context, null ); // 将该app注册到微信 msgApi.registerApp( "wxd930ea5d5a258f4f" ); |
3、调用统一下单api生成预付单,获取到prepay_id后将参数再次签名传输给APP发起支付。
例:
下面代码中的订单号是需要后台生成的
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
|
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder" ; WXPrePost post = new WXPrePost(); post.appid = "你的appId" ; post.mch_id = "你的商户号" ; post.nonce_str = StringUtils.genNonceStr(); //随机字符串 **1 post.body = "商品名称" ; post.detail = "商品的描述" ; post.out_trade_no = out_trade_no; //商户订单号 **2 post.total_fee = "商品价格" ; //单位是分 post.spbill_create_ip = getLocalIpAddress(); //ip地址 **3 post.notify_url = "" ; //这里是后台接受支付结果通知的url地址 post.trade_type = "APP" ; post.sign = genPackageSign(post); //签名 **4 List<NameValuePair> firstSignParams = getFirstSignParams(post); String xml = toXml(firstSignParams); String entity = null ; try { entity = new String(xml.getBytes(), "ISO8859-1" ); byte [] buf = Util.httpPost(url, entity); if (buf != null ) { String content = new String(buf); Map<String, String> map = decodeXml(content); if (map != null ) { //再次签名(参与签名的字段有 :Appid partnerId prepayId nonceStr TimeStamp package) String appId = "" ; String prepayId = "" ; String nonceStr = "" ; for (Map.Entry<String, String> entry : map.entrySet()) { if ( "appid" .equals(entry.getKey())) { appId = entry.getValue(); } else if ( "prepay_id" .equals(entry.getKey())) { prepayId = entry.getValue(); } else if ( "nonce_str" .equals(entry.getKey())) { nonceStr = entry.getValue(); } } Log.d(TAG, "run: :" + appId + "/" + prepayId + "/" + nonceStr + "/" ); String TimeStamp = String.valueOf(genTimeStamp()); //ok 获取二次签名 String secondPackageSign = genSecondPackageSign(getSecondSignParams(appId, prepayId, nonceStr, TimeStamp)); PayReq req = new PayReq(); req.appId = appId; req.partnerId = "商户号" ; req.prepayId = prepayId; req.nonceStr = nonceStr; req.timeStamp = TimeStamp; req.packageValue = "Sign=WXPay" ; req.sign = secondPackageSign; req.extData = "app data" ; // optional // System.out.println("genPackageSign3:"+post.getSign()+"/"+secondPackageSign); // 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信 mApi.sendReq(req); Log.d(TAG, "run: " + appId + "/" + prepayId + "/" + nonceStr + "/" + TimeStamp + "/" + secondPackageSign); } } } catch (Exception e) { } |
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
|
public static byte [] httpPost(String url, String entity) { if (url == null || url.length() == 0 ) { Log.e(TAG, "httpPost, url is null" ); return null ; } HttpClient httpClient = getNewHttpClient(); HttpPost httpPost = new HttpPost(url); try { httpPost.setEntity( new StringEntity(entity)); httpPost.setHeader( "Accept" , "application/json" ); httpPost.setHeader( "Content-type" , "application/json" ); HttpResponse resp = httpClient.execute(httpPost); if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { Log.e(TAG, "httpGet fail, status code = " + resp.getStatusLine().getStatusCode()); return null ; } return EntityUtils.toByteArray(resp.getEntity()); } catch (Exception e) { Log.e(TAG, "httpPost exception, e = " + e.getMessage()); e.printStackTrace(); return null ; } } |
1
2
3
4
5
|
//获取随机字符串的方法 public static String genNonceStr() { Random random = new Random(); return MD5.getMessageDigest(String.valueOf(random.nextInt( 10000 )).getBytes()); } |
1
2
3
4
5
6
7
8
9
10
11
|
private String toXml(List<NameValuePair> params) { StringBuilder sb = new StringBuilder(); sb.append( "<xml>" ); for ( int i = 0 ; i < params.size(); i++) { sb.append( "<" + params.get(i).getName() + ">" ); sb.append(params.get(i).getValue()); sb.append( "</" + params.get(i).getName() + ">" ); } sb.append( "</xml>" ); return sb.toString(); } |
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
|
public Map<String, String> decodeXml(String content) { try { Map<String, String> xml = new HashMap<>(); XmlPullParser parser = Xml.newPullParser(); parser.setInput( new StringReader(content)); int event = parser.getEventType(); while (event != XmlPullParser.END_DOCUMENT) { String nodeName = parser.getName(); switch (event) { case XmlPullParser.START_DOCUMENT: break ; case XmlPullParser.START_TAG: if (! "xml" .equals(nodeName)) { xml.put(nodeName, parser.nextText()); } break ; case XmlPullParser.END_TAG: break ; } event = parser.next(); } return xml; } catch (Exception e) { } return null ; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@NonNull private List<NameValuePair> getFirstSignParams(WXPrePost params) { List<NameValuePair> packageParams = new LinkedList<>(); packageParams.add( new BasicNameValuePair( "appid" , "appId" )); packageParams.add( new BasicNameValuePair( "body" , params.body)); packageParams.add( new BasicNameValuePair( "detail" , params.detail)); packageParams.add( new BasicNameValuePair( "mch_id" , "商户号" )); packageParams.add( new BasicNameValuePair( "nonce_str" , params.nonce_str)); packageParams.add( new BasicNameValuePair( "notify_url" , params.notify_url)); packageParams.add( new BasicNameValuePair( "out_trade_no" , params.out_trade_no)); packageParams.add( new BasicNameValuePair( "spbill_create_ip" , params.spbill_create_ip)); packageParams.add( new BasicNameValuePair( "total_fee" , params.total_fee + "" )); packageParams.add( new BasicNameValuePair( "trade_type" , params.trade_type)); packageParams.add( new BasicNameValuePair( "sign" , params.sign)); return packageParams; } |
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
|
public class WXPrePost { //必须带的参数 public String appid; //微信开放平台审核通过的应用APPID public String mch_id; //微信支付分配的商户号 public String nonce_str; //随机字符串,不长于32位。推荐随机数生成算法 public String sign; //签名,详见签名生成算法 public String body; // 商品描述交易字段格式根据不同的应用场景按照以下格式:APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。 public String out_trade_no; // 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号 public int total_fee; // 订单总金额,单位为分,详见支付金额 public String spbill_create_ip; // 用户端实际ip public String notify_url; // 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。(后台提供的) public String trade_type; // 支付类型 // 非必须携带的参数 public String device_info; // 终端设备号(门店号或收银设备ID),默认请传"WEB" public String detail; // 商品名称明细列表 public String attach; // 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 public String fee_type; // 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 // public String time_start; // 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 // public String time_expire; // 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则 注意:最短失效时间间隔必须大于5分钟 public String goods_tag; // 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠 // public String limit_pay; //no_credit--指定不能使用信用卡支付 public String getAppid() { return appid; } public void setAppid(String appid) { this .appid = appid; } public String getMch_id() { return mch_id; } public void setMch_id(String mch_id) { this .mch_id = mch_id; } public String getNonce_str() { return nonce_str; } public void setNonce_str(String nonce_str) { this .nonce_str = nonce_str; } public String getSign() { return sign; } public void setSign(String sign) { this .sign = sign; } public String getBody() { return body; } public void setBody(String body) { this .body = body; } public String getOut_trade_no() { return out_trade_no; } public void setOut_trade_no(String out_trade_no) { this .out_trade_no = out_trade_no; } public int getTotal_fee() { return total_fee; } public void setTotal_fee( int total_fee) { this .total_fee = total_fee; } public String getSpbill_create_ip() { return spbill_create_ip; } public void setSpbill_create_ip(String spbill_create_ip) { this .spbill_create_ip = spbill_create_ip; } public String getNotify_url() { return notify_url; } public void setNotify_url(String notify_url) { this .notify_url = notify_url; } public String getTrade_type() { return trade_type; } public void setTrade_type(String trade_type) { this .trade_type = trade_type; } public String getDevice_info() { return device_info; } public void setDevice_info(String device_info) { this .device_info = device_info; } public String getDetail() { return detail; } public void setDetail(String detail) { this .detail = detail; } public String getAttach() { return attach; } public void setAttach(String attach) { this .attach = attach; } public String getFee_type() { return fee_type; } public void setFee_type(String fee_type) { this .fee_type = fee_type; } public String getTime_start() { return time_start; } public void setTime_start(String time_start) { this .time_start = time_start; } public String getTime_expire() { return time_expire; } public void setTime_expire(String time_expire) { this .time_expire = time_expire; } public String getGoods_tag() { return goods_tag; } public void setGoods_tag(String goods_tag) { this .goods_tag = goods_tag; } public String getLimit_pay() { return limit_pay; } public void setLimit_pay(String limit_pay) { this .limit_pay = limit_pay; } } |
这里给出的参数都是可以移动端自己获取到的,当然,最好是后台提供给我们,出于安全性考虑
支付完成,微信会回调WXPayEntryActivity,这里就不详细说了,微信文档说的很清晰
在WXPayEntryActivity的onResp()里面返回的微信支付的结果(注:这个结果不能作为我们购买商品成功与否的结果,要以微信回调给回台,然后回台告诉我们的支付结果为准)
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
|
if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) { int code = resp.errCode; switch (code) { case 0 : Log.d(TAG, "onPayFinish, errCode = " + "支付成功" ); //微信支付成功后去调后台,以后台返回的支付结果为准 //这里是微信支付完成后的回调,在这里请求后台,让他来告诉我们到底支付成功没。 break ; case - 1 : Toast.makeText( this , "支付失败1" , Toast.LENGTH_SHORT).show(); Log.d(TAG, "onPayFinish, errCode = " + "支付失败1" ); finish(); break ; case - 2 : Toast.makeText( this , "支付取消" , Toast.LENGTH_SHORT).show(); Log.d(TAG, "onPayFinish, errCode = " + "支付取消" ); finish(); break ; default : // Toast.makeText(this, "支付失败2", Toast.LENGTH_SHORT).show(); Log.d(TAG, "onPayFinish, errCode = " + "支付失败2" ); setResult(RESULT_OK); finish(); break ; } } |
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://www.jianshu.com/p/348f2895cdc9