Android 接入支付寶

            <p>首先說說筆者的集成經歷,一開始集成時,像往常一樣百度了一下集成的方法,然後出來一大堆結果,以爲應該會很簡單,然而事實卻並非如此。網上的集成方法很多都是舊版本的集成,現在支付寶已經對sdk以及demo進行了更新,雖說和舊版差別不是很大,不過對於不瞭解整個流程的開發人員來說,確實一個極大的痛苦;當然在集成過程中遇到各種各樣的問題,甚至對一些流程根本就不瞭解,當然這跟筆者的水平有一定關係。<strong>最後給大家的建議是,不要心急,一步一步跟着流程走,其實集成是很簡單的。本文介紹的是沙箱環境下的集成,到時候只需要在簽約後將代碼中的各種ID改爲簽約後的即可(集成app支付需要和支付寶進行簽約)。筆者將整個流程分爲兩大部分,第一個是前期配置,第二個是開始集成。</strong></p>

前期配置

首先,進入開放平臺後,我們點擊應用

這裏寫圖片描述


接着我們點擊沙箱環境下的沙箱應用

這裏寫圖片描述

在這邊我們能看到支付寶給我們進行測試的應用ID和測試賬號等,我們點擊設置RSA2密鑰(這邊我已經設置好了,RSA(SHA1)可以不用設置)這裏寫圖片描述


那麼如何設置密鑰呢->查看密鑰生成方法 進入之後我們下載對應版本後,運行“RSA簽名驗籤工具.bat”(WINDOWS)或“RSA簽名驗籤工具.command”(MAC_OSX),然後如下圖勾選,點擊生成密鑰(注意保存)

這裏寫圖片描述


然後我們將這邊生成的應用公鑰複製並粘貼到剛剛的需要設置應用公鑰的地方,設置完成後會生成一個對應的支付寶公鑰

這裏寫圖片描述


好了,這邊需要特別注意: ”應用公鑰、應用私鑰、支付寶公鑰”這三個不要混淆、不要混淆、不要混淆。好了,第一部分的配置也差不多到這邊了。


開始集成

流程

我們先來看下集成的整個流程

這裏寫圖片描述

簡單介紹一下整個流程(圖中虛線標識商戶鏈路,實線標識支付寶鏈路。)

步驟1:用戶點擊進行付款
步驟2:客戶端從商戶的服務端獲取簽名後的訂單消息
步驟3:(商戶將訂單信息加簽)返回簽名後的訂單信息
步驟4、5、6、7、8:這幾個步驟是我們點擊確認支付後自動執行的,不需要我們執行(其中第8步返回最終的支付結果(即同步通知))
步驟9、10:商戶客戶端將同步通知的支付結果發送至支付寶服務端並返回最終結果(這兩個步驟可以不執行,因爲我們後面還有步驟12的異步通知)
步驟12、13:根據我們設置的異步通知地址(下面會介紹)獲取支付結果



好了,流程差不多就是這樣,接下來我們結合代碼開始實戰。


編碼

我們下載支付寶提供的SDK和DEMO(更新時間:2017/05/11) 解壓導入Android Studio後我們打開PayDemoActivity

這裏寫圖片描述
如果我們只想進行支付功能的話,只要設置上圖的APPID和RSA2_PRIVATE即可。當然,這是沙箱環境,我們需要在onCreate下添加這一句:

EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
  • 1

位置如下:

這裏寫圖片描述

當然整個支付的精華莫過於此處(代碼已給出註釋)

  boolean rsa2 = (RSA2_PRIVATE.length() > 0);
        Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(APPID, rsa2,"英語課程(20次)",20);//設置訂單詳情和價格
        String orderParam = OrderInfoUtil2_0.buildOrderParam(params);

        String privateKey = rsa2 ? RSA2_PRIVATE : RSA_PRIVATE;
        String sign = OrderInfoUtil2_0.getSign(params, privateKey, rsa2);//本地加簽
        final String orderInfo = orderParam + "&" + sign;//加簽後的訂單信息

        Runnable payRunnable = new Runnable() {

            @Override
            public void run() {
                PayTask alipay = new PayTask(PayDemoActivity.this);//調用支付接口
                Map<String, String> result = alipay.payV2(orderInfo, true);//支付結果
                Log.i("msp", result.toString());

                Message msg = new Message();
                msg.what = SDK_PAY_FLAG;
                msg.obj = result;
                mHandler.sendMessage(msg);
            }
        };

        Thread payThread = new Thread(payRunnable);
        payThread.start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25



