Android 集成支付寶支付詳解

一說到支付寶,相信沒有人不知道,生活中付款,轉帳都會用到。java

今天來詳細介紹下在Android中如何集成支付寶支付到本身的APP中去。讓APP可以擁有方便,快捷的支付功能。express

準備工做:macos

商戶在b.alipay.com裏進行產品簽約
RSA私鑰及公鑰生成
上傳RSA公鑰,簽名驗證
接口調用服務器

一.商戶在b.alipay.com裏進行產品簽約

 

商戶或者開發者到b.alipay.com進行產品簽約,獲取商戶的PID。app

 

 

 
二.RSA私鑰及公鑰生成
 
生成方式一(推薦):使用支付寶提供的一鍵生成工具:
 
Windwos: 點擊下載
MAC OSX: 點擊下載

解壓打開文件夾,直接運行「支付寶RAS密鑰生成器SHAwithRSA1024_V1.0.bat」(WINDOWS)或「SHAwithRSA1024_V1.0.command」(MACOSX),點擊「生成RSA密鑰」,會自動生成公私鑰,而後點擊「打開文件位置」,便可找到工具自動生成的密鑰。
 
 
生成方式二:也可使用OpenSSL工具命令生成
首先進入OpenSSL工具,再輸入如下命令。
 
[java]  view plain  copy
  1. <span style="font-size:12px">OpenSSL> genrsa -out rsa_private_key.pem   1024  #生成私鑰  
  2. OpenSSL> pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out rsa_private_key_pkcs8.pem #Java開發者須要將私鑰轉換成PKCS8格式  
  3. OpenSSL> rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem #生成公鑰  
  4. OpenSSL> exit #退出OpenSSL程序</span>  

通過以上步驟,開發者能夠在當前文件夾中(OpenSSL運行文件夾),看到rsa_private_key.pem(RSA私鑰)、rsa_private_key_pkcs8.pem(pkcs8格式RSA私鑰)和rsa_public_key.pem(對應RSA公鑰)3個文件。開發者將私鑰保留,將公鑰提交給支付寶網關,用於驗證簽名。如下爲私鑰文件和公鑰文件示例。

注意:對於使用Java的開發者,將pkcs8在console中輸出的私鑰去除頭尾、換行和空格,做爲開發者私鑰,對於.NET和PHP的開發者來講,無需進行pkcs8命令行操做。dom

PKCS8處理後的私鑰文件示例:異步

 

[java]  view plain  copy
  1. <span style="font-size:12px">-----BEGIN PRIVATE KEY-----  
  2. MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAN0yqPkLXlnhM+2H/57aHsYHaHXazr9pFQun907TMvmbR04wHChVsKVgGUF1hC0FN9hfeYT5v2SXg1WJSg2tSgk7F29SpsF0I36oSLCIszxdu7ClO7c22mxEVuCjmYpJdqb6XweAZzv4Is661jXP4PdrCTHRdVTU5zR9xUByiLSVAgMBAAECgYEAhznORRonHylm9oKaygEsqQGkYdBXbnsOS6busLi6xA+iovEUdbAVIrTCG9t854z2HAgaISoRUKyztJoOtJfI1wJaQU+XL+U3JIh4jmNx/k5UzJijfvfpT7Cv3ueMtqyAGBJrkLvXjiS7O5ylaCGuB0Qz711bWGkRrVoosPM3N6ECQQD8hVQUgnHEVHZYtvFqfcoq2g/onPbSqyjdrRu35a7PvgDAZx69Mr/XggGNTgT3jJn7+2XmiGkHM1fd1Ob/3uAdAkEA4D7aE3ZgXG/PQqlm3VbE/+4MvNl8xhjqOkByBOY2ZFfWKhlRziLEPSSAh16xEJ79WgY9iti+guLRAMravGrs2QJBAOmKWYeaWKNNxiIoF7/4VDgrcpkcSf3uRB44UjFSn8kLnWBUPo6WV+x1FQBdjqRviZ4NFGIP+KqrJnFHzNgJhVUCQFzCAukMDV4PLfeQJSmna8PFz2UKva8fvTutTryyEYu+PauaX5laDjyQbc4RIEMU0Q29CRX3BA8WDYg7YPGRdTkCQQCG+pjU2FB17ZLuKRlKEdtXNV6zQFTmFc1TKhlsDTtCkWs/xwkoCfZKstuV3Uc5J4BNJDkQOGm38pDRPcUDUh2/  
  3. -----END PRIVATE KEY-----</span>  

 

公鑰文件示例:ide

 

