ESignContractServer.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. <?php
  2. namespace App\Servers\ESign;
  3. use App\Models\Firm;
  4. use App\Models\FirmSeal;
  5. use App\Models\Member;
  6. use App\Models\MemberSeal;
  7. use App\Models\SignContract;
  8. use App\Models\SignRecord;
  9. use App\Servers\ESign\comm\EsignHttpHelper;
  10. use App\Servers\ESign\comm\EsignLogHelper;
  11. use App\Servers\ESign\comm\EsignUtilHelper;
  12. use App\Servers\ESign\emun\HttpEmun;
  13. use App\Servers\System\CommonServer;
  14. use App\Servers\System\SmsServer;
  15. class ESignContractServer
  16. {
  17. static private $server = null;
  18. public $config = [
  19. 'eSignAppId'=>'', //app id
  20. 'eSignAppSecret'=>'', // app key
  21. 'eSignHost'=>'https://smlopenapi.esign.cn', //模拟环境
  22. // 'url'=>'https://openapi.esign.cn' //正式环境
  23. ];
  24. function __construct()
  25. {
  26. $this->config['eSignAppId']=env('E_SIGN_APP_ID');
  27. $this->config['eSignAppSecret']=env('E_SIGN_APP_SECRET');
  28. $this->config['eSignPublicKey']=env('E_SIGN_PUBLIC_KEY');
  29. $this->config['eSignHost']=env('E_SIGN_HOST');
  30. }
  31. /**
  32. * 创建对象
  33. * @return ESignContractServer
  34. */
  35. static function creatServer()
  36. {
  37. if (empty(self::$server)) self::$server = new ESignContractServer();
  38. return self::$server;
  39. }
  40. function contentMd5($filePath){
  41. if(!file_exists($filePath)){
  42. EsignLogHelper::printMsg($filePath."文件不存在");exit;
  43. }
  44. return EsignUtilHelper::getContentBase64Md5($filePath);
  45. }
  46. /**
  47. * 上传合同文件至E签宝
  48. * @param $file_url
  49. * @return mixed
  50. */
  51. function fileUploadUrl($file_url){
  52. $config=$this->config;
  53. $file_name=CommonServer::creatServer()->getFileName($file_url);
  54. $filePath=public_path($file_url);
  55. $contentMd5=$this->contentMd5($filePath);
  56. $apiaddr="/v3/files/file-upload-url";
  57. $requestType = HttpEmun::POST;
  58. $data=[
  59. "contentMd5" =>$contentMd5,
  60. "contentType" =>"application/pdf",
  61. "convertToPDF" =>true,
  62. "fileName" =>$file_name,
  63. "fileSize" =>filesize($filePath)
  64. ];
  65. $paramStr = json_encode($data);
  66. //生成签名验签+json体的header
  67. $signAndBuildSignAndJsonHeader = EsignHttpHelper::signAndBuildSignAndJsonHeader($config['eSignAppId'], $config['eSignAppSecret'], $paramStr, $requestType, $apiaddr);
  68. //获取文件上传地址
  69. // EsignLogHelper::printMsg($signAndBuildSignAndJsonHeader);
  70. $response = EsignHttpHelper::doCommHttp($config['eSignHost'], $apiaddr, $requestType, $signAndBuildSignAndJsonHeader, $paramStr);
  71. // EsignLogHelper::printMsg($response->getStatus());
  72. // EsignLogHelper::printMsg($response->getBody());
  73. $fileUploadUrl=json_decode($response->getBody())->data->fileUploadUrl;
  74. $fileId=json_decode($response->getBody())->data->fileId;
  75. //文件流put上传
  76. $response=EsignHttpHelper::upLoadFileHttp($fileUploadUrl,$filePath,"application/pdf");
  77. // EsignLogHelper::printMsg($response->getStatus());
  78. // EsignLogHelper::printMsg($response->getBody());
  79. return $fileId;
  80. }
  81. /**
  82. * 获取文件状态
  83. * @param $fileId
  84. * @return int|mixed
  85. */
  86. function getFileStatus($fileId)
  87. {
  88. $config=$this->config;
  89. $apiaddr="/v3/files/{$fileId}";
  90. $requestType = HttpEmun::GET;
  91. //生成签名验签+json体的header
  92. $signAndBuildSignAndJsonHeader = EsignHttpHelper::signAndBuildSignAndJsonHeader($config['eSignAppId'], $config['eSignAppSecret'], "", $requestType, $apiaddr);
  93. //发起接口请求
  94. $response = EsignHttpHelper::doCommHttp($config['eSignHost'], $apiaddr, $requestType, $signAndBuildSignAndJsonHeader, "");
  95. $ret=json_decode($response->getBody(),true);
  96. if(isset($ret['code']) && $ret['code']==0){
  97. return $ret['data']['fileStatus'];
  98. }else{
  99. return 0;
  100. }
  101. }
  102. /**
  103. * 获取签署文件对应关键字坐标
  104. * @param $fileId
  105. * @param $keywords
  106. * @return array
  107. */
  108. function getKeywordPositions($fileId,$keywords)
  109. {
  110. $config=$this->config;
  111. $apiaddr="/v3/files/{$fileId}/keyword-positions";
  112. $requestType = HttpEmun::POST;
  113. $data=[
  114. "keywords" =>$keywords
  115. ];
  116. $paramStr = json_encode($data);
  117. //生成签名验签+json体的header
  118. $signAndBuildSignAndJsonHeader = EsignHttpHelper::signAndBuildSignAndJsonHeader($config['eSignAppId'], $config['eSignAppSecret'], $paramStr, $requestType, $apiaddr);
  119. //获取文件上传地址
  120. // EsignLogHelper::printMsg($signAndBuildSignAndJsonHeader);
  121. $response = EsignHttpHelper::doCommHttp($config['eSignHost'], $apiaddr, $requestType, $signAndBuildSignAndJsonHeader, $paramStr);
  122. // EsignLogHelper::printMsg($response->getStatus());
  123. // EsignLogHelper::printMsg($response->getBody());
  124. $ret=json_decode($response->getBody(),true);
  125. if(isset($ret['code']) && $ret['code']==0){
  126. return ['code'=>'1','msg'=>'ok','data'=>$ret['data']['keywordPositions']];
  127. }else{
  128. return ['code'=>'0','msg'=>$ret['message']];
  129. }
  130. }
  131. function setContractContract($sign_id,$fileId,$signJson,$file_name)
  132. {
  133. // $signJson = '[{"keyword":"fisher", "auth_type":"PSN", "id_no","AAAAA"}, {"keyword":"lily", "auth_type":"ORG", "id_no","AAAAA"}, {"keyword":"bob", "auth_type":"PSN", "id_no","AAAAA"}]';
  134. $signData = json_decode($signJson,true);
  135. $keywords=[];
  136. foreach ($signData as $k=>$v){
  137. $keywords[]=$v['keyword'];
  138. }
  139. //获取对应关键字定位
  140. //dd(111);
  141. $positionRet=ESignContractServer::creatServer()->getKeywordPositions($fileId,$keywords);
  142. if(empty($positionRet['code'])){
  143. return ['code'=>0,'msg'=>'签署区域定位失败'];
  144. }else{
  145. $positionData=$positionRet['data'];
  146. }
  147. $signers=[];
  148. $signUrl = [];
  149. foreach ($signData as $value){
  150. //对应关键字做xy坐标匹配
  151. foreach ($positionData as $item){
  152. if(count($item['positions'])<=0){
  153. continue;
  154. }
  155. if($item['keyword']==$value['keyword']){
  156. $position=end($item['positions']);
  157. $coordinate=end($position['coordinates']);
  158. $value['positionPage'] = $position['pageNum'];
  159. $value['positionX'] = $coordinate['positionX'];
  160. $value['positionY'] = $coordinate['positionY'];
  161. }
  162. }
  163. //签署方是机构还是个人
  164. if ($value['auth_type'] == 'PSN'){
  165. $member = Member::where('psnId',$value['id_no'])->first();
  166. //记录需要手动签署的
  167. $signUrl[]=[
  168. 'promissoryName'=>$member['name'],
  169. 'psnAccount'=>$member['phone'],
  170. 'orgName'=>'',
  171. 'id_no'=>$member['psnId'],
  172. ];
  173. //获取签章
  174. $m_id = $member->{'id'};
  175. // $seal=MemberSeal::where('m_id',$m_id)->where('seal_status',1)->select(['id','seal_id'])->first();
  176. // //印章ID
  177. // $assignedSealId=$seal->{'seal_id'};
  178. //组装信息
  179. $signersData = [
  180. "signerType" => 0,
  181. "psnSignerInfo" => [
  182. "psnAccount" => $member->{'phone'},
  183. 'psnInfo'=>[
  184. 'psnName'=>$member->{'name'},
  185. 'psnIDCardNum'=>$member->{'id_number'},
  186. 'psnIDCardType'=>'CRED_PSN_CH_IDCARD',
  187. ],
  188. ],
  189. "signFields" =>[
  190. [
  191. "fileId" =>$fileId,
  192. "normalSignFieldConfig" => [
  193. "autoSign" =>false,
  194. "signFieldStyle" =>1,
  195. // "assignedSealId"=>$assignedSealId, // 在signFields中设置 assignedSealId为授权企业印章id
  196. "signFieldPosition" => [
  197. "positionPage" =>$value['positionPage'].'',
  198. "positionX" =>(int)$value['positionX'],
  199. "positionY" =>(int)$value['positionY'],
  200. ]
  201. ],
  202. "signDateConfig" => [
  203. "dateFormat" => "yyyy-MM-dd"
  204. ],
  205. ]
  206. ],
  207. ];
  208. }else{
  209. $firm = Firm::where('orgId',$value['id_no'])->first();
  210. //记录需要手动签署的
  211. $signUrl[]=[
  212. 'promissoryName'=>$firm['legal_name'],
  213. 'psnAccount'=>$firm['phone'],
  214. 'orgName'=>$firm['firm_name'],
  215. 'id_no'=>$firm['orgId'],
  216. ];
  217. //获取用户签章
  218. $firm_id = $firm->{'id'};
  219. // $seal=FirmSeal::where('firm_id',$firm_id)->where('seal_type','CONTRACT')->select(['id','seal_id'])->first();
  220. // //印章ID
  221. // $assignedSealId=$seal->{'seal_id'};
  222. $signersData = [
  223. "signerType" => 1,
  224. "orgSignerInfo" => [
  225. "orgName" => $firm->{'firm_name'},
  226. 'orgInfo'=>[
  227. 'orgIDCardNum'=>$firm->{'license_number'},
  228. 'orgIDCardType'=>'CRED_ORG_USCC',
  229. ],
  230. "transactorInfo"=>[
  231. "psnAccount"=>$firm['phone'],
  232. 'psnInfo'=>[
  233. 'psnName'=>$firm['legal_name'],
  234. 'psnIDCardNum'=>$firm['legal_id_number'],
  235. 'psnIDCardType'=>'CRED_PSN_CH_IDCARD'
  236. ]
  237. ]
  238. ],
  239. "signFields" =>[
  240. [
  241. "fileId" =>$fileId,
  242. "normalSignFieldConfig" => [
  243. "autoSign" =>false,
  244. "signFieldStyle" =>1,
  245. // "assignedSealId"=>$assignedSealId, // 在signFields中设置 assignedSealId为授权企业印章id
  246. "signFieldPosition" => [
  247. "positionPage" =>$value['positionPage'].'',
  248. "positionX" =>(int)$value['positionX'],
  249. "positionY" =>(int)$value['positionY'],
  250. ]
  251. ],
  252. "signDateConfig" => [
  253. "dateFormat" => "yyyy-MM-dd"
  254. ],
  255. ]
  256. ],
  257. ];
  258. }
  259. $signers[]=$signersData;
  260. //对应签署方做记录
  261. SignRecord::create([
  262. 'sign_id' => $sign_id,
  263. 'authType' => $value['auth_type'],
  264. 'id_no' => $value['id_no'],
  265. 'keyword' => $value['keyword'],
  266. 'page_num' => $value['positionPage'],
  267. 'position_x' => $value['positionX'],
  268. 'position_y' => $value['positionY'],
  269. ]);
  270. }
  271. if(empty($signers)){
  272. return ['code'=>0,'msg'=>'暂无可执行电签印章'];
  273. }
  274. $flowId= $this->createByFile($fileId,$file_name.'.pdf',$file_name,$signers);
  275. if($flowId){
  276. SignContract::where('id',$sign_id)->update(['flow_id'=>$flowId,'status'=>1]);
  277. foreach ($signUrl as $item){
  278. $urlRet= $this->getSignUrl($flowId,$item['psnAccount'],$item['orgName']);
  279. $parts = parse_url($urlRet);
  280. $path = trim($parts['path'],'/');
  281. $zh_url = route('member.sign').'?'.$path;
  282. if($urlRet){
  283. // $content='尊敬的'.$item['promissoryName'].',您有待签署合同《'.$file_name.'》,点击签署 '.$urlRet.'。拒收请回复R';
  284. $zh_url=str_replace('http://','https://',$zh_url);
  285. $zh_url=str_replace('esign.jhcs888.com','app.jhcs888.com',$zh_url);
  286. $content='尊敬的'.$item['promissoryName'].',您有待签署合同《'.$file_name.'》,点击签署 '.$zh_url.'。拒收请回复R';
  287. SmsServer::creatServer()->sendContent($item['psnAccount'],$content);
  288. SignContract::where('id',$sign_id)->update(['sign_url'=>$urlRet]);
  289. SignRecord::where('sign_id',$sign_id)->where('id_no',$item['id_no'])->update(['sign_url'=>$urlRet]);
  290. }
  291. }
  292. return ['code'=>1,'msg'=>'发起签署成功'];
  293. }else{
  294. return ['code'=>0,'msg'=>'发起签署失败'];
  295. }
  296. }
  297. /**
  298. * 合同签署
  299. * @param $fileId
  300. * @return false
  301. */
  302. function createByFile($fileId,$fileName,$signFlowTitle,$signers)
  303. {
  304. EsignLogHelper::printMsg("**********基于文件发起签署调用开始**********");
  305. $config=$this->config;
  306. $apiaddr="/v3/sign-flow/create-by-file";
  307. $requestType = HttpEmun::POST;
  308. $data = [
  309. "docs" =>[
  310. [
  311. "fileId" =>$fileId,
  312. "fileName" =>$fileName
  313. ]
  314. ],
  315. "signFlowConfig" => [
  316. "signFlowTitle" =>$signFlowTitle,
  317. "autoStart"=>true,
  318. "autoFinish"=>true,
  319. 'notifyUrl'=>route('adminApi.notify.esignNotify')
  320. ],
  321. "signers"=>$signers
  322. ];
  323. // dd($data);
  324. $paramStr = json_encode($data);
  325. $signAndBuildSignAndJsonHeader = EsignHttpHelper::signAndBuildSignAndJsonHeader($config['eSignAppId'], $config['eSignAppSecret'], $paramStr, $requestType, $apiaddr);
  326. // EsignLogHelper::printMsg($signAndBuildSignAndJsonHeader);
  327. $response = EsignHttpHelper::doCommHttp($config['eSignHost'], $apiaddr, $requestType, $signAndBuildSignAndJsonHeader, $paramStr);
  328. // EsignLogHelper::printMsg($response->getStatus());
  329. EsignLogHelper::printMsg($response->getBody());
  330. $signFlowId = false;
  331. if($response->getStatus() == 200){
  332. $result = json_decode($response->getBody());
  333. if($result->code==0){
  334. $signFlowId = $result->data->signFlowId;
  335. EsignLogHelper::printMsg("基于文件发起签署接口调用成功,flowId: ".$signFlowId);
  336. }else{
  337. EsignLogHelper::printMsg("基于文件发起签署接口调用失败,错误信息: ".$result->message);
  338. }
  339. }else{
  340. EsignLogHelper::printMsg("基于文件发起签署接口调用失败,HTTP错误码".$response->getStatus());
  341. }
  342. EsignLogHelper::printMsg("**********基于文件发起签署调用结束**********");
  343. return $signFlowId;
  344. }
  345. /**
  346. * 合同签署链接
  347. * @param $flowId
  348. * @param $psnAccount
  349. * @param $orgName
  350. * @return null
  351. */
  352. function getSignUrl($flowId,$psnAccount,$orgName){
  353. EsignLogHelper::printMsg("**********获取合同文件签署链接开始**********");
  354. $config=$this->config;
  355. $apiaddr="/v3/sign-flow/%s/sign-url";
  356. $apiaddr = sprintf($apiaddr,$flowId);
  357. $requestType = HttpEmun::POST;
  358. $data = [
  359. "signFlowId" =>$flowId,
  360. "clientType" =>"ALL",
  361. "needLogin" =>false,
  362. "operator" => [
  363. "psnAccount" =>$psnAccount
  364. ],
  365. 'organization'=>[
  366. 'orgName'=>$orgName
  367. ],
  368. "urlType" =>2
  369. ];
  370. $paramStr = json_encode($data);
  371. $signAndBuildSignAndJsonHeader = EsignHttpHelper::signAndBuildSignAndJsonHeader($config['eSignAppId'], $config['eSignAppSecret'], $paramStr, $requestType, $apiaddr);
  372. EsignLogHelper::printMsg($signAndBuildSignAndJsonHeader);
  373. $response = EsignHttpHelper::doCommHttp($config['eSignHost'], $apiaddr, $requestType, $signAndBuildSignAndJsonHeader, $paramStr);
  374. EsignLogHelper::printMsg($response->getStatus());
  375. EsignLogHelper::printMsg($response->getBody());
  376. $url= null;
  377. if($response->getStatus() == 200){
  378. $url = json_decode($response->getBody())->data->shortUrl;
  379. EsignLogHelper::printMsg("获取合同文件签署链接调用成功,url: ".$url);
  380. }else{
  381. EsignLogHelper::printMsg("获取合同文件签署链接接口调用失败,HTTP错误码".$response->getStatus());
  382. }
  383. EsignLogHelper::printMsg("**********获取合同文件签署链接调用结束**********");
  384. return $url;
  385. }
  386. function downloadFileUrl($flow_id)
  387. {
  388. $config=$this->config;
  389. $apiaddr="/v3/sign-flow/{$flow_id}/file-download-url";
  390. $requestType = HttpEmun::GET;
  391. //生成签名验签+json体的header
  392. $signAndBuildSignAndJsonHeader = EsignHttpHelper::signAndBuildSignAndJsonHeader($config['eSignAppId'], $config['eSignAppSecret'], "", $requestType, $apiaddr);
  393. //发起接口请求
  394. $response = EsignHttpHelper::doCommHttp($config['eSignHost'], $apiaddr, $requestType, $signAndBuildSignAndJsonHeader, "");
  395. $ret=json_decode($response->getBody(),true);
  396. if(isset($ret['code']) && $ret['code']==0){
  397. return $ret['data'];
  398. }else{
  399. return 0;
  400. }
  401. }
  402. }