对接第三方支付接口-类似文件锁的编程小技巧

  在这次对接支付接口的时候,有如下场景:用户还款的时候,APP端只要请求了支付接口后,正常情况下,支付接口会同步返回结果状态,并且异步通知是否成功,支付状态以异步通知为准。这样的场景会出现一个问题,如果APP端请求了支付接口,异步通知迟迟未返回,这样一来,用户还款状态是无法更改(还款的逻辑处理实在异步通知里处理,因为一切以异步通知为准),并且对于用户来说他已经还款了,异步回调没来,可能支付成功,可能支付失败我们不知道,对于用户来说他已经支付还款了,按逻辑这一期还款他无需也不能做其他操作了,所以在回调通知来之前,这一期数据在APP端是需要做一个限制不能让用户操作,在这里加了一个“还款中”状态,标记请求支付接口之后,回调通知来之前的状态(正常情况下这段时间很短,短到让用户无法察觉)。

  加“还款中”的状态是可行的,后台服务端来更新还款记录的状态,由APP端请求,APP端在请求支付接口后支付接口同步返回成功后再请求后台的接口,更新还款记录的状态。这里有个问题:请求支付接口会有一个异步通知返回,我们在异步通知里进行相应的逻辑处理,包括更新还款记录的状态为“已还”,但是APP端同步请求成功后也会请求后台更新这条还款记录为“还款中”,操作同一条数据,我们一开始的做法是更新还款中的时候判断是否是“未还”状态,但是发现如果两个更新操作的方法“同时”处理,即异步通知还未更新为“已还”,更新为“还款中”的方法进去了,检索到这条记录仍是“未还”,同样会处理成“还款中”。这样一来有可能支付成功了,还款记录的状态还是“还款中”的情况,所以我们要解决并发的问题,人为控制如果回调通知已经来了,就没必要在请求后台改成“还款中”。

  这里用到的方法有点类似于“文件锁”,我们通过生成特定文件来标记是否异步通知成功(直接更新为“已还”),如果异步通知成功则不需要更新为“还款中”,如果先改成“还款中”,再改成“已还”也没有问题,如果两个方法同时进行,则根据生成的文件来标记一个方法是否提交完成,这里用到的是Spring的事务控制,另一个事务提交后再执行当前这个方法,主要做法为:在异步回调通知里的方法notifyUrl()开始的时候创建一个文件A,结束时删除这个文件;在更改成“还款中”的方法updateRpmting()开始时候也创建一个文件B,这里要判断A是否存在,如果A存在则当前线程延时60ms,等待notifyUrl()方法执行完毕,这时候还款记录的状态已经改成“已还”,不会再继续执行updateRpmting()方法改成“还款中”,当然在创建文件A的时候也会判断B是否存在,同样处理。这里生成文件的方法需要用java的synchronized来锁住,确保同一时间只有一次调用,生成一个文件。

RechargeUtil.java:(Globals.UNDER_LINE是定义的静态变量表示下划线 “_”)

  //创建一个唯一文件
    public static boolean creatOnlyFile(String rpmtIds,String myType, String otherType){
        // 创建一个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("创建文件失败!");
            e.printStackTrace();
        }
        return false;
    }

    //同一时间创建一个唯一文件
    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;
   }

异步回调通知里调用创建文件的方法:

