目录
1.创建 FaDaDaController 注入config
一、开发前准备
1.点击查看 法大大API文档
2.获取 法大大相关配置(配合官方技术支持)
- 创建应用获取 AppId、oppenCorpId、appSecret
- 创建模板获取模板id signTemplateId
二、引入依赖完成配置
1.引入法大大依赖
<dependency>
<groupId>com.fadada.api</groupId>
<artifactId>fasc-openapi-java-sdk</artifactId>
<version>{sdk-version}</version>
</dependency>
2.yml配置文件
#法大大配置
fadada:
oppenCorpId: xxxxxxxxx
appId: xxxxxx
appSecret: xxxxxxxxx
serverUrl: https://api.fadada.com/api/v5/
signTemplateId: xxxxxxxxxxxxxxx
3.配置文件config
创建配置文件 FaDaDaConfig
@Slf4j
@Data
@Component
@ConfigurationProperties(prefix = "fadada")
public class FaDaDaConfig {
@Value("${fadada.appId}")
private String appId;
@Value("${fadada.appSecret}")
private String appSecret;
@Value("${fadada.serverUrl}")
private String serverUrl;
@Value("${fadada.oppenCorpId}")
private String oppenCorpId;
@Value("${fadada.signTemplateId}")
private String signTemplateId;
@Bean
public OpenApiClient appClient() {
return new OpenApiClient(appId, appSecret, serverUrl);
}
//获取凭证
public String getAccessToken(OpenApiClient openApiClient) throws ApiException {
ServiceClient serviceClient = new ServiceClient(openApiClient);
log.info("获取凭证:{}", openApiClient.getAppId(), openApiClient.getAppSecret());
BaseRes<AccessTokenRes> res = serviceClient.getAccessToken();
ResultUtil.printLog(res, openApiClient.getJsonStrategy());
return res.getData().getAccessToken();
}
}
三、对接法大大API(参考API文档)
1.创建 FaDaDaController 注入config
@Slf4j
@RestController
@RequestMapping("/faDaDa")
public class FaDaDaController {
@Resource
private FaDaDaConfig faDaDaConfig;
}
2.编写生成随机数方法
/**
* 生成随机数
*/
private String generateNonce() {
// 生成一个随机数,最长32个字符
return UUID.randomUUID().toString().replace("-", "").substring(0, 32);
}
3. 创建签署任务
/**
* 创建签署任务
*/
public String createTest(String accessToken, Map<String, Object> bizContent) throws Exception {
// 构造请求URL
String url = faDaDaConfig.getServerUrl() + "/sign-task/create-with-template";
// Unix标准时间戳
String timestamp = Long.toString(System.currentTimeMillis());
// 构造请求头
Map<String, String> headers = new HashMap<>();
headers.put("X-FASC-App-Id", faDaDaConfig.getAppId());
headers.put("X-FASC-Sign-Type", "HMAC-SHA256");
headers.put("X-FASC-Timestamp", timestamp);
headers.put("X-FASC-Nonce", generateNonce());
headers.put("X-FASC-AccessToken", accessToken);
headers.put("X-FASC-Api-SubVersion", "5.1");
headers.put("bizContent", JSON.toJSONString(bizContent));
// 排序后的参数字符串,FddCryptUtil为法大大提供得签名工具类
String paramToSignStr = FddCryptUtil.sortParameters(headers);
// 计算之后得到签名字符串,该签名字符串需放到请求头中的X-FASC-Sign字段
String signature = FddCryptUtil.sign(paramToSignStr, timestamp, faDaDaConfig.getAppSecret());
headers.put("X-FASC-Sign", signature);
// 构造请求体
Map<String, Object> requestParams = new HashMap<>();
requestParams.put("bizContent", JSON.toJSONString(bizContent));
// 发送POST请求
String response = HttpUtils.Post(url, requestParams, headers);
// 解析返回结果
JSONObject jsonObject = JSON.parseObject(response);
JSONObject data = jsonObject.getJSONObject("data");
String taskId = data.getString("signTaskId");
return taskId;
}
4. 获取参与方签署链接
public Map<String, Object> getSignUrl(String accessToken, Map<String, Object> bizContent) throws Exception {
// 构造请求URL
String url = faDaDaConfig.getServerUrl() + "/sign-task/actor/get-url";
// Unix标准时间戳
String timestamp = Long.toString(System.currentTimeMillis());
// 构造请求头
Map<String, String> headers = new HashMap<>();
headers.put("X-FASC-App-Id", faDaDaConfig.getAppId());
headers.put("X-FASC-Sign-Type", "HMAC-SHA256");
headers.put("X-FASC-Timestamp", timestamp);
headers.put("X-FASC-Nonce", generateNonce());
headers.put("X-FASC-AccessToken", accessToken);
headers.put("X-FASC-Api-SubVersion", "5.1");
headers.put("bizContent", JSON.toJSONString(bizContent));
// 排序后的参数字符串,FddCryptUtil为法大大提供得签名工具类
String paramToSignStr = FddCryptUtil.sortParameters(headers);
// 计算之后得到签名字符串,该签名字符串需放到请求头中的X-FASC-Sign字段
String signature = FddCryptUtil.sign(paramToSignStr, timestamp, faDaDaConfig.getAppSecret());
headers.put("X-FASC-Sign", signature);
// 构造请求体
Map<String, Object> requestParams = new HashMap<>();
requestParams.put("bizContent", JSON.toJSONString(bizContent));
// 发送POST请求
String response = HttpUtils.Post(url, requestParams, headers);
// 解析返回结果
JSONObject jsonObject = JSON.parseObject(response);
String code = jsonObject.getString("code");
String msg = jsonObject.getString("msg");
Map<String, Object> result = new HashMap<>();
if ("100000".equals(code)) {
JSONObject data = jsonObject.getJSONObject("data");
String actorSignTaskUrl = data.getString("actorSignTaskUrl");
String actorSignTaskEmbedUrl = data.getString("actorSignTaskEmbedUrl");
result.put("actorSignTaskUrl", actorSignTaskUrl);
result.put("actorSignTaskEmbedUrl", actorSignTaskEmbedUrl);
} else {
result.put("error", msg);
}
return result;
}
5.前端请求接口、集合签署
@GetMapping("/xxxxx")
public Map<String, Object> test(@RequestParam Long memberId) throws Exception {
log.info("id:{}", memberId);
OpenApiClient apiClient = faDaDaConfig.appClient();
// 获取应用accessToken
String accessToken = faDaDaConfig.getAccessToken(apiClient);
Member member = memberService.getById(memberId);
// 一、创建签署任务
// 1.构造请求参数
Map<String, Object> bizContent = new HashMap<>();
bizContent.put("signTaskSubject", "xxxx签署任务");
bizContent.put("signTemplateId", faDaDaConfig.getSignTemplateId());
bizContent.put("autoStart", true);
bizContent.put("transReferenceId",member.getPhone());
Map<String, Object> initiator = new HashMap<>();
initiator.put("idType", "corp");
initiator.put("openId", faDaDaConfig.getOppenCorpId());
bizContent.put("initiator", initiator);
List<Map<String, Object>> actors = new ArrayList<>();
Map<String, Object> actor = new HashMap<>();
Map<String, Object> actorDetail = new HashMap<>();
actorDetail.put("actorId", "乙方");
actorDetail.put("actorType", "person");
actorDetail.put("actorName", member.getName());
actorDetail.put("permissions", Arrays.asList("fill", "sign"));
actorDetail.put("sendNotification", "false");
actor.put("actor", actorDetail);
Map<String, Object> signConfigInfo = new HashMap<>();
signConfigInfo.put("blockHere", false);
signConfigInfo.put("requestVerifyFree", false);
actor.put("signConfigInfo", signConfigInfo);
actors.add(actor);
bizContent.put("actors", actors);
// 2.调用方法
String taskId = createTest(accessToken, bizContent);
log.info("bizContent:{}", bizContent);
log.info("taskId:{}", taskId);
// 将taskId存入数据库(可选)
member.setSignTaskId(taskId);
member.setMid(memberId);
memberService.updateMember(member);
// 二、获取参与方签署链接
Map<String, Object> signUrlsBizContent = new HashMap<>();
signUrlsBizContent.put("signTaskId", taskId);
signUrlsBizContent.put("actorId", "乙方");
Map<String, Object> signUrls = getSignUrl(accessToken, signUrlsBizContent);
log.info("signUrlsBizContent:{}", signUrlsBizContent);
log.info("signUrls:{}", signUrls);
// 三、将签署链接返回给前端
Map<String, Object> result = new HashMap<>();
result.put("taskId", taskId);
result.putAll(signUrls);
return result;
}
这个方法用于处理前端请求,集成了前面的创建签署任务、获取签署链接,可加入自己的业务逻辑,其中参数比较多,需要对照官方文档来填写。
6.编写签署完成回调
/**
* 签署完成回调
*/
@PassToken
@ResponseBody
@PostMapping("/xxxxx")
public String fddEventCallback(@RequestHeader HttpHeaders headers,
@RequestParam("bizContent") String bizContent) throws Exception {
log.info("回调方法执行headers:{}", headers);
log.info("回调方法执行bizContent:{}", bizContent);
String appSecret = faDaDaConfig.getAppSecret();
// 获取请求头参数
String appId = headers.getFirst("X-FASC-App-Id");
String signType = headers.getFirst("X-FASC-Sign-Type");
String sign = headers.getFirst("X-FASC-Sign");
String timestamp = headers.getFirst("X-FASC-Timestamp");
// 事件名称,开发者可以根据不同事件名称去解析bizContent的值,实现不同的逻辑
String event = headers.getFirst("X-FASC-Event");
String nonce = headers.getFirst("X-FASC-Nonce");
// 验签
Map<String, String> paramMap = new HashMap<>();
paramMap.put("X-FASC-App-Id", appId);
paramMap.put("X-FASC-Sign-Type", "HMAC-SHA256");
paramMap.put("X-FASC-Timestamp", timestamp);
paramMap.put("X-FASC-Nonce", nonce);
paramMap.put("X-FASC-Event", event);
paramMap.put("bizContent", bizContent);
// 参数排序,ASCII码排序
String sortParam = FddCryptUtil.sortParameters(paramMap);
// 生成签名后可以进行校验
String signature = FddCryptUtil.sign(sortParam, timestamp, appSecret);
if (!signature.equals(sign)) {
log.error("日志记录,签名失败");
// 为了不重复接收该请求,建议这里返回success,返回success后这条消息法大大将中断重试回调机制
return "{\"msg\":\"success\"}";
}
// 解析bizContent
JSONObject bizContentJson = JSON.parseObject(bizContent);
String eventTime = bizContentJson.getString("eventTime");
String signTaskId = bizContentJson.getString("signTaskId");
String signTaskStatus = bizContentJson.getString("signTaskStatus");
// 业务处理
if ("sign-task-signed".equals(event)) {
// 处理签署任务完成的逻辑
log.info("签署任务完成回调, 任务ID: {}, 时间: {}", signTaskId, eventTime);
// 业务处理
// 写入你自己的逻辑
}
return "{\"msg\":\"success\"}";
} else {
log.warn("收到未处理的事件或状态, 事件: {}, 状态: {}", event, signTaskStatus);
return null;
}
}
四、在法大大平台配置回调地址
注意:回调地址必须是可以由外网访问的
......项目实测有效