Android開發:app工程整合銀聯-退貨退款功能

來源:互聯網
上載者:User

標籤:

一.前言

之前已經將銀聯支付功能進行了整合(伺服器端戳這裡,用戶端戳這裡),暫時將退款功能擱下了,今天抽了一小段光陰把這個洞給補上了。
其實有了上一次整合支付功能的經驗,對退貨退款的整合就很容易實現了。本文只講伺服器端的處理,用戶端根據需求寫好就行。
銀聯官方提供了一個退貨退款流程圖:

所以過程主要是:伺服器端組織好請求報文->銀聯絡統進行處理->將受理結果和處理結果返回給伺服器。

二.實現

我在代碼中做了一些注釋,所以看完代碼和注釋基本就沒問題了。前提條件依舊是,完成好各項配置工作,可以參考伺服器端的博文。
只是請注意一點:銀聯支付成功後會返回一個流水號,該流水號是後續操作的輸入(退貨、退款、查詢支付狀態等操作)而不是訂單號(原因很簡單啊,訂單號是我們按一定規則產生的,銀聯絡統肯定不認),所以必須將該流水號和我們需要操作的訂單進行綁定,當然最好的方式就是在訂單表裡增加一個流水號欄位。

1.第一步

組織請求報文,向銀聯後台發起退貨退款請求。

