編輯:關於Android編程
最近項目裡面需要支付功能,boos一致決定用微信支付,所以在網上查了很多資料,說的不全,完了就找以前的同事指教。算是成功集成上去了。在這裡做個總結記錄。
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。訂單信息需要的請求參數可以到官網上去對照,這裡只加入了一些必要的參數。
生成訂單信息方法:
//獲取產品訂單信息 private String genProductArgs() { StringBuffer xml = new StringBuffer(); try { String nonceStr = genNonceStr(); xml.append(""); ListpackageParams = new LinkedList (); packageParams.add(new BasicNameValuePair("appid", Constants.APP_ID)); //APPID packageParams.add(new BasicNameValuePair("body", "單價:" + singlePrice + " x " + payment_num.getText().toString() + "份")); //簡單描述 packageParams.add(new BasicNameValuePair("mch_id", Constants.MCH_ID)); //商戶ID packageParams.add(new BasicNameValuePair("nonce_str", nonceStr)); //隨機字符串 packageParams.add(new BasicNameValuePair("notify_url","http://www.weixin.qq.com/wxpay/pay.php")); //通知地址 packageParams.add(new BasicNameValuePair("out_trade_no",getTrade())); //商戶訂單號 packageParams.add(new BasicNameValuePair("spbill_create_ip",getLocalHostIp())); //終端IP //double price = Double.parseDouble(payment_num.getText().toString()) * (Integer.parseInt(singlePrice) * 100); double price = Double.parseDouble(singlePrice) * 100 * n; int priceInt = (int) price; packageParams.add(new BasicNameValuePair("total_fee", priceInt+"")); //微信接收int型價格 packageParams.add(new BasicNameValuePair("trade_type", "APP")); //支付類型 String sign = genAppSign(packageParams); packageParams.add(new BasicNameValuePair("sign", sign)); //簽名 String xmlstring = parseNodeToXML(packageParams); //轉化成xml return xmlstring; } catch (Exception e) { e.printStackTrace(); return null; } }
裡面涉及到某些參數的生成,這裡列出的是我們項目裡面的業務邏輯,當然不同項目可定是不同的。
//獲取訂單號 private String getTrade(){ long nowTime = System.currentTimeMillis(); SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmmss"); return format.format(new Date(nowTime)); } //獲取支付簽名Sign StringBuilder sb = new StringBuilder(); private String genAppSign(Listparams) { 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()).toUpperCase(); return appSign; } //獲取隨機字符串 private String genNonceStr() { Random random = new Random(); return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes()); } /** * 解析為xml格式 * @param treeNodes * @return */ public String parseNodeToXML(List treeNodes) { StringBuffer xmlnodes = new StringBuffer(); if (treeNodes != null && treeNodes.size() > 0) { xmlnodes.append(" "); for (int i = 0; i < treeNodes.size(); i++) { NameValuePair node = treeNodes.get(i); xmlnodes.append("<"+node.getName()+">").append(node.getValue()).append(""); } xmlnodes.append(" "); } //return xmlnodes.toString(); String xml = xmlnodes.toString(); try { xml = new String(xml.toString().getBytes(), "ISO8859-1"); //商品詳情為中文,將其轉化為統一編碼,不然獲取perpred_id失敗 return xml; } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } } //獲取手機IP public String getLocalHostIp() { String ipaddress = ""; try { Enumerationen = NetworkInterface.getNetworkInterfaces(); // 遍歷所用的網絡接口 while (en.hasMoreElements()) { NetworkInterface nif = en.nextElement();// 得到每一個網絡接口綁定的所有ip Enumeration inet = nif.getInetAddresses(); // 遍歷每一個接口綁定的所有ip while (inet.hasMoreElements()) { InetAddress ip = inet.nextElement(); if (!ip.isLoopbackAddress() && InetAddressUtils.isIPv4Address(ip.getHostAddress())) { return ip.getHostAddress(); } } } } catch (SocketException e) { Log.e("feige", "獲取本地ip地址失敗"); e.printStackTrace(); } return ipaddress; }
(3)、訪問微信後台指定接口,獲取perpay_id。
可以說前面的都是為了獲取這個perpay_id做准備的,官網上給出的指定接口是“https://api.mch.weixin.qq.com/pay/unifiedorder” 請求采用官網demo中util提供的請求方式
這裡采用異步處理方式,當請求指定接口得到perapy_id之後直接吊起支付的方式。
//調用支付獲取id public void gotoWechat() { new AsyncTask() { @Override protected Object doInBackground(Object[] objects) { //獲取Prepay_id String url = String.format("https://api.mch.weixin.qq.com/pay/unifiedorder"); String entity = genProductArgs(); //獲取訂單信息 byte[] buf = Util.httpPost(url, entity); String content = new String(buf); //請求成功返回的信息 //Log.e("orion", content); try { xmlParseTest(content); //解析返回的信息 } catch (IOException e) { e.printStackTrace(); } catch (XmlPullParserException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Object o) { super.onPostExecute(o); wechatPay(); } }.execute(); }
請求成功返回的數據當然也是xml格式的,需要解析並從中取到perpay_id(返回的結果不止perpay_id,包括其他信息,個人感覺吊起支付只需要perpay_id就行了)。這裡也是參考demo中的方式將獲取到的信息通過xml解析到
WeixinParentId對象當中。
/** * 解析xml * 返回prepay_id * 通過對象Books獲取數據 */ WeixinParentId book = null; //通過對象Books獲取數據 public void xmlParseTest(String str) throws IOException, XmlPullParserException { XmlPullParser pullParser = Xml.newPullParser(); //獲取XmlPullParser對象 //InputStream is = getContext().getAssets().open("parse.xml"); //解析文本 ByteArrayInputStream is = new ByteArrayInputStream(str.getBytes("UTF-8")); ArrayListbooks = null ; pullParser.setInput(is, "UTF-8"); int type = pullParser.getEventType(); //獲取事件類型 while (type != pullParser.END_DOCUMENT) { //結束文本 switch(type){ case XmlPullParser.START_DOCUMENT: //開始文本 books = new ArrayList if (pullParser.getName().equals("book")) { books.add(book); book = null; //置為空釋放資源 } break; } type = pullParser.next(); //指向下一個標記 } //Log.e("test", "book------id----" + book.getPrepay_id()); }(); break; case XmlPullParser.START_TAG: //開始標記 if (pullParser.getName().equals("xml")) { book = new WeixinParentId(); }else if (pullParser.getName().equals("return_msg")) { type = pullParser.next(); //指向下一個位置,不然無法獲取數據 book.setReturn_msg(pullParser.getText()); }else if (pullParser.getName().equals("appid")) { type = pullParser.next(); book.setAppid(pullParser.getText()); }else if (pullParser.getName().equals("prepay_id")) { type = pullParser.next(); book.setPrepay_id(pullParser.getText()); } break; case XmlPullParser.END_TAG: //結束標記
//獲取到perpay_id之後吊起微信支付 protected void wechatPay() { PayReq req = new PayReq(); req.appId = Constants.APP_ID; req.partnerId = Constants.MCH_ID; req.prepayId = book.getPrepay_id(); req.packageValue = "Sign=WXPay"; req.nonceStr = genNonceStr(); req.timeStamp = String.valueOf(genTimeStamp()); ListsignParams = new LinkedList (); 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"); // 在支付之前,如果應用沒有注冊到微信,應該先調用IWXMsg.registerApp將應用注冊到微信 //Log.e("test","book.getPrepay_id()----------"+book.getPrepay_id()+"-------genNonceStr()-------"+genNonceStr()+"--------genTimeStamp()-------"+genTimeStamp()+"---genAppSign(signParams)--"+genAppSign(signParams)); api.sendReq(req); // dialog.dismiss(); } //獲取時間搓 private long genTimeStamp() { return System.currentTimeMillis() / 1000; }
點擊確支付當然就是輸入密碼什麼的操作了,支付成功後有一個反饋信息如下圖:
點擊完成,當然是回到APP咯,這裡就是微信提供的一個回調了。也就是官網上強調的wxapi包下的
WXPayEntryActivity裡的Onresp()方法中做回調處理,該包必須在項目工程包目錄下才能回調成功。下面貼出的是示例工程結構
在回調中彈出了對話框提示用戶支付成功並處理其他邏輯。
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler { private static final String TAG = "MicroMsg.SDKSample.WXPayEntryActivity"; private IWXAPI api; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main2); api = WXAPIFactory.createWXAPI(this, Constants.APP_ID); api.handleIntent(getIntent(), this); api.registerApp(Constants.APP_ID); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); api.handleIntent(intent, this); } @Override public void onReq(BaseReq req) { } @Override public void onResp(BaseResp resp) { int errCode = resp.errCode; if (errCode == 0) { // 0成功 展示成功頁面 // Intent intent = new Intent("name"); // sendBroadcast(intent); // Log.e("test","支付成功的回調方法--onResp--"); // Toast.makeText(this,"支付完成",Toast.LENGTH_SHORT).show(); new AlertDialog.Builder(this).setMessage("支付成功").setPositiveButton("確定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); finish(); PaymentActivity.instance.finish(); Intent intent = new Intent(WXPayEntryActivity.this, PuzzGameActivity.class); intent.putExtra("ISPLAY",true); startActivity(intent); } }).setTitle("提示").create().show(); Toast.makeText(this,"點擊確定按鈕開始參與拼圖游戲活動",Toast.LENGTH_LONG).show(); } else if (errCode == -1) { //-1 錯誤 可能的原因:簽名錯誤、未注冊APPID、項目設置APPID不正確、注冊的APPID與設置的不匹配、其他異常等。 new AlertDialog.Builder(this).setMessage("支付出錯").setPositiveButton("確定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); finish(); } }).setTitle("提示").create().show(); finish(); } else if (errCode == -2) { //-2 用戶取消 無需處理。發生場景:用戶不支付了,點擊取消,返回APP。 finish(); } } }
這樣所有步奏就幾乎完全了,結合官方示例和文檔,應該可以快速的在項目中加入支付功能了。
當然,這裡所涉及到的步奏全是在app服務端進行的,官網上介意將獲取簽名等操作放在服務後台進行,也就是客服端將訂單信息傳給服務端。服務端返回吊起支付必要的信息
(包括perpay_id、商戶id 、簽名等),然後又客戶端吊起微信支付的。
本文實例為大家分享了Android帶有記住密碼功能的登陸界面實現代碼,供大家參考,具體內容如下1、設計思路主要采用SharedPreferences來保存用戶數據,本De
IntentService一、IntentService概述??上一篇我們聊到了HandlerThread,本篇我們就來看看HandlerThread在IntentSer
本文在上一篇初步介紹TableLayout常用屬性的基礎上,將進一步介紹如何UI設計器設計TableLayout + TableRow。由於實際應用中,經常需要在代碼裡往
本文演示:Android 界面跳轉及數據交換,通過一個小Demo展示全部過程。 效果如下所示: 1)MainActivity.java &n