編輯:關於Android編程
之前已經將銀聯支付功能進行了集成,暫時將退款功能擱下了,今天抽了一小段光陰把這個洞給補上了。其實有了上一次集成支付功能的經驗,對退貨退款的集成就很容易實現了。本文只講服務器端的處理,客戶端根據需求寫好就行。
銀聯官方提供了一個退貨退款流程圖:
所以過程主要是:服務器端組織好請求報文->銀聯系統進行處理->將受理結果和處理結果返回給服務器。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMSBpZD0="二實現">二.實現
我在代碼中做了一些注釋,所以看完代碼和注釋基本就沒問題了。
只是請注意一點:銀聯支付成功後會返回一個流水號,該流水號是後續操作的輸入(退貨、退款、查詢支付狀態等操作)而不是訂單號(原因很簡單啊,訂單號是我們按一定規則生成的,銀聯系統肯定不認),所以必須將該流水號和我們需要操作的訂單進行綁定,當然最好的方式就是在訂單表裡增加一個流水號字段。
組織請求報文,向銀聯後台發起退貨退款請求。
/**
* 退款流程
* @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;
}
後台通知地址中接收銀聯後台的處理結果通知,在上一篇中已經寫了後台通知處理,這次加上退款功能後就稍微修改了一下。
/**
* 後台通知處理
* @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不行,回路地址也不行。
最終效果圖,點擊save會保存到文件中,點擊show會從文件中讀取出內容並顯示。main.xml<?xml version=1.0 encoding=utf
GlideGlide是一個高效、開源、 Android設備上的媒體管理框架,它遵循BSD、MIT以及Apache 2.0協議發布。Glide具有獲取、解碼和展示視頻劇照、
ProgressBar 簡介ProgressBar是一種很常用的Ui,用於給復雜的操作顯示進度,提供更好的用戶相應。使用setProgress()incrementPro
今天要實現的功能是實現專輯倒影效果,這個功能已經屬於圖像處理方面的了,對圖像處理我不怎麼在行,等一下會介紹一個很實用的工具類,專門用來進行圖像處理的。這個工具類不是我寫的