Преглед на файлове

建模 e签宝封装的方法

mengqiankang преди 2 месеца
родител
ревизия
15761dd3da
променени са 38 файла, в които са добавени 3307 реда и са изтрити 2 реда
  1. 0 0
      alien_3rd_esign/client.py
  2. 5 0
      alien_3rd_esign/esigntool/__init__.py
  3. 211 0
      alien_3rd_esign/esigntool/esign_algorithm.py
  4. 11 0
      alien_3rd_esign/esigntool/esign_config.py
  5. 9 0
      alien_3rd_esign/esigntool/esign_emun.py
  6. 40 0
      alien_3rd_esign/esigntool/esign_file.py
  7. 18 0
      alien_3rd_esign/esigntool/esign_tool_info.py
  8. 24 0
      alien_3rd_esign/models.py
  9. 7 0
      alien_3rd_esign/router.py
  10. 90 0
      alien_3rd_esign/run/moduledemo/auth/auth_launch.py
  11. 113 0
      alien_3rd_esign/run/moduledemo/auth/auth_query.py
  12. 29 0
      alien_3rd_esign/run/moduledemo/fileandtemplate/expand.py
  13. 74 0
      alien_3rd_esign/run/moduledemo/fileandtemplate/file.py
  14. 138 0
      alien_3rd_esign/run/moduledemo/fileandtemplate/template.py
  15. 107 0
      alien_3rd_esign/run/moduledemo/members/org_member.py
  16. 46 0
      alien_3rd_esign/run/moduledemo/order/order_buy.py
  17. 112 0
      alien_3rd_esign/run/moduledemo/order/order_query.py
  18. 403 0
      alien_3rd_esign/run/moduledemo/seal/org_seals.py
  19. 191 0
      alien_3rd_esign/run/moduledemo/seal/psn_seals.py
  20. 59 0
      alien_3rd_esign/run/moduledemo/seal/upload_url.py
  21. 99 0
      alien_3rd_esign/run/moduledemo/sign/attachments_change.py
  22. 73 0
      alien_3rd_esign/run/moduledemo/sign/copiers_change.py
  23. 85 0
      alien_3rd_esign/run/moduledemo/sign/signfields_change.py
  24. 106 0
      alien_3rd_esign/run/moduledemo/sign/signflow_change.py
  25. 28 0
      alien_3rd_esign/run/moduledemo/sign/signflow_download.py
  26. 135 0
      alien_3rd_esign/run/moduledemo/sign/signflow_launch.py
  27. 55 0
      alien_3rd_esign/run/moduledemo/sign/signflow_query.py
  28. 48 0
      alien_3rd_esign/run/moduledemo/sign/signflow_rescission.py
  29. 204 0
      alien_3rd_esign/run/scenedome/b2b_autosign_demo.py
  30. 237 0
      alien_3rd_esign/run/scenedome/b2b_handsign_demo.py
  31. 214 0
      alien_3rd_esign/run/scenedome/b2c_handsign_demo.py
  32. 177 0
      alien_3rd_esign/run/scenedome/dynamic_table_demo.py
  33. 105 0
      alien_3rd_esign/run/scenedome/order_demo.py
  34. 48 0
      alien_3rd_esign/run/scenedome/signature_check_demo.py
  35. 0 0
      alien_3rd_esign/schemas.py
  36. 1 0
      alien_3rd_esign/services.py
  37. 4 2
      main.py
  38. 1 0
      pyproject.toml

+ 0 - 0
alien_3rd_esign/client.py


+ 5 - 0
alien_3rd_esign/esigntool/__init__.py

@@ -0,0 +1,5 @@
+from .esign_algorithm import *
+from .esign_emun import *
+from .esign_tool_info import *
+from .esign_file import *
+from .esign_config import *

+ 211 - 0
alien_3rd_esign/esigntool/esign_algorithm.py

@@ -0,0 +1,211 @@
+import base64
+import hashlib
+import hmac
+from functools import wraps
+from hashlib import sha256
+import time
+from esign_emun import httpMethodEnum
+import logging
+import json
+from urllib.parse import urlparse, parse_qs, urlencode
+
+
+
+def doContentMd5(data):
+    """
+    根据字符串计算Content-MD5
+    :param data:
+    :return:
+    """
+    obj = hashlib.md5()  # 实例化md5的时候可以给传个参数,这叫加盐
+    obj.update(data.encode("utf-8"))  # 是再加密的时候传入自己的一块字节,
+    secret = obj.digest()
+    return base64.b64encode(secret).decode('utf-8')
+
+
+def appendSignDataString(http_method, content_md5, url, accept="*/*",
+                         content_type="application/json; charset=UTF-8",
+                         **kwargs):
+    """
+    拼接待签名字符串
+    :param http_method: 统一用大写的POST GET DELETE PUT
+    :param content_md5:请求body体计算的md5值
+    :param url: 接口url地址,不带网关,例:/v1/accounts/createByThirdPartyUserId
+    :param accept: 不传默认"*/*"
+    :param content_type: 不传默认application/json; charset=UTF-8
+    :param kwargs: 可以传headers和date参数
+    :return: 拼接的待签名字符串
+    """
+    # 待签字符串
+    sign_data_str = "{}\n{}\n{}\n{}\n".format(http_method, accept, content_md5, content_type)
+    date = kwargs.get('date')
+    headers = kwargs.get('headers')
+    # 如果date是空的,直接拼接\n
+    if date == "" or date is None:
+        sign_data_str = "{}\n".format(sign_data_str)
+    else:
+        sign_data_str = "{}{}\n".format(sign_data_str, date)
+
+    # 如果header是空的,直接拼接url
+    if headers == "" or headers is None:
+        sign_data_str = "{}{}".format(sign_data_str, url)
+    else:
+        sign_data_str = "{}{}\n{}".format(sign_data_str, headers, url)
+    return sign_data_str
+
+
+def doSignatureBase64(message, secret):
+    """
+    根据待签字符串计算签名值
+    :param message: 待签名字符串
+    :param secret:密钥
+    :return:
+    """
+    key = secret.encode('utf-8')  # sha256加密的key
+    message = message.encode('utf-8')  # 待sha256加密的内容
+    sign = base64.b64encode(hmac.new(key, message, digestmod=sha256).digest()).decode()
+    return sign
+
+
+def getMillisecondStamp():
+    """
+    获取当前毫秒时间戳
+    :return:
+    """
+    return str(int(time.time() * 1000))
+
+
+def buildHeader(appid, content_md5, req_signature, accept="*/*",
+                content_type="application/json; charset=UTF-8",
+                auth_mode="Signature", **kwargs):
+    """
+    构造签名鉴权请求头
+    :param appid:
+    :param content_md5:
+    :param req_signature:
+    :param accept:
+    :param content_type:
+    :param auth_mode:
+    :param kwargs:
+    :return:
+    """
+    header = {"X-Tsign-Open-App-Id": appid,
+              "Content-Type": content_type,
+              "X-Tsign-Open-Ca-Timestamp": getMillisecondStamp(),
+              "Accept": accept,
+              "X-Tsign-Open-Ca-Signature": req_signature,
+              "Content-MD5": content_md5,
+              "X-Tsign-Open-Auth-Mode": auth_mode,
+              "X-Tsign-Dns-App-Id": appid}
+    for key, value in kwargs.items():
+        header[key] = value
+    return header
+
+
+def buildFileUploadHeader(contentType, contentMd5):
+    """
+    构建文件流上传请求头
+    :param contentType:
+    :param contentMd5:
+    :return:
+    """
+    header = {
+        'Content-Type': contentType,
+        'Content-MD5': contentMd5
+    }
+    return header
+
+
+def buildSignJsonHeader(appid, secret, http_method, url, body=None,
+                        **kwargs):
+    """
+    签名并构造签名鉴权和json请求体的请求头
+    :param appid:
+    :param secret:
+    :param body: 传入字典格式数据
+    :param http_method:
+    :param url:接口url地址,不带网关,例:/v1/accounts/createByThirdPartyUserId
+    :param kwargs:可以传headers和date参数以及其它自定义请求头参数
+    :return:
+    """
+    content_md5 = ""
+    # 判断是PUT或者POST请求,不需要计算计算md5,否则md5为空
+    if httpMethodEnum.PUT == http_method or httpMethodEnum.POST == http_method:
+        # 字典转json字符串
+        body = json.dumps(body)
+        # 生成md5
+        content_md5 = doContentMd5(body)
+    # 拼接待签名字符串
+    message = appendSignDataString(http_method, content_md5, apiPathSort(url), **kwargs)
+    # 传入待签名字符串生成签名值
+    req_signature = doSignatureBase64(message, secret)
+    # 生成请求头
+    header = buildHeader(appid, content_md5, req_signature, **kwargs)
+    logging.debug("开始运行".center(50, "-"))
+    logging.debug("计算md5的body:{}\n生成的md5:{}\n"
+                  "拼接的待签字符串:{}\n签名值:{}".format(body, content_md5, message, req_signature))
+    logging.debug("结束运行".center(50, "-"))
+    return header
+
+
+def apiPathSort(api_path, is_query=False, is_url_encode=False):
+    """
+    传入url对query做排序返回新的url
+    :param api_path:
+    :param is_url_encode:
+    :param is_query:
+    :return:
+    """
+    # 提取url参数
+    # 传入的api_path是不是整体都是query参数
+    if is_query:
+        query = api_path
+        urls = ""
+        path = ""
+        # 将字符串转换为字典
+        # 所得的字典的value都是以列表的形式存在,若列表中都只有一个值
+    else:
+        urls = "?"
+        url = urlparse(api_path)
+        query = url.query
+        path = url.path
+        # 将字符串转换为字典
+        # 所得的字典的value都是以列表的形式存在,若列表中都只有一个值
+    body = parse_qs(query)
+    body = {key: body[key][0] for key in sorted(body.keys())}
+    # 需要做urlencode编码
+    if is_url_encode:
+        body = urlencode(body)
+        urls = path + urls + body
+    else:
+        for key in body:
+            temp_str = key + "=" + body[key]
+            urls = urls + temp_str + "&"
+        urls = path + urls
+    urls = urls[:len(urls) - 1]
+    return urls
+
+
+def esign_run_print_outer(func):
+    """
+    装饰器用于打印运行函数的基本信息
+    :param func:
+    :return:
+    """
+
+    @wraps(func)
+    def esign_run_print(*args, **kwargs):
+        print("开始运行{}".format(func.__name__).center(50, "-"))
+        start_time = time.time()
+        response = func(*args, **kwargs)
+        end_time = time.time()
+        print("运行时间:{}".format(end_time - start_time))
+        print("结束运行{}".format(func.__name__).center(50, "-"))
+        print("\n\n")
+        return response
+
+    return esign_run_print
+
+
+if __name__ == '__main__':
+    pass

+ 11 - 0
alien_3rd_esign/esigntool/esign_config.py

@@ -0,0 +1,11 @@
+# 公共参数配置类
+class config:
+    def __init__(self):
+        """
+        项目配置类,在此配置公共参数
+        """
+        # 沙箱appid获取路径:https://open.esign.cn/doc/opendoc/saas_api/vwtg6m
+        # 沙箱appid获取路径:https://open.esign.cn/doc/opendoc/saas_api/zag8bm
+        self.appId = "7439100277"  # 项目appid
+        self.scert = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzRMTBAikgkfEVhcbhqaWSm4jfFB2aUHHeW9MvshM6wTdAGOWTwSdK0odMXo+t5C18tCrOkaYc9xjl3Zm4rRfDy9aT+8OPwr+roWbiP4k+E/k2I7+qlzG7XwVA6hw4ioUxY0sdxqIGz/qIgGtPIgEgiGENAgH5oKangoriqr/sj+qPet96q32taoCdbGj6yav/W8FDHw9t+fn/0OrPL4AiQgzDdlb43IhTg4RP0Gz02Og///Ck1j2GOaH2iIwIDAQAB"  # 项目密钥
+        self.host = "https://smlopenapi.esign.cn"  # 沙箱请求地址,正式环境地址:https://openapi.esign.cn