[java]  view plain  copy
  1. <span style="font-size:12px">-----BEGIN PUBLIC KEY-----  
  2. MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQWiDVZ7XYxa4CQsZoB3n7bfxLDkeGKjyQPt2FUtm4TWX9OYrd523iw6UUqnQ+Evfw88JgRnhyXadp+vnPKP7unormYQAfsM/CxzrfMoVdtwSiGtIJB4pfyRXjA+KL8nIa2hdQy5nLfgPVGZN4WidfUY/QpkddCVXnZ4bAUaQjXQIDAQAB  
  3. -----END PUBLIC KEY-----</span>  

 

三.上傳RSA公鑰,簽名驗證

登陸支付寶官方網站b.alipay.com,點擊導航欄中「個人商家服務」,點擊「查詢PID、Key」,在「合做夥伴密鑰管理」下(根據不一樣的產品選擇對應的入口),點擊「RSA加密」後的「添加密鑰」,把本身的公鑰複製進去工具

 

 

 
注意:上傳的公鑰是一行格式,不容許有註釋、空格、換行等!
點擊「確認上傳」,提示:上傳成功,說明已經成功上傳。
 

 

 
 
四.接口調用
首先,導入須要的支付寶SDK資源放入商戶應用工程的libs目錄下
 
 
右鍵 Build Path,將libs目錄下的alipaySDK-20150602.jar導入,選中Order and Export,勾選alipaySDK-20151014.jar
 
 
 
拷貝sdk提供的類到工程下:
 
 
這幾個類也很簡單,不須要作打的修改,只改動pay.java裏面的就行了
 
[java]  view plain  copy
  1. <span style="font-size:12px">// 商戶PID  
  2. public static final String PARTNER = "";  
  3. // 商戶收款帳號  
  4. public static final String SELLER = "";  
  5. // 商戶私鑰,pkcs8格式  
  6. public static final String RSA_PRIVATE = "";</span>  

