Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發:app工程集成銀聯-退貨退款功能

Android開發:app工程集成銀聯-退貨退款功能

編輯:關於Android編程

一.前言

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

所以過程主要是:服務器端組織好請求報文->銀聯系統進行處理->將受理結果和處理結果返回給服務器。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMSBpZD0="二實現">二.實現

我在代碼中做了一些注釋,所以看完代碼和注釋基本就沒問題了。
只是請注意一點:銀聯支付成功後會返回一個流水號,該流水號是後續操作的輸入(退貨、退款、查詢支付狀態等操作)而不是訂單號(原因很簡單啊,訂單號是我們按一定規則生成的,銀聯系統肯定不認),所以必須將該流水號和我們需要操作的訂單進行綁定,當然最好的方式就是在訂單表裡增加一個流水號字段。

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 data = new HashMap();

        /***銀聯全渠道系統,產品參數,除了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 reqData  = AcpService.sign(data,DemoBase.encoding_UTF8);        //報文中certId,signature的值是在signData方法中獲取並自動賦值的,只要證書配置正確即可。
        String url = SDKConfig.getConfig().getBackRequestUrl();                                 //交易請求url從配置文件讀取對應屬性文件acp_sdk.properties中的 acpsdk.backTransUrl
        Map 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 reqParam = Tool.getAllRequestParam(request);

        LogUtil.printRequestLog(reqParam);

        Map valideData = null;
        try
        {
            if (null != reqParam && !reqParam.isEmpty()) {
                Iterator> it = reqParam.entrySet().iterator();
                valideData = new HashMap(reqParam.size());
                while (it.hasNext()) {
                    Entry 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不行,回路地址也不行。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved