編輯:關於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("");
List packageParams = 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(List 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()).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 {
Enumeration en = 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")); ArrayList books = null ; pullParser.setInput(is, "UTF-8"); int type = pullParser.getEventType(); //獲取事件類型 while (type != pullParser.END_DOCUMENT) { //結束文本 switch(type){ case XmlPullParser.START_DOCUMENT: //開始文本 books = new ArrayList(); 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: //結束標記 if (pullParser.getName().equals("book")) { books.add(book); book = null; //置為空釋放資源 } break; } type = pullParser.next(); //指向下一個標記 } //Log.e("test", "book------id----" + book.getPrepay_id()); }
//獲取到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()); List signParams = 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實現帶有記住密碼功能的登陸界面
本文實例為大家分享了Android帶有記住密碼功能的登陸界面實現代碼,供大家參考,具體內容如下1、設計思路主要采用SharedPreferences來保存用戶數據,本De
Android 多線程之IntentService 完全詳解
IntentService一、IntentService概述??上一篇我們聊到了HandlerThread,本篇我們就來看看HandlerThread在IntentSer
Android入門之TableLayout應用解析(二)
本文在上一篇初步介紹TableLayout常用屬性的基礎上,將進一步介紹如何UI設計器設計TableLayout + TableRow。由於實際應用中,經常需要在代碼裡往
Android 界面跳轉及數據交換
本文演示:Android 界面跳轉及數據交換,通過一個小Demo展示全部過程。 效果如下所示: 1)MainActivity.java &n