首發地址: Android版-微信APP支付php
歡迎留言、轉發
微信極速開發系列文章(微信支付、受權獲取用戶信息等):點擊這裏java
目錄
一、註冊帳號、開發者認證
二、添加應用
三、申請微信支付
四、技術開發功能實現步驟介紹
五、代碼實例android
此項目已開源歡迎Start、PR、發起Issues一塊兒討論交流共同進步
https://github.com/Javen205/IJPay
http://git.oschina.net/javen205/IJPaygit
微信APP支付接入商戶服務中心 官方介紹文檔github
在開放平臺直接註冊,註冊郵箱不能與微信其餘的產品同號。ajax
比較坑的是微信公衆號中的支付(微信買單、刷卡、公衆號支付、wap支付)以及微信app支付都須要進行微信認證而不是公用一個微信商戶平臺(須要交兩次認證的費用)。算法
微信認證這個時間比較短(畢竟交了300大洋)通常一個工做日就會有人聯繫你覈查公司的資料。json
微信認證(開發者資質認證)經過以後就能夠在開放平臺添加應用了(這個須要審覈),應用經過以後就能夠申請微信支付了(也須要審覈)api
這個比較簡單,按照提示操做就行 上圖服務器
應用包名只定義,應用簽名能夠使用資源下載中心的簽名生成工具。務必記住包名以及簽名keystore文件的密碼,若是包名或者簽名文件不對打包是喚不起微信支付的。
若是添加的應用審覈經過了(一個工做日),就能夠直接申請微信支付了(7個工做日以內)。
審覈經過以後將會收到審覈經過的郵件,裏面有登陸商戶平臺的登陸帳戶、密碼、商戶號以及一些操做指引的說明。服務端生成預付訂單的簽名須要密鑰 設置方法能夠參考這裏
這裏主要聊聊Android微信支付,主要包括如下幾個步驟
一、商戶服務端生成訂單並在微信平臺生成預付訂單
二、客戶端調起微信支付進行支付
三、客戶端回調支付結果
四、服務端接收支付通知
調起微信支付前須要服務器生成支付訂單再調用【統一下單API】生成預付訂單prepayId,再生成簽名sign【調起支付API】
以上兩個步驟建議都在服務端完成,客戶端(Android)經過接口獲取對應的參數便可
經過微信提供的jar 喚起微信支付
參照微信SDK Sample,在net.sourceforge.simcpux.wxapi包路徑中實現WXPayEntryActivity類【包名或類名不一致會形成沒法回調】
栗子說明:認真反覆讀了幾遍,感受這句話有歧義是一個坑,測試的時候一直不回調。這裏他想說的意識以下:
好比你申請應用包名爲:javen.com
那麼回調的WXPayEntryActivity
類必須放到javen.com.wxapi
的包下面
支付結果通知【官方文檔】
代碼實現參考開源項目 【點擊這裏】
此項目已開源 【點擊這裏】 若是對你有幫助請點擊
Start
告訴我 hahaha 。如下代碼對應的目錄在com.javen.weixin.controller.WeixinPayController.java
中
/** * 微信APP支付 */ public void appPay(){ //不用設置受權目錄域名 //統一下單地址 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1# Map<String, String> params = new HashMap<String, String>(); params.put("appid", appid); params.put("mch_id", partner); params.put("nonce_str", System.currentTimeMillis() / 1000 + ""); params.put("body", "Javen微信公衆號極速開發"); String out_trade_no=System.currentTimeMillis()+""; params.put("attach", "custom json"); params.put("out_trade_no", out_trade_no); int price=10000; params.put("total_fee", price+""); String ip = IpKit.getRealIp(getRequest()); if (StrKit.isBlank(ip)) { ip = "127.0.0.1"; } params.put("spbill_create_ip", ip); params.put("notify_url", notify_url); params.put("trade_type", "APP"); String sign = PaymentKit.createSign(params, paternerKey); params.put("sign", sign); String xmlResult = PaymentApi.pushOrder(params); System.out.println(xmlResult); Map<String, String> result = PaymentKit.xmlToMap(xmlResult); String return_code = result.get("return_code"); String return_msg = result.get("return_msg"); if (StrKit.isBlank(return_code) || !"SUCCESS".equals(return_code)) { ajax.addError(return_msg); renderJson(ajax); return; } String result_code = result.get("result_code"); if (StrKit.isBlank(result_code) || !"SUCCESS".equals(result_code)) { ajax.addError(return_msg); renderJson(ajax); return; } // 如下字段在return_code 和result_code都爲SUCCESS的時候有返回 String prepay_id = result.get("prepay_id"); //封裝調起微信支付的參數 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12 Map<String, String> packageParams = new HashMap<String, String>(); packageParams.put("appid", appid); packageParams.put("partnerid", partner); packageParams.put("prepayid", prepay_id); packageParams.put("package", "Sign=WXPay"); packageParams.put("noncestr", System.currentTimeMillis() + ""); packageParams.put("timestamp", System.currentTimeMillis() / 1000 + ""); String packageSign = PaymentKit.createSign(packageParams, paternerKey); packageParams.put("sign", packageSign); String jsonStr = JsonUtils.toJson(packageParams); System.out.println("最新返回apk的參數:"+jsonStr); renderJson(jsonStr); }
使用單例模式統一入口,首先判斷微信客戶端是否安裝,若是有安裝再從商戶服務器獲取調起支付的參數
public class IPay { private static IPay mIPay; private Context mContext; private IPay(Context context) { mContext = context; } public static IPay getIntance(Context context){ if (mIPay == null) { synchronized(IPay.class){ if (mIPay == null) { mIPay = new IPay(context); } } } return mIPay; } //支付結果回調 public interface IPayListener{ void onPay(int code); } public void toTestPay(Order order,IPayListener listener){ if (order != null) { if (IPayLogic.getIntance(mContext.getApplicationContext()).isWeixinAvilible()) { Constants.payListener = listener; new TestPayPrepay(mContext).execute(); }else { Toast.makeText(mContext, "未安裝微信", Toast.LENGTH_LONG).show(); } }else { Toast.makeText(mContext, "參數異常 order is null", Toast.LENGTH_LONG).show(); } } }
調起微信支付、獲取調取微信支付參數、判斷微信是否安裝邏輯實現
public class IPayLogic { private static IPayLogic mIPayLogic; private Context mContext; private IPayLogic(Context context) { mContext = context; } public static IPayLogic getIntance(Context context){ if (mIPayLogic == null) { synchronized(IPayLogic.class){ if (mIPayLogic == null) { mIPayLogic = new IPayLogic(context); } } } return mIPayLogic; } //測試 public String testPay(){ return HttpKit.get(Constants.TESTPAY_URL); } /** * 調起支付 * @param appId * @param partnerId * @param prepayId * @param nonceStr * @param timeStamp * @param sign */ public void startWXPay(String appId,String partnerId,String prepayId, String nonceStr,String timeStamp,String sign){ IWXAPI api= WXAPIFactory.createWXAPI(mContext, null); api.registerApp(appId); boolean isPaySupported = api.getWXAppSupportAPI() >= Build.PAY_SUPPORTED_SDK_INT; if (!isPaySupported) { Toast.makeText(mContext, "請更新微信客戶端", Toast.LENGTH_SHORT).show(); return; } PayReq request = new PayReq(); request.appId = appId; request.partnerId = partnerId; request.prepayId= prepayId; request.packageValue = "Sign=WXPay"; request.nonceStr=nonceStr; request.timeStamp= timeStamp; request.sign= sign; api.sendReq(request); } /** * 判斷微信是否安裝 * @param context * @return */ public boolean isWeixinAvilible() { final PackageManager packageManager = mContext.getPackageManager();// 獲取packagemanager List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);// 獲取全部已安裝程序的包信息 if (pinfo != null) { for (int i = 0; i < pinfo.size(); i++) { String pn = pinfo.get(i).packageName; if (pn.equals("com.tencent.mm")) { return true; } } } return false; } }
HttpKit MD5 工具類
/** * HttpKit */ public class HttpKit { private HttpKit() {} /** * https 域名校驗 */ private class TrustAnyHostnameVerifier implements HostnameVerifier { public boolean verify(String hostname, SSLSession session) { return true; } } /** * https 證書管理 */ private class TrustAnyTrustManager implements X509TrustManager { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } } private static final String GET = "GET"; private static final String POST = "POST"; private static String CHARSET = "UTF-8"; private static final SSLSocketFactory sslSocketFactory = initSSLSocketFactory(); private static final TrustAnyHostnameVerifier trustAnyHostnameVerifier = new HttpKit().new TrustAnyHostnameVerifier(); private static SSLSocketFactory initSSLSocketFactory() { try { TrustManager[] tm = {new HttpKit().new TrustAnyTrustManager() }; SSLContext sslContext = SSLContext.getInstance("TLS"); // ("TLS", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); return sslContext.getSocketFactory(); } catch (Exception e) { throw new RuntimeException(e); } } public static void setCharSet(String charSet) { if (charSet.isEmpty()) { throw new IllegalArgumentException("charSet can not be blank."); } HttpKit.CHARSET = charSet; } private static HttpURLConnection getHttpConnection(String url, String method, Map<String, String> headers) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException { URL _url = new URL(url); HttpURLConnection conn = (HttpURLConnection)_url.openConnection(); if (conn instanceof HttpsURLConnection) { ((HttpsURLConnection)conn).setSSLSocketFactory(sslSocketFactory); ((HttpsURLConnection)conn).setHostnameVerifier(trustAnyHostnameVerifier); } conn.setRequestMethod(method); conn.setDoOutput(true); conn.setDoInput(true); conn.setConnectTimeout(19000); conn.setReadTimeout(19000); conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36"); if (headers != null && !headers.isEmpty()) for (Entry<String, String> entry : headers.entrySet()) conn.setRequestProperty(entry.getKey(), entry.getValue()); return conn; } /** * Send GET request */ public static String get(String url, Map<String, String> queryParas, Map<String, String> headers) { HttpURLConnection conn = null; try { conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), GET, headers); conn.connect(); return readResponseString(conn); } catch (Exception e) { throw new RuntimeException(e); } finally { if (conn != null) { conn.disconnect(); } } } public static String get(String url, Map<String, String> queryParas) { return get(url, queryParas, null); } public static String get(String url) { return get(url, null, null); } /** * Send POST request */ public static String post(String url, Map<String, String> queryParas, String data, Map<String, String> headers) { HttpURLConnection conn = null; try { conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), POST, headers); conn.connect(); OutputStream out = conn.getOutputStream(); out.write(data.getBytes(CHARSET)); out.flush(); out.close(); return readResponseString(conn); } catch (Exception e) { throw new RuntimeException(e); } finally { if (conn != null) { conn.disconnect(); } } } public static String post(String url, Map<String, String> queryParas, String data) { return post(url, queryParas, data, null); } public static String post(String url, String data, Map<String, String> headers) { return post(url, null, data, headers); } public static String post(String url, String data) { return post(url, null, data, null); } private static String readResponseString(HttpURLConnection conn) { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; try { inputStream = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, CHARSET)); String line = null; while ((line = reader.readLine()) != null){ sb.append(line).append("\n"); } return sb.toString(); } catch (Exception e) { throw new RuntimeException(e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { } } } } /** * Build queryString of the url */ private static String buildUrlWithQueryString(String url, Map<String, String> queryParas) { if (queryParas == null || queryParas.isEmpty()) return url; StringBuilder sb = new StringBuilder(url); boolean isFirst; if (url.indexOf("?") == -1) { isFirst = true; sb.append("?"); } else { isFirst = false; } for (Entry<String, String> entry : queryParas.entrySet()) { if (isFirst) isFirst = false; else sb.append("&"); String key = entry.getKey(); String value = entry.getValue(); if (!value.isEmpty()) try {value = URLEncoder.encode(value, CHARSET);} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);} sb.append(key).append("=").append(value); } return sb.toString(); } }
public class MD5 { public static String MD5sign(String s) { char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; try { byte[] btInput = s.getBytes("UTF-8"); // 得到MD5摘要算法的 MessageDigest 對象 MessageDigest mdInst = MessageDigest.getInstance("MD5"); // 使用指定的字節更新摘要 mdInst.update(btInput); // 得到密文 byte[] md = mdInst.digest(); // 把密文轉換成十六進制的字符串形式 int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str).toLowerCase(); } catch (Exception e) { e.printStackTrace(); return null; } } public static void main(String[] args) { System.out.println(MD5sign("Hello world")); } }
使用
AsyncTask
異步獲取調起微信支付的相關參數。固然你也能夠使用其餘的異步網絡請求開源框架
public class TestPayPrepay extends AsyncTask<Object, Integer, String> { private Context mContext; public TestPayPrepay(Context context) { this.mContext = context; } @Override protected String doInBackground(Object... params) { System.out.println("TestPayPrepay doInBackground"); return IPayLogic.getIntance(mContext).testPay(); } @Override protected void onPostExecute(String result) { try { if (result!=null) { System.out.println("TestPayPrepay result>"+result); JSONObject data = new JSONObject(result); if(!data.has("code")){ String sign = data.getString("sign"); String timestamp = data.getString("timestamp"); String noncestr = data.getString("noncestr"); String partnerid = data.getString("partnerid"); String prepayid = data.getString("prepayid"); String appid = data.getString("appid"); Toast.makeText(mContext, "正在調起支付", Toast.LENGTH_SHORT).show(); Constants.APP_ID = appid; IPayLogic.getIntance(mContext).startWXPay(appid, partnerid, prepayid, noncestr, timestamp, sign); }else{ String message = data.getString("message"); Log.d("PAY_GET", "返回錯誤"+message); Toast.makeText(mContext, "返回錯誤:"+message, Toast.LENGTH_SHORT).show(); } }else { System.out.println("get prepayid exception, is null"); } } catch (Exception e) { Log.e("PAY_GET", "異常:"+e.getMessage()); Toast.makeText(mContext, "異常:"+e.getMessage(), Toast.LENGTH_SHORT).show(); } super.onPostExecute(result); } }
支付結果回調
<activity android:name="[應用的包名].wxapi.WXPayEntryActivity" android:exported="true" android:theme="@android:style/Theme.Translucent" android:launchMode="singleTop" > </activity>
封裝的是SDK 因此這裏設置了一個透明的主題
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler{ private IWXAPI api; private IPayListener payListener; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE);//隱藏標題 LinearLayout ll = new LinearLayout(this); setContentView(ll); api = WXAPIFactory.createWXAPI(this, Constants.APP_ID); api.handleIntent(getIntent(), this); finish(); } @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) { payListener = Constants.payListener; int code = resp.errCode; System.out.println("onResp errCode>"+code); if (payListener!=null) { payListener.onPay(code); System.out.println("payListener callback"); }else { System.out.println("payListener not callback"); } } }
注意:若是回調的
code
一直返回-1
一、請檢查應用包名以及apk 的簽名是否與你提交到微信開放平臺的一致
二、請檢查返回調取微信支付的參數是否正確
大部分緣由是第一種
遺留問題:因爲支付應用的包名不固定
WXPayEntryActivity
沒法封裝到jar中,須要單獨在支付應用添加.wxapi
這個包名並複製WXPayEntryActivity
到此包中。若是有好的解決方案歡迎留言
微信開發系列文章 http://www.jianshu.com/p/a172a1b69fdd
推薦閱讀
極速開發微信公衆號之微信買單
極速開發微信公衆號之公衆號支付
極速開發微信公衆號之掃碼支付
極速開發微信公衆號之刷卡支付
極速開發微信公衆號之現金紅包
極速開發微信公衆號之模板消息
若是此文章對你有幫助請點擊喜歡告訴我
服務端源碼地址:http://git.oschina.net/javen205/weixin_guide 客戶端源碼地址:https://github.com/Javen205/HelloAndroid