1. 程式人生 > >Java web整合支付寶電腦支付介面(沙箱環境)

Java web整合支付寶電腦支付介面(沙箱環境)

前言

這是博主的第一篇部落格,剛剛畢業,打算把工作中遇到的一些比較普遍的問題記錄下來,如果有寫的不好的地方或者寫錯的地方歡迎指出,一定會改!嘿嘿嘿。先來立個flag,我希望三年之後能夠成為一名不怕bug,熱愛生活,健康快樂的富婆!

這篇部落格主要介紹如何在Java web專案中整合支付寶的電腦支付介面(會稍微介紹一下伺服器整合APP支付介面)。目前支付寶介面更新很快,在博主查詢資料的時候,很多都是即時到賬介面,APP支付則是移動支付介面等,所以想結合現在的電腦支付介面寫一篇博文。如果專案要正式接入支付寶介面的話,是要企業與支付寶建立應用以及簽約獲取APPID等等的,如果我們只是個人想要學習或者測試的話,可以使用沙箱環境,不需要建立應用和簽約,預設有很多應用!!正式環境的開發順序是:建立應用—簽約(電腦支付、APP支付、當面付、手機網站支付等等都需要單獨簽約!!)

配置沙箱環境

一、如果你是一個新手小白想要在專案中接入支付寶介面,首先你要做的是,進入支付寶官網開放平臺。點選“快速開發”下的“開發接入”,跳轉到新頁面後點擊“開發服務”下的“沙箱”。

二、新頁面就是支付寶配置沙箱環境的文件以及使用說明,這裡告訴我們沙箱環境預設給了我們一個APPID,需要我們自己配置RSA2(SHA256)的應用公鑰,需要先下載支付寶生成祕鑰的工具,如果是RSA2簽名格式的話,記住要生成2048位的!!不要被示例圖給迷惑了,當初博主就是這裡弄錯了!生成之後,進入沙箱應用,上傳剛剛生成的應用公鑰,上傳成功後會生成支付寶公鑰,應用私鑰以及支付寶公鑰會在程式碼配置中用到。如需要更改祕鑰,使用下載的工具重新生成再上傳即可!

上傳應用公鑰並獲取支付寶公鑰,沙箱環境與連結中的示例圖略有不同!

匯入支付寶電腦支付的demo

一、在沙箱應用的連結中,點選最下面的功能中的電腦網站支付,選擇左側SDK&DEMO的選單項,下載Java版的demo。

二、解壓剛剛下載的demo,匯入到eclipse中,目錄結構如下圖
在這裡插入圖片描述
三、修改AlipayConfig.java檔案,記住是商戶私鑰和支付寶公鑰!不要寫成應用公鑰!應用私鑰即商戶私鑰!
在這裡插入圖片描述
這裡的notify_url和return_url改成自己專案要返回頁面的地址,由於是沙箱環境,所以支付寶閘道器也有修改。return_url是指付款成功之後返回給使用者檢視的介面,如付款成功之後返回到商品詳情或者網站首頁等等。notify_url則是支付包與伺服器互動的頁面,使用者看不到,支付成功以notify_url返回的引數或者查詢訂單返回的引數為準。

電腦網站支付快速接入。

四、修改配置成功之後,執行專案。
頁面如下:
1.
在這裡插入圖片描述
2.
在這裡插入圖片描述
3.**使用沙箱應用下面的沙箱賬號裡的買家賬號登入,付款!**錢會直接打到賣家賬戶中!這裡面的錢可以自己手動新增,很有滿足感 !!!!瞬間變富婆啊!!如果想用二維碼支付,則到沙箱應用中掃描二維碼下載沙箱版的支付寶,再用沙箱的買家賬號登入就可以付款了!目前支援安卓版。

4.由於博主並沒有寫頁面,使用的是支付寶預設的,所以返回的是一串json資料。
在這裡插入圖片描述

5.沙箱除錯常見的錯誤如下圖,如果報的錯誤不在這裡面,請自行百度!
在這裡插入圖片描述

到這裡如果能夠成功付款或者查詢訂單等等,就可以看出我們的配置是沒有問題的!接下來就要與我們的Java web專案整合並且存訂單資料到資料庫中!

整合介面到Java web專案

一、博主的框架是公司同事搭的SSM框架,但是與我在網上看的也有點不太一樣,不過沒關係,整合起來就那麼幾個檔案。首先把上面那個demo裡的Alipayconfig.java放在專案中(目錄隨專案而定),然後匯入alipay-sdk-javaXXX.jar、commons-logging-1.1.1.jar這兩個jar包到WebContent\WEB-INF\lib中,(具體路徑隨專案不同而改變)。

