Android封裝支付寶支付
在做Android支付的時候肯定會用到支付寶支付, 根據官方給出的demo做起來非常費勁,所以我們需要一次簡單的封裝。 封裝的代碼也很簡單,就是將官網給的demo提取出一個類來方便使用。
/** * 支付寶支付 * * @author lenovo */public class Alipay { // 商戶PID public static final String PARTNER = '123456789'; // 商戶收款賬號 public static final String SELLER = 'qibin0506@gmail.com'; // 商戶私鑰,pkcs8格式 public static final String RSA_PRIVATE = 'rsa_private'; // 支付寶公鑰 public static final String RSA_PUBLIC = 'rsa_public'; private static final int SDK_PAY_FLAG = 1; private WeakReference<Activity> mActivity; private OnAlipayListener mListener; public Alipay(Activity activity) {mActivity = new WeakReference<Activity>(activity); } @SuppressLint('HandlerLeak') private Handler mHandler = new Handler() {public void handleMessage(Message msg) { if (msg.what == SDK_PAY_FLAG) {PayResult payResult = new PayResult((String) msg.obj);// 支付寶返回此次支付結果及加簽,建議對支付寶簽名信息拿簽約時支付寶提供的公鑰做驗簽String resultInfo = payResult.getResult();String resultStatus = payResult.getResultStatus();// 判斷resultStatus 為“9000”則代表支付成功,具體狀態碼代表含義可參考接口文檔if (TextUtils.equals(resultStatus, '9000')) { if (mListener != null) mListener.onSuccess();} else { // 判斷resultStatus 為非“9000”則代表可能支付失敗 // “8000”代表支付結果因為支付渠道原因或者系統原因還在等待支付結果確認, // 最終交易是否成功以服務端異步通知為準(小概率狀態) if (TextUtils.equals(resultStatus, '8000')) {if (mListener != null) mListener.onWait(); } else {// 其他值就可以判斷為支付失敗,包括用戶主動取消支付,或者系統返回的錯誤if (mListener != null) mListener.onCancel(); }} }} }; /** * 支付 * * @param title 標題 不能為空或者“” * @param desc 描述 不能為空或者“” * @param price 價格 不能為空或者“” * @param sn 商品唯一貨號 不能為空或者“” * @param url 服務器回調url 不能為空或者“” */ public void pay(String title, String desc, String price, String sn, String url) {// 訂單String orderInfo = getOrderInfo(title, desc, price, sn, url);// 對訂單做RSA 簽名String sign = sign(orderInfo);try { // 僅需對sign 做URL編碼 sign = URLEncoder.encode(sign, 'UTF-8');} catch (UnsupportedEncodingException e) { e.printStackTrace();}// 完整的符合支付寶參數規范的訂單信息final String payInfo = orderInfo + '&sign='' + sign + ''&'+ getSignType();Runnable payRunnable = new Runnable() { @Override public void run() {Activity activity = mActivity.get();if(activity == null) return;// 構造PayTask 對象PayTask alipay = new PayTask(activity);// 調用支付接口,獲取支付結果String result = alipay.pay(payInfo);Message msg = new Message();msg.what = SDK_PAY_FLAG;msg.obj = result;mHandler.sendMessage(msg); }};// 必須異步調用Thread payThread = new Thread(payRunnable);payThread.start(); } /** * create the order info. 創建訂單信息 * */ public String getOrderInfo(String subject, String body, String price, String sn, String url) {// 簽約合作者身份IDString orderInfo = 'partner=' + ''' + PARTNER + ''';// 簽約賣家支付寶賬號orderInfo += '&seller_id=' + ''' + SELLER + ''';// 商戶網站唯一訂單號orderInfo += '&out_trade_no=' + ''' + sn + ''';// 商品名稱orderInfo += '&subject=' + ''' + subject + ''';// 商品詳情orderInfo += '&body=' + ''' + body + ''';// 商品金額orderInfo += '&total_fee=' + ''' + price + ''';// 服務器異步通知頁面路徑orderInfo += '¬ify_url=' + ''' + url + ''';// 服務接口名稱, 固定值orderInfo += '&service='mobile.securitypay.pay'';// 支付類型, 固定值orderInfo += '&payment_type='1'';// 參數編碼, 固定值orderInfo += '&_input_charset='utf-8'';// 設置未付款交易的超時時間// 默認30分鐘,一旦超時,該筆交易就會自動被關閉。// 取值范圍:1m~15d。// m-分鐘,h-小時,d-天,1c-當天(無論交易何時創建,都在0點關閉)。// 該參數數值不接受小數點,如1.5h,可轉換為90m。orderInfo += '&it_b_pay='30m'';// extern_token為經過快登授權獲取到的alipay_open_id,帶上此參數用戶將使用授權的賬戶進行支付// orderInfo += '&extern_token=' + ''' + extern_token + ''';// 支付寶處理完請求后,當前頁面跳轉到商戶指定頁面的路徑,可空orderInfo += '&return_url='m.alipay.com'';// 調用銀行卡支付,需配置此參數,參與簽名, 固定值 (需要簽約《無線銀行卡快捷支付》才能使用)// orderInfo += '&paymethod='expressGateway'';return orderInfo; } /** * sign the order info. 對訂單信息進行簽名 * * @param content * 待簽名訂單信息 */ public String sign(String content) {return SignUtils.sign(content, RSA_PRIVATE); } /** * get the sign type we use. 獲取簽名方式 * */ public String getSignType() {return 'sign_type='RSA''; } public void setListener(OnAlipayListener l) {mListener = l; } /** * 支付回調接口 * * @author lenovo * */ public static class OnAlipayListener {/** * 支付成功 */public void onSuccess() {}/** * 支付取消 */public void onCancel() {}/** * 等待確認 */public void onWait() {} }}final class Base64 { private static final int BASELENGTH = 128; private static final int LOOKUPLENGTH = 64; private static final int TWENTYFOURBITGROUP = 24; private static final int EIGHTBIT = 8; private static final int SIXTEENBIT = 16; private static final int FOURBYTE = 4; private static final int SIGN = -128; private static char PAD = ’=’; private static byte[] base64Alphabet = new byte[BASELENGTH]; private static char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; static {for (int i = 0; i < BASELENGTH; ++i) { base64Alphabet[i] = -1;}for (int i = ’Z’; i >= ’A’; i--) { base64Alphabet[i] = (byte) (i - ’A’);}for (int i = ’z’; i >= ’a’; i--) { base64Alphabet[i] = (byte) (i - ’a’ + 26);}for (int i = ’9’; i >= ’0’; i--) { base64Alphabet[i] = (byte) (i - ’0’ + 52);}base64Alphabet[’+’] = 62;base64Alphabet[’/’] = 63;for (int i = 0; i <= 25; i++) { lookUpBase64Alphabet[i] = (char) (’A’ + i);}for (int i = 26, j = 0; i <= 51; i++, j++) { lookUpBase64Alphabet[i] = (char) (’a’ + j);}for (int i = 52, j = 0; i <= 61; i++, j++) { lookUpBase64Alphabet[i] = (char) (’0’ + j);}lookUpBase64Alphabet[62] = (char) ’+’;lookUpBase64Alphabet[63] = (char) ’/’; } private static boolean isWhiteSpace(char octect) {return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); } private static boolean isPad(char octect) {return (octect == PAD); } private static boolean isData(char octect) {return (octect < BASELENGTH && base64Alphabet[octect] != -1); } /** * Encodes hex octects into Base64 * * @param binaryData * Array containing binaryData * @return Encoded Base64 array */ public static String encode(byte[] binaryData) {if (binaryData == null) { return null;}int lengthDataBits = binaryData.length * EIGHTBIT;if (lengthDataBits == 0) { return '';}int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1: numberTriplets;char encodedData[] = null;encodedData = new char[numberQuartet * 4];byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;int encodedIndex = 0;int dataIndex = 0;for (int i = 0; i < numberTriplets; i++) { b1 = binaryData[dataIndex++]; b2 = binaryData[dataIndex++]; b3 = binaryData[dataIndex++]; l = (byte) (b2 & 0x0f); k = (byte) (b1 & 0x03); byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];}// form integral number of 6-bit groupsif (fewerThan24bits == EIGHTBIT) { b1 = binaryData[dataIndex]; k = (byte) (b1 & 0x03); byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; encodedData[encodedIndex++] = PAD; encodedData[encodedIndex++] = PAD;} else if (fewerThan24bits == SIXTEENBIT) { b1 = binaryData[dataIndex]; b2 = binaryData[dataIndex + 1]; l = (byte) (b2 & 0x0f); k = (byte) (b1 & 0x03); byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; encodedData[encodedIndex++] = PAD;}return new String(encodedData); } /** * Decodes Base64 data into octects * * @param encoded * string containing Base64 data * @return Array containind decoded data. */ public static byte[] decode(String encoded) {if (encoded == null) { return null;}char[] base64Data = encoded.toCharArray();// remove white spacesint len = removeWhiteSpace(base64Data);if (len % FOURBYTE != 0) { return null;// should be divisible by four}int numberQuadruple = (len / FOURBYTE);if (numberQuadruple == 0) { return new byte[0];}byte decodedData[] = null;byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;char d1 = 0, d2 = 0, d3 = 0, d4 = 0;int i = 0;int encodedIndex = 0;int dataIndex = 0;decodedData = new byte[(numberQuadruple) * 3];for (; i < numberQuadruple - 1; i++) { if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) {return null; }// if found 'no data' just return null b1 = base64Alphabet[d1]; b2 = base64Alphabet[d2]; b3 = base64Alphabet[d3]; b4 = base64Alphabet[d4]; decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);}if (!isData((d1 = base64Data[dataIndex++]))|| !isData((d2 = base64Data[dataIndex++]))) { return null;// if found 'no data' just return null}b1 = base64Alphabet[d1];b2 = base64Alphabet[d2];d3 = base64Data[dataIndex++];d4 = base64Data[dataIndex++];if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters if (isPad(d3) && isPad(d4)) {if ((b2 & 0xf) != 0)// last 4 bits should be zero{ return null;}byte[] tmp = new byte[i * 3 + 1];System.arraycopy(decodedData, 0, tmp, 0, i * 3);tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);return tmp; } else if (!isPad(d3) && isPad(d4)) {b3 = base64Alphabet[d3];if ((b3 & 0x3) != 0)// last 2 bits should be zero{ return null;}byte[] tmp = new byte[i * 3 + 2];System.arraycopy(decodedData, 0, tmp, 0, i * 3);tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));return tmp; } else {return null; }} else { // No PAD e.g 3cQl b3 = base64Alphabet[d3]; b4 = base64Alphabet[d4]; decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);}return decodedData; } /** * remove WhiteSpace from MIME containing encoded Base64 data. * * @param data * the byte array of base64 data (with WS) * @return the new length */ private static int removeWhiteSpace(char[] data) {if (data == null) { return 0;}// count characters that’s not whitespaceint newSize = 0;int len = data.length;for (int i = 0; i < len; i++) { if (!isWhiteSpace(data[i])) {data[newSize++] = data[i]; }}return newSize; }}class PayResult { private String resultStatus; private String result; private String memo; public PayResult(String rawResult) {if (TextUtils.isEmpty(rawResult)) return;String[] resultParams = rawResult.split(';');for (String resultParam : resultParams) { if (resultParam.startsWith('resultStatus')) {resultStatus = gatValue(resultParam, 'resultStatus'); } if (resultParam.startsWith('result')) {result = gatValue(resultParam, 'result'); } if (resultParam.startsWith('memo')) {memo = gatValue(resultParam, 'memo'); }} } @Override public String toString() {return 'resultStatus={' + resultStatus + '};memo={' + memo+ '};result={' + result + '}'; } private String gatValue(String content, String key) {String prefix = key + '={';return content.substring(content.indexOf(prefix) + prefix.length(),content.lastIndexOf('}')); } /** * @return the resultStatus */ public String getResultStatus() {return resultStatus; } /** * @return the memo */ public String getMemo() {return memo; } /** * @return the result */ public String getResult() {return result; }}class SignUtils { private static final String ALGORITHM = 'RSA'; private static final String SIGN_ALGORITHMS = 'SHA1WithRSA'; private static final String DEFAULT_CHARSET = 'UTF-8'; public static String sign(String content, String privateKey) {try { PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec( Base64.decode(privateKey)); KeyFactory keyf = KeyFactory.getInstance(ALGORITHM); PrivateKey priKey = keyf.generatePrivate(priPKCS8); java.security.Signature signature = java.security.Signature .getInstance(SIGN_ALGORITHMS); signature.initSign(priKey); signature.update(content.getBytes(DEFAULT_CHARSET)); byte[] signed = signature.sign(); return Base64.encode(signed);} catch (Exception e) { e.printStackTrace();}return null; }}
前面的幾個常量是需要去支付寶官網獲取的,獲取后直接替換就ok, 其他的代碼基本都是從demo中copy出來的, 現在我們就將支付功能封裝到了一個類中,那么如何使用呢?
Alipay alipay = new Alipay(OrderConfirmActivity.this);alipay.setListener(mAlipayListener);alipay.pay(desc, mOrder.getShopName(), String.valueOf(orderAmount), orderSn, url); /** * 支付寶支付回調 */ private Alipay.OnAlipayListener mAlipayListener = new Alipay.OnAlipayListener() { @Override public void onSuccess() {onOrderSubmitSuccess(); } @Override public void onCancel() {onUserOrderCanceled(); Toast.makeText(OrderConfirmActivity.this, R.string.pay_failed, Toast.LENGTH_SHORT).show(); } @Override public void onWait() { }};
new出對象來,只需要調用pay方法就ok啦, 不過支付的回調我們還是必須的,當然這個也不麻煩。這里說一下pay方法的幾個參數,
title 支付的標題desc 支付的描述price 支付的金額sn 商品的唯一貨號url 服務器的回調url這幾個參數在做支付的時候服務器都會給到,但是要注意一下,這幾個參數都不能為空或者空字符串,否則會支付失敗。
相關文章: