34. 尚融宝充值

整体流程

标的产生后,平台展示标的,投资人就可以在平台投资标的,获取收益;投资人投资标的必须满足以下条件:

34. 尚融宝充值_第1张图片

充值过程与绑定过程一致,也是在平台发送充值请求,跳转到资金托管平台,在资金托管平台完成充值,然后同步或异步返回或通知平台

相关数据库表

34. 尚融宝充值_第2张图片

参考文档

参考《汇付宝商户账户技术文档》3.9用户充值

充值

需求

step1:用户在个人中心点击 “充值”

step2:尚融宝展示账户充值页面
34. 尚融宝充值_第3张图片

step3:用户填写充值金额,点击“充值”按钮

step4:跳转到汇付宝页面(资金托管接口调用)

34. 尚融宝充值_第4张图片

step5:汇付宝验证用户交易密码

34. 尚融宝充值_第5张图片

后端

controller

UserAccountController.java

package com.indi.srb.core.controller.api;

@Api(tags = "用户账户")
@RestController
@RequestMapping("/api/core/userAccount")
@Slf4j
public class UserAccountController {
     
    @Resource
    private UserAccountService userAccountService;

    @ApiOperation("账户充值")
    @PostMapping("/auth/commitCharge/{chargeAmt}")
    public R recharge(
            @PathVariable BigDecimal chargeAmt,
            HttpServletRequest request) {
     
        String token = request.getHeader("token");
        Long userId = JwtUtils.getUserId(token);
        String formStr = userAccountService.commitCharge(chargeAmt, userId);
        return R.ok().setData("formStr", formStr);
    }
}

service

UserAccountService.java

    String commitCharge(BigDecimal chargeAmt, Long userId);