二、寫支付介面。這裡面可以對自己專案中的訂單表進行操作,先增加一條訂單,但是狀態為未付款。然後呼叫支付寶付款介面。

/**
     * 獲取訂單資料介面
     * @param request
     * @param response
     * @throws AlipayApiException 
     * @throws IOException 
     */
    @RequestMapping("viewOrder")
    public void viewOrder(HttpServletRequest req, Model mod, HttpServletResponse rep,
            @RequestParam(value = "goodId", required = true)Integer goodId) throws AlipayApiException, IOException{

        CommonResponse cr = new CommonResponse();   
        User cu = ViewSessionManager.getUserSession();
        if(cu == null){   //需要登入才能買東西
            cr.setMessage("未登入");
            cr.setData(null);
            cr.setCode(3109);
        }
        //系統下單
        OrderInfo  param = new OrderInfo();
        param.setGoodId(goodId);
        payService.alipayOrder(cu, param);   //生成訂單資訊,根據自己專案改動


      //獲得初始化的AlipayClient
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

        //設定請求引數
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(AlipayConfig.return_url);
        alipayRequest.setNotifyUrl(AlipayConfig.notify_url);

        //商戶訂單號,商戶網站訂單系統中唯一訂單號,必填
        String out_trade_no = param.getTradeCode();
        //付款金額,必填
        String total_amount = param.getMoney().toString();
        //訂單名稱,必填
        String subject = param.getSubject();
        //商品描述,可空
        String body = param.getRemark();

        alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," 
                + "\"total_amount\":\""+ total_amount +"\"," 
                + "\"subject\":\""+ subject +"\"," 
                + "\"body\":\""+ body +"\"," 
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");

        //請求
        String result = alipayClient.pageExecute(alipayRequest).getBody();

        rep.setContentType("text/html;charset=" + AlipayConfig.charset);
        rep.getWriter().write(result);//直接將完整的表單html輸出到頁面
        rep.getWriter().flush();
        rep.getWriter().close();
    }

三、寫同步通知return_url介面。這裡面可以更改訂單狀態,但是不以這個為準(若網速太卡或者使用者付完款關掉頁面,則不會跳到這個介面,所以訂單狀態可能就不會更改)

/**
     * 回撥路徑return_url
     * @param request
     * @param response
     * @throws AlipayApiException 
     * @throws UnsupportedEncodingException 
     */
    @RequestMapping("return_url.view")
    public String returnUrl(HttpServletRequest request, HttpServletResponse response) throws AlipayApiException, UnsupportedEncodingException{
        //獲取支付寶POST過來反饋資訊
        Map<String,String> params = new HashMap<String,String>();
        Map requestParams = request.getParameterMap();
        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)
        boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset,AlipayConfig.sign_type);
        if(signVerified) {
            //商戶訂單號
            String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");

            //支付寶交易號
            String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");

            //付款金額
            String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");

            request.setAttribute("out_trade_no", out_trade_no);
            request.setAttribute("trade_no", trade_no);
            request.setAttribute("total_amount", total_amount);


            log.info("訂單處理:系統訂單號" + out_trade_no + "支付寶交易號:" + trade_no);
            //系統處理根據支付寶回撥更改訂單狀態或者其他關聯表的資料
            OrderInfo order = payService.findOneByTradeCode(out_trade_no);
            if(order == null){
                signVerified = false;
                request.setAttribute("signVerified", signVerified); 
                request.setAttribute("reason", "商戶訂單號不存在");
                log.error("系統訂單:"+ out_trade_no + "不存在。");
            }else{
                if(!order.getMoney().toString().equals(total_amount)){
                    signVerified = false;
                    request.setAttribute("signVerified", signVerified); 
                    request.setAttribute("reason", "付款金額不對");
                    return "notify_url";
                }


                if(order.getTradeStatus() == 1){//判斷當前訂單是否已處理,避免重複處理
                    log.info("系統訂單:"+ out_trade_no + "無需重複處理。");
                }else{
                    order.setTradeStatus(1);//修改訂單狀態為已支付
                    Date payedAt = new Date();
                    order.setTransactionId(trade_no);
                    order.setPayedAt(payedAt);
                    payService.payOrder(order);
                    log.info("系統訂單:"+ out_trade_no + "成功支付。");
                }

            }
        }else{
            request.setAttribute("reason", "驗籤失敗");
        }
        request.setAttribute("signVerified", signVerified);
        return "return_url";
    }

