android_app开发微信支付集成

最近项目里面需要支付功能,boss一致决定用微信支付,所以在网上查了很多资料,说的不全,完了就找以前的同事指教。算是成功集成上去了。在这里做个总结记录。

1、在APP上集成微信支付,首先当然是当官网上去注册并获取到支付功能。这些不涉及到开发,官网上说的很详细,这里就不多做文章。获取到这些能力了就为开发提供了条件了。开发中会用到的就是平台给的APPID、APPsercet、以及商户平台上设置的APP_key。

2、具备了支付能力等前提条件之后,就是开发过程了。代码里面怎么才能吊起支付了,参照官网上的DEMO自己也做了一些总结和各方大神的指教。分为了下面几个步奏。

(1)、首先当然是将sdk配置进工程环境中,官网中下载Android端SDK,解压后将libmmsdk.jar导入工程,然后将DEMO中Constant.java(这里是参照官方demo的样式写的,当然也可以采用其他方式)、MD5.java、Util.java放入工程(我这里用到了这些),这些先决条件有了之后就可以下一步写代码操作了。

(2)、生成订单信息

生成订单信息采用了如下方法生成,生成订单信息需要签名文件,所以里面包含了生成签名。微信要求所有请求采用XML参数形式,所有生产订单信息之后又需要转换成xml。订单信息需要的请求参数可以到官网上去对照,这里只加入了一些必要的参数。

生成订单信息方法:

[java] view plain copy  android_app开发微信支付集成android_app开发微信支付集成
  1. //获取产品订单信息  
  2.     private String genProductArgs() {  
  3.         StringBuffer xml = new StringBuffer();  
  4.         try {  
  5.             String nonceStr = genNonceStr();  
  6.   
  7.             xml.append("</xml>");  
  8.             List<NameValuePair> packageParams = new LinkedList<NameValuePair>();  
  9.   
  10.             packageParams.add(new BasicNameValuePair("appid", Constants.APP_ID)); //APPID  
  11.   
  12.             packageParams.add(new BasicNameValuePair("body""单价:" + singlePrice + " x " + payment_num.getText().toString() + "份"));  //简单描述  
  13.   
  14.             packageParams.add(new BasicNameValuePair("mch_id", Constants.MCH_ID));  //商户ID  
  15.   
  16.             packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));   //随机字符串  
  17.   
  18.             packageParams.add(new BasicNameValuePair("notify_url","http://www.weixin.qq.com/wxpay/pay.php")); //通知地址  
  19.   
  20.             packageParams.add(new BasicNameValuePair("out_trade_no",getTrade()));  //商户订单号  
  21.   
  22.             packageParams.add(new BasicNameValuePair("spbill_create_ip",getLocalHostIp())); //终端IP  
  23.   
  24.             //double price = Double.parseDouble(payment_num.getText().toString()) * (Integer.parseInt(singlePrice) * 100);  
  25.             double price = Double.parseDouble(singlePrice) * 100 * n;  
  26.             int priceInt = (int) price;  
  27.             packageParams.add(new BasicNameValuePair("total_fee", priceInt+""));    //微信接收int型价格  
  28.   
  29.             packageParams.add(new BasicNameValuePair("trade_type""APP"));  //支付类型  
  30.   
  31.             String sign = genAppSign(packageParams);  
  32.             packageParams.add(new BasicNameValuePair("sign", sign));  //签名  
  33.   
  34.             String xmlstring = parseNodeToXML(packageParams);   //转化成xml  
  35.   
  36.             return xmlstring;  
  37.         } catch (Exception e) {  
  38.             e.printStackTrace();  
  39.             return null;  
  40.         }  
  41.     }  

构造这个xml请求参数采用的是httpclient生成的,所以引入了某些包。也可以采用其他方式生成支付订单,只要最后的形式与官网中的形式相同即可。

里面涉及到某些参数的生成,这里列出的是我们项目里面的业务逻辑,当然不同项目可定是不同的。

