
本文共 7213 字,大约阅读时间需要 24 分钟。
整体流程
标的产生后,平台展示标的,投资人就可以在平台投资标的,获取收益;投资人投资标的必须满足以下条件:
充值过程与绑定过程一致,也是在平台发送充值请求,跳转到资金托管平台,在资金托管平台完成充值,然后同步或异步返回或通知平台
相关数据库表
参考文档
参考《汇付宝商户账户技术文档》3.9用户充值
充值
需求
step1:用户在个人中心点击 “充值”
step2:尚融宝展示账户充值页面

step3:用户填写充值金额,点击“充值”按钮
step4:跳转到汇付宝页面(资金托管接口调用)
step5:汇付宝验证用户交易密码
后端
controller
UserAccountController.java
package com.indi.srb.core.controller.api;@Api(tags = "用户账户")@RestController@RequestMapping("/api/core/userAccount")@Slf4jpublic 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); // 组装表单 Mapmap = new HashMap<>(); map.put("agentId", HfbConst.AGENT_ID); map.put("agentBillNo", LendNoUtils.getNo()); 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
充值
汇付宝充值
script
pages/user/recharge.vue
ajax远程调用:远程服务器必修开放跨域访问权限
form表单远程调用:不受跨域控制、缺点需要组装表单
充值回调
需求
step7:异步回调(1)账户金额更改(2)添加交易流水
step8:用户点击“返回平台”,返回尚融宝
接口幂等性原则
什么是接口幂等性
接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次调用而产生了副作用。
举个最简单的例子,那就是支付,用户购买商品后支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条...这就没有保证接口的幂等性
存在的问题
当回调重试时,金额和流水会重复增加
解决方案
1、设置唯一索引
设置了唯一索引后,即使回调重复执行,遇到唯一索引,就会抛出异常,从而使事务回滚,这是保底方案,我们要尽量避免程序进入到这一步。
2、判断流水是否存在
判断流水如果存在,则从业务方法中直接退出
TransFlowService.java
boolean isSaveTransFlow(String agentBillNo);
后端UserAccount
controller
UserAccountController.java
@ApiOperation("充值回调") @PostMapping("/notify") public String notify(HttpServletRequest request){ // 获取返回来的回调参数 MapparamMap = 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(MapparamMap);
UserAccountServiceImpl.java
@Resource private TransFlowService transFlowService; @Transactional(rollbackFor = Exception.class) @Override public String notify(MapparamMap) { // 能进入到这,就说明充值成功了 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
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})
后端TransFlow
bo
TransFlowBO.java
@Data@NoArgsConstructor@AllArgsConstructorpublic 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) { QueryWrapperqueryWrapper = 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 queryWrapper = new QueryWrapper<>(); queryWrapper.eq("trans_No", agentBillNo); Integer count = baseMapper.selectCount(queryWrapper); return count > 0; }
发表评论
最新留言
关于作者