添加上對應的參數,java私鑰必定要是pkcs8格式的。
 
 
[java]  view plain  copy
  1. <span style="font-size:12px">public class Pay {  
  2.     // 商戶PID  
  3.     public static final String PARTNER = "";  
  4.     // 商戶收款帳號  
  5.     public static final String SELLER = "";  
  6.     // 商戶私鑰,pkcs8格式  
  7.     public static final String RSA_PRIVATE = "";  
  8.     private static final int SDK_PAY_FLAG = 1;  
  9.     private Activity activity;  
  10.   
  11.     public Pay(Activity activity) {  
  12.         this.activity = activity;  
  13.     }  
  14.   
  15.     /** 
  16.      * call alipay sdk pay. 調用SDK支付 
  17.      *  
  18.      */  
  19.     public void pay(String name, String msg, String orderno, String money,  
  20.             final Handler handler) {  
  21.         // 訂單  
  22.         String orderInfo = getOrderInfo(name, msg, orderno, money);  
  23.         // 對訂單作RSA 簽名  
  24.         String sign = sign(orderInfo);  
  25.         try {  
  26.             // 僅需對sign 作URL編碼  
  27.             sign = URLEncoder.encode(sign, "UTF-8");  
  28.         } catch (UnsupportedEncodingException e) {  
  29.             e.printStackTrace();  
  30.         }  
  31.   
  32.         // 完整的符合支付寶參數規範的訂單信息  
  33.         final String payInfo = orderInfo + "&sign=\"" + sign + "\"&"  
  34.                 + getSignType();  
  35.   
  36.         Runnable payRunnable = new Runnable() {  
  37.   
  38.             @Override  
  39.             public void run() {  
  40.                 // 構造PayTask 對象  
  41.                 PayTask alipay = new PayTask(activity);  
  42.                 // 調用支付接口,獲取支付結果  
  43.                 String result = alipay.pay(payInfo);  
  44.   
  45.                 Message msg = new Message();  
  46.                 msg.what = SDK_PAY_FLAG;  
  47.                 msg.obj = result;  
  48.                 handler.sendMessage(msg);  
  49.             }  
  50.         };  
  51.   
  52.         // 必須異步調用  
  53.         Thread payThread = new Thread(payRunnable);  
  54.         payThread.start();  
  55.     }  
  56.   
  57.     /** 
  58.      * create the order info. 建立訂單信息 
  59.      *  
  60.      */  
  61.     public String getOrderInfo(String subject, String body, String orderno,  
  62.             String price) {  
  63.   
  64.         // 簽約合做者身份ID  
  65.         String orderInfo = "partner=" + "\"" + PARTNER + "\"";  
  66.   
  67.         // 簽約賣家支付寶帳號  
  68.         orderInfo += "&seller_id=" + "\"" + SELLER + "\"";  
  69.   
  70.         // 商戶網站惟一訂單號  
  71.         orderInfo += "&out_trade_no=" + "\"" + orderno + "\"";  
  72.   
  73.         // 商品名稱  
  74.         orderInfo += "&subject=" + "\"" + subject + "\"";  
  75.   
  76.         // 商品詳情  
  77.         orderInfo += "&body=" + "\"" + body + "\"";  
  78.   
  79.         // 商品金額  
  80.         orderInfo += "&total_fee=" + "\"" + price + "\"";  
  81.   
  82.         // 服務器異步通知頁面路徑  
  83.         orderInfo += "¬ify_url=" + "\"" + "notify_URL" + "\"";  
  84.   
  85.         // 服務接口名稱, 固定值  
  86.         orderInfo += "&service=\"mobile.securitypay.pay\"";  
  87.   
  88.         // 支付類型, 固定值  
  89.         orderInfo += "&payment_type=\"1\"";  
  90.   
  91.         // 參數編碼, 固定值  
  92.         orderInfo += "&_input_charset=\"utf-8\"";  
  93.   
  94.         // 設置未付款交易的超時時間  
  95.         // 默認30分鐘,一旦超時,該筆交易就會自動被關閉。  
  96.         // 取值範圍:1m~15d。  
  97.         // m-分鐘,h-小時,d-天,1c-當天(不管交易什麼時候建立,都在0點關閉)。  
  98.         // 該參數數值不接受小數點,如1.5h,可轉換爲90m。  
  99.         orderInfo += "&it_b_pay=\"30m\"";  
  100.   
  101.         // extern_token爲通過快登受權獲取到的alipay_open_id,帶上此參數用戶將使用受權的帳戶進行支付  
  102.         // orderInfo += "&extern_token=" + "\"" + extern_token + "\"";  
  103.   
  104.         // 支付寶處理完請求後,當前頁面跳轉到商戶指定頁面的路徑,可空  
  105.         orderInfo += "&return_url=\"m.alipay.com\"";  
  106.   
  107.         // 調用銀行卡支付,需配置此參數,參與簽名, 固定值 (須要簽約《無線銀行卡快捷支付》才能使用)  
  108.         // orderInfo += "&paymethod=\"expressGateway\"";  
  109.   
  110.         return orderInfo;  
  111.     }  
  112.   
  113.     /** 
  114.      * get the out_trade_no for an order. 生成商戶訂單號,該值在商戶端應保持惟一(可自定義格式規範) 
  115.      *  
  116.      */  
  117.     public String getOutTradeNo() {  
  118.         SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss",  
  119.                 Locale.getDefault());  
  120.         Date date = new Date();  
  121.         String key = format.format(date);  
  122.   
  123.         Random r = new Random();  
  124.         key = key + r.nextInt();  
  125.         key = key.substring(0, 15);  
  126.         return key;  
  127.     }  
  128.   
  129.     /** 
  130.      * sign the order info. 對訂單信息進行簽名 
  131.      *  
  132.      * @param content 
  133.      *            待簽名訂單信息 
  134.      */  
  135.     public String sign(String content) {  
  136.         return SignUtils.sign(content, RSA_PRIVATE);  
  137.     }  
  138.   
  139.     /** 
  140.      * get the sign type we use. 獲取簽名方式 
  141.      *  
  142.      */  
  143.     public String getSignType() {  
  144.         return "sign_type=\"RSA\"";  
  145.     }  
  146.   
  147. }</span>  
 
其中 public void pay(String name, String msg, String orderno, String money,final Handler handler) 方法是調用支付是用到的,傳的參數爲商戶的名字,商品計費名稱,訂單號和價格,最後一個Handler handler參數是用來接收支付回調發送消息的
   支付的線程必須異步調用

// 服務器異步通知頁面路徑
orderInfo += "?ify_url=" + "\"" + "notify_URL" + "\""; 這裏要填寫真實的回到地址,是支付寶回調通知服務器的。
 