好了,完成如上操作即可通過客戶端發起支付了。是不是發現這跟筆者剛剛介紹的服務端沒有半點關係?其實,我們只是將加簽驗籤放在客戶端進行,這是非常不安全的。所以,下面開始介紹服務端的任務。



現在客戶端中大部分代碼都可以去掉,因爲將這些過程都被放在了放在了服務端,比如下面的這些參數

這裏寫圖片描述

我們所需要的代碼,只剩下一下兩部分,即調用支付接口的代碼和Handle部分(case INFO爲新增的代碼,下面會用到)

    Runnable payRunnable = new Runnable() {

            @Override
            public void run() {
                PayTask alipay = new PayTask(PayDetailActivity.this);
                Map<String, String> result = alipay.payV2(orderInfo, true);
                Log.i("msp", result.toString());

                Message msg = new Message();
                msg.what = SDK_PAY_FLAG;
                msg.obj = result;
                mHandler.sendMessage(msg);
            }
        };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
@SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @SuppressWarnings("unused")
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SDK_PAY_FLAG: {
                    @SuppressWarnings("unchecked")
                    PayResult payResult = new PayResult((Map<String, String>) msg.obj);
                    /**
                     對於支付結果,請商戶依賴服務端的異步通知結果。同步通知結果,僅作爲支付結束的通知。
                     */
                    String resultInfo = payResult.getResult();// 同步返回需要驗證的信息
                    String resultStatus = payResult.getResultStatus();
                    // 判斷resultStatus 爲9000則代表支付成功
                    if (TextUtils.equals(resultStatus, "9000")) {
                        // 該筆訂單是否真實支付成功,需要依賴服務端的異步通知。
                        Toast.makeText(PayDetailActivity.this, "支付成功", Toast.LENGTH_SHORT).show();
                        flag=true;
                        btn_pay.setVisibility(View.GONE);
                        detail_state.setText("已支付");
                    } else {
                        // 該筆訂單真實的支付結果,需要依賴服務端的異步通知。
                        Toast.makeText(PayDetailActivity.this, "支付失敗", Toast.LENGTH_SHORT).show();
                    }
                    break;
                }
                case INFO: {
                    orderInfo = (String) msg.obj;
                    EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
                    PayThread p = new PayThread();
                    p.start();

                    break;
                }
                default:
                    break;
            }
        };
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40


