26. 尚融宝账户绑定

准备工作

一、运行汇付宝

1、数据库

hfb.sql

2、程序

hfb

3、数据库配置

application-dev.yml 中修改数据库配置

4、启动程序

端口:9999

二、参考文档

1、传入参数

参考《汇付宝商户账户技术文档》业务接口3.1参数说明

2、返回结果

参考《汇付宝商户账户技术文档》业务接口3.2返回/通知结果

3、数据接口

参考《汇付宝商户账户技术文档》业务接口3.3账户绑定

账户创建

需求

step1:用户在个人中心点击 “立即开通” http://localhost:3000/user/bind

step2:尚融宝展示账户绑定页面

在这里插入图片描述

step3:用户填写基本信息(注意:身份证必须是尚未开户),点击“开户”按钮

step4:尚融宝后台创建用户绑定信息(新建user_bind记录)

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

在这里插入图片描述

后端准备工作

1、indi-common

util包引入HttpUtils.java工具类

这个工具类使用了另一个常见的远程连接工具:java.net.HttpURLConnection

package com.indi.common.util;

import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 *
 */
@Slf4j
public final class HttpUtils {

	static final String POST = "POST";
	static final String GET = "GET";
	static final int CONN_TIMEOUT = 30000;// ms
	static final int READ_TIMEOUT = 30000;// ms

	/**
	 * post 方式发送http请求.
	 * 
	 * @param strUrl
	 * @param reqData
	 * @return
	 */
	public static byte[] doPost(String strUrl, byte[] reqData) {
		return send(strUrl, POST, reqData);
	}

	/**
	 * get方式发送http请求.
	 * 
	 * @param strUrl
	 * @return
	 */
	public static byte[] doGet(String strUrl) {
		return send(strUrl, GET, null);
	}

	/**
	 * @param strUrl
	 * @param reqmethod
	 * @param reqData
	 * @return
	 */
	public static byte[] send(String strUrl, String reqmethod, byte[] reqData) {
		try {
			URL url = new URL(strUrl);
			HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();
			httpcon.setDoOutput(true);
			httpcon.setDoInput(true);
			httpcon.setUseCaches(false);
			httpcon.setInstanceFollowRedirects(true);
			httpcon.setConnectTimeout(CONN_TIMEOUT);
			httpcon.setReadTimeout(READ_TIMEOUT);
			httpcon.setRequestMethod(reqmethod);
			httpcon.connect();
			if (reqmethod.equalsIgnoreCase(POST)) {
				OutputStream os = httpcon.getOutputStream();
				os.write(reqData);
				os.flush();
				os.close();
			}
			BufferedReader in = new BufferedReader(new InputStreamReader(httpcon.getInputStream(),"utf-8"));
			String inputLine;
			StringBuilder bankXmlBuffer = new StringBuilder();
			while ((inputLine = in.readLine()) != null) {  
			    bankXmlBuffer.append(inputLine);  
			}  
			in.close();  
			httpcon.disconnect();
			return bankXmlBuffer.toString().getBytes();
		} catch (Exception ex) {
			log.error(ex.toString(), ex);
			return null;
		}
	}
	
	/**
	 * 从输入流中读取数据
	 * 
	 * @param inStream
	 * @return
	 * @throws Exception
	 */
	public static byte[] readInputStream(InputStream inStream) throws Exception {
		ByteArrayOutputStream outStream = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len = 0;
		while ((len = inStream.read(buffer)) != -1) {
			outStream.write(buffer, 0, len);
		}
		byte[] data = outStream.toByteArray();// 网页的二进制数据
		outStream.close();
		inStream.close();
		return data;
	}
}

2、service-base

添加依赖

    <!-- json解析 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
    </dependency>

3、service-core

创建hfb包,添加辅助类

FormHelper:构建表单工具

package com.indi.srb.core.hfb;

import java.util.Iterator;
import java.util.Map;

public class FormHelper {

    /**
     * 构建自动提交form表单
     * @param url 表单提交的url
     * @param paramMap 表单的提交项
     * @return
     */
    public static String buildForm(String url, Map<String, Object> paramMap) {

        StringBuffer inputStr = new StringBuffer();
        Iterator<Map.Entry<String, Object>> entries = paramMap.entrySet().iterator();
        while(entries.hasNext()){
            Map.Entry<String, Object> entry = entries.next();
            String key = entry.getKey();
            Object value = entry.getValue();
            inputStr.append("<input type='hidden' name='"+key+"' value='"+value+"'/>");
        }

        String formStr = "<!DOCTYPE html>\n" +
                "<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\">\n" +
                "<head>\n" +
                "</head>\n" +
                "<body>\n" +
                "<form name=\"form\" action=\""+url+"\" method=\"post\">\n"+

                inputStr +

                "</form>\n" +
                "<script>\n" +
                "\tdocument.form.submit();\n" +
                "</script>\n" +
                "</body>\n" +
                "</html>";
        return formStr;
    }

}

