明道云对接案例之法大大电子签章

分享 零代码明道云APaaS  收藏
0 / 1103

1.jpg

什么是电子签章

电子签章是电子签名的一种表现形式,利用图像处理技术将电子签名操作转化为与纸质文件盖章操作相同的可视效果,同时利用电子签名技术保障电子信息的真实性和完整性以及签名人的不可否认性。

电子合同是企业用途最广的一种合同形式,受《合同法》认可。但由于电子合同以数据电文为载体,涉及到一系列的技术标准,其中最核心、也最重要的是电子签名技术。根据国家法律规定,只有使用可靠电子签名签订的电子合同才具有法律效力。

明道云也十分重视电子签章的功能集成方案。技术顾问经过一段时间的产品调研、测试,以及为客户对接实践,终于形成了较为成熟的电子签章集成对接方案。那么今天,明道云的技术顾问就在这里介绍明道云与法大大的集成对接流程,并简单评价法大大集成的优缺点

对接流程

1.申请密钥

  • 官网联系客服
  • 操作开通申请
  • 在指定邮箱里收到密钥信息及 API 对接文档

2.注册账号、证书申请、印章上传

接口名称解释

  • 为用户注册法⼤大账号:account_register.api
  • 根据账号类型调用对应的接口
    • miget_person_verify_url.api 个⼈实名认证⻚面接
    • get_company_verify_url.api 企业实名认证⻚⾯接⼝
  • 申请实名证书:apply_cert.api

代码块示例

1.账号注册参数加密
var account_type = input.account_type=='个人'?'1':'2';
var v = input.v;
var app_secret = input.app_secret;
var app_id = input.app_id;
var crypto = require('crypto');
function pad2(n) { return n < 10 ? '0' + n : n }
function generateTimeReqestNumber() {
            var datee = new Date();
var localTime = datee .getTime();
var localOffset=datee .getTimezoneOffset()*60000; //获得当地时间偏移的毫秒数
var utc = localTime + localOffset; //utc即GMT时间
var offset =8; //以北京时间为例,东8区
var hawaii = utc + (3600000*offset); 
	var date = new Date(hawaii);
	date.setHours(date.getHours());
	return date.getFullYear().toString() + pad2(date.getMonth() + 1) + pad2(date.getDate()) + pad2(date.getHours()) + pad2(date.getMinutes()) + pad2(date.getSeconds());
}
function sha1(s) {
	var hash = crypto.createHash('sha1');
	hash.update(s);
	return hash.digest('hex').toUpperCase();
}
function md5(s) {
	var hash = crypto.createHash('md5');
	hash.update(s);
	return hash.digest('hex').toUpperCase();
}
function base64(s) {
	var b = new Buffer.from(s);
	return b.toString('base64');
}
function ast(s) {
	return s.split('').sort().join('');
}


var timestamp = generateTimeReqestNumber();	
var msg_digest = base64( sha1( app_id + md5(timestamp) + sha1(app_secret + account_type + input.openid)));
return {ts: timestamp, sign: msg_digest, appid: app_id, v: v, openid: input.open_id, accounttype: account_type};


1.2 调用注册方法
var url=input.url+'/api/account_register.api';
const fetch = require('node-fetch'); 
const FormData= require('form-data');
var result={};
let data= new FormData();
data.append("app_id",input.appid)
data.append("v",input.v)
data.append("timestamp",input.ts)
data.append("msg_digest",input.sign)
data.append("open_id",input.openid)
data.append("account_type",input.accounttype)
async function getAll(data){
    const res = await   fetch(url,{
    method:"post",
    body:data
}).then(function(response){
    if(response.ok){
 return  response.json();   
    }else{
  return   {"error":"无数据"};
}
}).catch(function(err){
   return   {"error":"Fetch错误:"+err};
});
    const result = await   res;
    return result;
}
result=await  getAll(data);
output={result:result}


2申请实名认证参数加密
var v = input.v;
var app_secret = input.appsecret;
var aid = input.aid;
var crypto = require('crypto');
var data={"customer_id":input.customerid,
"verified_way":3,
"page_modify":1,
"option":"add",
"lang":"zh",
"organization_type":0,
"notify_url":"换为你的接受地址"}


