39. 尚融宝提现和还款

提现

需求

在这里插入图片描述
放款成功后,借款人可以申请提现。

参考《汇付宝商户账户技术文档》3.15用户申请提现

在这里插入图片描述

hfb的user_account需要与srb的user_account保持统一

后端

enums

ResponseEnum.java

    USER_WITHDRAW_AMOUNT_ERROR(310,"余额不足,无法提现"),
    USER_AMOUNT_LESS_ZERO_ERROR(311,"提现金额必须大于0"),

controller

UserAccountController.java

    @ApiOperation("提现")
    @PostMapping("/auth/withdraw/{fetchAmt}")
    public R withdraw(
            @ApiParam(value = "提现金额",required = true)
            @PathVariable BigDecimal fetchAmt, HttpServletRequest request){
        String token = request.getHeader("token");
        Long userId = JwtUtils.getUserId(token);
        String formStr = userAccountService.withdraw(fetchAmt,userId);
        return R.ok().setData("formStr",formStr);
    }

    @ApiOperation("提现回调")
    @PostMapping("/notifyWithdraw")
    public String notifyWithdraw(HttpServletRequest request){
        Map<String, Object> map = RequestHelper.switchMap(request.getParameterMap());
        if (RequestHelper.isSignEquals(map)) {
            if ("0001".equals(map.get("resultCode"))) {
                 userAccountService.notifyWithdraw(map);
            }else{
                return "fail";
            }
        }else{
            return "fail";
        }
        return "success";
    }

service

