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) { // 簽約合作者身份ID String 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 groups if (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 spaces int 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 whitespace int 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
這幾個引數在做支付的時候伺服器都會給到,但是要注意一下,這幾個引數都不能為空或者空字串,否則會支付失敗。
相關文章
- SpringMVC 實現支付寶支付功能(沙箱環境)SpringMVC
- Android 透過scheme跳轉支付寶實現支付AndroidScheme
- pc端實現支付寶支付
- 支付寶截圖反饋功能實現
- SSM 實現支付寶支付功能(圖文詳解+完整程式碼)SSM
- Android 接入微信支付寶支付Android
- android 整合微信支付和支付寶支付其實很簡單Android
- React Native 原生模組封裝:支付寶示例React Native封裝
- 支付寶支付
- 支付寶小程式日期選擇元件 datePicker 封裝元件封裝
- Laravel實現支付寶分賬Laravel
- 微信支付,支付寶支付
- 支付寶記賬功能怎麼用?支付寶記賬功能的使用方法
- 免SDK實現微信/支付寶轉賬打賞功能
- Laravel 整合 EasyAlipay 實現支付寶手機網站 Wap 支付Laravel網站
- java實現沙箱測試環境支付寶支付(demo)和整合微信支付和支付寶支付到springmvc+spring+mybatis環境全過程(支付寶和微信支付、附原始碼)JavaSpringMVCMyBatis原始碼
- 利用java實現提現金額到支付寶賬戶的功能Java
- 關於微信支付,支付寶支付
- Java 支付寶支付,退款,單筆轉賬到支付寶賬戶(支付寶訂單退款)Java
- Django呼叫支付寶支付介面Django
- Java接入支付寶支付教程Java
- 支付寶alipay移動支付
- 支付寶、微信支付(.NET)
- vue-仿支付寶支付Vue
- PHP-Laravel支付寶支付和微信支付PHPLaravel
- 支付寶微信合單支付
- 支付寶 v3 驗籤如何實現
- 微信,支付寶小程式實現原理概述
- 微信支付封裝成npm 包封裝NPM
- 支付寶支付產品介面文件地址和支付寶代金券理解
- java對接支付寶支付(手機網站支付)Java網站
- 支付寶 InfoStr
- CS支付寶
- Django對接支付寶Alipay支付介面Django
- 手機APP如何接入支付寶支付APP
- flutter 支付寶APP支付 (包含後臺)FlutterAPP
- go-zero之App支付寶支付GoAPP
- 支付寶裡如何使用網銀支付?
- iOS使用Stripe整合支付寶Alipay支付iOS