[java] view plain copy  android_app开发微信支付集成android_app开发微信支付集成
  1. //获取订单号  
  2.     private String getTrade(){  
  3.         long nowTime = System.currentTimeMillis();  
  4.         SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmmss");  
  5.         return format.format(new Date(nowTime));  
  6.     }  
  7.   
  8.   
  9.   
  10.     //获取支付签名Sign  
  11.     StringBuilder sb = new StringBuilder();  
  12.     private String genAppSign(List<NameValuePair> params) {  
  13.         StringBuilder sb = new StringBuilder();  
  14.         for (int i = 0; i < params.size(); i++) {  
  15.             sb.append(params.get(i).getName());  
  16.             sb.append(‘=‘);  
  17.             sb.append(params.get(i).getValue());  
  18.             sb.append(‘&‘);  
  19.         }  
  20.         sb.append("key=");  
  21.         sb.append(Constants.API_KEY);  
  22.         this.sb.append("sign str\n" + sb.toString() + "\n\n");  
  23.         String appSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();  
  24.         return appSign;  
  25.     }  
  26.   
  27.     //获取随机字符串  
  28.     private String genNonceStr() {  
  29.         Random random = new Random();  
  30.         return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());  
  31.     }  
  32.   
  33.     /** 
  34.      * 解析为xml格式 
  35.      * @param treeNodes 
  36.      * @return 
  37.      */  
  38.     public String parseNodeToXML(List<NameValuePair> treeNodes) {  
  39.         StringBuffer xmlnodes = new StringBuffer();  
  40.         if (treeNodes != null && treeNodes.size() > 0) {  
  41.             xmlnodes.append("<xml>");  
  42.             for (int i = 0; i < treeNodes.size(); i++) {  
  43.                 NameValuePair node = treeNodes.get(i);  
  44.                 xmlnodes.append("<"+node.getName()+">").append(node.getValue()).append("</"+node.getName()+">");  
  45.             }  
  46.             xmlnodes.append("</xml>");  
  47.         }  
  48.         //return xmlnodes.toString();  
  49.         String xml = xmlnodes.toString();  
  50.         try {  
  51.             xml = new String(xml.toString().getBytes(), "ISO8859-1");  //商品详情为中文,将其转化为统一编码,不然获取perpred_id失败  
  52.             return xml;  
  53.         } catch (UnsupportedEncodingException e) {  
  54.             e.printStackTrace();  
  55.             return null;  
  56.         }  
  57.     }  
  58.   
  59.   
  60.     //获取手机IP  
  61.     public String getLocalHostIp() {  
  62.         String ipaddress = "";  
  63.         try {  
  64.             Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();  
  65.             // 遍历所用的网络接口  
  66.             while (en.hasMoreElements()) {  
  67.                 NetworkInterface nif = en.nextElement();// 得到每一个网络接口绑定的所有ip  
  68.                 Enumeration<InetAddress> inet = nif.getInetAddresses();  
  69.                 // 遍历每一个接口绑定的所有ip  
  70.                 while (inet.hasMoreElements()) {  
  71.                     InetAddress ip = inet.nextElement();  
  72.                     if (!ip.isLoopbackAddress() && InetAddressUtils.isIPv4Address(ip.getHostAddress())) {  
  73.                         return ip.getHostAddress();  
  74.                     }  
  75.                 }  
  76.             }  
  77.         }  
  78.         catch (SocketException e) {  
  79.             Log.e("feige""获取本地ip地址失败");  
  80.             e.printStackTrace();  
  81.         }  
  82.         return ipaddress;  
  83.     }  

(3)、访问微信后台指定接口,获取perpay_id。

可以说前面的都是为了获取这个perpay_id做准备的,官网上给出的指定接口是“https://api.mch.weixin.qq.com/pay/unifiedorder” 请求采用官网demo中util提供的请求方式

这里采用异步处理方式,当请求指定接口得到perapy_id之后直接吊起支付的方式。


[html] view plain copy  android_app开发微信支付集成android_app开发微信支付集成
  1. //调用支付获取id  
  2.     public void gotoWechat() {  
  3.         new AsyncTask() {  
  4.             @Override  
  5.             protected Object doInBackground(Object[] objects) {   //获取Prepay_id  
  6.                 String url = String.format("https://api.mch.weixin.qq.com/pay/unifiedorder");  
  7.                 String entity = genProductArgs();   //获取订单信息  
  8.                 byte[] buf = Util.httpPost(url, entity);  
  9.                 String content = new String(buf);  //请求成功返回的信息  
  10.                 //Log.e("orion", content);  
  11.                 try {  
  12.                     xmlParseTest(content);  //解析返回的信息  
  13.                 } catch (IOException e) {  
  14.                     e.printStackTrace();  
  15.                 } catch (XmlPullParserException e) {  
  16.                     e.printStackTrace();  
  17.                 }  
  18.                 return null;  
  19.             }  
  20.   
  21.             @Override  
  22.             protected void onPostExecute(Object o) {  
  23.                 super.onPostExecute(o);  
  24.                 wechatPay();  
  25.             }  
  26.         }.execute();  
  27.     }  


