-
Android 微信支付详解与Demo
最近公司弄Ionic框架,项目中需要微信支付,无奈,把我调过去弄,期间也是几近崩溃,好在皇天不负有心人,在看别人的文档,终于是在项目中集成了微信支付,下面作为一个小白的我,想要把我的经验分享给大家,希望对大家有所帮助。
先给一个可用的demo吧(运行前先看txt文件)
http://download.csdn.net/detail/simon_crystin/9699743;这个demo是基于eclipse开发的,博主也在Android Studio开发过微信支付,原理都是一样的,大家把这个demo弄懂了,在AS上面也是一样的。
(温馨提示:大家下载下来可能会出错,也有可能不会。下面给出出错的解决方法:1.进入项目中的WeIXinPay->Build Path->configure build path,移除那个报错的jar包。 2.会出现资源找不到的情况,这是因为你没有v7包,下载一个v7包,或者把出错的地方都删除,只是一个主题,删除了看起来不好看而已,当然,你也可以用你有的主题。 还有一个问题需要提出来,就是你可能按照里面的text操作的仍然调不起客户端,有可能是你没有安装微信客户端,因为我没有做判断。这个demo不会出现只能成功支付一次的情况,博主亲测有效。出现只能支付一次只能说明你的签名没有对应)
1.去微信开放平台申请微信支付服务,绑定自己的应用这里具体不多讲,但是一定要申请完成,将会得到是三个参数
//appid 微信分配的公众账号ID public static final String APP_ID = ""; //商户号 微信分配的公众账号ID public static final String MCH_ID = ""; // API密钥,在商户平台设置 public static final String API_KEY= "";
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
**坑点提示:在微信开发平台设置包名和签名。这里的包名一定要和你自己的包名一样,就是manifest中的package,签名一定要和你用官方app生成的一样(https://open.weixin.qq.com/zh_CN/htmledition/res/dev/download/sdk/Gen_Signature_Android.apk)。
微信会根据你的填写的包名,然后对你的keystore进行一种算法,生成你的签名。包名和签名一定要和微信开放平台的相同。不过这里需要注意的是,如果你发布的正式版本,需要用官方app重新生成签名,然后在开放平台重新设置sign,因为测试版本的keystore与正式版的keystore不一样。总之,就是你用的keystore生成的sign要和微信开放平台的时刻保持一致。**2.准备工作做好了,接下来就是开发了,先下载微信的jar包,导入。
微信支付分为三个步骤
1.生成prepayId@Override protected Map<String, String> doInBackground(String... params) { // TODO Auto-generated method stub String url=String.format(params[0]); String entity=getProductArgs(); Log.e("Simon",">>>>"+entity); byte[] buf=Util.httpPost(url, entity); String content = new String(buf); Log.e("orion", "----"+content); Map<String,String> xml=decodeXml(content); return xml; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
2.生成签名参数
private void genPayReq() { req.appId = Constants.APP_ID; req.partnerId = Constants.MCH_ID; if (resultunifiedorder!=null) { req.prepayId = resultunifiedorder.get("prepay_id"); req.packageValue = "prepay_id="+resultunifiedorder.get("prepay_id"); } else { Toast.makeText(MainActivity.this, "prepayid为空", Toast.LENGTH_SHORT).show(); } req.nonceStr = getNonceStr(); req.timeStamp = String.valueOf(genTimeStamp()); List<NameValuePair> signParams = new LinkedList<NameValuePair>(); signParams.add(new BasicNameValuePair("appid", req.appId)); signParams.add(new BasicNameValuePair("noncestr", req.nonceStr)); signParams.add(new BasicNameValuePair("package", req.packageValue)); signParams.add(new BasicNameValuePair("partnerid", req.partnerId)); signParams.add(new BasicNameValuePair("prepayid", req.prepayId)); signParams.add(new BasicNameValuePair("timestamp", req.timeStamp)); req.sign = genAppSign(signParams); sb.append("sign\n"+req.sign+"\n\n"); textView.setText(sb.toString()); Log.e("Simon", "----"+signParams.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
- 27
- 28
- 29
- 30
- 31
- 32
3.调起支付。
/* * 调起微信支付 */ private void sendPayReq() { msgApi.registerApp(Constants.APP_ID); msgApi.sendReq(req); Log.i(">>>>>", req.partnerId); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
下面给出完整代码
package com.alpha.live; import java.io.StringReader; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; import org.xmlpull.v1.XmlPullParser; import com.tencent.mm.sdk.modelpay.PayReq; import com.tencent.mm.sdk.openapi.IWXAPI; import com.tencent.mm.sdk.openapi.WXAPIFactory; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.util.Xml; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; /** * Created by Simon on 2016/12/2. */ public class MainActivity extends Activity implements OnClickListener { private Button submitButton; private Button confirmButton; private TextView textView; private StringBuffer sb; private Map<String,String> resultunifiedorder; private PayReq req; private final IWXAPI msgApi = WXAPIFactory.createWXAPI(this, null); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); submitButton=(Button) findViewById(R.id.bt_submit_order); confirmButton=(Button) findViewById(R.id.bt_corfirm); textView=(TextView) findViewById(R.id.tv_prepay_id); submitButton.setOnClickListener(this); confirmButton.setOnClickListener(this); sb=new StringBuffer(); req=new PayReq(); } @Override public void onClick(View v) { // TODO Auto-generated method stub switch (v.getId()) { case R.id.bt_submit_order: String urlString="https://api.mch.weixin.qq.com/pay/unifiedorder"; PrePayIdAsyncTask prePayIdAsyncTask=new PrePayIdAsyncTask(); prePayIdAsyncTask.execute(urlString); //生成prepayId break; case R.id.bt_corfirm: genPayReq();//生成签名参数 sendPayReq();//调起支付 break; default: break; } } /* * 调起微信支付 */ private void sendPayReq() { msgApi.registerApp(Constants.APP_ID); msgApi.sendReq(req); Log.i(">>>>>", req.partnerId); } private long genTimeStamp() { return System.currentTimeMillis() / 1000; } private void genPayReq() { req.appId = Constants.APP_ID; req.partnerId = Constants.MCH_ID; if (resultunifiedorder!=null) { req.prepayId = resultunifiedorder.get("prepay_id"); req.packageValue = "prepay_id="+resultunifiedorder.get("prepay_id"); } else { Toast.makeText(MainActivity.this, "prepayid为空", Toast.LENGTH_SHORT).show(); } req.nonceStr = getNonceStr(); req.timeStamp = String.valueOf(genTimeStamp()); List<NameValuePair> signParams = new LinkedList<NameValuePair>(); signParams.add(new BasicNameValuePair("appid", req.appId)); signParams.add(new BasicNameValuePair("noncestr", req.nonceStr)); signParams.add(new BasicNameValuePair("package", req.packageValue)); signParams.add(new BasicNameValuePair("partnerid", req.partnerId)); signParams.add(new BasicNameValuePair("prepayid", req.prepayId)); signParams.add(new BasicNameValuePair("timestamp", req.timeStamp)); req.sign = genAppSign(signParams); sb.append("sign\n"+req.sign+"\n\n"); textView.setText(sb.toString()); Log.e("Simon", "----"+signParams.toString()); } private String genAppSign(List<NameValuePair> params) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < params.size(); i++) { sb.append(params.get(i).getName()); sb.append('='); sb.append(params.get(i).getValue()); sb.append('&'); } sb.append("key="); sb.append(Constants.API_KEY); this.sb.append("sign str\n"+sb.toString()+"\n\n"); String appSign = MD5.getMessageDigest(sb.toString().getBytes()); Log.e("Simon","----"+appSign); return appSign; } private class PrePayIdAsyncTask extends AsyncTask<String,Void, Map<String, String>> { private ProgressDialog dialog; @Override protected void onPreExecute() { // TODO Auto-generated method stub super.onPreExecute(); dialog = ProgressDialog.show(MainActivity.this, "提示", "正在提交订单"); } @Override protected Map<String, String> doInBackground(String... params) { // TODO Auto-generated method stub String url=String.format(params[0]); String entity=getProductArgs(); Log.e("Simon",">>>>"+entity); byte[] buf=Util.httpPost(url, entity); String content = new String(buf); Log.e("orion", "----"+content); Map<String,String> xml=decodeXml(content); return xml; } @Override protected void onPostExecute(Map<String, String> result) { // TODO Auto-generated method stub super.onPostExecute(result); if (dialog != null) { dialog.dismiss(); } sb.append("prepay_id\n"+result.get("prepay_id")+"\n\n"); textView.setText(sb.toString()); resultunifiedorder=result; } } public Map<String,String> decodeXml(String content) { try { Map<String, String> xml = new HashMap<String, String>(); 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)==false){ //实例化student对象 xml.put(nodeName,parser.nextText()); } break; case XmlPullParser.END_TAG: break; } event = parser.next(); } return xml; } catch (Exception e) { Log.e("Simon","----"+e.toString()); } return null; } private String getProductArgs() { // TODO Auto-generated method stub StringBuffer xml=new StringBuffer(); try { String nonceStr=getNonceStr(); xml.append("<xml>"); List<NameValuePair> packageParams=new LinkedList<NameValuePair>(); packageParams.add(new BasicNameValuePair("appid",Constants.APP_ID)); packageParams.add(new BasicNameValuePair("body", "APP pay test")); packageParams.add(new BasicNameValuePair("mch_id", Constants.MCH_ID)); packageParams.add(new BasicNameValuePair("nonce_str", nonceStr)); packageParams.add(new BasicNameValuePair("notify_url", "https://www.baidu.com"));//写你们的回调地址 packageParams.add(new BasicNameValuePair("out_trade_no",genOutTradNo())); packageParams.add(new BasicNameValuePair("total_fee", "1")); packageParams.add(new BasicNameValuePair("trade_type", "APP")); String sign=getPackageSign(packageParams); packageParams.add(new BasicNameValuePair("sign", sign)); String xmlString=toXml(packageParams); return xmlString; } catch (Exception e) { // TODO: handle exception return null; } } //生成订单号,测试用,在客户端生成 private String genOutTradNo() { Random random = new Random(); // return "dasgfsdg1234"; //订单号写死的话只能支付一次,第二次不能生成订单 return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes()); } //生成随机号,防重发 private String getNonceStr() { // TODO Auto-generated method stub Random random=new Random(); return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes()); } /** 生成签名 */ private String getPackageSign(List<NameValuePair> params) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < params.size(); i++) { sb.append(params.get(i).getName()); sb.append('='); sb.append(params.get(i).getValue()); sb.append('&'); } sb.append("key="); sb.append(Constants.API_KEY); String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase(); Log.e("Simon",">>>>"+packageSign); return packageSign; } /* * 转换成xml */ 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>"); Log.e("Simon",">>>>"+sb.toString()); 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
- 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
- 283
接下来就是有个支付结果的页面代码。是微信官方提供的一个类。你要在manifest注册这个类。这里需要注意的是,这个类必须放在wxapi包下,你自己新建一个包即可。
为了大家可以直接运行这个demo,我的微信加签都是在本地执行的,获取prepayid和加签都应该在服务端完成,还有最终的支付返回结果也是以服务端的为准。
*下面给出运行结果图*
大家下载demo然后把参数换了,弄下keystore,包名,签名。应该就可以用了。
版权声明:本文为博主原创文章,未经博主允许不得转载。
-
海边的卡夫卡Fu
你好,我现在是模块化开发,在这其中集成微信支付,到时候以library的形式倒进宿主app.请问我在开放平台创建应用的时候留的包名,是我该模块的包名,还是宿主app的包名昨天 14:45
-
gcsky5211
回复gcsky5211:就是不想展示什么,直接略过这个界面,因为微信有支付成功展示的界面了,所以想直接返回原画面,但现在返回会回到WXPayEntryActivity里,在回到原画面,会闪烁下...2017-04-10 09:23
-
qq_36546669
博主能说下keystore具体怎么用么,我是菜鸟,看了txt文档还是不懂keystore该怎么弄,也出现了测试一次成功后面不成功的情况,我用的是Android Studio我能找到debug.keystore文件但后面不知道怎么做了。2017-03-10 18:54
-
a123191047
packageParams.add(new BasicNameValuePair("body", "APP pay test"));请问我把这里面“APP pay test”,换成中文,报签名错误呢2017-02-16 17:28
-
yangjingfang5
后来我发现。是不是这个没涉及到支付成功或者不成功的回调接口?还是那个回调是在微信里调起的?WXPayEntryActivity这个类。因为我需要在里面做跳转网页2016-12-29 15:41
-
阿丫000
你好,你的demo是可以运行的。但是我运行官方给的demo为什么一直运行不起来呢?官方给个demo中有一个PahActivity,大概在第30几的行有这样一个地址“http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=android” ,我运行官方demo的时候总是报这个地址拒绝呢2016-12-28 16:54
发表评论