function pad2(n) { return n < 10 ? '0' + n : n }
function generateTimeReqestNumber() {
            var datee = new Date();
var localTime = datee .getTime();
var localOffset=datee .getTimezoneOffset()*60000; //获得当地时间偏移的毫秒数
var utc = localTime + localOffset; //utc即GMT时间
var offset =8; //以北京时间为例,东8区
var hawaii = utc + (3600000*offset); 
	var date = new Date(hawaii);
	date.setHours(date.getHours());
	return date.getFullYear().toString() + pad2(date.getMonth() + 1) + pad2(date.getDate()) + pad2(date.getHours()) + pad2(date.getMinutes()) + pad2(date.getSeconds());
}
function sha1(s) {
	var hash = crypto.createHash('sha1');
	hash.update(s);
	return hash.digest('hex').toUpperCase();
}
function md5(s) {
	var hash = crypto.createHash('md5');
	hash.update(s);
	return hash.digest('hex').toUpperCase();
}
function base64(s) {
	var b = new Buffer.from(s);
	return b.toString('base64');
}
function ast(s) {
	return s.split('').sort().join('');
}
var newData = {},params='';
Object.keys(data).sort().map(key => {
  newData[key]=data[key]
  params=params+=data[key];
})
var timestamp = generateTimeReqestNumber();	
var msg_digest = base64( sha1(aid + md5(timestamp) + sha1(app_secret+params)));
newData["app_id"]=aid ;
newData["m_verified_way"]=4;
newData.timestamp=timestamp;
newData.v=v;
newData["msg_digest"]=msg_digest;
output={ts:timestamp,sign:msg_digest,'app_id':aid, v: v, params:params,newData:JSON.stringify(newData )};


2.2调用实名认证接口难道链接
var indata=JSON.parse(input.data);
var url=input.url+'/api/get_company_verify_url.api';
const fetch = require('node-fetch'); 
const FormData= require('form-data');
var result={};
let data= new FormData();