HfbConst:常量定义

package com.indi.srb.core.hfb;

/**
 * 汇付宝常量定义
 */
public class HfbConst {

    //给商户分配的唯一标识
    public static final String AGENT_ID = "999888";
    //签名key
    public static final String SIGN_KEY = "9876543210";


    /**
     * 用户绑定
     */
    //用户绑定汇付宝平台url地址
    public static final String USERBIND_URL = "http://localhost:9999/userBind/BindAgreeUserV2";
    //用户绑定异步回调
    public static final String USERBIND_NOTIFY_URL = "http://localhost/api/core/userBind/notify";
    //用户绑定同步回调
    public static final String USERBIND_RETURN_URL = "http://localhost:3000/user";

    /**
     * 充值
     */
    //充值汇付宝平台url地址
    public static final String RECHARGE_URL = "http://localhost:9999/userAccount/AgreeBankCharge";
    //充值异步回调
    public static final String RECHARGE_NOTIFY_URL = "http://localhost/api/core/userAccount/notify";
    //充值同步回调
    public static final String RECHARGE_RETURN_URL = "http://localhost:3000/user";

    /**
     * 投标
     */
    //充值汇付宝平台url地址
    public static final String INVEST_URL = "http://localhost:9999/userInvest/AgreeUserVoteProject";
    //充值异步回调
    public static final String INVEST_NOTIFY_URL = "http://localhost/api/core/lendItem/notify";
    //充值同步回调
    public static final String INVEST_RETURN_URL = "http://localhost:3000/user";

    /**
     * 放款
     */
    public static final String MAKE_LOAD_URL = "http://localhost:9999/userInvest/AgreeAccountLendProject";

    /**
     * 提现
     */
    //充值汇付宝平台url地址
    public static final String WITHDRAW_URL = "http://localhost:9999/userAccount/CashBankManager";
    //充值异步回调
    public static final String WITHDRAW_NOTIFY_URL = "http://localhost/api/core/userAccount/notifyWithdraw";
    //充值同步回调
    public static final String WITHDRAW_RETURN_URL = "http://localhost:3000/user";

    /**
     * 还款扣款
     */
    //充值汇付宝平台url地址
    public static final String BORROW_RETURN_URL = "http://localhost:9999/lendReturn/AgreeUserRepayment";
    //充值异步回调
    public static final String BORROW_RETURN_NOTIFY_URL = "http://localhost/api/core/lendReturn/notifyUrl";
    //充值同步回调
    public static final String BORROW_RETURN_RETURN_URL = "http://localhost:3000/user";

}

RequestHelper.java:签名工具

package com.indi.srb.core.hfb;

import com.alibaba.fastjson.JSONObject;
import com.indi.common.util.HttpUtils;
import com.indi.common.util.MD5;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

@Slf4j
public class RequestHelper {

    public static void main(String[] args) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("d", "4");
        paramMap.put("b", "2");
        paramMap.put("c", "3");
        paramMap.put("a", "1");
    }

    /**
     * 请求数据获取签名
     * @param paramMap
     * @return
     */
    public static String getSign(Map<String, Object> paramMap) {
        if(paramMap.containsKey("sign")) {
            paramMap.remove("sign");
        }
        TreeMap<String, Object> sorted = new TreeMap<>(paramMap);
        StringBuilder str = new StringBuilder();
        for (Map.Entry<String, Object> param : sorted.entrySet()) {
            str.append(param.getValue()).append("|");
        }
        str.append(HfbConst.SIGN_KEY);
        log.info("加密前:" + str.toString());
        String md5Str = MD5.encrypt(str.toString());
        log.info("加密后:" + md5Str);
        return md5Str;
    }

    /**
     * Map转换
     * @param paramMap
     * @return
     */
    public static Map<String, Object> switchMap(Map<String, String[]> paramMap) {
        Map<String, Object> resultMap = new HashMap<>();
        for (Map.Entry<String, String[]> param : paramMap.entrySet()) {
            resultMap.put(param.getKey(), param.getValue()[0]);
        }
        return resultMap;
    }

    /**
     *  签名校验
     * @param paramMap
     * @return
     */
    public static boolean isSignEquals(Map<String, Object> paramMap) {
        String sign = (String)paramMap.get("sign");
        String md5Str = getSign(paramMap);
        if(!sign.equals(md5Str)) {
            return false;
        }
        return true;
    }

    /**
     * 获取时间戳
     * @return
     */
    public static long getTimestamp() {
        return new Date().getTime();
    }

    /**
     * 封装同步请求
     * @param paramMap
     * @param url
     * @return
     */
    public static JSONObject sendRequest(Map<String, Object> paramMap, String url){
        String result = "";
        try {
            //封装post参数
            StringBuilder postdata = new StringBuilder();
            for (Map.Entry<String, Object> param : paramMap.entrySet()) {
                postdata.append(param.getKey()).append("=")
                        .append(param.getValue()).append("&");
            }
            log.info(String.format("--> 发送请求到汇付宝:post data %1s", postdata));
            byte[] reqData = postdata.toString().getBytes("utf-8");
            byte[] respdata = HttpUtils.doPost(url,reqData);
            result = new String(respdata);
            log.info(String.format("--> 汇付宝应答结果:result data %1s", result));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return JSONObject.parseObject(result);
    }
}