四、非同步通知notify_url介面,與上面return_url的幾乎相同,只是最後輸出:

 response.setContentType("text/html;charset=" + AlipayConfig.charset);
            response.getWriter().write("success");//直接將完整的表單html輸出到頁面
            response.getWriter().flush();
            response.getWriter().close();

方法為void,還有一點需要注意的是:必須保證伺服器非同步通知頁面(notify_url)上無任何字元,如空格、HTML標籤、開發系統自帶丟擲的異常提示資訊等
電腦網站支付結果非同步通知
資料庫中訂單狀態的更新以這個介面中的為準!

五、查詢訂單介面。這裡的返回型別CommonResponse 是我們公司自己的,你們改成自己的返回型別就好.我們這個是ajax呼叫介面,然後將orderString的資料返回,實際上是json資料。

/**
     * 支付寶交易查詢介面
     * @param request
     * @param response
     * @throws UnsupportedEncodingException 
     * @throws AlipayApiException 
     */
    @RequestMapping("queryOrder")
    @ResponseBody
    public CommonResponse queryOrder(HttpServletRequest req, Model mod, HttpServletResponse rep,
            @RequestParam(value = "tradeCode", required = true)String tradeCode,
            @RequestParam(value = "tradeNo", required = true)String tradeNo) throws UnsupportedEncodingException, AlipayApiException{

        CommonResponse cr = new CommonResponse();
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type); //獲得初始化的AlipayClient
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();//建立API對應的request類
        request.setBizContent("{" +
                "   \"out_trade_no\":\""+tradeCode+"\"," +
                "   \"trade_no\":\""+tradeNo+"\"" +
                "  }");//設定業務引數
        //根據response中的結果繼續業務邏輯處理
        String orderString = null;  
        try {
                //呼叫查詢方法
                AlipayTradeQueryResponse response = alipayClient.execute(request);//通過alipayClient呼叫API,獲得對應的response類
                orderString = response.getBody();//就是orderString 可以直接給客戶端請求,無需再做處理。
            } catch (AlipayApiException e) {
                e.printStackTrace();
        }
        cr.setData(orderString);    //返回orderString
        return cr;
    }

伺服器端新增APP支付介面

與電腦支付介面類似,可以直接使用同一個jar包,不過在付款介面的入參裡需要新增token。下面是部分程式碼。我相信你們足夠聰明能夠改成自己的!

CommonResponse cr = new CommonResponse();   
        Cache<String, User> apiAccessTokenCache = cacheManager.getCache("apiAccessTokenCache");
        User cu = apiAccessTokenCache.get(token);//登入時候產生的token,通過token獲得User
        if(cu == null){
            cr.setMessage("token值不對或已失效");
            cr.setData(null);
            cr.setCode(3109);
            return cr;
        }
        //系統下單
        OrderInfo  param = new OrderInfo();
        param.setGoodId(goodId);
        payService.order(cu, param);   //生成訂單資訊

        //例項化客戶端
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
        //例項化具體API對應的request類,類名稱和介面名稱對應,當前呼叫介面名稱:alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已經封裝掉了公共引數,這裡只需要傳入業務引數。以下方法為sdk的model入參方式(model和biz_content同時存在的情況下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        String time = Long.toString(System.currentTimeMillis()/1000);
        model.setBody(param.getRemark());
        model.setSubject(param.getSubject());
        model.setOutTradeNo(param.getTradeCode());
        model.setTimeoutExpress(time);
        model.setTotalAmount(param.getMoney()+"");
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        request.setReturnUrl(AlipayConfig.return_url);
        request.setNotifyUrl(AlipayConfig.notify_url);
        String orderString = null;  
        try {
                //這裡和普通的介面呼叫不同,使用的是sdkExecute
                AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
                orderString = response.getBody();//就是orderString 可以直接給客戶端請求,無需再做處理。
            } catch (AlipayApiException e) {
                e.printStackTrace();
        }
        cr.setData(orderString);    //返回orderString
        return cr;

轉換成正式環境

需要修改配置檔案中的APPID,支付寶公鑰,應用私鑰,以及支付寶閘道器!有時改了閘道器之後跳轉的還是沙箱環境的閘道器,需要clean一下伺服器(我的是Tomcat)

結語

上面就是博主總結的Java web專案整合支付寶電腦支付介面!希望這一篇博文能夠解決你的問題,祝好運!如果有疑問可以給我留言,看到了如果我會的問題就會回答!☺☺