+ 9 - 0
alien_3rd_esign/esigntool/esign_emun.py

@@ -0,0 +1,9 @@
+from enum import Enum
+
+
+class httpMethodEnum(str, Enum):
+    POST = "POST"
+    GET = "GET"
+    DELETE = "DELETE"
+    PUT = "PUT"
+

+ 40 - 0
alien_3rd_esign/esigntool/esign_file.py

@@ -0,0 +1,40 @@
+import base64
+import hashlib
+import os
+
+
+# 文件辅助类
+class fileHelp:
+    def __init__(self, fileUrl):
+        """
+        为文件类定义初始化公共参数
+        :param fileUrl:
+        """
+        self.fileOs = os.path.abspath(os.path.join(os.getcwd(), fileUrl))
+        self.contentMd5 = self.__content_encoding()  # 初始化文件MD5
+        self.fileName = os.path.basename(self.fileOs)  # 初始化文件名
+        self.fileSize = os.path.getsize(self.fileOs)  # 初始化文件大小
+
+    def __content_encoding(self):
+        """
+        文件转 bytes 加密并使用 base64 编码
+        :param fileOs: 文件路径
+        :return: 返回加密编码后的字符串
+        """
+
+        with open(self.fileOs, 'rb') as f:
+            content = f.read()
+        content_md5 = hashlib.md5()
+        content_md5.update(content)
+        content_base64 = base64.b64encode(content_md5.digest())
+        return content_base64.decode("utf-8")
+
+    def getBinFile(self):
+        """
+        获取文件流
+        :param fileOs:
+        :return:
+        """
+        with open(self.fileOs, 'rb') as f:
+            binfile = f.read()
+        return binfile

+ 18 - 0
alien_3rd_esign/esigntool/esign_tool_info.py

@@ -0,0 +1,18 @@
+# coding=utf-8
+sdk_version = "esigntool1.0.1"
+supported_version = "py3.0 MORE THAN"
+info = "esigntool核心工具包,主要处理e签宝公有云产品接口调用时的签名计算," \
+       "通过esigntool.build_sign_json_header构造签名鉴权+json数据格式的请求头," \
+       "让开发者无需关注具体的请求签名算法,专注于接口业务的json参数构造"
+
+
+def get_esign_sdk_info():
+    return info
+
+
+def get_esign_sdk_verion():
+    return sdk_version
+
+
+def get_esign_sdk_supported_version():
+    return supported_version

+ 24 - 0
alien_3rd_esign/models.py

@@ -0,0 +1,24 @@
+from datetime import datetime
+from typing import Optional
+from sqlalchemy import String, DateTime, BigInteger
+from sqlalchemy.orm import Mapped, mapped_column
+
+from alien_database.base import Base, AuditMixin
+
+
+class Esign_Store(Base, AuditMixin):
+    """
+    店铺模型
+    """
+    __tablename__ = "esign_store"
+
+    store_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, comment="店铺id")
+    business_segment: Mapped[str] = mapped_column(String(100), comment="经营板块")
+    merchant_name: Mapped[str] = mapped_column(String(100), comment="商家姓名")
+    contact_phone: Mapped[str] = mapped_column(String(20), comment="联系电话")
+    signing_status: Mapped[str] = mapped_column(String(20), default="未签署", comment="签署状态(已签署,未签署,已到期)")
+    signing_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="签署时间")
+    effective_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="生效时间")
+    expiry_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="到期时间")
+
+

+ 7 - 0
alien_3rd_esign/router.py

@@ -0,0 +1,7 @@
+from fastapi import APIRouter
+
+router = APIRouter()
+
+@router.get("/")
+async def index():
+    return {"module": "alien_second", "status": "initialized"}

+ 90 - 0
alien_3rd_esign/run/moduledemo/auth/auth_launch.py

@@ -0,0 +1,90 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+# 认证和授权服务API - 实名认证 - 发起类
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def getPsnAuthUrl(mobile):
+    """
+    获取个人认证&授权页面链接
+    :param mobile:实名用户手机号
+    :return:请求响应
+    """
+    api_path = "/v3/psn-auth-url"
+    method = esigntool.httpMethodEnum.POST
+    body = {
+        "clientType": "ALL",  # 自动适配移动端或PC端
+        "authorizeConfig": {  # 实名认证可以不设置此参数,授权可以默认所有权限都设置
+            "authorizedScopes": ["get_psn_identity_info", "psn_initiate_sign", "manage_psn_resource"]
+        },
+        "redirectConfig": {
+            "redirectUrl": "https://www.baidu.com"
+        },
+        "psnAuthConfig": {
+            "psnAccount": mobile,
+            "psnInfo": {
+                "psnName": ""
+            }
+        }
+    }
+    if mobile == "":
+        print("请设置实名用户个人手机号")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, api_path, body=body)
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+# 获取机构认证 & 授权页面链接
+@esign_run_print_outer
+def getOrgAuthUrl(mobile, orgName):
+    """
+    :param mobile:实名用户手机号
+    :param orgName:实名企业名称
+    :return:请求响应
+    """
+    api_path = "/v3/org-auth-url"
+    method = esigntool.httpMethodEnum.POST
+    body = {
+        "clientType": "ALL",  # 自动适配移动端或PC端
+        "authorizeConfig": {  # 实名认证可以不设置此参数,授权可以默认所有权限都设置
+            "authorizedScopes": ["get_org_identity_info", "get_psn_identity_info", "org_initiate_sign",
+                                 "psn_initiate_sign", "manage_org_resource", "manage_psn_resource", "use_org_order"]
+        },
+        "redirectConfig": {
+            "redirectUrl": "https://www.baidu.com"
+        },
+        "orgAuthConfig": {
+            "orgName": orgName,
+            "transactorInfo": {
+                "psnAccount": mobile
+            }
+        }
+    }
+    if mobile == "" or orgName == "":
+        print("请设置实名用户个人手机号和实名企业的名称")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, api_path, body=body)
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+if __name__ == '__main__':
+    if config.appId == "" or config.scert == "":
+        print("请设置应用appId和应用Secret")
+        exit()
+    mobile = ""  # 请输入个人联系手机号
+    orgName = ""  # 请输入企业名称
+    getPsnAuthUrl(mobile)  # 获取个人认证&授权页面链接
+    getOrgAuthUrl(mobile, orgName)  # 获取机构认证 & 授权页面链接

+ 113 - 0
alien_3rd_esign/run/moduledemo/auth/auth_query.py

@@ -0,0 +1,113 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+
+# 认证和授权服务API - 实名认证 - 查询类
+
+config = esigntool.config()  # 初始化配置类
+
+
+# 查询个人认证信息
+@esign_run_print_outer
+def findPsnIdentityInfo(mobile):
+    """
+    :param mobile:实名用户手机号
+    :return:请求响应
+    """
+    api_path = "/v3/persons/identity-info?psnAccount={}".format(mobile)
+    method = esigntool.httpMethodEnum.GET
+    if mobile == "":
+        print("请设置实名用户个人手机号")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+# 查询个人授权信息
+@esign_run_print_outer
+def findPsnAuthInfo(psnId):
+    """
+    :param psnId:实名用户账号ID
+    :return:请求响应
+    """
+    api_path = "/v3/persons/{}/authorized-info".format(psnId)
+    method = esigntool.httpMethodEnum.GET
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+# 查询机构认证信息
+@esign_run_print_outer
+def findOrgIdentityInfo(orgName):
+    """
+    :param orgName:实名企业名称
+    :return:请求响应
+    """
+    api_path = "/v3/organizations/identity-info?orgName={}".format(orgName)
+    method = esigntool.httpMethodEnum.GET
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+# 查询机构授权信息
+@esign_run_print_outer
+def findOrgAuthInfo(OrgId):
+    """
+    :param OrgId:实名企业账号ID
+    :return:请求响应
+    """
+    api_path = "/v3/organizations/{}/authorized-info".format(OrgId)
+    method = esigntool.httpMethodEnum.GET
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+# 查询认证授权流程详情
+@esign_run_print_outer
+def findAuthFlowInfo(flowId):
+    """
+    :param flowId:实名流程ID
+    :return:请求响应
+    """
+    api_path = "/v3/auth-flow/{}".format(flowId)
+    method = esigntool.httpMethodEnum.GET
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+if __name__ == '__main__':
+    if config.appId == "" or config.scert == "":
+        print("请设置应用appId和应用Secret")
+        exit()
+    mobile = "1***"  # 请输入个人联系手机号
+    orgName = "***企业"  # 请输入企业名称
+    findPsnIdentityInfo(mobile)  # 查询个人认证信息
+    findOrgIdentityInfo(orgName)  # 查询机构认证信息
+    psnId = "***"  # 请输入个人认证帐号,由【查询个人认证信息】接口返回
+    OrgId = "***"  # 请输入企业认证帐号,由【查询企业认证信息】接口返回
+    flowId = "***"  # 请输入流程ID,由【发起认证&授权】接口返回
+    findPsnAuthInfo(psnId)  # 查询个人授权信息
+    findOrgAuthInfo(OrgId)  # 查询企业授权信息
+    findAuthFlowInfo(flowId)  # 查询认证授权流程详情

+ 29 - 0
alien_3rd_esign/run/moduledemo/fileandtemplate/expand.py