还有一些枚举类,太多了,我就不添加了,看看以后传到gitee上吧

后端实现

vo

UserBindVO.java

package com.indi.srb.core.pojo.vo;

@Data
@ApiModel(description = "账户绑定")
public class UserBindVO {
    @ApiModelProperty(value = "用户姓名")
    private String name;

    @ApiModelProperty(value = "身份证号")
    private String idCard;

    @ApiModelProperty(value = "银行卡号")
    private String bankNo;

    @ApiModelProperty(value = "银行类型")
    private String bankType;

    @ApiModelProperty(value = "手机号")
    private String mobile;
}

service

UserBindService.java

    String commitBindUser(UserBindVO userBindVO,Long userId);

UserBindServiceImpl.java

	@Transactional(rollbackFor = Exception.class)
    @Override
    public String commitBindUser(UserBindVO userBindVO, Long userId) {
        // 查询其他人的绑定信息
        QueryWrapper<UserBind> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .eq("id_card", userBindVO.getIdCard())
                .ne("user_id", userId);
        UserBind userBind = baseMapper.selectOne(queryWrapper);
        // 有没有人用过这个身份证号码
        Assert.isNull(userBind, ResponseEnum.USER_BIND_IDCARD_EXIST_ERROR);

        // 查询当前用户绑定信息
        queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        userBind = baseMapper.selectOne(queryWrapper);

        // 判断是否有绑定记录
        if (userBind == null) {
            // 没有则创建
            userBind = new UserBind();
            // 把用户填的类信息拷贝到绑定表对应的实体类
            BeanUtils.copyProperties(userBindVO,userBind);
            userBind.setId(userId);
            userBind.setStatus(UserBindEnum.NO_BIND.getStatus());
            baseMapper.insert(userBind);
        }else{
            // 有则更新
            BeanUtils.copyProperties(userBindVO,userBind);
            baseMapper.updateById(userBind);
        }

        // 创建自动提交表单
        Map<String, Object> map = new HashMap<>();
        map.put("agentId", HfbConst.AGENT_ID);
        map.put("agentUserId",userBind.getUserId());
        map.put("personalName",userBindVO.getName());
        map.put("idCard",userBindVO.getIdCard());
        map.put("bankNo",userBindVO.getBankNo());
        map.put("bankType",userBindVO.getBankType());
        map.put("mobile",userBindVO.getMobile());
        map.put("returnUrl",HfbConst.USERBIND_RETURN_URL);
        map.put("notifyUrl",HfbConst.USERBIND_NOTIFY_URL);
        map.put("timestamp", RequestHelper.getTimestamp());
        map.put("sign",RequestHelper.getSign(map));

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

controller

UserBindController.java

@Api(tags = "用户认证")
@Slf4j
@RestController
@RequestMapping("/api/core/userBind")
public class UserBindController {
    @Resource
    private UserBindService userBindService;

    @ApiOperation("账户绑定提交数据")
    @PostMapping("/auth/bind")
    public R bind(@RequestBody UserBindVO userBindVO, HttpServletRequest request){
        String token = request.getHeader("token");
        Long userId = JwtUtils.getUserId(token);
        String formStr = userBindService.commitBindUser(userBindVO, userId);
        return R.ok().setData("formStr",formStr);
    }
}

前端实现

pages/user/bind.vue

commitBind() {
      this.$alert(
        '<div style="size: 18px;color: red;">您即将前往汇付宝绑定账号</div>',
        '前往汇付宝资金托管平台',
        {
          dangerouslyUseHTMLString: true,
          confirmButtonText: '立即前往',
          callback: (action) => {
            if (action === 'confirm') {
              this.$axios
                .$post('/api/core/userBind/auth/bind', this.userBind)
                .then((response) => {
                  document.write(response.data.formStr)
                })
            }
          },
        }
      )
},

账户绑定

账户绑定流程

尚融宝应该在网站引导客户在“资金托管平台“开设账户。也就是说投资人和借款人必须在资金托管平台页面上自主开户,与尚融宝建立对应的账户体系。

以后所有的资金操作都会到“资金托管平台”进行,绑定账户流程如图:

在这里插入图片描述

相关数据库表

srb:

在这里插入图片描述

绑定步骤

step6:汇付宝验证用户身份、用户设置交易密码

在这里插入图片描述

step7:点击确定汇付宝创建绑定账号(新建user_bind和user_account记录)

step8:异步回调

尚融宝user_bind表更新bind_code字段、status字段

尚融宝user_info表更新 bind_code字段、name字段、idCard字段、bind_status字段

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

在这里插入图片描述

后端实现

pojo

UserInfo.java

    /**
     * 用户完成汇付宝认证时使用
     *
     */
    public UserInfo(Long id, String name, String idCard, Integer bindStatus, String bindCode) {
        this.id = id;
        this.name = name;
        this.idCard = idCard;
        this.bindStatus = bindStatus;
        this.bindCode = bindCode;
    }

service

UserBindService.java

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

UserBindServiceImpl.java

    @Transactional(rollbackFor = Exception.class)
	@Override
    public void notify(Map<String, Object> map) {
        String bindCode = (String) map.get("bindCode");
        String agentUserId = (String) map.get("agentUserId");

        // 更新用户绑定表:绑定状态、绑定协议
        QueryWrapper<UserBind> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", agentUserId);
        UserBind userBind = baseMapper.selectOne(queryWrapper);
        userBind.setBindCode(bindCode);
        userBind.setStatus(UserBindEnum.BIND_OK.getStatus());
        baseMapper.updateById(userBind);

        // 更新用户表:用户名称、身份证、绑定状态、绑定协议
        UserInfo userInfo = new UserInfo(
                new Long(agentUserId),
                userBind.getName(),
                userBind.getIdCard(),
                UserBindEnum.BIND_OK.getStatus(),
                bindCode
        );
        userInfoMapper.updateById(userInfo);
    }

controller

UserBindController.java

    @ApiOperation("绑定之后的回调")
    @PostMapping("/notify")
    public String notify(HttpServletRequest request){
        Map<String, Object> map = RequestHelper.switchMap(request.getParameterMap());
        log.info("用户账号绑定异步回调:"+ JSON.toJSONString(map));
        if (!RequestHelper.isSignEquals(map)){
            log.error("用户账号绑定异步回调签名错误:"+JSON.toJSONString(map));
            return "fail";
        }

        userBindService.notify(map);
        return "success";
    }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
在程序中使用MVVM框架时,通常需要将视图(View)和视图模型(ViewModel)进行。在WPF或者UWP等框架中,可以使用数据技术来实现这一目的。但是,在使用数据时,我们需要手动为每个View设置其对应的ViewModel,这会导致代码冗余和可维护性差。 为了解决这个问题,我们可以使用ViewModelLocator来自动视图和视图模型。ViewModelLocator是一个ViewModel的容器,它可以根据View的类型自动创建对应的ViewModel,并将其到View上。具体而言,我们可以在ViewModelLocator中义一个autowireViewModel方法,用于创建ViewModel实例并将其与View进行。 下面是一个示例代码: ```csharp public class ViewModelLocator { public MainViewModel MainViewModel => new MainViewModel(); public static void AutowireViewModel(object view) { var viewModelType = view.GetType().FullName + "Model"; var viewModel = Activator.CreateInstance(Type.GetType(viewModelType)); ((FrameworkElement)view).DataContext = viewModel; } } ``` 在这个示例中,我们义了一个MainViewModel,并将其与MainView进行。在AutowireViewModel方法中,我们首先获取View的类型,然后根据类型名称创建ViewModel实例。最后,我们将ViewModel实例与View进行,使得View可以直接访问ViewModel中的属性和方法。 使用ViewModelLocator时,我们只需要调用AutowireViewModel方法即可自动View和ViewModel: ```csharp public partial class MainView : Window { public MainView() { InitializeComponent(); ViewModelLocator.AutowireViewModel(this); } } ``` 这样,我们就可以实现自动ViewModel的功能,从而实现代码的简洁和可维护性的提高。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值