public void notifyUrl(PayRpmtEntity payRpmt, String noAgree ,JSONObject resultMap) throws Exception{

        //1.根据“还款-支付记录表”获取rpmtIds,循环修改还款计划状态
        String rpmtIds = payRpmt.getRpmtIds();

        //创建一个唯一文件
        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{

        //创建一个唯一文件
        int size = 0;
        while (size < 30 && !RechargeUtil.creatOnlyFile(rpmtIds,"02", "01")) {
            Thread.sleep(100);
            size++;
        }

        if (size >= 30) {
            log.info("****************************状态处理中****************************");
            return ;
        }

        // TODO 逻辑处理// 删除锁文件
        RechargeUtil.deleteOnlyFile(rpmtIds, "02");
    }
时间: 2024-10-15 02:14:02

对接第三方支付接口-类似文件锁的编程小技巧的相关文章

对接第三方支付接口-记录

对接支付接口,得知这种第三方的支付接口的对接方式一般为:组装接口需要的参数,以json数据格式或者其他格式(大多数为json),请求第三方支付接口的url,并且将回调的通知地址放在参数中,等支付完成后,一般成功后会异步请求这个通知接口,会返回相应的数据给系统做一系列的数据操作,然后返回一个成功状态.这样一次第三方支付接口的请求算结束. 由于这次是与APP端开发人员对接,场景为:用户在APP上进行还款操作,APP开发人员将该期还款计划的id和还款方式(主动还款/代扣)传给后台,后台服务端将传递的参

第三方支付接口现号,第三方支付接口对接,第三方支付接口申请

第三方支付公司太多,到目前为止一共有5批206家拿牌支付公司:国付宝,支付宝,环迅,汇潮,易宝,快钱,智付,等等不管您从事与哪家,我们都必须面对这些事实,每家支付公司都有自己的优势跟劣势. 目前国家对第三方支付平台的管控越来越严格,导致目前第三方支付平台的申请越来越严格.特别是对一些个别行业,申请更是难上加难.如果在申请第三方支付平台的过程中需要帮助,请您联系我.QQ:804752009 各类第三方支付接口,包括(支付宝,微信扫码,银盛,商银信,创瑞宝,易通,易宝,国付宝,多付宝,彩虹支付,锐付

第三方支付接口哪家好怎么选择

第三方支付接口哪家好怎么选 第三方支付接口哪家好怎么选,今天小编带大家详细了解下                              支付通道咨询:QQ2119017259:手机VX: 13360876492 随着无线通信网络的发展,特别是苹果iPhone和谷歌Android智能手机的迅速普及,移动支付的发展得到了推动.根据数据研究公司IDC的数据,2017年全球移动支付金额将超过1万亿美元.强劲的数据意味着未来几年全球移动支付业务将继续增强. 移动支付主要分为近场支付和远程支付.所谓的近

对接支付宝支付接口开发详细步骤(证书签名方式)

对接支付宝支付接口,官方文档已经写的很清楚了,但是也有很多像我一样的小白,第一次对接支付宝支付接口,会有些迷茫,所以我在此写下这篇文章,给我和我一样的同学,一点思路吧.需要思路的可以私聊我 两大支付平台感觉都有坑人之处吧(终归是学艺不精,哈哈哈哈!!) 不过支付宝相做的较好的一点是有技术和你在线调试,这就很舒服,哈哈. 寻找技术地址:支付宝开放平台 - 开发者中心 - 网页&移动应用 - 右边有个立即咨询 - 智能问答(多发送几次就有技术出来了) 第一步:(先要在支付宝进行操做,拿到我们需要开发

hadoop编程小技巧(5)---自定义输入文件格式类InputFormat

Hadoop代码测试环境:Hadoop2.4 应用:在对数据需要进行一定条件的过滤和简单处理的时候可以使用自定义输入文件格式类. Hadoop内置的输入文件格式类有: 1)FileInputFormat<K,V>这个是基本的父类,我们自定义就直接使用它作为父类: 2)TextInputFormat<LongWritable,Text>这个是默认的数据格式类,我们一般编程,如果没有特别指定的话,一般都使用的是这个:key代表当前行数据距离文件开始的距离,value代码当前行字符串:

积累的VC编程小技巧之文件操作

1.删除文件夹 // 删除文件夹及其所有内容void CBaseDoc::RemoveFolder(const CString &strPathName){    CString path = strPathName;    if (path.Right(1) != _T("\\"))        path += _T("\\");    path += _T("*.*");    CFileFind ff;    BOOL res =

积累的VC编程小技巧之工具条和状态条

1.工具条和状态条中控件的添加: 方法⑴.只能在ToolBar里创建控件:首先,在ToolBar中创建一个Button,其ID为ID_TOOL_COMBO(我们要将创建的控件放在该Button的位置上). 其次,新创建一个类CMainToolBar,要从CToolBar继承(创建过程大概如下:选择工程/增加到工程/新的类:也可以选择工程的根,然后点击右键,选择新的类:或者CTL+W,选择增加类/新的类 --- 然后在class type里选择Generic Class,在Name栏里输入新类的名

hadoop编程小技巧(2)---计数器Counter

Hadoop代码测试版本:2.4 应用场景:在Hadoop编程的时候,有时我们在进行我们算法逻辑的时候想附带了解下数据的一些特性,比如全部数据的记录数有多少,map的输出有多少等等信息(这些是在算法运行完毕后,直接有的),就可以使用计数器Counter. 如果是针对很特定的数据的一些统计,比如统计以1开头的所有记录数等等信息,这时就需要自定义Counter.自定义Counter有两种方式,第一种,定义枚举类型,类似: public enum MyCounters{ ALL_RECORDS,ONE

hadoop编程小技巧(7)---自定义输出文件格式以及输出到不同目录

代码测试环境:Hadoop2.4 应用场景:当需要定制输出数据格式时可以采用此技巧,包括定制输出数据的展现形式,输出路径,输出文件名称等. Hadoop内置的输出文件格式有: 1)FileOutputFormat<K,V>  常用的父类: 2)TextOutputFormat<K,V> 默认输出字符串输出格式: 3)SequenceFileOutputFormat<K,V> 序列化文件输出: 4)MultipleOutputs<K,V> 可以把输出数据输送到