UserAccountServiceImpl.java

    @Resource
    private UserInfoMapper userInfoMapper;
    
	@Override
    public String commitCharge(BigDecimal chargeAmt, Long userId) {
     
        // 判断用户绑没绑定
        UserInfo userInfo = userInfoMapper.selectById(userId);
        String bindCode = userInfo.getBindCode();
        Assert.notEmpty(bindCode, ResponseEnum.USER_NO_BIND_ERROR);

        // 组装表单
        Map<String, Object> map = new HashMap<>();
        map.put("agentId", HfbConst.AGENT_ID);
        map.put("agentBillNo", LendNoUtils.getChargeNo());
        map.put("bindCode", bindCode);
        map.put("chargeAmt", chargeAmt);
        map.put("feeAmt", new BigDecimal(0));
        map.put("notifyUrl", HfbConst.RECHARGE_NOTIFY_URL);
        map.put("returnUrl", HfbConst.RECHARGE_RETURN_URL);
        map.put("timestamp", RequestHelper.getTimestamp());
        map.put("sign", RequestHelper.getSign(map));

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

前端

template

pages/user/recharge.vue

<template>
  <div class="personal-main">
    <div class="personal-pay">
      <h3><i>充值i>h3>
      <div class="quick-pay-wrap">
        <h4>
          <span class="quick-tit pay-cur"><em>汇付宝充值em>span>
        h4>
        <form id="form" name="form" method="post" action="">
          <div class="quick-main">
            <div class="fl quick-info">
              <div class="info-1">
                <span class="info-tit">充值金额span>
                <span class="info1-input">
                  <input
                    type="text"
                    class="pay-money-txt"
                    maxlength="10"
                    v-model="chargeAmt"
                  />
                  <em>em>
                span>
              div>
              <div class="bank-check" id="bank-check2">
                <b class="selected" id="bankProtocol1">b>
                <span class="bank-agree">
                  我同意并接受
                  <a href="#" target="_blank">
                    《尚融宝投资咨询与管理服务电子协议》
                  a>
                span>
              div>
              <input
                type="button"
                value="充值"
                class="btn-paycz"
                @click="commitCharge()"
              />
            div>

            <div class="pay-tipcon" style="height: 110px;">
              <b>温馨提示:b><br />
              1、为了您的资金安全,您的账户资金由第三方汇付宝进行托管。<br />
              2、充值前请注意您的银行卡充值限额,以免造成不便。<br />
              3、为了您的资金安全,建议充值前进行实名认证。<br />
              4、如果充值遇到任何问题,请联系客服:4006-001-999。
            div>
          div>
        form>
      div>
    div>
  div>
template>

script

pages/user/recharge.vue

ajax远程调用:远程服务器必修开放跨域访问权限

form表单远程调用:不受跨域控制、缺点需要组装表单

<script>
export default {
      
  data() {
      
    return {
      
      chargeAmt: 0,
    }
  },
  methods: {
      
    commitCharge() {
      
      this.$alert(
        '
您即将前往汇付宝充值
'
, '前往汇付宝资金托管平台', { dangerouslyUseHTMLString: true, confirmButtonText: '立即前往', callback: (action) => { if (action === 'confirm') { this.$axios .$post( '/api/core/userAccount/auth/commitCharge/' + this.chargeAmt ) .then((response) => { document.write(response.data.formStr) }) } }, } ) }, }, }
script>

充值回调

需求

step7:异步回调(1)账户金额更改(2)添加交易流水

step8:用户点击“返回平台”,返回尚融宝

34. 尚融宝充值_第6张图片

接口幂等性原则

什么是接口幂等性

接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次调用而产生了副作用。

举个最简单的例子,那就是支付,用户购买商品后支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条...这就没有保证接口的幂等性

存在的问题

当回调重试时,金额和流水会重复增加

34. 尚融宝充值_第7张图片

解决方案

1、设置唯一索引

设置了唯一索引后,即使回调重复执行,遇到唯一索引,就会抛出异常,从而使事务回滚,这是保底方案,我们要尽量避免程序进入到这一步。

34. 尚融宝充值_第8张图片

2、判断流水是否存在

判断流水如果存在,则从业务方法中直接退出

TransFlowService.java

    boolean isSaveTransFlow(String agentBillNo);

后端UserAccount

controller

UserAccountController.java

    @ApiOperation("充值回调")
    @PostMapping("/notify")
    public String notify(HttpServletRequest request){
     
        // 获取返回来的回调参数
        Map<String, Object> paramMap = RequestHelper.switchMap(request.getParameterMap());
        log.info("用户充值异步回调:"+ JSON.toJSONString(paramMap));

        // 校验签名
        if(RequestHelper.isSignEquals(paramMap)){
     
            // 判断返回结果
            // 充值成功
            if ("0001".equals(paramMap.get("resultCode"))){
     
                return userAccountService.notify(paramMap);
            }else{
     
                log.info("充值失败:"+JSON.toJSONString(paramMap));
                // 如果想让汇付宝发起失败重试的话,则返回非success字段,如果不需要重试,则返回success
                // 此处返回success,是因为汇付宝那边已经充值失败了,所以根本就不需要重试,再重试也是失败,所以直接给用户返回错误信息即可。
                return "success";
            }
        }else{
     
            // 此处可能是因为服务器签名错误、或者是请求超时,
            // 验证签名的时候,汇付宝那边会发过一个时间戳过来,这个时间戳一般都是实时的,这边接收之后发现是好几分钟之前的,
            // 这就可能是请求被别人拦截了,然后模拟了一个请求,给发过来了,时间花费较长,可能就会导致请求超时
            // 所以需要发起汇付宝重试
            log.info("充值签名错误:"+JSON.toJSONString(paramMap));
            return "fail";
        }
    }

service

UserAccountService.java

    String notify(Map<String, Object> paramMap);

UserAccountServiceImpl.java

    @Resource
    private TransFlowService transFlowService;

    @Transactional(rollbackFor = Exception.class)
	@Override
    public String notify(Map<String, Object> paramMap) {
     
        // 能进入到这,就说明充值成功了
        log.info("充值成功:" + JSONObject.toJSONString(paramMap));

        String agentBillNo = (String) paramMap.get("agentBillNo");
        if (transFlowService.haveTransFlow(agentBillNo)) {
     
            log.warn("幂等性返回");            
            return "success";
        } 

        String bindCode = (String) paramMap.get("bindCode");
        String chargeAmt = (String) paramMap.get("chargeAmt");

        // 根据绑定协议查到用户id,然后更新账户余额、冻结金额、
        baseMapper.updateAccount(bindCode, new BigDecimal(chargeAmt), new BigDecimal(0));

        // 增加交易流水
        TransFlowBO transFlowBO = new TransFlowBO(
                agentBillNo,
                bindCode,
                new BigDecimal(chargeAmt),
                TransTypeEnum.RECHARGE,
                TransTypeEnum.RECHARGE.getTransTypeName()
        );
        transFlowService.saveTransFlow(transFlowBO);
        return "success";
    }

mapper

UserAccountMapper.java

    void updateAccount(@Param("bindCode") String bindCode,
                       @Param("amount") BigDecimal amount,
                       @Param("freezeAmount") BigDecimal freezeAmount);

UserAccountMapper.xml



<mapper namespace="com.indi.srb.core.mapper.UserAccountMapper">
	<update id="updateAccount">
		update user_account
		set
			amount = amount + #{amount},
			freeze_amount = freeze_amount + #{freezeAmount}
		where
			user_id = (select user_id from user_bind where bind_code = #{bindCode})
	update>
mapper>

后端TransFlow

bo

TransFlowBO.java

@Data
@NoArgsConstructor
@AllArgsConstructor
public class TransFlowBO {
     
    private String agentBillNo; // 商户充值订单号
    private String bindCode;    // 绑定协议
    private BigDecimal amount;  // 充值金额
    private TransTypeEnum transTypeEnum;    // 交易类型
    private String memo;    // 备注
}

service

TransFlowService.java

    void saveTransFlow(TransFlowBO transFlowBO);

    boolean isSaveTransFlow(String agentBillNo);

TransFlowServiceImpl.java

    @Resource
    private UserInfoMapper userInfoMapper;

    @Override
    public void saveTransFlow(TransFlowBO transFlowBO) {
     
        QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("bind_code", transFlowBO.getBindCode());
        UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);

        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userInfo.getId());
        transFlow.setUserName(userInfo.getName());
        transFlow.setTransNo(transFlowBO.getAgentBillNo());
        transFlow.setTransType(transFlowBO.getTransTypeEnum().getTransType());
        transFlow.setTransTypeName(transFlowBO.getTransTypeEnum().getTransTypeName());
        transFlow.setTransAmount(transFlowBO.getAmount());
        transFlow.setMemo(transFlowBO.getMemo());
        baseMapper.insert(transFlow);
    }

    @Override
    public boolean isSaveTransFlow(String agentBillNo) {
     
        QueryWrapper<TransFlow> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("trans_No", agentBillNo);
        Integer count = baseMapper.selectCount(queryWrapper);
        return count > 0;
    }

你可能感兴趣的:(尚融宝,java,vue.js)