編輯:關於Android編程
微信接入支付流程跟支付寶差不多,這裡介紹一下接入流程以及注意事項。
接入流程:
1.1、到微信開放平台添加移動應用,申請權限
到微信開放平台注冊開發者賬號,並添加應用,申請支付權限,等待審核,需要提前做,審核需要時間,一般在7個工作日內。
獲取的參數配置:
1.2、後台設置
這裡應用跟應用簽名和包名掛鉤,也就是說一個移動應用對應一個APPID,表明了配置參數的不可公用性。另外要注意調試的時候簽名問題,可以先設置為debug簽名,等調試成功換成正式簽名。一般調試問題就是簽名問題。
簽名工具下載地址:
Android.apk">https://open.weixin.qq.com/zh_CN/htmledition/res/dev/download/sdk/Gen_Signature_Android.apk
1.3、注冊吊起支付
// 通過WXAPIFactory工廠,獲取IWXAPI的實例 IWXAPI api = WXAPIFactory.createWXAPI(this, Constants.APP_ID, false); // 將該app注冊到微信 api.registerApp(Constants.APP_ID);
商戶服務器生成支付訂單,先調用統一下單API(詳見第7節)生成預付單,獲取到prepay_id後將參數再次簽名傳輸給APP發起支付,由服務器端進行操作,簡化了客戶端的復雜度跟安全性。
訂單參數:
這裡是服務器端生成預支付訂單並加簽名返回。
調用方式:
api = WXAPIFactory.createWXAPI(this, "wxb4ba3c02aa476ea1"); Button appayBtn = (Button) findViewById(R.id.appay_btn); appayBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //訂單接口 String url = "http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=android"; Button payBtn = (Button) findViewById(R.id.appay_btn); payBtn.setEnabled(false); Toast.makeText(PayActivity.this, "獲取訂單中...", Toast.LENGTH_SHORT).show(); try{ byte[] buf = Util.httpGet(url); if (buf != null && buf.length > 0) { String content = new String(buf); Log.e("get server pay params:",content); JSONObject json = new JSONObject(content); if(null != json && !json.has("retcode") ){ PayReq req = new PayReq(); //req.appId = "wxf8b4f85f3a794e77"; // 測試用appId //訂單參數 req.appId = json.getString("appid"); req.partnerId = json.getString("partnerid"); req.prepayId = json.getString("prepayid"); req.nonceStr = json.getString("noncestr"); req.timeStamp = json.getString("timestamp"); req.packageValue = json.getString("package"); req.sign = json.getString("sign"); req.extData = "app data"; // optional Toast.makeText(PayActivity.this, "正常調起支付", Toast.LENGTH_SHORT).show(); // 在支付之前,如果應用沒有注冊到微信,應該先調用IWXMsg.registerApp將應用注冊到微信 api.sendReq(req); }else{ Log.d("PAY_GET", "返回錯誤"+json.getString("retmsg")); Toast.makeText(PayActivity.this, "返回錯誤"+json.getString("retmsg"), Toast.LENGTH_SHORT).show(); } }else{ Log.d("PAY_GET", "服務器請求錯誤"); Toast.makeText(PayActivity.this, "服務器請求錯誤", Toast.LENGTH_SHORT).show(); } }catch(Exception e){ Log.e("PAY_GET", "異常:"+e.getMessage()); Toast.makeText(PayActivity.this, "異常:"+e.getMessage(), Toast.LENGTH_SHORT).show(); } payBtn.setEnabled(true); } });
1.4、支付結果回調
在包名.wxapi包路徑中實現WXPayEntryActivity類(包名或類名不一致會造成無法回調),在WXPayEntryActivity類中實現onResp函數,支付完成後,微信APP會返回到商戶APP並回調onResp函數,開發者需要在該函數中接收通知,判斷返回錯誤碼,如果支付成功則去後台查詢支付結果再展示用戶實際支付結果。注意一定不能以客戶端返回作為用戶支付的結果,應以服務器端的接收的支付通知或查詢API返回的結果為准。
清單文件:
Activity設置:
package net.sourceforge.simcpux.wxapi; import net.sourceforge.simcpux.Constants; import net.sourceforge.simcpux.R; import com.tencent.mm.sdk.constants.ConstantsAPI; import com.tencent.mm.sdk.modelbase.BaseReq; import com.tencent.mm.sdk.modelbase.BaseResp; import com.tencent.mm.sdk.openapi.IWXAPI; import com.tencent.mm.sdk.openapi.IWXAPIEventHandler; import com.tencent.mm.sdk.openapi.WXAPIFactory; import android.app.Activity; import android.app.AlertDialog; import android.content.Intent; import android.os.Bundle; import android.util.Log; 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.pay_result); api = WXAPIFactory.createWXAPI(this, Constants.APP_ID); api.handleIntent(getIntent(), this); } @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) { Log.d(TAG, "errCode = " + resp.errCode); if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.app_tip); builder.setMessage(getString(R.string.pay_result_callback_msg, String.valueOf(resp.errCode))); builder.show(); } } }
errCode:
0 支付成功
-1 支付出錯,可能的原因:簽名錯誤、未注冊APPID、項目設置APPID不正確、注冊的APPID與設置的不匹配、其他異常等。
-2 用戶取消支付
這樣一個完整的流程就走完了,下面來說一下老版本關於在客戶端加簽的流程。因為現在要求加簽過程都扔到服務器端進行操作。
舊版本的操作總結:
商戶系統和微信支付系統主要交互說明:
步驟1:用戶在商戶APP中選擇商品,提交訂單,選擇微信支付。
步驟2:商戶後台收到用戶支付單,調用微信支付統一下單接口。
步驟3:統一下單接口返回正常的prepay_id,再按簽名規范重新生成簽名後,將數據傳輸給APP。參與簽名的字段名為appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式為Sign=WXPay
步驟4:商戶APP調起微信支付。
步驟5:商戶後台接收支付通知。
步驟6:商戶後台查詢支付結果。
這裡主要講一下步驟2,步驟3,這兩步現在是放在服務器端進行的操作,以前舊版本很多都是放在客戶端進行的。
步驟2:
需要調用微信的下單接口,那麼需要了解微信的接口規則。
標注紅都是應該注意的地方。
簽名算法:
private String genProductArgs(BaseOrder order) { StringBuffer xml = new StringBuffer(); try { //生成隨機數算法 String nonceStr = genNonceStr(); //第一步:對參數按照key=value的格式,並按照參數名ASCII字典序排序如下: xml.append(""); ListpackageParams = new LinkedList (); packageParams.add(new BasicNameValuePair("appid", Constants.APP_ID)); packageParams.add(new BasicNameValuePair("body", order.getTitle())); packageParams.add(new BasicNameValuePair("mch_id", Constants.MCH_ID)); packageParams.add(new BasicNameValuePair("nonce_str", nonceStr)); packageParams.add(new BasicNameValuePair("notify_url", order.getNotifyUrl())); packageParams.add(new BasicNameValuePair("out_trade_no", order.getOrderNo())); packageParams.add(new BasicNameValuePair("spbill_create_ip", "127.0.0.1")); packageParams.add(new BasicNameValuePair("total_fee", order.getWXTotalFee() + "")); packageParams.add(new BasicNameValuePair("trade_type", "APP")); //第二步:拼接API密鑰: String sign = genPackageSign(packageParams); packageParams.add(new BasicNameValuePair("sign", sign)); //轉為成xml格式 String xmlstring = toXml(packageParams); //有漢字時需要用ISO8859-1編碼,沒有時用utf-8 return new String(xmlstring.toString().getBytes(), "ISO8859-1"); } catch (Exception e) { Log.e(TAG, "genProductArgs fail, ex = " + e.getMessage()); return null; } } //轉為成xml格式 private String toXml(List params) { StringBuilder sb = new StringBuilder(); sb.append(" "); for (int i = 0; i < params.size(); i++) { sb.append("<" + params.get(i).getName() + ">"); sb.append(params.get(i).getValue()); sb.append(""); } sb.append(" "); Log.e(TAG, sb.toString()); return sb.toString(); } //生成隨機數算法 private String genNonceStr() { Random random = new Random(); return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes()); }
步驟三:
private void genPayReq() { req = new PayReq(); req.appId = Constants.APP_ID; req.partnerId = Constants.MCH_ID; req.prepayId = resultunifiedorder.get("prepay_id"); req.packageValue = "prepay_id=" + resultunifiedorder.get("prepay_id"); 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"); } private long genTimeStamp() { return System.currentTimeMillis() / 1000; } 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()); Log.e(TAG, appSign); return appSign; }
簽名比較麻煩啊,建議還是按照新版的來吧,省事,安全!
我們在開發中常常會注意到一些Crash,這正是很頭疼的,而且Crash會帶來很多意想不到的狀態,很惡心,所以今天,我們來著重研究一下Crash,同時,我們也將使用第三方
接著上篇文章,現在在通過Android實際開發和源碼再探觀察者模式,listview是我們日常開發中必用的控件,雖然之前就有listview的替代品(recyclervi
Andrioid 編譯系統是你用於build,test,runapp的工具箱。編譯系統的運行,可以通過Android Studio的菜單或者是獨立的命令行。通過編譯系統的
一丶音樂播放頁實現功能1.音樂格信息顯示,大圖顯示2.播放功能,上一曲,下一曲,暫停3.音樂進度顯示4.切換播放模式二丶顯示效果三丶原理及代碼實現1.自定義接口回調的方法