@@ -0,0 +1,29 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+
+# 拓展功能API
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def templatePageCreate():
+    """
+    复制合同模板
+    :return:
+    """
+    docTemplateId = "fgs***fefw"  # 声明请求变量
+    api_path = "/v3/doc-templates/{}/copy".format(docTemplateId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    templatePageCreate()  # 复制合同模板

+ 74 - 0
alien_3rd_esign/run/moduledemo/fileandtemplate/file.py

@@ -0,0 +1,74 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+from esigntool.esign_file import fileHelp
+
+
+# 文件类API
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def getFileUploadUrl(file):
+    """
+    获取文件上传地址
+    :return:
+    """
+    contentType = "application/pdf"  # 声明请求变量
+    body = {
+        "contentMd5": file.contentMd5,
+        "contentType": contentType,
+        "convert2Pdf": False,
+        "fileName": file.fileName,
+        "fileSize": file.fileSize
+    }  # 构建请求参数body体
+    api_path = "/v3/files/file-upload-url"
+    method = esigntool.httpMethodEnum.POST
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    fileUploadUrl = resp.json()['data']['fileUploadUrl']  # 获取文件上传路径
+    print(resp.text)
+    return fileUploadUrl
+
+
+@esign_run_print_outer
+def fileStreamUpload(binfile, fileUploadUrl):
+    """
+    文件流上传服务器
+    :return:
+    """
+    contentMd5 = file.contentMd5  # 声明请求变量
+    contentType = "application/pdf"  # 声明请求变量
+    method = esigntool.httpMethodEnum.PUT  # 声明请求方法
+    json_headers = esigntool.buildFileUploadHeader(contentType, contentMd5)  # 构建请求头
+    resp = requests.request(method, fileUploadUrl, data=binfile, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def keywordsPositions():
+    """
+    追检索文件关键字坐标
+    :return:
+    """
+    fileId = "df********grgter"  # 声明请求变量
+    keyword = "关键字1,关键字2"  # 声明请求变量
+    api_path = "/v3/files/{}/keyword-positions?keywords={}".format(fileId, keyword)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 计算签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    fileUrl = "D:\\**文件\\**.pdf"  # 请输入本地文件路径
+    file = fileHelp(fileUrl)  # 初始化文件辅助类
+    fileUploadUrl = getFileUploadUrl(file)  # 获取文件上传路径
+    fileStreamUpload(file.getBinFile(), fileUploadUrl)  # 文件流上传
+    keywordsPositions()  # 关键字查询

+ 138 - 0
alien_3rd_esign/run/moduledemo/fileandtemplate/template.py

@@ -0,0 +1,138 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+
+# 合同模板类API
+
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def templatePageCreate():
+    """
+    获取制作合同模板页面
+    :return:
+    """
+    body = {
+        "docTemplateName": "某公司的劳动合同模板",
+        "docTemplateType": 0,
+        "fileId": "0e99de7ce***9db2cd69",
+        "redirectUrl": "https://www.xxx.cn/"
+    }  # 构建请求body体
+    api_path = "/v3/doc-templates/doc-template-create-url"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def templateEditUrl():
+    """
+    获取编辑合同模板页面
+    :return:
+    """
+    body = {
+        "redirectUrl": "https://www.xxx.com/"
+    }  # 构建请求body体
+    docTemplateId = "fs*********fd"  # 声明请求变量
+    api_path = "/v3/doc-templates/{}/doc-template-edit-url".format(docTemplateId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送网络请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def templateDetail():
+    """
+    查询合同模板中控件详情
+    :return:
+    """
+    docTemplateId = "fs*********fd"  # 声明请求变量
+    api_path = "/v3/doc-templates/{}".format(docTemplateId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送网络请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def templateCreateDoc():
+    """
+    填写模板生成文件
+    :return:
+    """
+    body = {
+        "docTemplateId": "8726f6b**03a56d",
+        "fileName": "某公司的交易协议签署文件",
+        "components": [
+            {
+                "componentId": "59af7766***36ef41b",
+                "componentKey": "",
+                "componentValue": "这里是填充的文本"
+            },
+            {
+                "componentId": "7315e9af**72d2dac40",
+                "componentKey": "",
+                "componentValue": "2022/01/01"
+            }
+        ]
+    }
+    api_path = "/v3/files/create-by-doc-template"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送网络请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def templatePageList():
+    """
+    查询合同模板列表
+    :return:
+    """
+    pageNum = "1"  # 声明请求变量
+    pageSize = "10"  # 声明请求变量
+    api_path = "/v3/doc-templates?pageNum={}&pageSize={}".format(pageNum, pageSize)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送网络请求
+    print(resp.text)
+    return resp
+
+
+def templateDelete():
+    """
+    删除合同模板
+    :return:
+    """
+    docTemplateId = "123********gs"  # 声明请求变量
+    api_path = "/v3/doc-templates/{}".format(docTemplateId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.DELETE  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送网络请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    templatePageCreate()  # 获取制作合同模板页面
+    templateEditUrl()  # 获取编辑合同模板页面
+    templateDetail()  # 查询合同模板中控件详情
+    templateCreateDoc()  # 填写模板生成文件
+    templatePageList()  # 查询合同模板列表
+    templateDelete()  # 删除合同模板

+ 107 - 0
alien_3rd_esign/run/moduledemo/members/org_member.py

@@ -0,0 +1,107 @@
+import requests
+import esigntool
+from esigntool import esign_run_print_outer
+
+
+# 企业机构成员服务API
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def create_templateseals():
+    """
+    添加企业机构成员
+    :return:
+    """
+    # 构建请求body体
+
+    body = {
+        "members": [
+            {
+                "psnId": "d3fcf19***0ddc60a13",
+                "memberName": "成员1的姓名",
+                "employeeNum": "100"
+            },
+            {
+                "psnId": "501a277***6b19b7211",
+                "memberName": "成员2的姓名",
+                "employeeNum": "101"
+            }
+        ]
+    }
+    orgId = "501a277***6b19b7211"
+
+    api_path = "/v3/organizations/{}/members".format(orgId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def members_delete():
+    """
+    移除企业机构成员
+    :return:DELETE
+    """
+    # 构建请求body体
+
+    body = {
+        "memberPsnIds": ['d3fcf1b***dc60a13', '8d28bfe***23b135f']
+    }
+    orgId = "c7e00*****541e7"
+    api_path = "/v3/organizations/{}/members".format(orgId)  # 拼接请求路径
+    print(api_path)
+    method = esigntool.httpMethodEnum.DELETE  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def member_list():
+    """
+    查询企业机构成员
+    :return:
+    """
+    orgId = "11"
+    pageNum = "11"
+    pageSize = "22"
+
+    api_path = "/v3/organizations/{}/member-list?pageNum={}&pageSize={}".format(orgId, pageNum,
+                                                                                pageSize)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def administrators():
+    """
+    查询企业机构管理员
+    :return:
+    """
+    orgId = "11"
+
+    api_path = "/v3/organizations/{}/administrators".format(orgId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    create_templateseals()  # 添加企业机构成员
+    members_delete()  # 移除企业机构成员
+    member_list()  # 查询企业机构成员
+    administrators()  # 查询企业机构管理员

+ 46 - 0
alien_3rd_esign/run/moduledemo/order/order_buy.py

@@ -0,0 +1,46 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+
+# 套餐服务API - 套餐购买 - 购买类
+
+config = esigntool.config()  # 初始化配置类
+
+
+# 获取购买e签宝套餐链接
+@esign_run_print_outer
+def getBuyOrgOrderUrl(orgId, PsnId):
+    """
+    :param orgId:购买方企业账号
+    :param PsnId:购买方企业经办人账号
+    :return:请求响应
+    """
+    api_path = "/v3/orders/org-place-order-url"
+    method = esigntool.httpMethodEnum.POST
+    body = {
+        "orgId": orgId,  # 机构账号ID(购买方orgId)
+        "transactorPsnId": PsnId,  # 经办人个人账号ID(购买操作个人psnId)
+        "redirectUrl": "https://www.baidu.com",
+        "notifyUrl": "https://www.esign.cn/1",
+        "customBizNum": "1111111"  # 自定义编号
+    }
+    if orgId == "" or PsnId == "":
+        print("请设置企业账号ID和经办人账号ID")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, api_path, body=body)
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+if __name__ == '__main__':
+    if config.appId == "" or config.scert == "":
+        print("请设置应用Appid和应用Secret")
+        exit()
+    orgId = ""  # 请输入企业帐号ID
+    PsnId = ""  # 请输入个人帐号ID
+    getBuyOrgOrderUrl(orgId, PsnId)  # 获取购买e签宝套餐链接

+ 112 - 0
alien_3rd_esign/run/moduledemo/order/order_query.py

@@ -0,0 +1,112 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+
+# 套餐服务API - 套餐购买 - 查询类
+
+config = esigntool.config()  # 初始化配置类
+
+
+# 查询e签宝套餐余量
+@esign_run_print_outer
+def findOrderQuantity(orgId):
+    """
+    :param orgId:购买方企业账号
+    :return:请求响应
+    """
+    api_path = "/v3/orders/remaining-quantity?orgId={}&distributor=true".format(orgId)
+    method = esigntool.httpMethodEnum.GET
+    if orgId == "":
+        print("请设置实名企业账号ID")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+# 查询e签宝套餐license
+@esign_run_print_outer
+def findOrderLicense(orgId):
+    """
+    :param orgId:购买方企业账号
+    :return:请求响应
+    """
+    api_path = "/v1/mix/license/query"
+    method = esigntool.httpMethodEnum.POST
+    body = {
+        "orgOid": orgId,  # 机构账号ID(购买方orgId)
+        "page": 1,
+        "pageSize": 20
+    }
+    if orgId == "":
+        print("请设置实名企业账号ID")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, api_path, body=body)
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+# 查询套餐订单列表
+@esign_run_print_outer
+def findOrderList(orgId):
+    """
+    :param orgId:购买方企业账号
+    :return:请求响应
+    """
+    api_path = "/v3/orders/order-list?orgId={}&distributor=true".format(orgId)
+    method = esigntool.httpMethodEnum.GET
+    if orgId == "":
+        print("请设置实名企业账号ID")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+# 查询套餐订单列表(页面版)
+@esign_run_print_outer
+def getOrderListUrl(orgId, psnId):
+    """
+    :param orgId:购买方企业账号
+    :param PsnId:购买方企业经办人账号
+    :return:请求响应
+    """
+    api_path = "/v3/orders/org-order-manage-url"
+    method = esigntool.httpMethodEnum.POST
+    body = {
+        "orgId": orgId,  # 机构账号ID(购买方orgId)
+        "transactorPsnId": psnId,
+        "distributor": 'true'
+    }
+    if orgId == "" or psnId == "":
+        print("请设置实名企业账号ID和经办人账号ID")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, api_path, body=body)
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+if __name__ == '__main__':
+    if config.appId == "" or config.scert == "":
+        print("请设置应用Appid和应用Secret")
+        exit()
+    orgId = "3236725******98cdf9f18d5"  # 请输入实名企业帐号ID
+    psnId = "12394U28935Y******T34Y3894"  # 请输入实名个人帐号ID
+    findOrderQuantity(orgId)  # 查询e签宝套餐余量
+    findOrderLicense(orgId)  # 查询e签宝套餐license
+    findOrderList(orgId)  # 查询套餐订单列表
+    getOrderListUrl(orgId, psnId)  # 查询套餐订单列表(页面版)

+ 403 - 0
alien_3rd_esign/run/moduledemo/seal/org_seals.py

@@ -0,0 +1,403 @@
+import requests
+
+import esigntool
+from esigntool import esign_run_print_outer
+
+# 印章服务API
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def create_templateseals():
+    """
+    创建机构模板印章
+    :return:
+    """
+    # 构建请求body体
+
+    body = {
+        "orgId": "0c5bd492**8bfbf",
+        "sealName": "xx企业公章",
+        "sealTemplateStyle": "PUBLIC_ROUND_STAR",
+        "sealOpacity": "100",
+        "sealColor": "RED",
+        "sealOldStyle": "OLD_12",
+        "sealSize": "40_40"
+    }
+    api_path = "/v3/seals/org-seals/create-by-template"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def create_orgimage():
+    """
+    创建机构图片印章
+    :return:
+    """
+    # 构建请求body体
+
+    body = {
+        "orgId": "0c5bd492**48bfbf",
+        "sealImageFileKey": "$c3c7170e-xx-xx-xx-xx",
+        "sealName": "这是一个自定义的印章名称",
+        "sealWidth": "20",
+        "sealHeight": "10",
+        "sealBizType": "CONTRACT"
+    }
+    api_path = "/v3/seals/org-seals/create-by-image"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def internal_auth():
+    """
+    内部成员授权
+    :return:
+    """
+    # 构建请求body体
+
+    body = {
+        "orgId": "0c5bd49***48bfbf",
+        "sealId": "02590082-xx-xx-xx-2138db4d7b73",
+        "effectiveTime": 1636525541000,
+        "expireTime": 1668061541000,
+        "transactorPsnId": "c7e0029***10541e7",
+        "authorizedPsnIds": ["7ffcae***f0a8f6", "00371e8***c21ed09"],
+        "sealAuthScope": {
+            "templateIds": ["ALL"]
+        },
+        "sealRole": "SEAL_EXAMINER",
+        "redirectUrl": "https://www.xxx.cn/"
+    }
+    api_path = "/v3/seals/org-seals/internal-auth"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def external_auth():
+    """
+    跨企业授权
+    :return:
+    """
+    # 构建请求body体
+
+    body = {
+        "orgId": "0c5bd49**648bfbf",
+        "effectiveTime": 1636525541000,
+        "expireTime": 1668061541000,
+        "transactorPsnId": "c7e00294**41e7",
+        "authorizedOrgInfo": {
+            "orgName": "这是一个受托企业的名称",
+            "orgIDCardNum": "91330108******1222"
+        },
+        "sealId": "02590082-xx-xx-xx-2138db4d7b73",
+        "redirectUrl": "https://www.xx.cn/"
+    }
+    api_path = "/v3/seals/org-seals/internal-auth"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def auth_delete():
+    """
+    解除印章授权
+    :return:
+    """
+    # 构建请求body体
+
+    body = {
+        "orgId": "0c5bd492**48bfbf",
+        "deleteType": "sealIds",
+        "sealIds": ["02590082-xx-xx-xx-2138db4d7b73", "a1ccfad7-xx-xx-xx-84c51bf3a7e4"]
+    }
+    api_path = "/v3/seals/org-seals/auth-delete"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def authorization():
+    """
+    查询授权书签署链接
+    :return:
+    """
+    orgId = "c7e00290541e7"
+    sealAuthBizId = "1caebb40***31f7f95087de"
+    api_path = "/v3/seals/org-seals/authorization-sign-url?orgId={}&sealAuthBizId={}".format(orgId,
+                                                                                             sealAuthBizId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def internal_detail():
+    """
+    查询对内部成员授权详情
+    :return:
+    """
+
+    orgId = "c7e00290541e7"
+    pageNum = "11"
+    pageSize = "22"
+    sealId = "02590082-**-**-**-2138db4d7b73"
+
+    api_path = "/v3/seals/org-seals/internal-auth?orgId={}&pageNum={}&pageSize={}&sealId={}".format(orgId, pageNum,
+                                                                                                    pageSize,
+                                                                                                    sealId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def external_detail():
+    """
+    查询对外部企业授权详情
+    :return:
+    """
+
+    orgId = "c7e00290541e7"
+    pageNum = "11"
+    pageSize = "22"
+    sealId = "02590082-**-**-**-2138db4d7b73"
+
+    api_path = "/v3/seals/org-seals/external-auth?orgId={}&pageNum={}&pageSize={}&sealId={}".format(orgId, pageNum,
+                                                                                                    pageSize,
+                                                                                                    sealId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def reauthorization():
+    """
+    修改印章授权期限
+    :return:
+    """
+    # 构建请求body体
+
+    body = {
+        "sealAuthBizId": "7eb81942-xx-xx-xx-b59aa2ba479f",
+        "orgId": "0c5bd4924***5648bfbf",
+        "transactorPsnId": "c7e002***ea310541e7",
+        "effectiveTime": "1636525541000",
+        "expireTime": "1668061541000",
+        "redirectUrl": "http://www.xxx.cn/"
+    }
+    api_path = "/v3/seals/org-seals/reauthorization"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def own_seal():
+    """
+    查询企业内部印章
+    :return:
+    """
+
+    orgId = "c7e00290541e7"
+    pageNum = "11"
+    pageSize = "22"
+    sealId = "02590082-**-**-**-2138db4d7b73"
+
+    api_path = "/v3/seals/org-own-seal-list?orgId={}&pageNum={}&pageSize={}&sealId={}".format(orgId, pageNum,
+                                                                                              pageSize,
+                                                                                              sealId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def authorized_seal():
+    """
+    查询被外部企业授权印章
+    :return:
+    """
+
+    orgId = "c7e00290541e7"
+    pageNum = "11"
+    pageSize = "22"
+
+    api_path = "/v3/seals/org-authorized-seal-list?orgId={}&pageNum={}&pageSize={}".format(orgId, pageNum,
+                                                                                           pageSize)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def info_seal():
+    """
+    查询指定印章详情(机构)
+    :return:
+    """
+
+    orgId = "c7e00290541e7"
+    sealId = "02590082-**-**-**-2138db4d7b73"
+
+    api_path = "/v3/seals/org-seal-info?orgId={}&sealId={}".format(orgId, sealId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def default_seal():
+    """
+    设置机构默认印章
+    :return:
+    """
+    # 构建请求body体
+
+    body = {
+        "orgId": "c7e0029***41e7",
+        "sealId": "b984059f-xx-xx-xx-7e09a890b929"
+
+    }
+    api_path = "/v3/seals/org-seals/set-default-seal"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def orgseals_delete():
+    """
+    删除机构印章
+    :return:DELETE
+    """
+
+    orgId = "c7e00290541e7"
+    sealId = "1caebb40***31f7f95087de"
+
+    api_path = "/v3/seals/org-seal?orgId={}&sealId={}".format(orgId, sealId)  # 拼接请求路径
+    print(api_path)
+    method = esigntool.httpMethodEnum.DELETE  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def org_sealurl():
+    """
+    获取创建机构印章页面链接
+    :return:
+    """
+    # 构建请求body体
+
+    body = {
+
+        "orgId": "0c5bd492***48bfbf",
+        "transactorPsnId": "c7e00294***10541e7",
+        "customBizNum": "这是一串开发者内部系统自定义的编号",
+        "sealName": "这是个预定义的企业印章名称",
+        "sealColor": "RED",
+        "redirectUrl": "http://www.xxx.cn/",
+        "uneditableFields": ["sealName", "sealColor"]
+
+    }
+    api_path = "/v3/seals/org-seal-create-url"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def manage_sealurl():
+    """
+    获取管理机构印章页面链接
+    :return:
+    """
+    # 构建请求body体
+
+    body = {
+
+        "orgId": "0c5bd492**8bfbf",
+        "transactorPsnId": "c7e00294729***10541e7"
+
+    }
+    api_path = "/v3/seals/org-seals-manage-url"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    # create_templateseals()  # 创建机构模板印章
+    # create_orgimage()  # 创建企业图片印章
+    # internal_auth()  # 内部成员授权
+    external_auth()  # 跨企业授权
+    auth_delete()  # 解除印章授权
+    authorization()  # 查询授权书签署链接
+    internal_detail()  # 查询对内部成员授权详情
+    external_detail()  # 查询对外部企业授权详情
+    reauthorization()  # 修改印章授权期限
+    own_seal()  # 查询企业内部印章
+    authorized_seal()  # 查询被外部企业授权印章
+    info_seal()  # 查询指定印章详情(机构)
+    default_seal()  # 设置机构默认印章
+    orgseals_delete()  # 设置机构默认印章
+    org_sealurl()  # 获取创建机构印章页面链接
+    manage_sealurl()  # 获取管理机构印章页面链接

+ 191 - 0
alien_3rd_esign/run/moduledemo/seal/psn_seals.py

@@ -0,0 +1,191 @@
+import requests
+
+import esigntool
+from esigntool import esign_run_print_outer
+
+# 印章服务API
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def createPsn_seals():
+    """
+       创建个人模板印章
+       :return:
+       """
+    # 构建请求body体
+
+    body = {
+        "psnId": "c7e002***0541e7",
+        "sealName": "这是一个自定义的名称",
+        "sealTemplateStyle": "SQUARE_LEFT_BORDER",
+        "sealSize": "20_20",
+        "sealColor": "RED",
+        "sealSuffix": "2",
+        "sealOldStyle": "OLD_1",
+        "sealOpacity": "100"
+    }
+    api_path = "/v3/seals/psn-seals/create-by-template"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def create_image():
+    """
+       创建个人图片印章
+       :return:
+       """
+    # 构建请求body体
+
+    body = {
+        "psnId": "c7e0029***0541e7",
+        "sealImageFileKey": "$c3c7170e-xx-xx-xx-xx",
+        "sealName": "这是一个自定义的印章名称",
+        "sealWidth": "20",
+        "sealHeight": "10",
+        "sealColor": "RED"
+    }
+    api_path = "/v3/seals/psn-seals/create-by-image"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def seals_query():
+    """
+    查询个人印章列表
+    :return:
+           """
+
+    psnId = "c7e00290541e7"
+    pageNum = "1"
+    pageSize = "20"
+
+    api_path = "/v3/seals/psn-seal-list?psnId={}&pageNum={}&pageSize={}".format(psnId, pageNum, pageSize)  # 拼接请求路径
+    print(api_path)
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def sealsinfo_query():
+    """
+    查询指定印章详情
+    :return:
+    """
+    psnId = "c7e0****41e7"
+    sealId = "1caebb40***31f7f95087de"
+
+    api_path = "/v3/seals/psn-seal-info?psnId={}&sealId={}".format(psnId, sealId)  # 拼接请求路径
+    print(api_path)
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def change_seals():
+    """
+    设置个人默认印章
+    :return:
+    """
+    # 构建请求body体
+    body = {
+        "psnId": "c7e0029***41e7",
+        "sealId": "d2749c50-xx-xx-xx-ce4482c7747f"
+    }
+    api_path = "/v3/seals/psn-seals/set-default-seal"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def seals_delete():
+    """
+    删除个人印章
+    :return:DELETE
+    """
+    psnId = "c7e00290541e7"
+    sealId = "1caebb40***31f7f95087de"
+    api_path = "/v3/seals/psn-seal?psnId={}&sealId={}".format(psnId, sealId)  # 拼接请求路径
+    print(api_path)
+    method = esigntool.httpMethodEnum.DELETE  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def create_sealsurl():
+    """
+    获取创建个人印章页面链接
+    :return:
+    """
+    # 构建请求body体
+    body = {
+        "psnId": "c7e002***0541e7",
+        "sealName": "这是一个预定义印章名称",
+        "customBizNum": "这是一串开发者内部系统自定义的编号",
+        "redirectUrl": "https://www.xxx.cn/",
+        "uneditableFields": ["sealName", "sealOpacity"],
+    }
+    api_path = "/v3/seals/psn-seal-create-url"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def manage_sealsurl():
+    """
+    获取管理个人印章页面链接
+    :return:
+    """
+    # 构建请求body体
+    body = {
+        "psnId": "c7e002***0541e7"
+    }
+    api_path = "/v3/seals/psn-seals-manage-url"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    createPsn_seals()  # 创建个人模板印章
+    create_image()  # 创建个人图片印章
+    seals_query()  # 查询个人印章列表
+    sealsinfo_query()  # 查询指定印章详情
+    change_seals()  # 设置个人默认印章
+    seals_delete()  # 删除个人印章
+    create_sealsurl()  # 获取创建个人印章页面链接
+    manage_sealsurl()  # 获取管理个人印章页面链接

+ 59 - 0
alien_3rd_esign/run/moduledemo/seal/upload_url.py

@@ -0,0 +1,59 @@
+import requests
+
+import base64
+import hashlib
+import os
+import esigntool
+from esigntool import esign_run_print_outer, fileHelp
+
+# 印章服务API
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def image_upload():
+    """
+    上传印章图片
+    :return:
+    """
+    contentType = "application/octet-stream"
+    body = {
+        "contentMd5": file.contentMd5,
+        "contentType": contentType,
+        "fileName": file.fileName,
+        "fileSize": file.fileSize
+    }
+    api_path = "/v3/files/file-key"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    fileUploadUrl = resp.json()['data']['fileUploadUrl']  # 获取文件上传路径
+    fileKey = resp.json()['data']['fileKey']  # 获取文件上传路径
+    print("fileKey,请保存本地,用于后续获取图片:", fileKey)
+    return fileUploadUrl
+
+
+@esign_run_print_outer
+def create_image(fileUploadUrl, binfile):
+    """
+    创建个人图片印章
+    :return:
+    """
+    # 构建请求body体
+    contentMd5 = file.contentMd5  # 声明请求变量
+    contentType = "application/octet-stream"  # 声明请求变量
+    method = esigntool.httpMethodEnum.PUT  # 声明请求方法
+    json_headers = esigntool.buildFileUploadHeader(contentType, contentMd5)  # 构建请求头
+    resp = requests.request(method, fileUploadUrl, data=binfile, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    fileUrl = "D:\\1.jpg"
+    file = fileHelp(fileUrl)  # 初始化文件辅助类
+    fileUploadUrl = image_upload()  # 获取文件流上传地址
+    create_image(fileUploadUrl, file.getBinFile())  # 上传文件流

+ 99 - 0
alien_3rd_esign/run/moduledemo/sign/attachments_change.py

@@ -0,0 +1,99 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+# 合同文件签署服务API - 待签文件及附属材料 - 变更类
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def addUnsignedFiles():
+    """
+    追加待签文件
+    :return:
+    """
+    body = {
+        "unsignedFiles": [
+            {
+                "fileId": "ea3151a8d***a53d3f4c",
+                "fileName": "入职证明.pdf"
+            }
+        ]
+    }
+    # 构建请求body体
+    sign_flowId = "df********grgter"  # 声明变量入参
+    api_path = "/v3/sign-flow/{}/unsigned-files".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 计算签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def deleteUnsignedFiles():
+    """
+    删除待签署文件
+    :return:
+    """
+    sign_flowId = "df********grgter"  # 声明变量入参
+    sign_FieldIds = "urerutheirt"  # 声明变量入参
+    api_path = "/v3/sign-flow/{}/unsigned-files?fileIds={}".format(sign_flowId, sign_FieldIds)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.DELETE  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def addAttachments():
+    """
+    追加附属材料
+    :return:
+    """
+    body = {
+        "attachmentList": [
+            {
+                "fileId": "ea3151***a53d3f4c",
+                "fileName": "入职手册.pdf"
+            }
+        ]
+    }
+    # 构建请求body体
+    sign_flowId = "df********grgter"  # 声明变量入参
+    api_path = "/v3/sign-flow/{}/attachments".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 计算签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def deleteAttachments():
+    """
+    删除附属材料
+    :return:
+    """
+    sign_flowId = "df********grgter"  # 声明变量入参
+    sign_FieldIds = "xxx1,xxx2"  # 声明变量入参
+    api_path = "/v3/sign-flow/{}/attachments?fileIds={}".format(sign_flowId, sign_FieldIds)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.DELETE  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    addUnsignedFiles()  # 追加待签文件
+    deleteUnsignedFiles()  # 删除待签署文件
+    addAttachments()  # 追加附属材料
+    deleteAttachments()  # 删除附属材料

+ 73 - 0
alien_3rd_esign/run/moduledemo/sign/copiers_change.py

@@ -0,0 +1,73 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+# 合同文件签署服务API - 抄送方 - 变更类
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def addCopiers():
+    """
+    添加抄送方
+    :return:
+    """
+    sign_flowId = "df********grgter"  # 声明变量入参
+    body = {
+        "copiers": [
+            {
+                "copierOrgInfo": {
+                    "orgName": "这是个抄送通知企业的名称",
+                    "orgId": ""
+                },
+                "copierPsnInfo": {
+                    "psnAccount": "153****7650",
+                    "psnId": ""
+                }
+            }
+        ]
+    }  # 构建请求body体
+    api_path = "/v3/sign-flow/{}/copiers".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def deleteCopiers():
+    """
+    删除签署区
+    :return:
+    """
+    sign_flowId = "df********grgter"  # 声明变量入参
+    body = {
+        "copiers": [
+            {
+                "copierPsnInfo": {
+                    "psnId": "",
+                    "psnAccount": "151****0101"
+                },
+                "copierOrgInfo": {
+                    "orgId": "",
+                    "orgName": "这是个抄送方的企业名称"
+                }
+            }
+        ]
+    }  # 构建请求body体
+    api_path = "/v3/sign-flow/{}/copiers/delete".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    addCopiers()  # 添加抄送方
+    deleteCopiers()  # 删除抄送方

+ 85 - 0
alien_3rd_esign/run/moduledemo/sign/signfields_change.py

@@ -0,0 +1,85 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+# 合同文件签署服务API - 签署区 - 变更类
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def addSignFields():
+    """
+    追加签署区
+    :return:
+    """
+    body = {
+        "signers": [
+            {
+                "noticeConfig": {
+                    "noticeTypes": "1"
+                },
+                "psnSignerInfo": {
+                    "psnAccount": "153****0000"
+                },
+                "signConfig": {
+                    "forcedReadingTime": 10,
+                    "signOrder": 2
+                },
+                "signFields": [
+                    {
+                        "customBizNum": "202201010001",
+                        "fileId": "0e99de7c***09db2cd69",
+                        "remarkSignFieldConfig": {
+                            "aiCheck": 0,
+                            "freeMode": True,
+                            "movableSignField": True,
+                            "remarkContent": "我已阅读并知悉",
+                            "remarkFontSize": 20,
+                            "signFieldHeight": 100,
+                            "signFieldPosition": {
+                                "acrossPageMode": "",
+                                "positionPage": "1",
+                                "positionX": 100,
+                                "positionY": 100
+                            },
+                            "signFieldWidth": 100
+                        },
+                        "signFieldType": 1
+                    }
+                ],
+                "signerType": 0
+            }
+        ]
+    }  # 构建请求body体
+    sign_flowId = "df********grgter"  # 声明变量入参
+    api_path = "/v3/sign-flow/{}/signers/sign-fields".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def deleteSignFields():
+    """
+    删除签署区
+    :return:
+    """
+    sign_flowId = "df********grgter"  # 声明变量入参
+    sign_FieldIds = "xxx1,xxx2"  # 声明变量入参
+    api_path = "/v3/sign-flow/{}/signers/sign-fields?signFieldIds={}".format(sign_flowId, sign_FieldIds)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.DELETE  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    addSignFields()  # 追加签署区
+    deleteSignFields()  # 删除签署区

+ 106 - 0
alien_3rd_esign/run/moduledemo/sign/signflow_change.py

@@ -0,0 +1,106 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+# 合同文件签署服务API - 签署流程 - 变更类
+
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def signFlowStart():
+    """
+    开启签署流程
+    :return:
+    """
+    sign_flowId = "fr5845******834y656"  # 声明变量入参
+    api_path = "/v3/sign-flow/{}/start".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def signFlowFinish():
+    """
+    完结签署流程
+    :return:
+    """
+    sign_flowId = "fr5845******834y656"  # 声明变量入参
+    api_path = "/v3/sign-flow/{}/finish".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def signFlowRevoke():
+    """
+    撤销签署流程
+    :return:
+    """
+    sign_flowId = "fr5845******834y656"  # 声明变量入参
+    api_path = "/v3/sign-flow/{}/revoke".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def signFlowDelay():
+    """
+    延期签署截止时间
+    :return:
+    """
+    sign_flowId = "fr5845******834y656"  # 声明变量入参
+    body = {
+        "signFlowExpireTime": 1654849671000
+    }  # 构建请求body体
+    api_path = "/v3/sign-flow/{}/delay".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def signFlowUrge():
+    """
+    催签流程中签署人
+    :return:
+    """
+    sign_flowId = "fr5845******834y656"  # 声明变量入参
+    body = {
+        "noticeTypes": "1",
+        "urgedOperator": {
+            "psnAccount": "183****0101"
+        }
+    }  # 构建请求body体
+    api_path = "/v3/sign-flow/{}/urge".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    signFlowStart()  # 开启签署流程
+    signFlowFinish()  # 完结签署流程
+    signFlowRevoke()  # 撤销签署流程
+    signFlowDelay()  # 延期签署截止时间
+    signFlowUrge()  # 催签流程中签署人

+ 28 - 0
alien_3rd_esign/run/moduledemo/sign/signflow_download.py

@@ -0,0 +1,28 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+# 合同文件签署服务API - 签署流程 - 下载类
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def fileDownloadUrl():
+    """
+    下载已签署文件及附属材料
+    :return:
+    """
+    sign_flowId = "df********grgter"  # 声明变量入参
+    api_path = "/v3/sign-flow/{}/file-download-url".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    fileDownloadUrl()  # 下载已签署文件及附属材料

+ 135 - 0
alien_3rd_esign/run/moduledemo/sign/signflow_launch.py

@@ -0,0 +1,135 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+# 合同文件签署服务API - 签署流程 - 发起类
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def signFlowCreateByFile():
+    """
+    基于文件发起签署
+    :return:
+    """
+    body = {
+        "signFlowConfig": {
+            "noticeTypes": {
+                "noticeTypes": "1"
+            },
+            "signConfig": {
+                "availableSignClientTypes": "1",
+                "showBatchDropSealButton": True
+            },
+            "autoFinish": True,
+            "notifyUrl": "",
+            "signFlowTitle": "小****同.pdf"
+        },
+        "signers": [{
+            "signConfig": {
+                "forcedReadingTime": 5,
+                "signOrder": 1
+            },
+            "signFields": [{
+                "signFieldType": 0,
+                "normalSignFieldConfig": {
+                    "signFieldPosition": {
+                        "positionY": "331.422",
+                        "positionPage": 4,
+                        "positionX": "51.306"
+                    },
+                    "signFieldStyle": 1
+                },
+                "fileId": "95ce531*******1b"
+            }],
+            "signerType": 0,
+            "psnSignerInfo": {
+                "psnAccount": "15*****7"
+            }
+        }, {
+            "signConfig": {
+                "signOrder": 3
+            },
+            "orgSignerInfo": {
+                "orgName": "阳**苑",
+                "orgSignerInfo": {
+                    "$ref": "@"
+                }
+            },
+            "signFields": [{
+                "signFieldType": 0,
+                "normalSignFieldConfig": {
+                    "signFieldPosition": {},
+                    "signFieldStyle": 1
+                },
+                "fileId": "95ce******5831b"
+            }],
+            "signerType": 1
+        }],
+        "docs": [{
+            "fileName": "小*****同.pdf",
+            "fileId": "95c*****31b"
+        }]
+    }  # 构建请求body体
+    api_path = "/v3/sign-flow/create-by-file"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp)
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def signUrl():
+    """
+    获取合同文件签署链接
+    :return:
+    """
+    body = {
+        "clientType": "ALL",
+        "needLogin": True,
+        "operator": {
+            "psnAccount": "183****0101"
+        },
+        "urlType": 2
+    }  # 构建请求body体
+    sign_flowId = "23423****25435"  # 声明请求参数
+    api_path = "/v3/sign-flow/{}/sign-url".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送http请求
+    print(resp)
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def batchSignUrl():
+    """
+    获取批量签页面链接(多流程)
+    :return:
+    """
+    body = {
+        "operatorId": "c7e0029472914ce4a3xxxe7",
+        "redirectUrl": "xxx",
+        "signFlowIds": ["ba619b6d0***bf8e9xxe4", "7b90e0d09***29a67dx53"]
+    }  # 构建请求body体
+    api_path = "/v3/sign-flow/batch-sign-url"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送http请求
+    print(resp)
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    signFlowCreateByFile()  # 基于文件发起签署
+    signUrl()  # 获取合同文件签署链接
+    batchSignUrl()  # 获取批量签页面链接(多流程)

+ 55 - 0
alien_3rd_esign/run/moduledemo/sign/signflow_query.py

@@ -0,0 +1,55 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+# 合同文件签署服务API - 签署流程 - 查询类
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def signFlowDetail():
+    """
+    查询签署流程详情
+    :return:
+    """
+    sign_flowId = "df********grgter"  # 声明请求参数
+    api_path = "/v3/sign-flow/{}/detail".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def signFlowList():
+    """
+    查询签署流程列表
+    :return:
+    """
+    api_path = "/v3/sign-flow/sign-flow-list"  # 拼接请求路径
+    body = {
+        "operator": {
+            "psnAccount": "183****0101",
+            "psnId": ""
+        },
+        "pageNum": 1,
+        "pageSize": 20,
+        "signFlowStartTimeFrom": 1648801671000,
+        "signFlowStartTimeTo": 1651393671000,
+        "signFlowStatus": [1, 2]
+    }  # 构建请求body体
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    signFlowDetail()  # 查询签署流程详情
+    signFlowList()  # 查询签署流程列表

+ 48 - 0
alien_3rd_esign/run/moduledemo/sign/signflow_rescission.py

@@ -0,0 +1,48 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+# 合同文件签署服务API - 合同解约服务
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def rescissionUrl():
+    """
+    获取合同解约链接
+    :return:
+    """
+    # 构建请求body体
+    sign_flowId = "df********grgter"  # 声明请求参数
+    body = {
+        "rescissionInitiator": {
+            "orgInitiator": {
+                "orgId": "0c5bd49248***5648bfbf",
+                "transactor": {
+                    "psnId": "c7e002947***310541e7"
+                }
+            }
+        },
+        "signFlowConfig": {
+            "chargeConfig": {
+                "chargeMode": 0
+            },
+            "noticeConfig": {
+                "noticeTypes": "1,2"
+            },
+            "notifyUrl": "https://xx.xx.xx/callback"
+        }
+    }  # 构建请求body体
+    api_path = "/v3/sign-flow/{}/rescission-url".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    rescissionUrl()  # 获取合同解约链接

+ 204 - 0
alien_3rd_esign/run/scenedome/b2b_autosign_demo.py

@@ -0,0 +1,204 @@
+# -*- coding: UTF-8 -*-
+import time
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+from esigntool.esign_file import fileHelp
+
+"""
+ * 本文件适用于平台方自动+企业用户自动签署场景      
+ * 基本流程如下:
+ * 1、企业用户将印章授权给平台方,可通过登录e签宝官网企业控制台完成,或者通过印章开放服务完成:https://open.esign.cn/doc/opendoc/seal3/qkxyha
+ * 2、发起签署时,设置signFields入参规则:signers.orgSignerInfo对象中的orgId为平台方企业账号id,assignedSealId为授权企业的印章id,autoSign设置为true
+ * 3、流程完结后,下载签署后文件
+ """
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def findOrgIdentityInfo(orgName):
+    """
+    查询机构认证信息
+    :return:
+    """
+    api_path = "/v3/organizations/identity-info?orgName={}".format(orgName)
+    method = esigntool.httpMethodEnum.GET
+    if orgName == "":
+        print("请设置实名企业名称")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    print(resp.text)
+    orgId = resp.json()['data']['orgId']
+    return orgId
+
+
+@esign_run_print_outer
+def getFileUploadUrl(file):
+    """
+    获取文件上传地址
+    :return:
+    """
+    contentType = "application/pdf"  # 声明请求变量
+    body = {
+        "contentMd5": file.contentMd5,
+        "contentType": contentType,
+        "convert2Pdf": False,
+        "fileName": file.fileName,
+        "fileSize": file.fileSize
+    }  # 构建请求参数body体
+    api_path = "/v3/files/file-upload-url"
+    method = esigntool.httpMethodEnum.POST
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    fileUploadUrl = resp.json()['data']['fileUploadUrl']  # 获取文件上传路径
+    fileId = resp.json()['data']['fileId']
+    print(resp.text)
+    return fileUploadUrl, fileId
+
+
+@esign_run_print_outer
+def fileStreamUpload(binfile, fileUploadUrl):
+    """
+    文件流上传服务器
+    :return:
+    """
+    contentMd5 = file.contentMd5  # 声明请求变量
+    contentType = "application/pdf"  # 声明请求变量
+    method = esigntool.httpMethodEnum.PUT  # 声明请求方法
+    json_headers = esigntool.buildFileUploadHeader(contentType, contentMd5)  # 构建请求头
+    resp = requests.request(method, fileUploadUrl, data=binfile, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def signFlowCreateByFile(fileId, orgId, sealId):
+    """
+    基于文件发起签署
+    :return:
+    """
+    body = {
+        "docs": [
+            {
+                "fileId": fileId,
+                "fileName": "xx企业劳动合同.pdf"
+            }
+        ],
+        "attachments": [
+            {
+                "fileId": fileId,
+                "fileName": "入职材料.pdf"
+            }
+        ],
+        "signers": [
+            {
+                "orgSignerInfo": {
+                    "orgId": orgId
+                },
+                "signConfig": {
+                    "signOrder": 1
+                },
+                "signFields": [
+                    {
+                        "customBizNum": "自定义编码",
+                        "fileId": fileId,
+                        "normalSignFieldConfig": {
+                            "autoSign": True,
+                            "signFieldPosition": {
+                                "positionPage": "1",
+                                "positionX": 300,
+                                "positionY": 300
+                            },
+                            "signFieldStyle": 1
+                        }
+                    }
+                ],
+                "signerType": 1
+            },
+            {
+                "orgSignerInfo": {
+                    "orgId": orgId
+                },
+                "signConfig": {
+                    "signOrder": 1
+                },
+                "signFields": [
+                    {
+                        "customBizNum": "自定义编码",
+                        "fileId": fileId,
+                        "normalSignFieldConfig": {
+                            "autoSign": True,
+                            "assignedSealId": sealId,  # 传入授权后的印章ID
+                            "signFieldPosition": {
+                                "positionPage": "1",
+                                "positionX": 100,
+                                "positionY": 200
+                            },
+                            "signFieldStyle": 1
+                        }
+                    }
+                ],
+                "signerType": 1
+            }
+        ],
+        "signFlowConfig": {
+            "autoFinish": True,  # 自动完结,为false的时候需要单独调用对应完结接口
+            "autoStart": True,  # 自动开启,为false的时候需要单独调用对应开启接口
+            "noticeConfig": {
+                "noticeTypes": "1"
+            },
+            "notifyUrl": "http://xx.xx.86.172:8081/asyn/notify",
+            "redirectConfig": {
+                "redirectDelayTime": "3",
+                "redirectUrl": "http://www.esign.cn"
+            },
+            "signConfig": {
+                "availableSignClientTypes": "1",
+                "showBatchDropSealButton": True
+            },
+            "signFlowTitle": "企业员工劳动合同签署"
+        }
+    }  # 构建请求body体
+    api_path = "/v3/sign-flow/create-by-file"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    sign_flowId = resp.json()['data']['signFlowId']
+    return sign_flowId
+
+
+@esign_run_print_outer
+def fileDownloadUrl(sign_flowId):
+    """
+    下载已签署文件及附属材料
+    :return:
+    """
+    api_path = "/v3/sign-flow/{}/file-download-url".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    if config.appId == "" or config.scert == "":
+        print("请设置应用Appid和应用Secret")
+        exit()
+    fileUrl = "D://**文件//1.pdf"  # 本地文件路径
+    orgName = "**企业"  # 平台自身企业名称,即appid所属的企业名称
+    sealId = "*******fef"  # 通过印章开放服务来获取印章ID
+    orgId = findOrgIdentityInfo(orgName)  # 查询平台自身实名帐号ID
+    file = fileHelp(fileUrl)  # 初始化文件辅助类
+    fileUploadUrl, fileId = getFileUploadUrl(file)  # 获取文件ID&文件上传路径
+    fileStreamUpload(file.getBinFile(), fileUploadUrl)  # 上传文件流
+    sign_flowId = signFlowCreateByFile(fileId, orgId, sealId)  # 发起一步签署:主动发送签署短信给签署用户
+    time.sleep(2)
+    fileDownloadUrl(sign_flowId)  # 合同结束后(个人用户签署完成后),下载签署后合同

+ 237 - 0
alien_3rd_esign/run/scenedome/b2b_handsign_demo.py

@@ -0,0 +1,237 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+from esigntool.esign_file import fileHelp
+
+# 签署场景:平台方自动签署(签署方1)+企业用户手动签署流程(签署方2)
+# 平台方(签署方1):appId所属企业为平台方,默认平台方已完成实名认证,通过获取企业签署帐号来实现平台方自动签署。
+# 企业用户手动签署(签署方2):通过传入企业名称和经办人信息,来实现企业用户手动签署。
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def findOrgIdentityInfo(orgName):
+    """
+    :param orgName
+    查询机构认证信息
+    :return: 企业实名主体帐号ID
+    """
+    api_path = "/v3/organizations/identity-info?orgName={}".format(orgName)
+    method = esigntool.httpMethodEnum.GET
+    if orgName == "":
+        print("请设置实名企业名称")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    print(resp.text)
+    orgId = resp.json()['data']['orgId']
+    return orgId
+
+
+@esign_run_print_outer
+def getFileUploadUrl(file):
+    """
+    获取文件上传地址
+    :param file 文件辅助类初始化
+    :return: fileUploadUrl, fileId  文件流上传路径,文件ID
+    """
+    contentType = "application/pdf"  # 声明请求变量
+    body = {
+        "contentMd5": file.contentMd5,
+        "contentType": contentType,
+        "convert2Pdf": False,
+        "fileName": file.fileName,
+        "fileSize": file.fileSize
+    }  # 构建请求参数body体
+    api_path = "/v3/files/file-upload-url"
+    method = esigntool.httpMethodEnum.POST
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    fileUploadUrl = resp.json()['data']['fileUploadUrl']  # 获取文件上传路径
+    fileId = resp.json()['data']['fileId']
+    print(resp.text)
+    return fileUploadUrl, fileId
+
+
+@esign_run_print_outer
+def fileStreamUpload(binfile, fileUploadUrl):
+    """
+    :param binfile 文件流
+    :param fileUploadUrl 文件流上传路径
+    方法名:文件流上传服务器
+    :return: 文件上传结果
+    """
+    contentMd5 = file.contentMd5  # 声明请求变量
+    contentType = "application/pdf"  # 声明请求变量
+    method = esigntool.httpMethodEnum.PUT  # 声明请求方法
+    json_headers = esigntool.buildFileUploadHeader(contentType, contentMd5)  # 构建请求头
+    resp = requests.request(method, fileUploadUrl, data=binfile, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def signFlowCreateByFile(fileId, orgId,psnAccount):
+    """
+    :param fileId 文件ID
+    :param orgId  企业实名主体帐号ID,用于实现自动平台放自动签署
+    基于文件发起签署
+    :return: 签署流程ID
+    """
+    body = {
+        "docs": [{
+            "fileId": fileId,
+            "fileName": "xx企业劳动合同.pdf"
+        }],
+        "attachments": [{
+            "fileId": fileId,
+            "fileName": "入职材料.pdf"
+        }],
+        "signers": [{
+            "orgSignerInfo": {
+                "orgName": orgName,  # 签署方1:平台自动签署
+                "transactorInfo": {
+                    "psnAccount": psnAccount
+                }
+            },
+            "signConfig": {
+                "forcedReadingTime": "10",
+                "signOrder": 2
+            },
+            "signFields": [{
+                "customBizNum": "202200001111",
+                "fileId": fileId,
+                "normalSignFieldConfig": {
+                    "autoSign": False,
+                    "freeMode": False,
+                    "movableSignField": False,
+                    "signFieldPosition": {
+                        "positionPage": "1",
+                        "positionX": 100,
+                        "positionY": 200
+                    },
+                    "signFieldSize": 200,
+                    "signFieldStyle": 1
+                },
+                "signFieldType": 0
+            }],
+            "signerType": 1
+        }, {
+            "orgSignerInfo": {
+                "orgId": orgId  # 签署方2:企业用户手动签署
+            },
+            "signConfig": {
+                "forcedReadingTime": "10",
+                "signOrder": 1
+            },
+            "signFields": [{
+                "customBizNum": "202200001111",
+                "fileId": fileId,
+                "normalSignFieldConfig": {
+                    "autoSign": True,
+                    "freeMode": False,
+                    "movableSignField": False,
+                    "signFieldPosition": {
+                        "positionPage": "1",
+                        "positionX": 300,
+                        "positionY": 200
+                    },
+                    "signFieldSize": 200,
+                    "signFieldStyle": 1
+                },
+                "signFieldType": 0
+            }],
+            "signerType": 1
+        }],
+        "signFlowConfig": {
+            "autoFinish": False,
+            "autoStart": True,
+            "chargeConfig": {
+                "chargeMode": 0
+            },
+            "noticeConfig": {
+                "noticeTypes": "1"
+            },
+            "notifyUrl": "http://xx.xx.86.172:8081/asyn/notify",
+            "redirectConfig": {
+                "redirectDelayTime": "3",
+                "redirectUrl": "http://www.xx.cn/"
+            },
+            "signConfig": {
+                "availableSignClientTypes": "1",
+                "showBatchDropSealButton": True
+            },
+            "authConfig": {
+                "psnAvailableAuthModes": ["PSN_BANK4_AUTHCODE"],
+                "willingnessAuthModes": ["FACE_TECENT_CLOUD_H5"],
+                "orgAvailableAuthModes": ["ORG_BANK_TRANSFER"]
+            },
+            "signFlowTitle": "企业员工劳动合同签署"
+        }
+    }  # 构建请求body体
+    api_path = "/v3/sign-flow/create-by-file"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    sign_flowId = resp.json()['data']['signFlowId']
+    return sign_flowId
+
+
+@esign_run_print_outer
+def signUrl(sign_flowId):
+    """
+    获取合同文件签署链接
+    :param 签署流程ID
+    :return:
+    """
+    body = {
+        "clientType": "ALL",
+        "needLogin": True,
+        "operator": {
+            "psnAccount": psnAccount
+        },
+        "urlType": 2
+    }  # 构建请求body体
+    api_path = "/v3/sign-flow/{}/sign-url".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送http请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def fileDownloadUrl(sign_flowId):
+    """
+    :param 签署流程ID
+    下载已签署文件及附属材料
+    :return:
+    """
+    api_path = "/v3/sign-flow/{}/file-download-url".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    fileUrl = "D:\\**文件\\*.pdf"  # 本地文件路径
+    orgName = "**企业"  # 签署方企业名称
+    psnAccount = "1******"  # 签署人手机号或者邮箱地址
+    orgId = findOrgIdentityInfo(orgName)  # 查询平台方实名帐号ID
+    file = fileHelp(fileUrl)  # 初始化文件辅助类
+    fileUploadUrl, fileId = getFileUploadUrl(file)  # 获取文件ID&文件上传路径
+    fileStreamUpload(file.getBinFile(), fileUploadUrl)  # 上传文件流
+    sign_flowId = signFlowCreateByFile(fileId, orgId, psnAccount)  # 发起一步签署:主动发送签署短信给签署用户
+    signUrl(sign_flowId)  # 获取签署链接
+    fileDownloadUrl(sign_flowId)  # 合同结束后,下载签署后合同

+ 214 - 0
alien_3rd_esign/run/scenedome/b2c_handsign_demo.py

@@ -0,0 +1,214 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+from esigntool.esign_file import fileHelp
+
+# 签署场景:平台方自动签署(签署方1)+个人用户手动签署流程(签署方2)
+# 平台方(签署方1):appId所属企业为平台方,默认平台方已完成实名认证,通过获取企业签署帐号来实现平台方自动签署。
+# 个人用户手动签署(签署方2):通过传入个人信息,来实现个人用户手动签署。
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def findOrgIdentityInfo(orgName):
+    """
+    查询机构认证信息
+    :return:
+    """
+    api_path = "/v3/organizations/identity-info?orgName={}".format(orgName)
+    method = esigntool.httpMethodEnum.GET
+    if orgName == "":
+        print("请设置实名企业名称")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    print(resp.text)
+    orgId = resp.json()['data']['orgId']
+    return orgId
+
+
+@esign_run_print_outer
+def getFileUploadUrl(file):
+    """
+    获取文件上传地址
+    :return:
+    """
+    contentType = "application/pdf"  # 声明请求变量
+    body = {
+        "contentMd5": file.contentMd5,
+        "contentType": contentType,
+        "convert2Pdf": False,
+        "fileName": file.fileName,
+        "fileSize": file.fileSize
+    }  # 构建请求参数body体
+    api_path = "/v3/files/file-upload-url"
+    method = esigntool.httpMethodEnum.POST
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    fileUploadUrl = resp.json()['data']['fileUploadUrl']  # 获取文件上传路径
+    fileId = resp.json()['data']['fileId']
+    print(resp.text)
+    return fileUploadUrl, fileId
+
+
+@esign_run_print_outer
+def fileStreamUpload(binfile, fileUploadUrl):
+    """
+    文件流上传服务器
+    :return:
+    """
+    contentMd5 = file.contentMd5  # 声明请求变量
+    contentType = "application/pdf"  # 声明请求变量
+    method = esigntool.httpMethodEnum.PUT  # 声明请求方法
+    json_headers = esigntool.buildFileUploadHeader(contentType, contentMd5)  # 构建请求头
+    resp = requests.request(method, fileUploadUrl, data=binfile, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def signFlowCreateByFile(fileId, orgId, psnAccount):
+    """
+    基于文件发起签署
+    :return:
+    """
+    body = {
+        "docs": [{
+            "fileId": fileId,
+            "fileName": "xx企业劳动合同.pdf"
+        }],
+        "attachments": [{
+            "fileId": fileId,
+            "fileName": "入职材料.pdf"
+        }],
+        "signers": [{
+            "psnSignerInfo": {
+                "psnAccount": psnAccount
+            },
+            "signConfig": {
+                "forcedReadingTime": "10",
+                "signOrder": 2
+            },
+            "signFields": [{
+                "customBizNum": "自定义编码",
+                "fileId": fileId,
+                "normalSignFieldConfig": {
+                    "freeMode": False,
+                    "movableSignField": False,
+                    "psnSealStyles": "0,1",
+                    "signFieldPosition": {
+                        "positionPage": "1",
+                        "positionX": 100,
+                        "positionY": 100
+                    },
+                    "signFieldStyle": 1
+                }
+            }],
+            "signerType": 0
+        }, {
+            "orgSignerInfo": {
+                "orgId": orgId
+            },
+            "signConfig": {
+                "signOrder": 1
+            },
+            "signFields": [{
+                "customBizNum": "自定义编码",
+                "fileId": fileId,
+                "normalSignFieldConfig": {
+                    "autoSign": True,
+                    "signFieldPosition": {
+                        "positionPage": "1",
+                        "positionX": 100,
+                        "positionY": 200
+                    },
+                    "signFieldStyle": 1
+                }
+            }],
+            "signerType": 1
+        }],
+        "signFlowConfig": {
+            "autoFinish": False,
+            "autoStart": True,
+            "noticeConfig": {
+                "noticeTypes": "1"
+            },
+            "notifyUrl": "http://xx.xx.86.172:8081/asyn/notify",
+            "redirectConfig": {
+                "redirectDelayTime": "3",
+                "redirectUrl": "http://www.esign.cn"
+            },
+            "signConfig": {
+                "availableSignClientTypes": "1",
+                "showBatchDropSealButton": True
+            },
+            "signFlowTitle": "企业员工劳动合同签署"
+        }
+    }  # 构建请求body体
+    api_path = "/v3/sign-flow/create-by-file"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    sign_flowId = resp.json()['data']['signFlowId']
+    return sign_flowId
+
+
+@esign_run_print_outer
+def signUrl(sign_flowId, psnAccount):
+    """
+    获取合同文件签署链接
+    :return:
+    """
+    body = {
+        "clientType": "ALL",
+        "needLogin": True,
+        "operator": {
+            "psnAccount": psnAccount
+        },
+        "urlType": 2
+    }  # 构建请求body体
+    api_path = "/v3/sign-flow/{}/sign-url".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送http请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def fileDownloadUrl(sign_flowId):
+    """
+    下载已签署文件及附属材料
+    :return:
+    """
+    api_path = "/v3/sign-flow/{}/file-download-url".format(sign_flowId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+if __name__ == '__main__':
+    if config.appId == "" or config.scert == "":
+        print("请设置应用Appid和应用Secret")
+        exit()
+    fileUrl = "D:\\**文件\\1.pdf"  # 本地文件路径
+    orgName = "***企业"  # 平台自身企业名称,即appid所属的企业名称
+    psnAccount = "1*****"  # 个人用户手机号
+    orgId = findOrgIdentityInfo(orgName)  # 查询平台自身实名帐号ID
+    file = fileHelp(fileUrl)  # 初始化文件辅助类
+    fileUploadUrl, fileId = getFileUploadUrl(file)  # 获取文件ID&文件上传路径
+    fileStreamUpload(file.getBinFile(), fileUploadUrl)  # 上传文件流
+    sign_flowId = signFlowCreateByFile(fileId, orgId, psnAccount)  # 发起一步签署:主动发送签署短信给签署用户
+    signUrl(sign_flowId, psnAccount)  # 手动获取当前签署任务中,个人的签署链接
+    fileDownloadUrl(sign_flowId)  # 合同结束后(个人用户签署完成后),下载签署后合同

+ 177 - 0
alien_3rd_esign/run/scenedome/dynamic_table_demo.py

@@ -0,0 +1,177 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+from esigntool.esign_file import fileHelp
+import time
+
+# 模版场景:支持动态表格,上传的文件必须是.doc 或 .docx 格式
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def getFileUploadUrl(file):
+    """
+    获取文件上传地址
+    :return:
+    """
+    contentType = "application/octet-stream"  # 声明请求变量
+    body = {
+        "contentMd5": file.contentMd5,
+        "contentType": contentType,
+        "convert2Pdf": False,
+        "convertToHTML": True,
+        "fileName": file.fileName,
+        "fileSize": file.fileSize
+    }  # 构建请求参数body体
+    api_path = "/v3/files/file-upload-url"
+    method = esigntool.httpMethodEnum.POST
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    print(resp.text)
+    fileUploadUrl = resp.json()['data']['fileUploadUrl']  # 获取文件上传路径
+    fileId = resp.json()['data']['fileId']
+    print(resp.text)
+    return fileUploadUrl, fileId
+
+
+@esign_run_print_outer
+def fileStreamUpload(binfile, fileUploadUrl):
+    """
+    文件流上传服务器
+    :return:
+    """
+    contentMd5 = file.contentMd5  # 声明请求变量
+    contentType = "application/octet-stream"  # 声明请求变量
+    method = esigntool.httpMethodEnum.PUT  # 声明请求方法
+    json_headers = esigntool.buildFileUploadHeader(contentType, contentMd5)  # 构建请求头
+    resp = requests.request(method, fileUploadUrl, data=binfile, headers=json_headers)  # 发送请求
+    print(resp.text)
+    return resp
+
+
+# 查询文件上传状态
+@esign_run_print_outer
+def getUploadFileState(fileId):
+    """
+    :param fileId:文件ID
+    :return:
+    """
+    api_path = "/v3/files/{}".format(fileId)
+    method = esigntool.httpMethodEnum.GET
+    if fileId == "":
+        print("请设置文件ID")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    status = resp.json()['data']['fileStatus']
+    print(resp.text)
+    return status
+
+
+@esign_run_print_outer
+def templatePageCreate(fileId):
+    """
+    获取制作合同模板页面
+    :return:
+    """
+    body = {
+        "docTemplateName": "某公司的劳动合同模板",
+        "docTemplateType": 1,
+        "fileId": fileId,
+        "redirectUrl": "https://www.esign.cn/"
+    }  # 构建请求body体
+    if fileId == "":
+        print("请设置文件ID")
+        exit()
+    api_path = "/v3/doc-templates/doc-template-create-url"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送请求
+    docTemplateCreateUrl = resp.json()['data']['docTemplateCreateUrl']
+    docTemplateId = resp.json()['data']['docTemplateId']
+    print(resp.text)
+    return docTemplateCreateUrl, docTemplateId
+
+
+@esign_run_print_outer
+def templateDetail(docTemplateId):
+    """
+    查询合同模板中控件详情
+    :return:
+    """
+    body = {
+        "redirectUrl": "https://www.esign.com/"
+    }
+    if docTemplateId == "":
+        print("请设置模版ID")
+        exit()
+    api_path = "/v3/doc-templates/{}".format(docTemplateId)  # 拼接请求路径
+    method = esigntool.httpMethodEnum.GET  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送网络请求
+    print(resp.text)
+    return resp
+
+
+@esign_run_print_outer
+def templateCreateDoc(docTemplateId):
+    """
+    填写模板生成文件
+    :return:
+    """
+    body = {
+        "docTemplateId": docTemplateId,
+        "fileName": "测试文件",
+        "components": [
+            {
+                "componentKey": "table",
+                "componentValue": '[{"row":{"column1":"1","column2":"2","column3":"3","column4":"4","column5":"5"}},'
+                                  '{"row":{"column1":"11","column2":"22","column3":"33","column4":"44",'
+                                  '"column5":"55"}},{"row":{"column1":"111","column2":"222","column3":"333",'
+                                  '"column4":"444","column5":"555"}},{"insertRow":"true","row":{"column1":"1111",'
+                                  '"column2":"2222","column3":"3333","column4":"4444","column5":"5555"}}] '
+            }
+        ]
+    }
+    if docTemplateId == "":
+        print("请设置模版ID")
+        exit()
+    api_path = "/v3/files/create-by-doc-template"  # 拼接请求路径
+    method = esigntool.httpMethodEnum.POST  # 声明请求方法
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert,
+                                                 method, api_path, body)  # 签名并构造签名鉴权json请求头
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)  # 发送网络请求
+    print(resp.text)
+    fileId = resp.json()['data']['fileId']
+    return fileId
+
+
+if __name__ == '__main__':
+    fileUrl = "D:\\**文件\\1.docx"  # 本地文件路径,默认场景为动态模板,此处文件格式必须为.doc 或 .docx 格式
+    if config.appId == "" or config.scert == "" or fileUrl == "":
+        print("请设置应用Appid和应用Secret,以及文件地址")
+        exit()
+    file = fileHelp(fileUrl)  # 初始化文件辅助类
+    fileUploadUrl, fileId = getFileUploadUrl(file)  # 获取文件ID&文件上传路径
+    fileStreamUpload(file.getBinFile(), fileUploadUrl)  # 上传文件流
+    status = getUploadFileState(fileId)  # 查询上传的文件状态
+
+    # 判断上传的文件是否转化完成,完成后才可以继续执行后续操作,2代表文件已转换为HTML
+    while status != 2:
+        time.sleep(1)
+        status = getUploadFileState(fileId)
+    docTemplateCreateUrl, docTemplateId = templatePageCreate(fileId)  # 获取模版ID&在线设置模版的链接
+
+    # 获取到的docTemplateCreateUrl打开并设置动态表格控件,设置之后提交再运行后续接口,以设置3*5的表格为例,自定义编码设置为table
+
+    # newFileId = templateCreateDoc(docTemplateId) #填充动态表格内容
+    # time.sleep(3)   #填充需要时间
+    # getUploadFileState(newFileId)
+    # 最终打印的效果中,表格变成了4*5,说明可以通过参数来自动添加行数

+ 105 - 0
alien_3rd_esign/run/scenedome/order_demo.py

@@ -0,0 +1,105 @@
+# -*- coding: UTF-8 -*-
+import esigntool
+import requests
+from esigntool import esign_run_print_outer
+
+# EP订单场景:需要联系交付顾问协助配置商品信息,沙箱环境获取到商品购买链接后,可以购买测试套餐进行业务测试
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def getBuyOrgOrderUrl(orgId,transactorPsnId):
+    """
+    获取购买e签宝套餐链接
+    :return:请求响应
+    """
+    api_path = "/v3/orders/org-place-order-url"
+    method = esigntool.httpMethodEnum.POST
+    body = {
+        "orgId": orgId,  # 机构账号ID(购买方orgId)
+        "transactorPsnId": transactorPsnId,  # 经办人个人账号ID(购买操作个人psnId)
+        "redirectUrl": "https://www.baidu.com",
+        "notifyUrl": "https://www.esign.cn/1",
+        "customBizNum": "1111111"  # 自定义编号
+    }
+    if orgId == "" or transactorPsnId == "":
+        print("请设置企业账号ID和经办人账号ID")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, api_path, body=body)
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+@esign_run_print_outer
+def findOrderQuantity(orgId):
+    """
+    查询e签宝套餐余量
+    :return:请求响应
+    """
+    api_path = "/v3/orders/remaining-quantity?orgId={}&distributor=true".format(orgId)
+    method = esigntool.httpMethodEnum.GET
+    if orgId == "":
+        print("请设置实名企业账号ID")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+@esign_run_print_outer
+def findOrderList(orgId):
+    """
+    查询套餐订单列表
+    :return:请求响应
+    """
+    api_path = "/v3/orders/order-list?orgId={}&distributor=true".format(orgId)
+    method = esigntool.httpMethodEnum.GET
+    if orgId == "":
+        print("请设置实名企业账号ID")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, esigntool.apiPathSort(api_path))
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=None, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+@esign_run_print_outer
+def getOrderListUrl(orgId, transactorPsnId):
+    """
+    查询套餐订单列表(页面版)
+    :return:请求响应
+    """
+    api_path = "/v3/orders/org-order-manage-url"
+    method = esigntool.httpMethodEnum.POST
+    body = {
+        "orgId": orgId,  # 机构账号ID(购买方orgId)
+        "transactorPsnId": transactorPsnId,
+        "distributor": 'true'
+    }
+    if orgId == "" or transactorPsnId == "":
+        print("请设置实名企业账号ID和经办人账号ID")
+        exit()
+    # 签名并构造签名鉴权json请求头
+    json_headers = esigntool.buildSignJsonHeader(config.appId, config.scert, method, api_path, body=body)
+    # 发起请求
+    resp = requests.request(method, config.host + api_path, json=body, headers=json_headers)
+    print(resp.text)
+    return resp.text
+
+
+if __name__ == '__main__':
+    orgId = "32367*********f9f18d5"  # 购买方orgId,需要先通过实名接口,获取购买方企业orgId
+    transactorPsnId = "2cc69e5*********ce1d3c"  # 购买操作个人psnId,需要先通过实名接口,获取购买方企业psnId
+    getBuyOrgOrderUrl(orgId, transactorPsnId)  # 获取购买e签宝套餐链接
+    findOrderQuantity(orgId)  # 查询e签宝套餐余量
+    findOrderList(orgId)  # 查询套餐订单列表
+    getOrderListUrl(orgId, transactorPsnId)  # 查询套餐订单列表(页面版)

+ 48 - 0
alien_3rd_esign/run/scenedome/signature_check_demo.py

@@ -0,0 +1,48 @@
+# -*- coding: UTF-8 -*-
+import hmac
+from _sha256 import sha256
+import esigntool
+from esigntool import esign_run_print_outer
+
+# 回调通知验证签名方法:通过对返回参数计算签名,并和回调中的签名对比,如一致则验证成功
+
+config = esigntool.config()  # 初始化配置类
+
+
+@esign_run_print_outer
+def signatureCheck():
+    # notifyUrl = "http://saledemo.tsign.cn:9090/asyn/notify?belong=tianyin"   例:异步通知请求地址
+    scert = config.scert  # 项目密钥
+    signture = "0e437d238*********8b3d72d6a7393af9f7e947"  # 异步通知获取到的签名值
+    time_stamp = "1661****383"  # 回调header的X-Tsign-Open-TIMESTAMP
+    query_param = ""  # 客户设置的回调地址可能包含query数据,例如callback?accountId=aaa&orderNo=001。(e签宝平台不会追加任何参数)
+    receive_body = "{\"action\":\"SIGN_MISSON_COMPLETE\",\"timestamp\":1661941631789," \
+                   "\"signFlowId\":\"227b71ae3*********e612a12a1\",\"customBizNum\":\"202200001111\"," \
+                   "\"signOrder\":1,\"operateTime\":166*****31000,\"signResult\":2,\"resultDescription\":\"签署完成\"," \
+                   "\"organization\":{\"orgId\":\"f0fee875*****00885a66d83\",\"orgName\":\"**测试企业\"}}"
+    # 即通知实际内容,按照整体的字节流来处理
+    sign_body = time_stamp + query_param + receive_body  # 最终参与验签的请求参数
+    print(sign_body)
+    req_signature = doSignature(sign_body, config.scert)
+    print(req_signature)
+    if req_signature == signture:
+        return print("验签成功")
+    else:
+        return print("验签失败")
+
+
+def doSignature(message, secret):
+    """
+    根据待签字符串计算签名值
+    :param message: 待签名字符串
+    :param secret:密钥
+    :return:
+    """
+    key = secret.encode('utf-8')  # sha256加密的key
+    message = message.encode('utf-8')  # 待sha256加密的内容
+    sign = hmac.new(key, message, digestmod=sha256).hexdigest()
+    return sign
+
+
+if __name__ == '__main__':
+    signatureCheck()  # 执行验签

+ 0 - 0
alien_3rd_esign/schemas.py


+ 1 - 0
alien_3rd_esign/services.py

@@ -0,0 +1 @@
+from alien_database.session import get_db

+ 4 - 2
main.py

@@ -3,11 +3,13 @@ from alien_store.router import router as store_router
 from alien_store_platform.router import router as platform_router
 from alien_second.router import router as second_router
 from alien_lawyer.router import router as lawyer_router
+from alien_3rd_esign.router import router as alien_3rd_esign
 from alien_gateway.config import settings
 
+
 app = FastAPI(
     title=settings.PROJECT_NAME,
-    description="Python 3.12 重构版本 - 异星云 (Alien Cloud)",
+    description="Python 3.12 重构版本 - Alien Cloud",
     version="1.0.0",
 )
 
@@ -16,7 +18,7 @@ app.include_router(store_router, prefix="/api/store", tags=["Store"])
 app.include_router(platform_router, prefix="/api/platform", tags=["Platform"])
 app.include_router(second_router, prefix="/api/second", tags=["Second"])
 app.include_router(lawyer_router, prefix="/api/lawyer", tags=["Lawyer"])
-
+app.include_router(alien_3rd_esign, prefix="/api/alien_3rd_esign", tags=["alien_3rd_esign"])
 @app.get("/", tags=["Health Check"])
 async def root():
     return {

+ 1 - 0
pyproject.toml

@@ -20,6 +20,7 @@ python-multipart = "^0.0.6"
 httpx = "^0.26.0"
 pymysql = "^1.1.0"
 redis = "^5.0.1"
+requests = "^2.32.5"
 
 [build-system]
 requires = ["poetry-core>=1.0.0"]