请求成功返回的数据当然也是xml格式的,需要解析并从中取到perpay_id(返回的结果不止perpay_id,包括其他信息,个人感觉吊起支付只需要perpay_id就行了)。这里也是参考demo中的方式将获取到的信息通过xml解析到

WeixinParentId对象当中。

[java] view plain copy android_app开发微信支付集成android_app开发微信支付集成
  1. /** 
  2.      * 解析xml 
  3.      * 返回prepay_id 
  4.      * 通过对象Books获取数据 
  5.      */  
  6.     WeixinParentId book = null;                //通过对象Books获取数据  
  7.     public void xmlParseTest(String str) throws IOException, XmlPullParserException {  
  8.         XmlPullParser pullParser = Xml.newPullParser();            //获取XmlPullParser对象  
  9.         //InputStream is = getContext().getAssets().open("parse.xml");   //解析文本  
  10.         ByteArrayInputStream is = new ByteArrayInputStream(str.getBytes("UTF-8"));  
  11.         ArrayList<WeixinParentId> books = null ;  
  12.   
  13.         pullParser.setInput(is, "UTF-8");  
  14.         int type = pullParser.getEventType();    //获取事件类型  
  15.         while (type != pullParser.END_DOCUMENT) {   //结束文本</books>  
  16.             switch(type){  
  17.                 case XmlPullParser.START_DOCUMENT:    //开始文本<books>  
  18.                     books = new ArrayList<WeixinParentId>();  
  19.                     break;  
  20.                 case XmlPullParser.START_TAG:    //开始标记   <book>  
  21.                     if (pullParser.getName().equals("xml")) {  
  22.                         book = new WeixinParentId();  
  23.                     }else if (pullParser.getName().equals("return_msg")) {  
  24.                         type = pullParser.next();    //指向下一个位置,不然无法获取数据  
  25.                         book.setReturn_msg(pullParser.getText());  
  26.                     }else if (pullParser.getName().equals("appid")) {  
  27.                         type = pullParser.next();  
  28.                         book.setAppid(pullParser.getText());  
  29.                     }else if (pullParser.getName().equals("prepay_id")) {  
  30.                         type = pullParser.next();  
  31.                         book.setPrepay_id(pullParser.getText());  
  32.                     }  
  33.   
  34.                     break;  
  35.                 case XmlPullParser.END_TAG:   //结束标记      </books>  
  36.                     if (pullParser.getName().equals("book")) {  
  37.                         books.add(book);  
  38.                         book = null;    //置为空释放资源  
  39.                     }  
  40.                     break;  
  41.             }  
  42.             type = pullParser.next();    //指向下一个标记  
  43.   
  44.         }  
  45.         //Log.e("test", "book------id----" + book.getPrepay_id());  
  46.     }  
成功执行这不之后,book对象中perpay_id已经被赋值。只需在异步中取出使用即可。

[java] view plain copy android_app开发微信支付集成android_app开发微信支付集成
  1. //获取到perpay_id之后吊起微信支付  
  2.     protected void wechatPay() {  
  3.         PayReq req = new PayReq();  
  4.         req.appId = Constants.APP_ID;  
  5.         req.partnerId = Constants.MCH_ID;  
  6.         req.prepayId = book.getPrepay_id();  
  7.         req.packageValue = "Sign=WXPay";  
  8.         req.nonceStr = genNonceStr();  
  9.         req.timeStamp = String.valueOf(genTimeStamp());  
  10.   
  11.         List<NameValuePair> signParams = new LinkedList<NameValuePair>();  
  12.         signParams.add(new BasicNameValuePair("appid", req.appId));  
  13.         signParams.add(new BasicNameValuePair("noncestr", req.nonceStr));  
  14.         signParams.add(new BasicNameValuePair("package", req.packageValue));  
  15.         signParams.add(new BasicNameValuePair("partnerid", req.partnerId));  
  16.         signParams.add(new BasicNameValuePair("prepayid", req.prepayId));  
  17.         signParams.add(new BasicNameValuePair("timestamp", req.timeStamp));  
  18.         req.sign = genAppSign(signParams);  
  19.         sb.append("sign\n" + req.sign + "\n\n");  
  20.         // 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信  
  21.         //Log.e("test","book.getPrepay_id()----------"+book.getPrepay_id()+"-------genNonceStr()-------"+genNonceStr()+"--------genTimeStamp()-------"+genTimeStamp()+"---genAppSign(signParams)--"+genAppSign(signParams));  
  22.         api.sendReq(req);  
  23.         // dialog.dismiss();  
  24.   
  25.     }  
  26.   
  27.     //获取时间搓  
  28.     private long genTimeStamp() {  
  29.         return System.currentTimeMillis() / 1000;  
  30.     }  