orderInfo += "&it_b_pay=\"30m\""; 交易的超時時間默認30分鐘,能夠本身設置。 其他地方不用太大改動。
 
 
[java]  view plain  copy
  1. <span style="font-size:12px">public class AppActivity extends Activity {  
  2.   
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         Button btn_pay = (Button) findViewById(R.id.btn_pay);  
  8.         btn_pay.setOnClickListener(new OnClickListener() {  
  9.   
  10.             @Override  
  11.             public void onClick(View v) {  
  12.                 Pay pay = new Pay(AppActivity.this);  
  13.                 pay.pay("商戶名稱", "商品計費名稱", "訂單號", "1", handler);  
  14.   
  15.             }  
  16.         });  
  17.   
  18.     }  
  19.   
  20.     Handler handler = new Handler() {  
  21.           
  22.         @Override  
  23.         public void handleMessage(Message msg) {  
  24.             super.handleMessage(msg);  
  25.             switch (msg.what) {  
  26.             case 1: {  
  27.                 PayResult payResult = new PayResult((String) msg.obj);  
  28.                 // 支付寶返回這次支付結果及加簽,建議對支付寶簽名信息拿簽約時支付寶提供的公鑰作驗籤  
  29.                 // String resultInfo = payResult.getResult();  
  30.                 String resultStatus = payResult.getResultStatus();  
  31.                 if (TextUtils.equals(resultStatus, "9000")) {  
  32.                     Toast.makeText(AppActivity.this, "支付成功",  
  33.                             Toast.LENGTH_SHORT).show();  
  34.                 } else {  
  35.                     // 「8000」表明支付結果由於支付渠道緣由或者系統緣由還在等待支付結果確認,最終交易是否成功以服務端異步通知爲準(小几率狀態)  
  36.                     if (TextUtils.equals(resultStatus, "8000")) {  
  37.                         Toast.makeText(AppActivity.this, "支付結果確認中",  
  38.                                 Toast.LENGTH_SHORT).show();  
  39.                     } else {  
  40.                         Toast.makeText(AppActivity.this, "支付失敗",  
  41.                                 Toast.LENGTH_LONG).show();  
  42.                     }  
  43.                 }  
  44.                 break;  
  45.             }  
  46.             }  
  47.         }  
  48.     };  
  49.   
  50. }</span>  

這裏是模擬調用支付,點擊按鈕開始跳轉支付, handler收到消息後判斷支付狀態。9000爲支付成功,8000爲支付確認中,其他支付失敗
 
支付時出現偶爾出現java.security.spec.InvalidKeySpecException: java.lang.RuntimeException錯誤,須要調整一下RSA簽名私鑰,SignUtils 類
 
把KeyFactory keyf = KeyFactory.getInstance("RSA");
改爲KeyFactory keyf = KeyFactory.getInstance("RSA", "BC");
 
[java]  view plain  copy
  1. <span style="font-size:12px">public class SignUtils {  
  2.   
  3.     private static final String ALGORITHM = "RSA";  
  4.   
  5.     private static final String SIGN_ALGORITHMS = "SHA1WithRSA";  
  6.   
  7.     private static final String DEFAULT_CHARSET = "UTF-8";  
  8.   
  9.     public static String sign(String content, String privateKey) {  
  10.         try {  
  11.             PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(  
  12.                     Base64.decode(privateKey));  
  13.             KeyFactory keyf = KeyFactory.getInstance("RSA", "BC");  
  14.             PrivateKey priKey = keyf.generatePrivate(priPKCS8);  
  15.   
  16.             java.security.Signature signature = java.security.Signature  
  17.                     .getInstance(SIGN_ALGORITHMS);  
  18.   
  19.             signature.initSign(priKey);  
  20.             signature.update(content.getBytes(DEFAULT_CHARSET));  
  21.   
  22.             byte[] signed = signature.sign();  
  23.   
  24.             return Base64.encode(signed);  
  25.         } catch (Exception e) {  
  26.             e.printStackTrace();  
  27.         }  
  28.   
  29.         return null;  
  30.     }  
  31.   
  32. }</span>  
 
公鑰私鑰必定要傳正確才能簽名成功
 
另外的Base64.java和PayResult.java兩個類就不用作修改了。
 
常見的支付錯誤碼以下:
 
 
在商戶應用工程的AndroidManifest.xml文件裏面添加聲明:
 
[java]  view plain  copy
  1. <span style="font-size:12px">  
  2.   
  3.   
  4.  </span>  
[java]  view plain  copy
  1. <span style="font-size:12px">  
  2.   
  3.   
  4.   
  5. </span>  

特別注意事項:

 
測試場景必定注意,安裝支付寶錢包則直接跳轉app支付,沒安裝則進入H5網頁支付。
 
未安裝支付寶錢包測試以下:
 
 
 
[java]  view plain  copy
  1. Button btn_pay = (Button) findViewById(R.id.btn_pay);  
  2.         btn_pay.setOnClickListener(new OnClickListener() {  
  3.   
  4.             @Override  
  5.             public void onClick(View v) {  
  6.                 Pay pay = new Pay(AppActivity.this);  
  7.                 pay.pay("商戶名稱", "商品計費名稱", "1231321321", "1", handler);  
  8.   
  9.             }  
  10.         });  

傳入參數是我任意填寫的。
 
點擊「支付按鈕」,開始支付,未安裝支付寶錢包,會自動跳轉H5網頁支付,第一次會輸入手機號獲取驗證碼,自動記錄。再次進入時直接到確認付款頁面,支付中途取消返回結果碼爲6001。
相關文章
相關標籤/搜索