对接第三方支付接口-类似文件锁的编程小技巧
在這次對接支付接口的時候,有如下場景:用戶還款的時候,APP端只要請求了支付接口后,正常情況下,支付接口會同步返回結果狀態(tài),并且異步通知是否成功,支付狀態(tài)以異步通知為準。這樣的場景會出現(xiàn)一個問題,如果APP端請求了支付接口,異步通知遲遲未返回,這樣一來,用戶還款狀態(tài)是無法更改(還款的邏輯處理實在異步通知里處理,因為一切以異步通知為準),并且對于用戶來說他已經(jīng)還款了,異步回調沒來,可能支付成功,可能支付失敗我們不知道,對于用戶來說他已經(jīng)支付還款了,按邏輯這一期還款他無需也不能做其他操作了,所以在回調通知來之前,這一期數(shù)據(jù)在APP端是需要做一個限制不能讓用戶操作,在這里加了一個“還款中”狀態(tài),標記請求支付接口之后,回調通知來之前的狀態(tài)(正常情況下這段時間很短,短到讓用戶無法察覺)。
加“還款中”的狀態(tài)是可行的,后臺服務端來更新還款記錄的狀態(tài),由APP端請求,APP端在請求支付接口后支付接口同步返回成功后再請求后臺的接口,更新還款記錄的狀態(tài)。這里有個問題:請求支付接口會有一個異步通知返回,我們在異步通知里進行相應的邏輯處理,包括更新還款記錄的狀態(tài)為“已還”,但是APP端同步請求成功后也會請求后臺更新這條還款記錄為“還款中”,操作同一條數(shù)據(jù),我們一開始的做法是更新還款中的時候判斷是否是“未還”狀態(tài),但是發(fā)現(xiàn)如果兩個更新操作的方法“同時”處理,即異步通知還未更新為“已還”,更新為“還款中”的方法進去了,檢索到這條記錄仍是“未還”,同樣會處理成“還款中”。這樣一來有可能支付成功了,還款記錄的狀態(tài)還是“還款中”的情況,所以我們要解決并發(fā)的問題,人為控制如果回調通知已經(jīng)來了,就沒必要在請求后臺改成“還款中”。
這里用到的方法有點類似于“文件鎖”,我們通過生成特定文件來標記是否異步通知成功(直接更新為“已還”),如果異步通知成功則不需要更新為“還款中”,如果先改成“還款中”,再改成“已還”也沒有問題,如果兩個方法同時進行,則根據(jù)生成的文件來標記一個方法是否提交完成,這里用到的是Spring的事務控制,另一個事務提交后再執(zhí)行當前這個方法,主要做法為:在異步回調通知里的方法notifyUrl()開始的時候創(chuàng)建一個文件A,結束時刪除這個文件;在更改成“還款中”的方法updateRpmting()開始時候也創(chuàng)建一個文件B,這里要判斷A是否存在,如果A存在則當前線程延時60ms,等待notifyUrl()方法執(zhí)行完畢,這時候還款記錄的狀態(tài)已經(jīng)改成“已還”,不會再繼續(xù)執(zhí)行updateRpmting()方法改成“還款中”,當然在創(chuàng)建文件A的時候也會判斷B是否存在,同樣處理。這里生成文件的方法需要用java的synchronized來鎖住,確保同一時間只有一次調用,生成一個文件。
RechargeUtil.java:(Globals.UNDER_LINE是定義的靜態(tài)變量表示下劃線 “_”)
//創(chuàng)建一個唯一文件public static boolean creatOnlyFile(String rpmtIds,String myType, String otherType){// 創(chuàng)建一個files目錄下面日期為子目錄的rpmtId.txt文件String myFileName=files+"/" + DateUtils.date2Str(DateUtils.yyyyMMdd)+"/" + DigestUtils.md5Hex(rpmtIds) + Globals.UNDER_LINE + myType + ".txt";String otherFileName=files+"/" + DateUtils.date2Str(DateUtils.yyyyMMdd)+"/" + DigestUtils.md5Hex(rpmtIds) + Globals.UNDER_LINE + otherType + ".txt";System.out.println("文件地址為:" + myFileName);File myFile=new File(myFileName);if(!myFile.getParentFile().exists()){myFile.getParentFile().mkdirs();}File otherFile=new File(otherFileName);try {return createSynchronizedFile(myFile, otherFile);} catch (IOException e) {System.out.println("創(chuàng)建文件失敗!");e.printStackTrace();}return false;}//同一時間創(chuàng)建一個唯一文件public synchronized static boolean createSynchronizedFile(File myFile, File otherFile) throws IOException {if(!otherFile.exists()){myFile.createNewFile();return true;}return false;}//刪除存在的鎖文件
public static boolean deleteOnlyFile(String rpmtIds,String myType){
File myFile=new File(files+"/" + DateUtils.date2Str(DateUtils.yyyyMMdd)+"/" + DigestUtils.md5Hex(rpmtIds) + Globals.UNDER_LINE + myType + ".txt");
if(myFile.exists()){
return myFile.delete();
}
return true;
}
?
異步回調通知里調用創(chuàng)建文件的方法:
public void notifyUrl(PayRpmtEntity payRpmt, String noAgree ,JSONObject resultMap) throws Exception{//1.根據(jù)“還款-支付記錄表”獲取rpmtIds,循環(huán)修改還款計劃狀態(tài)String rpmtIds = payRpmt.getRpmtIds();//創(chuàng)建一個唯一文件int size = 0;while (size < 30 && !RechargeUtil.creatOnlyFile(rpmtIds,"01", "02")) {Thread.sleep(100);size++;}if (size >= 30) {log.info("****************************同步回調處理中****************************");resultMap.put("ret_code", "1005");resultMap.put("ret_msg", "支付處理失敗");return ;}// TODO 邏輯處理 // 刪除鎖文件 RechargeUtil.deleteOnlyFile(rpmtIds, "01");
}
?更改成“還款中”的時候:
/*** 將還款計劃該成還款中* @Title: updateRpmt * @param rpmtIdsStr* @throws Exception */public void updateRpmting(String[] rpmtIdsStr,String rpmtIds) throws Exception{//創(chuàng)建一個唯一文件int size = 0;while (size < 30 && !RechargeUtil.creatOnlyFile(rpmtIds,"02", "01")) {Thread.sleep(100);size++;}if (size >= 30) {log.info("****************************狀態(tài)處理中****************************");return ;}// TODO 邏輯處理// 刪除鎖文件RechargeUtil.deleteOnlyFile(rpmtIds, "02");}?
轉載于:https://www.cnblogs.com/mark8080/p/6201763.html
總結
以上是生活随笔為你收集整理的对接第三方支付接口-类似文件锁的编程小技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 超出内容用省略号替代
- 下一篇: c语言作业