测试环境域名:

https://identitytest.fangxinq...

key:GoexwR3Toe

secret:72e9ff5d6a49491690a4cd22eb9402b1

config配置

    // 放心签配置
    'FXQ_APPID'                 => 'GoexwR3Toe',
    'FXQ_SECRET'                => '72e9ff5d6a49491690a4cd22eb9402b1',
    'FXQ_IDENTITYURL'           => 'https://identitytest.fangxinqian.cn',
    'FXQ_RESTAPI'               => 'https://restapitest.fangxinqian.cn',
    'FXQ_CONTRACT'              => 'https://*****.pdf',

Fangxinqian.php

<?php
namespace app\common\service;

use think\Db;
use think\Config;
use think\Cache;

class Fangxinqian
{
    private static $instance = null ;
    public static function ins()
    {
        if( self::$instance == null ){
            self::$instance = new self();
        }
        return self::$instance;
    }

    //签章生成
    public function seal_personal($realname)
    {
        $token = $this->getToken();
        $data = [
            "name" => $realname, //真实姓名
            "rtype" => 1, //返回类型(0:Base64,1:oss地址链接,默认为0)
            'color' => 0, //颜色(0:红色,1:蓝色,2:黑色,默认为0)
            'font' => 1, //字体(0:仿宋,1:楷体,2:黑体,默认为0)
            'type' => 0, //形状(0:长方形,1:正方形,默认为0)
        ];
        $signString  = "";
        $data = $this->sortParam($data);
        $this->readParams($data,$signString);
        $nonce = date('YmdHis').rand(111,99999);
        $sign = md5(sha1(base64_encode($signString."||token=".$token."||nonce=".$nonce)));
        $result = $this->serverSubmit(config('FXQ_RESTAPI').'/seal/v1/personal', $data, 'post', $token, $nonce, $sign);
        // var_dump($result);
        if ( !empty($result) && $result['code'] == '10000' ) {
            return $result['data'];
        }
        return false;
    }

    //公安二要素
    public function shiming($realname, $idcardno)
    {
        $token = $this->getToken();
        $data = [
            "realName" => $realname, //真实姓名
            "idCardNo" => $idcardno, //身份证号码
        ];
        $signString  = "";
        $data = $this->sortParam($data);
        $this->readParams($data,$signString);
        $nonce = date('YmdHis').rand(111,99999);
        $sign = md5(sha1(base64_encode($signString."||token=".$token."||nonce=".$nonce)));
        $result = $this->serverSubmit(config('FXQ_IDENTITYURL').'/identity/v1/2', $data, 'post', $token, $nonce, $sign);
        // var_dump($result);
        if ( !empty($result) && $result['code'] == '10000' ) {
            return $result['data']['state'];
        }
        return 2;
    }

    //单文件签署
    public function port_sign($realname, $idcardno, $seal)
    {
        $token = $this->getToken();
        $data = [
            "contract" => config('FXQ_CONTRACT'), //url地址或者base64
            "type" => 1 , //合同返回类型(0:Base64,1:oss地址,默认为0)
            "signDate" => [ 'enable' => 1 ], //签章日期(跟随在印章底部)
            "signers"=> [
                [
                    "name" => $realname,
                    "idno" => $idcardno,
                    "seal" => $seal,//图片
                    // "height" => 300, //传了此参数才会加盖骑缝章,反之则不加盖骑缝章 (建议值在100 - 600之间)
                    "areas" =>[
                        [
                            "x"=>114,
                            "y"=>66,
                            "page"=>1
                        ]
                    ]
                ]
            ],
        ];
        $signString  = "";
        //排序
        $data = $this->sortParam($data);
        //签名加密
        $this->readParams($data,$signString);
        //流水号,每次请求保证唯一,五分钟之类不能重复
        $nonce = date('YmdHis').rand(111,99999);

        // echo "加密sign是--".$signString."||token=".$token."||nonce=".$nonce."\n";
        $sign = md5(sha1(base64_encode($signString."||token=".$token."||nonce=".$nonce)));
        // echo '发起签署----------------开始';
        // echo "\n";
        $result = $this->serverSubmit(config('FXQ_RESTAPI').'/contract/v1/port/sign', $data, 'post', $token, $nonce, $sign);
        // var_dump($result);
        // echo 'result:'.json_encode($result);
        // echo '发起签署结果:'.$result['msg'];
        // echo "\n";
        // echo '发起签署----------------结束';
        // echo "\n";
        return $result;
    }