/**     * 退款流程     * @param orderId  //需要退貨退款的訂單ID     * @param request     * @param response     * @throws UnsupportedEncodingException      */    @RequestMapping(value = "/pay/refund/{orderId}")    @ResponseBody    public JSONObject refund(@PathVariable("orderId") String orderId,HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException     {        //防止亂碼,根據業務需求,這兩句可有可無        request.setCharacterEncoding(DemoBase.encoding_UTF8);        response.setContentType("text/html; charset="+ DemoBase.encoding_UTF8);        //json用於將資料返回給用戶端        JSONObject json = new JSONObject();        System.out.println("退款開始");        //獲得該訂單的資訊        Order order  = orderDAO.getOrder(orderId);        if(order==null)        {            json.put("result", "0");            return json;        }           //獲得該訂單的流水號        String orderQueryId = order.getOrderQueryId();        //獲得該訂單的總價        String orderOilTotalPrice = order.getOrderOilTotalPrice();        Map<String, String> data = new HashMap<String, String>();        /***銀聯全渠道系統,產品參數,除了encoding自行選擇外其他不需修改***/        data.put("version", DemoBase.version);               //版本號碼        data.put("encoding", DemoBase.encoding_UTF8);             //字元集編碼 可以使用UTF-8,GBK兩種方式        data.put("signMethod", "01");                        //簽名方法 目前只支援01-RSA方式認證加密        data.put("txnType", "04");                           //交易類型 04-退貨               data.put("txnSubType", "00");                        //交易子類型  預設00              data.put("bizType", "000201");                       //業務類型        data.put("channelType", "08");                       //渠道類型,07-PC,08-手機             /***商戶接入參數***/        data.put("merId", DemoBase.merId);                //商戶號碼,請改成自己申請的商戶號或者open上註冊得來的777商戶號測試        data.put("accessType", "0");                         //接入類型,商戶接入固定填0,不需修改               //一定要注意,該orderId並不是我們自己的訂單id,而是退款申請這條業務的id,銀聯提供的DemoBase.getOrderId()是根據系統時間產生的。        data.put("orderId", DemoBase.getOrderId());          //商戶訂單號,8-40位元字字母,不能含“-”或“_”,可以自行定製規則,重新產生,不同於原消費              data.put("txnTime", DemoBase.getCurrentTime());      //訂單發送時間,格式為YYYYMMDDhhmmss,必須取目前時間,否則會報txnTime無效               data.put("currencyCode", "156");                     //交易幣種(境內商戶一般是156 人民幣)             //一定注意,退款金額必須是整數,而且單位是分。        data.put("txnAmt", Double.valueOf(orderOilTotalPrice).intValue()*100+"");                          //****退貨金額,單位分,不要帶小數點。退貨金額小於等於原消費金額,當小於的時候可以多次退貨至退貨累計金額等於原消費金額               //data.put("txnAmt",orderOilTotalPrice);        //這個透傳欄位很有用,因為前面的orderId已經不是我們自己的訂單id了,而我們在後台通知裡肯定還需要這個訂單id,因為我們需要修改該訂單的狀態,可是data中根本不能put我們自己的訂單id,那麼這個欄位就是用來存放一些我們想傳遞給後台通知的資料,因為data中的資料都會完整的全部返回給後台通知,我這裡只是在透傳欄位裡放了一個orderId,如果有更多的資料需要傳遞,只需要用map或者json儲存資料,然後轉成String就可以了        data.put("reqReserved", orderId);                    //請求方保留網域,透傳欄位(可以實現商戶自訂參數的追蹤)本交易的後台通知,對本交易的交易狀態查詢交易、對賬檔案中均會原樣返回,商戶可以按需上傳,長度為1-1024個位元組          //後台通知地址必須是真實ip,因為銀聯後台要將通知post到這個後台地址。          data.put("backUrl", DemoBase.backUrl);               //後台通知地址,後台通知參數詳見open.unionpay.com協助中心 下載  產品介面規範  網關支付產品介面規範 退貨交易 商戶通知,其他說明同消費交易的後台通知        /***要調通交易以下欄位必須修改***/        //流水號,這才是銀聯後台認識的標識符,該流水號是在支付成功後獲得的。        data.put("origQryId", orderQueryId);      //****原消費交易返回的的queryId,可以從消費交易後台通知介面中或者交易狀態查詢介面中擷取        /**請求參數設定完畢,以下對請求參數進行簽名並發送http post請求,接收同步應答報文------------->**/        Map<String, String> reqData  = AcpService.sign(data,DemoBase.encoding_UTF8);        //報文中certId,signature的值是在signData方法中擷取並自動賦值的,只要認證配置正確即可。        String url = SDKConfig.getConfig().getBackRequestUrl();                                 //交易請求url從設定檔讀取對應屬性檔案acp_sdk.properties中的 acpsdk.backTransUrl        Map<String, String> rspData = AcpService.post(reqData, url,DemoBase.encoding_UTF8);//這裡調用signData之後,調用submitUrl之前不能對submitFromData中的索引值對做任何修改,如果修改會導致驗簽不通過        /**對應答碼的處理,請根據您的商務邏輯來編寫程式,以下應答碼處理邏輯僅供參考------------->**/        //應答碼規範參考open.unionpay.com協助中心 下載  產品介面規範  《平台接入介面規範-第5部分-附錄》        if(!rspData.isEmpty()){            if(AcpService.validate(rspData, DemoBase.encoding_UTF8)){                LogUtil.writeLog("驗證簽名成功");                String respCode = rspData.get("respCode") ;                if(("00").equals(respCode)){                    //交易已受理(不代表交易已成功),等待接收後台通知更新訂單狀態,也可以主動發起 查詢交易確定交易狀態。                    //TODO                    json.put("result", "1");                    return json;                }else if(("03").equals(respCode)||                         ("04").equals(respCode)||                         ("05").equals(respCode)){                    //後續需發起交易狀態查詢交易確定交易狀態                    //TODO                }else{                    //其他應答碼為失敗請排查原因                    //TODO                }            }else{                LogUtil.writeErrorLog("驗證簽名失敗");                //TODO 檢查驗證簽名失敗的原因            }        }else{            //未返回正確的http狀態            LogUtil.writeErrorLog("未擷取到返回報文或返回http狀態代碼非200");        }        json.put("result", "0");        return json;    }
2.第二步

後台通知地址中接收銀聯背景處理結果通知,在上一篇中已經寫了後台通知處理,這次加上退款功能後就稍微修改了一下。

/**     * 後台通知處理     * @param request     * @param response     */    @RequestMapping(value = "/pay/backRcvResponse")    @ResponseBody    public void backRcvResponse(HttpServletRequest request, HttpServletResponse response)     {        System.out.println("後台通知驗簽開始");        //return AcpService.validateAppResponse(sign, DemoBase.encoding_UTF8);        //System.out.println("驗簽開始");        String encoding = request.getParameter(SDKConstants.param_encoding);        // 擷取銀聯通知伺服器發送的後台通知參數        Map<String, String> reqParam = Tool.getAllRequestParam(request);        LogUtil.printRequestLog(reqParam);        Map<String, String> valideData = null;        try        {            if (null != reqParam && !reqParam.isEmpty()) {                Iterator<Entry<String, String>> it = reqParam.entrySet().iterator();                valideData = new HashMap<String, String>(reqParam.size());                while (it.hasNext()) {                    Entry<String, String> e = it.next();                    String key = (String) e.getKey();                    String value = (String) e.getValue();                    value = new String(value.getBytes(encoding), encoding);                    valideData.put(key, value);                }            }            //重要!驗證簽名前不要修改reqParam中的索引值對的內容,否則會驗簽不過            if (!AcpService.validate(valideData, encoding)) {                LogUtil.writeLog("驗證簽名結果[失敗].");                //驗簽失敗,需解決驗簽問題            } else {                LogUtil.writeLog("驗證簽名結果[成功].");                //【註:為了安全驗簽成功才應該寫商戶的成功處理邏輯】交易成功,更新商戶訂單狀態                //String orderId =valideData.get("orderId"); //擷取後台通知的資料                //Order order = orderDAO.getOrder(orderId);                //擷取交易類型,可參考官方文檔                String txnType = valideData.get("txnType");                System.out.println(Integer.valueOf(txnType));                Order order;                switch(Integer.valueOf(txnType))//交易類型                {                    case 01://消費                        String orderId =valideData.get("orderId"); //擷取後台通知的資料,其他欄位也可用類似方式擷取                        String payTime = valideData.get("txnTime");                        String orderQueryId = valideData.get("queryId");                        //String respCode =valideData.get("respCode"); //擷取應答碼,收到後台通知了respCode的值一般是00,可以不需要根據這個應答碼判斷。                        //if(orderId!=null&&!"".equals(orderId))                        //{                            order = orderDAO.getOrder(orderId);                            if(order!=null)                            {                                System.out.println("更新支付狀態:"+orderId);                                System.out.println("payTime"+payTime);                                order.setOrderPayStatus(1);                                order.setOrderPayTime(payTime+".0");                                order.setOrderQueryId(orderQueryId);                                //order.setOrderPayTime(new SimpleDateFormat(" yyyy-MM-dd HH:mm:ss ").parse(payTime));                                boolean sucess = orderDAO.update(order);                                    System.out.println("sucess"+sucess);                            }                        //}                        break;                    case 04://退貨&退款                    //獲得透傳欄位的資料,我這裡就是訂單id                        order = orderDAO.getOrder(valideData.get("reqReserved"));                        if(order!=null)                        {                        //更新訂單狀態                            order.setOrderPayStatus(2);                            order.setOrderStatus(2);                            orderDAO.update(order);                        }                        break;                    default:                        break;                }            }            LogUtil.writeLog("BackRcvResponse接收後台通知結束");            //返回給銀聯伺服器http 200  狀態代碼            response.getWriter().print("ok");        }        catch(Exception e){}    }

ok,還是看一下效果吧。respCode = 00表示成功。

三.問題

不會遇到問題是不可能的,當然按照我上面代碼的實現的也只是能把基本流程跑通,從安全性上來講,還差太多…
1.退款金額必須是整數
比如下面圖中的情況:

2.data中的orderId不是自己的orderId,而是本次退款交易的id。
3.後台通知地址一定是真是ip,localhost不行,迴路地址也不行。

Android開發:app工程整合銀聯-退貨退款功能

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.