提交 0d6594cd authored 作者: zyx's avatar zyx

新增 E签宝 三方接口对接

上级 b0a79c6f
...@@ -192,9 +192,9 @@ ...@@ -192,9 +192,9 @@
} }
}, },
"@god/node-com": { "@god/node-com": {
"version": "2.0.10", "version": "2.0.13",
"resolved": "http://registry.npm.godzyx.com/@god/node-com/download/@god/node-com-2.0.10.tgz", "resolved": "http://registry.npm.godzyx.com/@god/node-com/download/@god/node-com-2.0.13.tgz",
"integrity": "sha1-Eyv9nf3f4mTFPhe2GCQgopgIs2M=", "integrity": "sha1-tVBs7RFtARpXKkiHuYFN1V0EOkk=",
"requires": { "requires": {
"crypto-js": "^4.0.0", "crypto-js": "^4.0.0",
"form-data": "^3.0.0", "form-data": "^3.0.0",
...@@ -568,6 +568,14 @@ ...@@ -568,6 +568,14 @@
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
}, },
"bufferutil": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.3.tgz",
"integrity": "sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw==",
"requires": {
"node-gyp-build": "^4.2.0"
}
},
"busboy": { "busboy": {
"version": "0.2.14", "version": "0.2.14",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
...@@ -1333,9 +1341,9 @@ ...@@ -1333,9 +1341,9 @@
}, },
"dependencies": { "dependencies": {
"type": { "type": {
"version": "2.0.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz",
"integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==" "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA=="
} }
} }
}, },
...@@ -2038,11 +2046,6 @@ ...@@ -2038,11 +2046,6 @@
} }
} }
}, },
"nan": {
"version": "2.14.1",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
"integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw=="
},
"natural-compare": { "natural-compare": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
...@@ -2064,10 +2067,15 @@ ...@@ -2064,10 +2067,15 @@
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
}, },
"node-gyp-build": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
"integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg=="
},
"nodemailer": { "nodemailer": {
"version": "6.4.8", "version": "6.4.17",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.8.tgz", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.17.tgz",
"integrity": "sha512-UbJD0+g5e2H20bWv7Rpj3B+N3TMMJ0MLoLwaGVJ0k3Vo8upq0UltwHJ5BJfrpST1vFa91JQ8cf7cICK5DSIo1Q==" "integrity": "sha512-89ps+SBGpo0D4Bi5ZrxcrCiRFaMmkCt+gItMXQGzEtZVR3uAD3QAQIDoxTWnx3ky0Dwwy/dhFrQ+6NNGXpw/qQ=="
}, },
"normalize-package-data": { "normalize-package-data": {
"version": "2.5.0", "version": "2.5.0",
...@@ -2953,6 +2961,14 @@ ...@@ -2953,6 +2961,14 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"utf-8-validate": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.4.tgz",
"integrity": "sha512-MEF05cPSq3AwJ2C7B7sHAA6i53vONoZbMGX8My5auEVm6W+dJ2Jd/TZPyGJ5CH42V2XtbI5FD28HeHeqlPzZ3Q==",
"requires": {
"node-gyp-build": "^4.2.0"
}
},
"util-deprecate": { "util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
...@@ -2994,14 +3010,15 @@ ...@@ -2994,14 +3010,15 @@
} }
}, },
"websocket": { "websocket": {
"version": "1.0.31", "version": "1.0.33",
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.31.tgz", "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.33.tgz",
"integrity": "sha512-VAouplvGKPiKFDTeCCO65vYHsyay8DqoBSlzIO3fayrfOgU94lQN5a1uWVnFrMLceTJw/+fQXR5PGbUVRaHshQ==", "integrity": "sha512-XwNqM2rN5eh3G2CUQE3OHZj+0xfdH42+OFK6LdC2yqiC0YU8e5UK0nYre220T0IyyN031V/XOvtHvXozvJYFWA==",
"requires": { "requires": {
"bufferutil": "^4.0.1",
"debug": "^2.2.0", "debug": "^2.2.0",
"es5-ext": "^0.10.50", "es5-ext": "^0.10.50",
"nan": "^2.14.0",
"typedarray-to-buffer": "^3.1.5", "typedarray-to-buffer": "^3.1.5",
"utf-8-validate": "^5.0.2",
"yaeti": "^0.0.6" "yaeti": "^0.0.6"
} }
}, },
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
"eslint-plugin-standard": "^3.1.0" "eslint-plugin-standard": "^3.1.0"
}, },
"dependencies": { "dependencies": {
"@god/node-com": "^2.0.10", "@god/node-com": "^2.0.13",
"body-parser": "^1.18.3", "body-parser": "^1.18.3",
"child_process": "^1.0.2", "child_process": "^1.0.2",
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
......
const fs = require('fs')
// 自带模块 crypto
const crypto = require('crypto')
const path = require('path')
const com = require('@god/node-com')
const Proxy = com.Proxy
const md5 = com.Tool.md5
// [开方平台API](https://open.esign.cn/doc/detail?id=opendoc%2Fpaas_api%2Fvwtg6m&namespace=opendoc%2Fpaas_api)
const appPro1 = {
AppId: '5111635045',
AppSecret: '0f35445117f738c9292c52ad8af94064'
}
// 数据文件必须存 固定地点
const appPro2 = {
AppId: '5111635068',
AppSecret: '596c54b16ea817be0a1952d75e600435',
fileId: 'a830a10b55ff46bebc0edc4cec15ebc0',
// accountId: '1ca237fc1e6a466e878bc2274a9c1cec',
// flowId: '9926fcd1956d41ab8e081c4480562f53'
}
const appTest = {
AppId: '7438837529',
AppSecret: '1994cd14f5f3eb3c8d23b23c4250919e',
fileId: 'a830a10b55ff46bebc0edc4cec15ebc0',
// accountId: '1ca237fc1e6a466e878bc2274a9c1cec',
// flowId: '9926fcd1956d41ab8e081c4480562f53'
}
let _accessToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJnSWQiOiIzZWVkMDZkODE1MmQ0YzNlOGMwOGM2ZTQzNjA5ODViZCIsImFwcElkIjoiNTExMTYzNTA2OCIsIm9JZCI6IjhiYTNkMDY0ZDZjYTQ0MThhM2Q0YzNkNTI4MDVlOWJiIiwidGltZXN0YW1wIjoxNjA5NDMxOTg5NTQ4fQ.3F4izbojHlWj80zA4mUcgBZTWJn9IDzQsd-tHQw7cr8'
let _info = {
domain_test: 'smlopenapi.esign.cn',
domain_pro: 'openapi.esign.cn'
}
// const _app = appPro2
// const _domain = _info.domain_pro
const _app = appTest
const _domain = _info.domain_test
const _getAccessToken = (req, res) => {
Proxy.reqHttps({
hostname: _domain,
path: '/v1/oauth2/access_token',
method: 'GET',
data: {},
query: {
appId: _app.AppId,
secret: _app.AppSecret,
grantType: 'client_credentials'
},
headers: {
'Host': _domain
}
}, function (str, obj) {
let jsonErr = { code: 500, message: '_getAccessToken 接口 调用失败' }
let _json = {}
try {
_json = JSON.parse(str)
} catch (e) {
console.log(e)
res.status(200).json(jsonErr)
return
}
if (_json.code !== 0) {
res.status(200).json(_json)
return
}
_accessToken = _json.data.token
res.status(200).json(_json)
})
}
// 需要传一个 file - 暂时只支持单文件
const _getUploadUrl = (req, res) => {
// 暂时先指定 originalname,中文本地服务存在问题
req.files[0].originalname = req.body.name || req.files[0].originalname
// 计算 content-md5
let buffer = fs.readFileSync(req.files[0].path)
let str_md5 = crypto.createHash('md5').update(buffer).digest('hex')
console.log(str_md5)
let str_base64_md5 = Buffer.from(crypto.createHash('md5').update(buffer).digest()).toString('base64')
console.log(str_base64_md5)
// 重命名
fs.renameSync(req.files[0].path, req.files[0].destination + req.files[0].originalname)
req.files[0].path = req.files[0].destination + req.files[0].originalname
Proxy.reqHttps({
hostname: _domain,
path: '/v1/files/getUploadUrl',
method: 'POST',
data: {
accountId: '',
contentMd5: str_base64_md5,
contentType: 'application/octet-stream',
convert2Pdf: /pdf/gi.test(req.files[0].originalname),
fileName: req.files[0].originalname, // req.files[0].originalname 暂时存在一个问题,1.本地服务中文乱码
fileSize: req.files[0].size
},
query: {},
headers: {
'Host': _domain,
'Content-Type': 'application/json; charset=UTF-8',
'X-Tsign-Open-Token': _accessToken,
'X-Tsign-Open-App-Id': _app.AppId
}
}, function (str, obj) {
let jsonErr = { code: 500, message: '_getUploadUrl 接口 第一步调用失败' }
let _json = {}
try {
_json = JSON.parse(str)
} catch (e) {
console.log(e)
res.status(200).json(jsonErr)
return
}
if (_json.code !== 0) {
res.status(200).json(_json)
return
}
// 上一个接口调用没问题,再调用下一个接口
let fileId = _json.data.fileId
console.log(fileId)
_uploadFileFromOctetStream(_json.data.uploadUrl, str_base64_md5, req.files[0].path, (_json) => {
_json.fileId = fileId
_app.fileId = fileId
res.status(200).json(_json)
})
})
}
// 通过 application/octet-stream 方式 上传 文件
const _uploadFileFromOctetStream = (uploadUrl, str_base64_md5, filePath, callback) => {
let buffer = fs.readFileSync(filePath)
console.log(uploadUrl)
Proxy.reqHttps({
hostname: uploadUrl.replace(/http(s|):\/\//gi, '').replace(/\/.*$/gi, ''),
path: uploadUrl.replace(/http(s|):\/\/[^\/]*?\//gi, '/'),
method: 'PUT',
data: Buffer.from(buffer, 'binary'),
query: {},
headers: {
'content-md5': str_base64_md5,
'content-type': 'application/octet-stream',
}
}, function (str, obj) {
let jsonErr = { code: 500, message: '_getUploadUrl 接口 第二步 _uploadFileFromOctetStream 调用失败' }
let _json = {}
try {
_json = JSON.parse(str)
} catch (e) {
console.log(e)
_json = jsonErr
}
callback(_json)
})
}
// 根据 fileId 查询文件上传状态
const _getInfoToUploadFile = (req, res) => {
Proxy.reqHttps({
hostname: _domain,
path: '/v1/files/' + req.body.fileId,
method: 'GET',
data: {},
query: {},
headers: {
'Host': _domain,
'Content-Type': 'application/json; charset=UTF-8',
'X-Tsign-Open-Token': _accessToken,
'X-Tsign-Open-App-Id': _app.AppId
}
}, function (str, obj) {
console.log(str)
// callback(str)
res.status(200).json(JSON.parse(str))
})
}
// 先调用创建用户 再一建发起签署,返回url
const getOneStepUrl = (req, res) => {
_token(req, res, () => {
_createUser(req, res, (accountId) => {
req.accountId = accountId
_signFlowId(req, res, (flowId) => {
req.flowId = flowId
_getUrl(req, res, (_json) => {
res.status(200).json(_json)
})
})
})
})
}
const _token = (req, res, callback) => {
// 获取token
Proxy.reqHttps({
hostname: _domain,
path: '/v1/oauth2/access_token',
method: 'GET',
data: {},
query: {
appId: _app.AppId,
secret: _app.AppSecret,
grantType: 'client_credentials'
},
headers: {
'Host': _domain
}
}, function (str, obj) {
let jsonErr = { code: 500, message: 'getOneStepUrl 接口 第一步 /v1/oauth2/access_token调用失败' }
let _json = {}
try {
_json = JSON.parse(str)
} catch (e) {
console.log(e)
res.status(200).json(jsonErr)
return
}
if (_json.code !== 0) {
res.status(200).json(_json)
return
}
_accessToken = _json.data.token
callback && callback()
})
}
const _createUser = (req, res, callback) => {
// 创建用户
Proxy.reqHttps({
hostname: _domain,
path: '/v1/accounts/createByThirdPartyUserId',
method: 'POST',
data: {
"email": req.body.email || "",
"idNumber": "",
"idType": "",
"mobile": req.body.mobile || "",
"name": req.body.name || "张彦新",
"thirdPartyUserId": req.body.uuid || "17601621100"
},
query: {},
headers: {
'Host': _domain,
'Content-Type': 'application/json; charset=UTF-8',
'X-Tsign-Open-Token': _accessToken,
'X-Tsign-Open-App-Id': _app.AppId
}
}, function (str, obj) {
let jsonErr = { code: 500, message: 'getOneStepUrl 接口 第二步 /v1/accounts/createByThirdPartyUserId调用失败' }
let _json = {}
try {
_json = JSON.parse(str)
} catch (e) {
console.log(e)
res.status(200).json(jsonErr)
return
}
// 账号已存在 - 可以继续签署
if (_json.code !== 0 && _json.code !== 53000000) {
res.status(200).json(_json)
return
}
// accountId 传入,防止高并发时,出现意外
callback && callback(_json.data.accountId)
})
}
const _signFlowId = (req, res, callback) => {
// 一建发起签署
Proxy.reqHttps({
hostname: _domain,
path: '/api/v2/signflows/createFlowOneStep',
method: 'POST',
data: {
"attachments": [],
"copiers": [],
"docs":[{
"fileId": _app.fileId,
"fileName": "",
"filePassword": ""
}],
"flowInfo": {
"autoArchive": true,
"autoInitiate": true,
"businessScene": "Test 一键发起部署",
"flowConfigInfo": {
"noticeDeveloperUrl": "",
"noticeType": "",
"redirectUrl": "",
"signPlatform": ""
},
"initiatorAccountId": "",
"initiatorAuthorizedAccountId": "",
"remark": ""
},
"signers": [{
"platformSign": false,
"signerAccount": {
"authorizedAccountId": "",
"signerAccountId": req.accountId
},
"signfields": [{
"autoExecute": false,
"fileId": _app.fileId,
"signType": 0,
"posBean": {
"posPage": ""
},
"sealId": "",
"sealType": "0, 1",
"signDateBean": {
"format": ""
}
}],
"thirdOrderNo": ""
}]
},
query: {},
headers: {
'Host': _domain,
'Content-Type': 'application/json; charset=UTF-8',
'X-Tsign-Open-Token': _accessToken,
'X-Tsign-Open-App-Id': _app.AppId
}
}, function (str, obj) {
let jsonErr = { code: 500, message: 'getOneStepUrl 接口 第三步 /api/v2/signflows/createFlowOneStep调用失败' }
let _json = {}
try {
_json = JSON.parse(str)
} catch (e) {
console.log(e)
res.status(200).json(jsonErr)
return
}
if (_json.code !== 0) {
res.status(200).json(_json)
return
}
// 每次生成的flowId不一致,防止高并发时出现意外
callback && callback(_json.data.flowId)
})
}
const _getUrl = (req, res, callback) => {
// 获取url
Proxy.reqHttps({
hostname: _domain,
path: '/v1/signflows/' + req.flowId + '/executeUrl',
method: 'GET',
data: {},
query: {
flowId: req.flowId,
accountId: req.accountId
},
headers: {
'Host': _domain,
'Content-Type': 'application/json; charset=UTF-8',
'X-Tsign-Open-Token': _accessToken,
'X-Tsign-Open-App-Id': _app.AppId
}
}, function (str, obj) {
let jsonErr = { code: 500, message: 'getOneStepUrl 接口 第四步 /v1/signflows/{flowId}/executeUrl调用失败' }
let _json = {}
try {
_json = JSON.parse(str)
} catch (e) {
console.log(e)
res.status(200).json(jsonErr)
return
}
if (_json.code !== 0) {
res.status(200).json(_json)
return
}
console.log(_json)
_json.data.flowId = req.flowId
_json.data.accountId = req.accountId
_json.data.fileId = _app.fileId
callback && callback(_json)
})
}
// 获取签署后的文件
const getAfterSignFile = (req, res) => {
Proxy.reqHttps({
hostname: _domain,
path: '/v1/signflows/' + req.body.flowId + '/documents',
method: 'GET',
data: {},
query: {},
headers: {
'Host': _domain,
'Content-Type': 'application/json; charset=UTF-8',
'X-Tsign-Open-Token': _accessToken,
'X-Tsign-Open-App-Id': _app.AppId
}
}, function (str, obj) {
console.log(str)
// callback(str)
res.status(200).json(JSON.parse(str))
})
}
module.exports = {
_getAccessToken: _getAccessToken,
_getUploadUrl: _getUploadUrl,
_getInfoToUploadFile: _getInfoToUploadFile,
getOneStepUrl: getOneStepUrl,
getAfterSignFile: getAfterSignFile
}
...@@ -14,6 +14,7 @@ const _nts = require('../controller/NextToSend') ...@@ -14,6 +14,7 @@ const _nts = require('../controller/NextToSend')
const _pr = require('../controller/ProxyRequest') const _pr = require('../controller/ProxyRequest')
const _wxcsm = require('../controller/wxChartSuccessMonitor') const _wxcsm = require('../controller/wxChartSuccessMonitor')
const _cm = require('../controller/CkeditorMonitor') const _cm = require('../controller/CkeditorMonitor')
const _es = require('../controller/ESignMonitor')
router.use(bodyParser.json({ limit: '60mb' })) router.use(bodyParser.json({ limit: '60mb' }))
router.use(bodyParser.urlencoded({ extended: true, limit: '60mb' })) router.use(bodyParser.urlencoded({ extended: true, limit: '60mb' }))
...@@ -67,6 +68,12 @@ router.use('/get/wx-chart/*', _wxcsm.get) ...@@ -67,6 +68,12 @@ router.use('/get/wx-chart/*', _wxcsm.get)
router.use('/set/wx-chart/*', _wxcsm.set) router.use('/set/wx-chart/*', _wxcsm.set)
// 支持 ckeditor 图片上传 // 支持 ckeditor 图片上传
router.use('/form/ckeditor-upload*', upload.any(), _cm.ckeditorUpload) router.use('/form/ckeditor-upload*', upload.any(), _cm.ckeditorUpload)
// 对接 e签宝 传入 姓名+手机号 返回 签署url
router.use('/get/sign/access/token', upload.any(), _es._getAccessToken)
router.use('/get/sign/upload/url', upload.any(), _es._getUploadUrl)
router.use('/get/sign/info/upload/file', upload.any(), _es._getInfoToUploadFile)
router.use('/get/sign/url', upload.any(), _es.getOneStepUrl)
router.use('/get/sign/download/url', upload.any(), _es.getAfterSignFile)
router.use('/api/*', upload.any(), (req, res) => { res.send('暂无该接口') }) router.use('/api/*', upload.any(), (req, res) => { res.send('暂无该接口') })
router.use('*', upload.any(), (req, res) => { res.send('接口未定义') }) router.use('*', upload.any(), (req, res) => { res.send('接口未定义') })
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论