Android app第三方支付寶支付接入教程
支付寶的接入相對(duì)比較簡(jiǎn)單,看看支付寶官網(wǎng)的文檔基本都能搞定,但是切記一點(diǎn)讓你們的后臺(tái)也要搞清楚支付寶的流程,重中之重。
1、注意事項(xiàng)
開發(fā)前一定要閱讀支付寶官方文檔
強(qiáng)烈建議簽名等處理在后臺(tái)處理,我這個(gè)是測(cè)試是在自己本地寫的,不要吐槽
想獲取支付寶合作商戶ID,及支付寶公鑰請(qǐng)點(diǎn)擊支付寶鏈接,生成密鑰及PKCS8轉(zhuǎn)碼工具在文檔中
添加Android.permission.INTERNET權(quán)限和android.permission.ACCESS_NETWORK_STATE權(quán)限
要導(dǎo)入支付寶的包
2、特別注意事項(xiàng)(坑點(diǎn))
下載支付寶的官網(wǎng)之后生成你所需要的密鑰及PKCS8轉(zhuǎn)碼一定要小心
代碼:
MainActivity中調(diào)起支付:Pay pay = new Pay(AffirmOrderActivity.this, paymoney);
Pay 類
public class Pay { /**以下四項(xiàng)這些數(shù)值機(jī)密填入自己申請(qǐng)的就好**/ // 商戶PID public static final String PARTNER = ""; // 商戶收款賬號(hào) public static final String SELLER = ""; // 商戶私鑰,pkcs8格式 public static final String RSA_PRIVATE = ""; // 支付寶公鑰 public static final String RSA_PUBLIC = ""; private Activity context; private String mgoods,mprice,micon; private int mtype; private String mOutTradeNo; public Pay(Activity activity, String goods,String goodsmore,String price,String icon,int type, String outTradeNo) { context = activity; mprice = price; pay(price); } private Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case SDK_PAY_FLAG: { PayResult payResult = new PayResult((String) msg.obj); /** * 同步返回的結(jié)果必須放置到服務(wù)端進(jìn)行驗(yàn)證(驗(yàn)證的規(guī)則請(qǐng)看https://doc.open.alipay.com/doc2/ * detail.htm?spm=0.0.0.0.xdvAU6&treeId=59&articleId=103665& * docType=1) 建議商戶依賴異步通知 */ String resultInfo = payResult.getResult();// 同步返回需要驗(yàn)證的信息 String resultStatus = payResult.getResultStatus(); // 判斷resultStatus 為“9000”則代表支付成功,具體狀態(tài)碼代表含義可參考接口文檔 if (TextUtils.equals(resultStatus, "9000")) { Toast.makeText(context, "支付成功", Toast.LENGTH_SHORT).show(); } else { // 判斷resultStatus 為非"9000"則代表可能支付失敗 // "8000"代表支付結(jié)果因?yàn)橹Ц肚涝蚧蛘呦到y(tǒng)原因還在等待支付結(jié)果確認(rèn),最終交易是否成功以服務(wù)端異步通知為準(zhǔn)(小概率狀態(tài)) if (TextUtils.equals(resultStatus, "8000")) { Toast.makeText(context, "支付結(jié)果確認(rèn)中", Toast.LENGTH_SHORT).show(); } else { // 其他值就可以判斷為支付失敗,包括用戶主動(dòng)取消支付,或者系統(tǒng)返回的錯(cuò)誤 Toast.makeText(context, "支付失敗", Toast.LENGTH_SHORT).show(); } } break; } default: break; } }; }; /** * call alipay sdk pay. 調(diào)用SDK支付 * */ public void pay(String goods,String goodsmore,String price) { if (TextUtils.isEmpty(PARTNER) || TextUtils.isEmpty(RSA_PRIVATE) || TextUtils.isEmpty(SELLER)) { new AlertDialog.Builder(context).setTitle("警告").setMessage("需要配置PARTNER | RSA_PRIVATE| SELLER") .setPositiveButton("確定", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialoginterface, int i) { // // finish(); } }).show(); return; } String orderInfo = getOrderInfo(goods, goodsmore, price); String sign = sign(orderInfo); try { /** * 僅需對(duì)sign 做URL編碼 */ sign = URLEncoder.encode(sign, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } /** * 完整的符合支付寶參數(shù)規(guī)范的訂單信息 */ final String payInfo = orderInfo + "&sign=\"" + sign + "\"&" + getSignType(); Log.i("lff", "orderInfo == " +orderInfo); Log.i("lff", "sign == " +sign); Runnable payRunnable = new Runnable() { @Override public void run() { // 構(gòu)造PayTask 對(duì)象 PayTask alipay = new PayTask(context); // 調(diào)用支付接口,獲取支付結(jié)果 String result = alipay.pay(payInfo, true); Message msg = new Message(); msg.what = SDK_PAY_FLAG; msg.obj = result; mHandler.sendMessage(msg); } }; // 必須異步調(diào)用 Thread payThread = new Thread(payRunnable); payThread.start(); } /** * get the sdk version. 獲取SDK版本號(hào) * */ public void getSDKVersion() { PayTask payTask = new PayTask(context); String version = payTask.getVersion(); Toast.makeText(context, version, Toast.LENGTH_SHORT).show(); } /** * create the order info. 創(chuàng)建訂單信息 * */ private String getOrderInfo(String subject, String body, String price) { // 簽約合作者身份ID String orderInfo = "partner=" + "\"" + PARTNER + "\""; // 簽約賣家支付寶賬號(hào) orderInfo += "&seller_id=" + "\"" + SELLER + "\""; // 商戶網(wǎng)站唯一訂單號(hào) orderInfo += "&out_trade_no=" + "\"" + getOutTradeNo() + "\""; // 商品名稱 orderInfo += "&subject=" + "\"" + subject + "\""; // 商品詳情 orderInfo += "&body=" + "\"" + body + "\""; // 商品金額 orderInfo += "&total_fee=" + "\"" + "0.01" + "\""; // 服務(wù)器異步通知頁面路徑 // orderInfo += "¬ify_url=" + "\"" + "http://notify.msp.hk/notify.htm" + "\""; orderInfo += "¬ify_url=" + "\"" + "http://112.74.129.252/new/zfbCallback" + "\""; // 服務(wù)接口名稱, 固定值 orderInfo += "&service=\"mobile.securitypay.pay\""; // 支付類型, 固定值 orderInfo += "&payment_type=\"1\""; // 參數(shù)編碼, 固定值 orderInfo += "&_input_charset=\"utf-8\""; // 設(shè)置未付款交易的超時(shí)時(shí)間 // 默認(rèn)30分鐘,一旦超時(shí),該筆交易就會(huì)自動(dòng)被關(guān)閉。 // 取值范圍:1m~15d。 // m-分鐘,h-小時(shí),d-天,1c-當(dāng)天(無論交易何時(shí)創(chuàng)建,都在0點(diǎn)關(guān)閉)。 // 該參數(shù)數(shù)值不接受小數(shù)點(diǎn),如1.5h,可轉(zhuǎn)換為90m。 orderInfo += "&it_b_pay=\"30m\""; // extern_token為經(jīng)過快登授權(quán)獲取到的alipay_open_id,帶上此參數(shù)用戶將使用授權(quán)的賬戶進(jìn)行支付 // orderInfo += "&extern_token=" + "\"" + extern_token + "\""; // 支付寶處理完請(qǐng)求后,當(dāng)前頁面跳轉(zhuǎn)到商戶指定頁面的路徑,可空 orderInfo += "&return_url=\"m.alipay.com\""; // 調(diào)用銀行卡支付,需配置此參數(shù),參與簽名, 固定值 (需要簽約《無線銀行卡快捷支付》才能使用) // orderInfo += "&paymethod=\"expressGateway\""; return orderInfo; } /** * get the out_trade_no for an order. 生成商戶訂單號(hào),該值在商戶端應(yīng)保持唯一(可自定義格式規(guī)范) * */ private String getOutTradeNo() { // SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault()); // Date date = new Date(); // String key = format.format(date); // // Random r = new Random(); // key = key + r.nextInt(); // key = key.substring(0, 15); // return key; return mOutTradeNo; } /** * sign the order info. 對(duì)訂單信息進(jìn)行簽名 * * @param content * 待簽名訂單信息 */ private String sign(String content) { return SignUtils.sign(content, RSA_PRIVATE); } /** * get the sign type we use. 獲取簽名方式 * */ private String getSignType() { return "sign_type=\"RSA\""; } }
其它的工具類,原封不動(dòng)拷貝過去就好
Base64 類
package com.easyhomework.teacher.ui; public 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; } }
總結(jié)一下支付寶支付步驟:1、生成秘鑰私鑰等(一般是后臺(tái)處理的) ;2、支付寶包復(fù)制帶項(xiàng)目中 ;3、調(diào)用支付(上述MainActivity和pay類) 4、添加工具類
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家學(xué)習(xí)Android軟件編程有所幫助。
相關(guān)文章
Android App開發(fā)中自定義View和ViewGroup的實(shí)例教程
這篇文章主要介紹了Android App開發(fā)中自定義View和ViewGroup的實(shí)例教程,分別介紹了進(jìn)度條和圖片上傳并排列的例子,效果很好很強(qiáng)大,需要的朋友可以參考下2016-05-05Android中AnimationDrawable使用的簡(jiǎn)單實(shí)例
這篇文章介紹了Android中AnimationDrawable使用的簡(jiǎn)單實(shí)例,有需要的朋友可以參考一下2013-10-10Android實(shí)現(xiàn)屏幕各尺寸的獲取的示例
本篇文章主要介紹了Android實(shí)現(xiàn)屏幕各尺寸的獲取的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09Android 解決嵌套Fragment無法接收onCreateOptionsMenu事件的問題
本文主要介紹Android Fragment無法接收onCreateOptionsMenu事件的問題,這里給出解決辦法以及詳細(xì)代碼,希望能幫助有需要的小伙伴2016-07-07Kotlin協(xié)程開發(fā)之Flow的融合與Channel容量及溢出策略介紹
這篇文章主要介紹了Kotlin協(xié)程:Flow的融合、Channel容量、溢出策略,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-09-09Android自定義控件ViewFipper實(shí)現(xiàn)豎直跑馬燈效果
這篇文章主要為大家詳細(xì)介紹了Android自定義控件ViewFipper實(shí)現(xiàn)豎直跑馬燈效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Android使用Scrolling機(jī)制實(shí)現(xiàn)Tab吸頂效果
app 首頁中經(jīng)常要實(shí)現(xiàn)首頁頭卡共享,tab 吸頂,內(nèi)容區(qū)通過 ViewPager 切換的需求,以前往往是利用事件處理來完成,但是這些也有一定的弊端和滑動(dòng)方面不如意的地方,本文我們利用NestedScrolling機(jī)制來實(shí)現(xiàn),感興趣的朋友可以參考下2024-01-01