b2b_handsign_demo.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. # -*- coding: UTF-8 -*-
  2. import esigntool
  3. import requests
  4. from esigntool import esign_run_print_outer
  5. from esigntool.esign_file import fileHelp
  6. # 签署场景:平台方自动签署(签署方1)+企业用户手动签署流程(签署方2)
  7. # 平台方(签署方1):appId所属企业为平台方,默认平台方已完成实名认证,通过获取企业签署帐号来实现平台方自动签署。
  8. # 企业用户手动签署(签署方2):通过传入企业名称和经办人信息,来实现企业用户手动签署。
  9. config = esigntool.config() # 初始化配置类
  10. @esign_run_print_outer
  11. def findOrgIdentityInfo(orgName):
  12. """
  13. :param orgName
  14. 查询机构认证信息
  15. :return: 企业实名主体帐号ID
  16. """
  17. api_path = "/v3/organizations/identity-info?orgName={}".format(orgName)
  18. method = esigntool.httpMethodEnum.GET
  19. if orgName == "":
  20. print("请设置实名企业名称")
  21. exit()
  22. # 签名并构造签名鉴权json请求头
  23. json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
  24. # 发起请求
  25. resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
  26. print(resp.text)
  27. orgId = resp.json()['data']['orgId']
  28. return orgId
  29. @esign_run_print_outer
  30. def getFileUploadUrl(file):
  31. """
  32. 获取文件上传地址
  33. :param file 文件辅助类初始化
  34. :return: fileUploadUrl, fileId 文件流上传路径,文件ID
  35. """
  36. contentType = "application/pdf" # 声明请求变量
  37. body = {
  38. "contentMd5": file.contentMd5,
  39. "contentType": contentType,
  40. "convert2Pdf": False,
  41. "fileName": file.fileName,
  42. "fileSize": file.fileSize
  43. } # 构建请求参数body体
  44. api_path = "/v3/files/file-upload-url"
  45. method = esigntool.httpMethodEnum.POST
  46. json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
  47. method, api_path, body) # 签名并构造签名鉴权json请求头
  48. resp = requests.request(method, config.host + api_path, json=body, headers=json_headers) # 发送请求
  49. fileUploadUrl = resp.json()['data']['fileUploadUrl'] # 获取文件上传路径
  50. fileId = resp.json()['data']['fileId']
  51. print(resp.text)
  52. return fileUploadUrl, fileId
  53. @esign_run_print_outer
  54. def fileStreamUpload(binfile, fileUploadUrl):
  55. """
  56. :param binfile 文件流
  57. :param fileUploadUrl 文件流上传路径
  58. 方法名:文件流上传服务器
  59. :return: 文件上传结果
  60. """
  61. contentMd5 = file.contentMd5 # 声明请求变量
  62. contentType = "application/pdf" # 声明请求变量
  63. method = esigntool.httpMethodEnum.PUT # 声明请求方法
  64. json_headers = esigntool.buildFileUploadHeader(contentType, contentMd5) # 构建请求头
  65. resp = requests.request(method, fileUploadUrl, data=binfile, headers=json_headers) # 发送请求
  66. print(resp.text)
  67. return resp
  68. @esign_run_print_outer
  69. def signFlowCreateByFile(fileId, orgId,psnAccount):
  70. """
  71. :param fileId 文件ID
  72. :param orgId 企业实名主体帐号ID,用于实现自动平台放自动签署
  73. 基于文件发起签署
  74. :return: 签署流程ID
  75. """
  76. body = {
  77. "docs": [{
  78. "fileId": fileId,
  79. "fileName": "xx企业劳动合同.pdf"
  80. }],
  81. "attachments": [{
  82. "fileId": fileId,
  83. "fileName": "入职材料.pdf"
  84. }],
  85. "signers": [{
  86. "orgSignerInfo": {
  87. "orgName": orgName, # 签署方1:平台自动签署
  88. "transactorInfo": {
  89. "psnAccount": psnAccount
  90. }
  91. },
  92. "signConfig": {
  93. "forcedReadingTime": "10",
  94. "signOrder": 2
  95. },
  96. "signFields": [{
  97. "customBizNum": "202200001111",
  98. "fileId": fileId,
  99. "normalSignFieldConfig": {
  100. "autoSign": False,
  101. "freeMode": False,
  102. "movableSignField": False,
  103. "signFieldPosition": {
  104. "positionPage": "1",
  105. "positionX": 100,
  106. "positionY": 200
  107. },
  108. "signFieldSize": 200,
  109. "signFieldStyle": 1
  110. },
  111. "signFieldType": 0
  112. }],
  113. "signerType": 1
  114. }, {
  115. "orgSignerInfo": {
  116. "orgId": orgId # 签署方2:企业用户手动签署
  117. },
  118. "signConfig": {
  119. "forcedReadingTime": "10",
  120. "signOrder": 1
  121. },
  122. "signFields": [{
  123. "customBizNum": "202200001111",
  124. "fileId": fileId,
  125. "normalSignFieldConfig": {
  126. "autoSign": True,
  127. "freeMode": False,
  128. "movableSignField": False,
  129. "signFieldPosition": {
  130. "positionPage": "1",
  131. "positionX": 300,
  132. "positionY": 200
  133. },
  134. "signFieldSize": 200,
  135. "signFieldStyle": 1
  136. },
  137. "signFieldType": 0
  138. }],
  139. "signerType": 1
  140. }],
  141. "signFlowConfig": {
  142. "autoFinish": False,
  143. "autoStart": True,
  144. "chargeConfig": {
  145. "chargeMode": 0
  146. },
  147. "noticeConfig": {
  148. "noticeTypes": "1"
  149. },
  150. "notifyUrl": "http://xx.xx.86.172:8081/asyn/notify",
  151. "redirectConfig": {
  152. "redirectDelayTime": "3",
  153. "redirectUrl": "http://www.xx.cn/"
  154. },
  155. "signConfig": {
  156. "availableSignClientTypes": "1",
  157. "showBatchDropSealButton": True
  158. },
  159. "authConfig": {
  160. "psnAvailableAuthModes": ["PSN_BANK4_AUTHCODE"],
  161. "willingnessAuthModes": ["FACE_TECENT_CLOUD_H5"],
  162. "orgAvailableAuthModes": ["ORG_BANK_TRANSFER"]
  163. },
  164. "signFlowTitle": "企业员工劳动合同签署"
  165. }
  166. } # 构建请求body体
  167. api_path = "/v3/sign-flow/create-by-file" # 拼接请求路径
  168. method = esigntool.httpMethodEnum.POST # 声明请求方法
  169. json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
  170. method, api_path, body) # 签名并构造签名鉴权json请求头
  171. resp = requests.request(method, config.host + api_path, json=body, headers=json_headers) # 发送请求
  172. print(resp.text)
  173. sign_flowId = resp.json()['data']['signFlowId']
  174. return sign_flowId
  175. @esign_run_print_outer
  176. def signUrl(sign_flowId):
  177. """
  178. 获取合同文件签署链接
  179. :param 签署流程ID
  180. :return:
  181. """
  182. body = {
  183. "clientType": "ALL",
  184. "needLogin": True,
  185. "operator": {
  186. "psnAccount": psnAccount
  187. },
  188. "urlType": 2
  189. } # 构建请求body体
  190. api_path = "/v3/sign-flow/{}/sign-url".format(sign_flowId) # 拼接请求路径
  191. method = esigntool.httpMethodEnum.POST # 请求方法
  192. json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
  193. method, api_path, body) # 签名并构造签名鉴权json请求头
  194. resp = requests.request(method, config.host + api_path, json=body, headers=json_headers) # 发送http请求
  195. print(resp.text)
  196. return resp
  197. @esign_run_print_outer
  198. def fileDownloadUrl(sign_flowId):
  199. """
  200. :param 签署流程ID
  201. 下载已签署文件及附属材料
  202. :return:
  203. """
  204. api_path = "/v3/sign-flow/{}/file-download-url".format(sign_flowId) # 拼接请求路径
  205. method = esigntool.httpMethodEnum.GET # 声明请求方法
  206. json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
  207. method, api_path) # 签名并构造签名鉴权json请求头
  208. resp = requests.request(method, config.host + api_path, json=None, headers=json_headers) # 发送请求
  209. print(resp.text)
  210. return resp
  211. if __name__ == '__main__':
  212. fileUrl = "D:\\**文件\\*.pdf" # 本地文件路径
  213. orgName = "**企业" # 签署方企业名称
  214. psnAccount = "1******" # 签署人手机号或者邮箱地址
  215. orgId = findOrgIdentityInfo(orgName) # 查询平台方实名帐号ID
  216. file = fileHelp(fileUrl) # 初始化文件辅助类
  217. fileUploadUrl, fileId = getFileUploadUrl(file) # 获取文件ID&文件上传路径
  218. fileStreamUpload(file.getBinFile(), fileUploadUrl) # 上传文件流
  219. sign_flowId = signFlowCreateByFile(fileId, orgId, psnAccount) # 发起一步签署:主动发送签署短信给签署用户
  220. signUrl(sign_flowId) # 获取签署链接
  221. fileDownloadUrl(sign_flowId) # 合同结束后,下载签署后合同