把前面的异步操作方法赋给一个按钮点击事件,如果所有步奏正确,就可以进入支付界面了,如下图:
android_app开发微信支付集成

点击确支付当然就是输入密码什么的操作了,支付成功后有一个反馈信息如下图:

android_app开发微信支付集成

点击完成,当然是回到APP咯,这里就是微信提供的一个回调了。也就是官网上强调的wxapi包下的
WXPayEntryActivity里的Onresp()方法中做回调处理,该包必须在项目工程包目录下才能回调成功。下面贴出的是示例工程结构

android_app开发微信支付集成


在回调中弹出了对话框提示用户支付成功并处理其他逻辑。


[java] view plain copy  android_app开发微信支付集成android_app开发微信支付集成
  1. public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {  
  2.   
  3.     private static final String TAG = "MicroMsg.SDKSample.WXPayEntryActivity";  
  4.   
  5.     private IWXAPI api;  
  6.   
  7.   
  8.     @Override public void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.   
  11. //        setContentView(R.layout.activity_main2);  
  12.         api = WXAPIFactory.createWXAPI(this, Constants.APP_ID);  
  13.         api.handleIntent(getIntent(), this);  
  14.         api.registerApp(Constants.APP_ID);  
  15.     }  
  16.   
  17.   
  18.     @Override  
  19.     protected void onNewIntent(Intent intent) {  
  20.         super.onNewIntent(intent);  
  21.         setIntent(intent);  
  22.         api.handleIntent(intent, this);  
  23.     }  
  24.   
  25.   
  26.     @Override  
  27.     public void onReq(BaseReq req) {  
  28.   
  29.     }  
  30.   
  31.   
  32.     @Override  
  33.     public void onResp(BaseResp resp) {  
  34.         int errCode = resp.errCode;  
  35.   
  36.         if (errCode == 0) {  
  37.   
  38.             // 0成功 展示成功页面  
  39.             // Intent intent = new Intent("name");  
  40.             // sendBroadcast(intent);  
  41.             // Log.e("test","支付成功的回调方法--onResp--");  
  42.             // Toast.makeText(this,"支付完成",Toast.LENGTH_SHORT).show();  
  43.   
  44.             new AlertDialog.Builder(this).setMessage("支付成功").setPositiveButton("确定"new DialogInterface.OnClickListener() {  
  45.                 @Override  
  46.                 public void onClick(DialogInterface dialog, int which) {  
  47.                     dialog.dismiss();  
  48.                     finish();  
  49.                     PaymentActivity.instance.finish();  
  50.   
  51.                     Intent intent = new Intent(WXPayEntryActivity.this, PuzzGameActivity.class);  
  52.                     intent.putExtra("ISPLAY",true);  
  53.                     startActivity(intent);  
  54.   
  55.                 }  
  56.             }).setTitle("提示").create().show();  
  57.   
  58.             Toast.makeText(this,"点击确定按钮开始参与拼图游戏活动",Toast.LENGTH_LONG).show();  
  59.   
  60.         }  
  61.         else if (errCode == -1) {  
  62.             //-1 错误 可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等。  
  63.             new AlertDialog.Builder(this).setMessage("支付出错").setPositiveButton("确定"new DialogInterface.OnClickListener() {  
  64.                 @Override  
  65.                 public void onClick(DialogInterface dialog, int which) {  
  66.                     dialog.dismiss();  
  67.                     finish();  
  68.                 }  
  69.             }).setTitle("提示").create().show();  
  70.             finish();  
  71.         }  
  72.         else if (errCode == -2) {  
  73.             //-2 用户取消 无需处理。发生场景:用户不支付了,点击取消,返回APP。  
  74.             finish();  
  75.         }  
  76.     }  
  77.   
  78.   
  79. }  

这样所有步骤就几乎完全了,结合官方示例和文档,应该可以快速的在项目中加入支付功能了。


当然,这里所涉及到的步骤全是在app客户端进行的,官网上建议将获取签名等操作放在服务后台进行,应该是为了安全吧,也就是客服端将订单信息传给服务端。服务端返回吊起支付必要的信息

(包括perpay_id、商户id 、签名等),然后由客户端吊起微信支付的。

android_app开发微信支付集成

上一篇:微信公众号开发--消息的接收与被动回复消息


下一篇:移动终端为何不显示JSP及解决办法