    //获取鉴权token
    public function getToken()
    {
        $token = Cache::store('redis')->get('fangxinqian_token');

        if ( empty($token) ) {
            $arr = [ "key"=> config('FXQ_APPID'), "secret"=> config('FXQ_SECRET') ];
            $result = $this->serverSubmit(config('FXQ_IDENTITYURL').'/auth/v1/token', $arr);
            // var_dump($result);
            if ( !empty($result) && $result['code'] == '10000' ) {
                Cache::store('redis')->set('fangxinqian_token', $result['data'], 3600);
                return $result['data'];
            }
        }
        return $token;
    }

    public function serverSubmit($url, $parameter, $re = 'post', $token = "", $nonce = "", $sign = "")
    {
        // var_dump($url);
        $parameter = json_encode($parameter,true);
        if($token){
            $headers[]  =  "Content-Type:application/json";
            $headers[]  =  "token: ". $token;
            $headers[]  =  "fxq-nonce: ". $nonce;
            $headers[]  =  "fxq-sign: ". $sign;
        }else{
            $headers = array('Content-Type: application/json;charset=utf-8;');
        }
        //初始化
        $curl = curl_init();
        //设置抓取的url
        curl_setopt($curl, CURLOPT_URL, $url);
        //设置头文件的信息作为数据流输出
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        //设置请求头
        curl_setopt($curl, CURLOPT_HEADER, 1);
        //设置获取的信息以文件流的形式返回,而不是直接输出。
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);

        //设置提交方式
        if ($re == 'post')
        {
            //post提交方式
            curl_setopt($curl, CURLOPT_POST, 1);
            //设置post数据
            curl_setopt($curl, CURLOPT_POSTFIELDS, $parameter);
        }
        elseif ($re == 'get')
        {

        }
        //执行命令
        $data = curl_exec($curl);

        //分离头信息与数据主体
        $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
        $datas['header'] = substr($data, 0, $headerSize);
        $datas['body'] = substr($data, $headerSize);

        //关闭URL请求
        curl_close($curl);
        unset($data);
        //显示获得的数据

        return json_decode($datas['body'], true);
    }

    public static function readParams($data,&$string)
    {
        $size = count($data);
        foreach ($data as $key=>$val){
            $size --;
            self::readChildParams($key,json_encode($val),$string,$size);
        }
        return $string;
    }

   public static function readChildParams($key,$value,&$string,$index)
   {
        if(is_string($key)){
            $string.= $key."=";
        }
        if(self::startsWith($value,"{")){
            $string.="{";
            self::readParams(json_decode($value,true),$string);
            $string.="}";
        }elseif (self::startsWith($value,"[")){
            $string.="[";
            $list  = json_decode($value,true);
            $size = count($list);
            foreach ($list as $list1){
                $size --;
                self::readChildParams(null,json_encode($list1),$string,$size);
            }
            $string.="]";
        }else{
            //去除双引号
            $string.= str_replace('"', '',json_decode($value,true));
        }
        if($index != 0){
            $string.= "||";
        }
        return $string;
    }

    public static function startsWith($haystack, $needle, $case=true)
    {
        if ($case){
            return strncasecmp($haystack, $needle, strlen($needle)) == 0 ;
        }else{
            return strncmp($haystack, $needle, strlen($needle)) == 0;
        }
    }

    public static function sortParam(&$param)
    {
        if(is_array($param)){
            ksort($param);
            foreach ($param as &$value){
                self::sortParam($value);
            }
        }
        return $param;
    }


} 
03-05 13:56