Object.keys(indata).forEach((key) => {
 data.append(key,indata[key]);
})
async function getAll(data){
    const res = await   fetch(url,{
    method:"post",
    body:data
}).then(function(response){
    if(response.ok){
 return  response.json();   
    }else{
  return   {"error":"无数据"};
}
}).catch(function(err){
   return   {"error":"Fetch错误:"+err};
});
    const result = await   res;
    return result;
}
result=await  getAll(data);
output={result:re

拿到接口返回的链接,使用打开界面节点来打开新连接。认证成功后,在你的信息接受地址中调用申请证书接口。

3.配置“上传合同及单个签署”的流程


接口名称解释

  • uploaddocs.api 合同上传接⼝
  • extsign_auto.api 自动签接⼝

代码块示例

1.合同上传参数加密
var crypto = require('crypto');
var doc_title='', doc_type='';
var v = input.v;
var app_secret = input.appsecret;
var aid = input.aid;
var contractid=input.contractid+'-'+parseInt(input.ind).toString();
var imgurl=input.imgurl
var types=['.doc','.docx','.pdf','.jpg','.jpeg','.png','.bmp']
//法大大不支持图片签章,e签宝支持.这里是为了后面方法公共
function getfiletype(f){
  var obj={'ftype':'','fname':''};
  for(var i=0;i<types.length;i++){
   var find=f.indexOf(types[i])
   var temp=f;
   if(find>-1){
      obj.ftype=types[i];
      temp=temp.substring(0,find);
      obj.fname=getfilename(find,temp);
      break;
    }
  }
  return obj;
}
function getfilename(find,f){
    var fname=''
    f=f.substring(0,find);
    var nind=f.lastIndexOf('/');
    if(nind>-1){
      fname=f.substring(nind+1,f.length);
    }else{
       nind=f.lastIndexOf('=');
       if(nind>-1){
       fname=f.substring(nind+1,f.length);
      }
    }
    return fname;
}
var obj=getfiletype(imgurl);
doc_type=obj.ftype;
doc_title=obj.fname;
var data={
"contract_id":contractid,
"doc_title":doc_title,
"doc_type":doc_type,
'doc_url':imgurl}


function pad2(n) { return n < 10 ? '0' + n : n }
function generateTimeReqestNumber() {
            var datee = new Date();
var localTime = datee .getTime();
var localOffset=datee .getTimezoneOffset()*60000; //获得当地时间偏移的毫秒数
var utc = localTime + localOffset; //utc即GMT时间
var offset =8; //以北京时间为例,东8区
var hawaii = utc + (3600000*offset); 
	var date = new Date(hawaii);
	date.setHours(date.getHours());
	return date.getFullYear().toString() + pad2(date.getMonth() + 1) + pad2(date.getDate()) + pad2(date.getHours()) + pad2(date.getMinutes()) + pad2(date.getSeconds());
}
function sha1(s) {
	var hash = crypto.createHash('sha1');
	hash.update(s);
	return hash.digest('hex').toUpperCase();
}
function md5(s) {
	var hash = crypto.createHash('md5');
	hash.update(s);
	return hash.digest('hex').toUpperCase();
}
function base64(s) {
	var b = new Buffer.from(s);
	return b.toString('base64');
}
function ast(s) {
	return s.split('').sort().join('');
}
var newData = {},params='';
Object.keys(data).sort().map(key => {
  newData[key]=data[key]
  params=params+=data[key];
})
var timestamp = generateTimeReqestNumber();	
var msg_digest = base64( sha1(aid + md5(timestamp) + sha1(app_secret+data.contract_id)));
newData["app_id"]=aid ;
newData.timestamp=timestamp;
newData.v=v;
newData["msg_digest"]=msg_digest;
output={ts:timestamp,sign:msg_digest,'app_id':aid, v: v, params:params,newData:JSON.stringify(newData),contractid:contractid,doc_title:doc_title};


1.1 合同上传
const fetch = require('node-fetch'); 
const FormData= require('form-data');


var indata=JSON.parse(input.data);
var url=input.url+'/api/uploaddocs.api';
var result={};
var tempparams='';
let data= new FormData();


Object.keys(indata).forEach((key) => {
 data.append(key,indata[key]);
})
async function getAll(data){
    const res = await   fetch(url,{
    method:"post",
    body:data
}).then(function(response){
    if(response.ok){
 return  response.json();   
    }else{
  return   {"error":"无数据"};
}
}).catch(function(err){
   return   {"error":"Fetch错误:"+err};
});
    const result = await   res;
    return result;
}
result=await  getAll(data);


output={result:res

剩下的接口调用方法几乎一样,请查看 API 接口来调用。

特别注意几点:

  • 每个接口的签名规则都需要细看。
  • 最后自动签署的流程,需要设置异步通知的地址,因为存在“签署中”的状态。
  • 根据异步通知中的接受到的数据,在工作流设置签署后的文件。

看到这里,有细心的客户会发现,这怎么是单个文件签署呢。一个事务可能涉及到多个合同,那么我的批量签章呢?我的骑缝章呢?——这其实就是我对目前法大大集成的优缺点评价。(以下仅为作者个人观点,非官方立场 )

法大大集成评价

  • 优点
  • 接口简单,操作简单
  • 缺点
  • 获取文档的过程较冗长麻烦。法大大的对接文档不是一次性全部提供,当你要在某个场景使用法大大,咨询对方的客户顾问后,才能得到该场景的文档。
  • 后期维护成本会增加。签署入口不是由统一的接口接入,需要按情况 (单个/批量/骑缝)做分支,而不是由接口中的参数控制。
  • 批量接口不支持自定义印章位置。
  • 不支持图片签署,仅支持 doc 和 pdf 格式文件。

总结一句,如果仅仅是单流程签署的话,推荐法大大;涉及复杂的流程签署,可以多对比其他的电子签章类产品。

结语

务实的明道云,在技术领域不断取得创新突破。每次更新迭代、集成方案,都带给您最美的期待。下一篇文章里,我们将介绍明道云和 e 签宝的对接案例和流程方法,敬请关注!