UserAccountService.java

    String withdraw(BigDecimal fetchAmt, Long userId);

    void notifyWithdraw(Map<String, Object> map);
    @Override
    public String withdraw(BigDecimal fetchAmt, Long userId) {
        BigDecimal amount = this.getAmount(userId);

        // 余额是否大于等于提现金额
        Assert.isTrue(fetchAmt.doubleValue() <= amount.doubleValue(), ResponseEnum.USER_WITHDRAW_AMOUNT_ERROR);

        // 不能是负数
        Assert.isTrue(fetchAmt.doubleValue() > 0, ResponseEnum.USER_AMOUNT_LESS_ZERO_ERROR);

        Map<String, Object> map = new HashMap<>();
        map.put("agentId", HfbConst.AGENT_ID);
        map.put("agentBillNo", LendNoUtils.getWithdrawNo());

        String bindCode = userBindService.getBindCodeByUserId(userId);
        map.put("bindCode", bindCode);

        map.put("fetchAmt", fetchAmt);
        map.put("feeAmt", BigDecimal.ZERO);
        map.put("notifyUrl", HfbConst.WITHDRAW_NOTIFY_URL);
        map.put("returnUrl", HfbConst.WITHDRAW_RETURN_URL);
        map.put("timestamp", RequestHelper.getTimestamp());
        map.put("sign", RequestHelper.getSign(map));

        String formStr = FormHelper.buildForm(HfbConst.WITHDRAW_URL, map);
        return formStr;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void notifyWithdraw(Map<String, Object> map) {
        String agentBillNo = (String) map.get("agentBillNo");
        boolean result = transFlowService.haveTransFlow(agentBillNo);
        if (result) {
            log.warn("幂等性返回");
            return;
        }

        String bindCode = (String) map.get("bindCode");
        String fetchAmt = (String) map.get("fetchAmt");

        baseMapper.updateAccount(bindCode, new BigDecimal("-" + fetchAmt), BigDecimal.ZERO);

        TransFlowBO transFlowBO = new TransFlowBO(
                agentBillNo,
                bindCode,
                TransTypeEnum.WITHDRAW,
                new BigDecimal(fetchAmt),
                "提现"
        );
        transFlowService.saveTransFlow(transFlowBO);
    }

前端

script

pages/user/withdraw.vue

  methods: {
    commitWithdraw() {
      this.$alert(
        '<div style="size: 18px;color: red;">您即将前往汇付宝提现</div>',
        '前往汇付宝资金托管平台',
        {
          dangerouslyUseHTMLString: true,
          confirmButtonText: '立即前往',
          callback: (action) => {
            if (action === 'confirm') {
              //提现
              this.$axios
                .$post('/api/core/userAccount/auth/withdraw/' + this.fetchAmt)
                .then((response) => {
                  document.write(response.data.formStr)
                })
            }
          },
        }
      )
    },
  },

放款

放款成功后,会生成借款人的还款计划与出借人的回款计划,然后借款人按照还款计划日期操作还款即可。
参考《汇付宝商户账户技术文档》3.14还款扣款,处理业务即可

需求

在这里插入图片描述

后端

controller

LendReturnController.java

    @ApiOperation("还款")
    @PostMapping("/auth/repayment/{lendReturnId}")
    public R repayment(
            @ApiParam(value = "还款id",required = true)
            @PathVariable Long lendReturnId, HttpServletRequest request){
        String token = request.getHeader("token");
        Long userId = JwtUtils.getUserId(token);
        String formStr = lendReturnService.repayment(lendReturnId,userId);
        return R.ok().setData("formStr",formStr);
    }

	@ApiOperation("还款回调")
    @PostMapping("/notify")
    public String notify(HttpServletRequest request){
        Map<String, Object> map = RequestHelper.switchMap(request.getParameterMap());
        if (RequestHelper.isSignEquals(map)){
            if ("0001".equals(map.get("resultCode"))) {
                lendReturnService.notify(map);
            }else{
                return "fail";
            }
        }else{
            return "fail";
        }
        return "success";
    }

service

LendReturnService.java

    String repayment(Long lendReturnId, Long userId);

	void notify(Map<String, Object> map);

LendReturnServiceImpl.java

    @Resource
    private LendMapper lendMapper;

    @Resource
    private UserBindService userBindService;

    @Resource
    private UserAccountService userAccountService;

    @Override
    public String repayment(Long lendReturnId, Long userId) {
        LendReturn lendReturn = baseMapper.selectById(lendReturnId);

        Assert.isTrue(userAccountService.getAmount(userId).doubleValue() >= lendReturn.getTotal().doubleValue(), ResponseEnum.NOT_SUFFICIENT_FUNDS_ERROR);

        Lend lend = lendMapper.selectById(lendReturn.getLendId());

        String borrowerBindCode = userBindService.getBindCodeByUserId(userId);
        Map<String, Object> map = new HashMap<>();
        map.put("agentId", HfbConst.AGENT_ID);
        map.put("agentGoodsName", lend.getTitle());
        map.put("agentBatchNo", lendReturn.getReturnNo());
        map.put("fromBindCode", borrowerBindCode);
        map.put("totalAmt", lendReturn.getTotal());
        map.put("note", "");
        List<Map<String, Object>> mapList = getLendItemReturnDetail(lendReturnId);
        map.put("data", JSONObject.toJSONString(mapList));
        map.put("voteFeeAmt", BigDecimal.ZERO);
        map.put("returnUrl", HfbConst.BORROW_RETURN_RETURN_URL);
        map.put("notifyUrl", HfbConst.BORROW_RETURN_NOTIFY_URL);
        map.put("timestamp", RequestHelper.getTimestamp());
        map.put("sign", RequestHelper.getSign(map));

        String formStr = FormHelper.buildForm(HfbConst.BORROW_RETURN_URL, map);
        return formStr;
    }
    
    @Resource
    private LendItemReturnService lendItemReturnService;

    @Resource
    private LendItemMapper lendItemMapper;

    private List<Map<String, Object>> getLendItemReturnDetail(Long lendReturnId) {
        List<LendItemReturn> lendItemReturnList = lendItemReturnService.listByLendReturnId(lendReturnId);
        LendReturn lendReturn = baseMapper.selectById(lendReturnId);
        Lend lend = lendMapper.selectById(lendReturn.getLendId());

        List<Map<String, Object>> mapList = new ArrayList<>();
        lendItemReturnList.forEach(item -> {
            Map<String, Object> map = new HashMap<>();
            LendItem lendItem = lendItemMapper.selectById(item.getLendItemId());
            String investBindCode = userBindService.getBindCodeByUserId(item.getInvestUserId());
            map.put("agentProjectCode", lend.getLendNo());
            map.put("voteBillNo", lendItem.getLendItemNo());
            map.put("toBindCode", investBindCode);
            map.put("transitAmt", item.getTotal());
            map.put("baseAmt", item.getPrincipal());
            map.put("benifitAmt", item.getInterest());
            map.put("feeAmt",BigDecimal.ZERO);
            mapList.add(map);
        });
        return mapList;
    }    

LendReturnServiceImpl.java

    @Resource
    private TransFlowService transFlowService;

    @Resource
    private UserAccountMapper userAccountMapper;
	
	@Override
    public void notify(Map<String, Object> map) {
        // 幂等性判断
        String agentBatchNo = map.get("agentBatchNo").toString();
        boolean result = transFlowService.haveTransFlow(agentBatchNo);
        if (result) {
            return;
        }

        // 更新还款
        BigDecimal voteFeeAmt = new BigDecimal(map.get("voteFeeAmt").toString());
        QueryWrapper<LendReturn> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("return_no", agentBatchNo);
        LendReturn lendReturn = baseMapper.selectOne(queryWrapper);
        lendReturn.setStatus(1);
        lendReturn.setFee(voteFeeAmt);
        lendReturn.setRealReturnTime(LocalDateTime.now());
        baseMapper.updateById(lendReturn);

        // 更新标的
        Lend lend = lendMapper.selectById(lendReturn.getLendId());
        if (lendReturn.getLast()) {
            lend.setStatus(LendStatusEnum.PAY_OK.getStatus());
        }
        lendMapper.updateById(lend);

        // 借款人账户转出
        BigDecimal totalAmt = new BigDecimal(map.get("totalAmt").toString());
        String borrowerBindCode = userBindService.getBindCodeByUserId(lendReturn.getUserId());
        userAccountMapper.updateAccount(borrowerBindCode, totalAmt.negate(), BigDecimal.ZERO);

        // 生成借款人交易流水
        TransFlowBO transFlowBO = new TransFlowBO(
                agentBatchNo,
                borrowerBindCode,
                TransTypeEnum.RETURN_DOWN,
                totalAmt,
                "借款人还款,项目编号:" + lend.getLendNo() + ",项目名称:" + lend.getTitle()
        );
        transFlowService.saveTransFlow(transFlowBO);

        
        List<LendItemReturn> lendItemReturnList = lendItemReturnService.listByLendReturnId(lendReturn.getId());
        lendItemReturnList.forEach(item -> {
            // 更新回款
            item.setStatus(1);
            item.setRealReturnTime(LocalDateTime.now());
            lendItemReturnService.updateById(item);

            // 更新投资记录
            LendItem lendItem = lendItemMapper.selectById(item.getLendItemId());
            lendItem.setRealAmount(lendItem.getRealAmount().add(item.getInterest()));
            lendItemMapper.updateById(lendItem);

            // 更新投资人账户
            String investBindCode = userBindService.getBindCodeByUserId(item.getInvestUserId());
            userAccountMapper.updateAccount(investBindCode,item.getTotal(),BigDecimal.ZERO);

            // 生成投资人交易流水
            TransFlowBO investTransFlow = new TransFlowBO(
                    LendNoUtils.getReturnItemNo(),
                    investBindCode,
                    TransTypeEnum.INVEST_BACK,
                    item.getTotal(),
                    "投资人回款,项目编号:" + lend.getLendNo() + ",项目名称:" + lend.getTitle()
            );
            transFlowService.saveTransFlow(investTransFlow);
        });
    }

前端

script

pages/lend/_id.vue

  methods: {
	commitReturn(lendReturnId) {
      this.$alert(
        '<div style="size: 18px;color: red;">您即将前往汇付宝确认还款</div>',
        '前往汇付宝资金托管平台',
        {
          dangerouslyUseHTMLString: true,
          confirmButtonText: '立即前往',
          callback: (action) => {
            if (action === 'confirm') {
              this.$axios
                .$post('/api/core/lendReturn/auth/repayment/' + lendReturnId)
                .then((response) => {
                  document.write(response.data.formStr)
                })
            }
          },
        }
      )
    },

测试各个表的变化

尚融宝

lend_return

fee:手续费没设置,所以是0

第一次还款
在这里插入图片描述
第二次还款
在这里插入图片描述
第三次还款
在这里插入图片描述

lend

status:还款中是2,还完最后一次,会修改状态

第一次还款
在这里插入图片描述
第二次还款
在这里插入图片描述
第三次还款
在这里插入图片描述
trans_flow
在这里插入图片描述
user_account

还款之前
在这里插入图片描述
第一次还款
在这里插入图片描述
第二次还款
在这里插入图片描述
第三次还款
在这里插入图片描述

lend_item

第一次还款

在这里插入图片描述

第二次还款

在这里插入图片描述

第三次还款

在这里插入图片描述

lend_item_return

第一次还款
在这里插入图片描述
第二次还款
在这里插入图片描述

第三次还款
在这里插入图片描述

汇付宝

user_account

在这里插入图片描述

user_return
在这里插入图片描述

user_item_return
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值