將Runnable修改如下:

    class PayThread extends Thread{

        public void run(){
            //EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
            PayTask alipay = new PayTask(PayDetailActivity.this);
            Map<String, String> result = alipay.payV2(orderInfo, true);
            Log.i("msp", result.toString());

            Message msg = new Message();
            msg.what = SDK_PAY_FLAG;
            msg.obj = result;
            mHandler.sendMessage(msg);

        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16



然後在客戶端新建一個線程(用於從服務器獲取加簽的訂單詳情)

class  accessThread2 extends Thread{

        @Override
        public void run() {
            Message msg1=mHandler.obtainMessage();
            msg1.what=INFO;
            msg1.obj= GetPostUtil.sendPost("http://10.143.224.18:8080/HttpServer/Myserver","name=qwe123");
            mHandler.sendMessage(msg1);
           // super.run();
        }

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

總結一下客戶端的工作,先啓動線程從服務器獲取到加簽後的訂單詳情,賦給msg1.obj;然後在Handle執行case INFO 部分的代碼,接着調用PayTask 接口,並將支付結果賦給msg.obj,最後執行case SDK_PAY_FLAG這部分的代碼。好了,客戶端相比之前就整潔清爽多了。


接下來是服務端,我們在工程中導入服務端的SDK然後新建一個類用於獲取加簽後的訂單詳情(代碼已給出註釋):

public class GetSign {
    /** 支付寶支付業務:入參app_id */
    public static final String APPID = ""

    /** 支付寶網關*/
    public static final String GATE = "https://openapi.alipay.com/gateway.do";

    /** 支付寶私鑰*/
    public static final String APP_PRIVATE_KEY = "";

    /** 支付寶公鑰 */
    public static final String ALIPAY_PUBLIC_KEY = "";

    /** 編碼方式 */
    public static final String CHARSET = "utf-8";

    public static AlipayTradeAppPayResponse response;

    public static String getsign(){
        AlipayClient alipayClient = new DefaultAlipayClient(GATE, 
                APPID, 
                APP_PRIVATE_KEY, 
                "json", 
                CHARSET, 
                ALIPAY_PUBLIC_KEY, 
                "RSA2");

        //實例化具體API對應的request類,類名稱和接口名稱對應,當前調用接口名稱:alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已經封裝掉了公共參數,這裏只需要傳入業務參數。以下方法爲sdk的model入參方式(model和biz_content同時存在的情況下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();

        model.setBody("我是測試數據");
        model.setSubject("App支付測試Java");
        model.setOutTradeNo("100342312764512");
        model.setTimeoutExpress("30m");
        model.setTotalAmount("0.01");
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        request.setNotifyUrl("商戶外網可以訪問的異步地址");
        try {
                //這裏和普通的接口調用不同,使用的是sdkExecute
                response = alipayClient.sdkExecute(request);
                System.out.println(response.getBody());//就是orderString 可以直接給客戶端請求,無需再做處理。
            } catch (AlipayApiException e) {
                e.printStackTrace();
        }
        return response.getBody();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

以上代碼就是加簽的過程。其中response.getBody()即爲加簽過的訂單詳情。這邊需要注意的是,訂單數據格式不能隨意亂寫,比如OutTradeNo只能爲數字、英文或下劃線;此外,OutTradeNo不可以重複,若重複則會出現系統繁忙等錯誤。代碼中各請求參數說明點這


再接下來,就是我們的異步通知了。在上一步的請求參數中,request.setNotifyUrl就是支付寶通知我們的地址。什麼時候會觸發呢?交易成功、交易創建、交易關閉、支付成功。由支付寶主動發起。來看一下回調部分的代碼:

public String aliPay_notify(Map requestParams){
        System.out.println("支付寶支付結果通知"+requestParams.toString());
        //獲取支付寶POST過來反饋信息
        Map<String,String> params = new HashMap<String,String>();

        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                            : valueStr + values[i] + ",";
          }
          //亂碼解決,這段代碼在出現亂碼時使用。
          //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
          params.put(name, valueStr);
         }
        //切記alipaypublickey是支付寶的公鑰,請去open.alipay.com對應應用下查看。
        //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
        try {
            boolean flag = AlipaySignature.rsaCheckV1(params, alipay_public_key, charset, "RSA2");
            if(flag){
                if("TRADE_SUCCESS".equals(params.get("trade_status"))){
                    //付款金額
                    String amount = params.get("buyer_pay_amount");
                    //商戶訂單號
                    String out_trade_no = params.get("out_trade_no");
                    //支付寶交易號
                    String trade_no = params.get("trade_no");
                    //附加數據
                    String passback_params = URLDecoder.decode(params.get("passback_params"));

                }
            }
        } catch (AlipayApiException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    return "success";
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

在if(flag)中,我們需要做如下判斷:
**1、商戶需要驗證該通知數據中的out_trade_no是否爲商戶系統中創建的訂單號
2、判斷total_amount是否確實爲該訂單的實際金額(即商戶訂單創建時的金額)
3、校驗通知中的seller_id(或者seller_email) 是否爲out_trade_no這筆單據的對應的操作方(有的時候,一個商戶可能有多個seller_id/seller_email)
4、驗證app_id是否爲該商戶本身。**
在上述驗證通過後商戶必須根據支付寶不同類型的業務通知,正確的進行不同的業務處理,並且過濾重複的通知結果數據。在支付寶的業務通知中,只有交易通知狀態爲TRADE_SUCCESS或TRADE_FINISHED時,支付寶纔會認定爲買家付款成功。


好了,整個開發流程大概就這樣。如果要換爲正式環境下的話只需要將參數修改爲簽約後支付寶提供的參數,切記去掉onCreate下的這句代碼:

EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
  • 1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章