JAVA、PHP、前端、APP、网站开发 - 开发技术学习

开发技术学习 » 编程开发 » 海关179,对接保税仓,php对接海关179公告

海关179,对接保税仓,php对接海关179公告

此文被围观775 日期: 2022-03-04 分类 : 编程开发  标签:  ··

完整代码请点击:

https://download.csdn.net/download/adophper/83339172

直接上代码:

<?php


namespace appapicontroller;


use appcommoncontrollerApi;
use appcommonserviceClientService;
use appcommonserviceWechatService;
use thinkCache;
use thinkDb;

/**
 * 保税海关179接口
 * Class Customs
 * @package appapicontroller
 */
class Custom extends Api
{
    // 无需登录的接口,*表示全部
    protected $noNeedLogin = ['*'];
    // 无需鉴权的接口,*表示全部
    protected $noNeedRight = ['*'];
    //redis缓存key
    private $key = 'openReq';

    /**
     * 查询订单信息
     * @param $orderno
     * @return array
     * @throws thinkException
     * @throws thinkdbexceptionDataNotFoundException
     * @throws thinkdbexceptionModelNotFoundException
     * @throws thinkexceptionDbException
     */
    private function searchOrderInfo($orderno) {
        if (empty($orderno)) return [];
        $order = Db::name('order_info')->where(['order_sn' => $orderno])->find();
        if (empty($order)) return [];
        $goods = Db::name('order_goods')->where('order_id='.$order['order_id'])->select();
        foreach($goods as $v){
            $goods_list[] = array(
                'gname'    => Sbc2Dbc($v['goods_name']),
                'itemLink' => '没有产品连接',
            );
        }
        //查询支付信息
        $pay_info = Db::name('pay_log')->where('order_id', $order['order_id'])->find();

        //微信订单附加信息提交接口
        $customdeclareorder = $pay_info['customdeclareorder'];
        if (empty($customdeclareorder)) {
            $wechatService = new WechatService();
            $customdeclareorder = $wechatService->customDeclareOrder($order['order_sn'], $pay_info['transid']);
        }
        $verify_department = $customdeclareorder['verify_department'];
        $verDept = 3;//其他-OTHERS(如余额支付,零钱通支付等)
        if ($verify_department == 'UNIONPAY') {
            $verDept = 1;//银联-
        } else if ($verify_department == 'NETSUNION') {
            $verDept = 2;//网联
        } else {
            //TODO
        }
        ////////实现以上方法//////////
        $resp = array(
            'initalRequest'    => $pay_info['request'],    //发送给支付企业的原始请求,微信xml,支付宝url
            'initalResponse'   => $pay_info['response'], //支付成功后回调的原始请求
            //交易流水号(支付报关时有返回,微信:verify_department_trade_id,支付宝:pay_transaction_id)
            'payTransactionId' => $pay_info['transid'],
            //参数:verDept,填:1,2,3;支付报关返回,支付宝参数直接用:ver_dept,
            //微信参数名:verify_department,需要转换:'UNIONPAY'=>1,'NETSUNION'=> 2,'OTHERS'=>3
            'verDept'          => $verDept,
            'totalAmount'      => $order['money_paid'],
            'tradingTime'      => date('YmdHis', $order['pay_time']),    //时间格式,date('YmdHis'),例子:20181212041803
            'goods'            => $goods_list,
            'ebpCode'          => 'xxxxxxxxxx',          //电商平台十位海关编码
            'payCode'          => '4403169D3W',          //支付企业十位海关编码,微信:4403169D3W,支付宝:31222699S7,其他的按实际填
            'currency'         => 142,                   //币种,只能填142
            'recpAccount'      => 'xxxxxxxxxxxxxxxxx', //公司对公银行卡号(最终收款的卡号)
            'recpName'         => 'xxxxxxxxxxxxxxxxxxx',          //电商平台海关备案名称
            'note'             => '没有产品连接',
        );

        return $resp;
    }

    /**
     * 保税查询订单详情
     * @throws thinkdbexceptionDataNotFoundException
     * @throws thinkdbexceptionModelNotFoundException
     * @throws thinkexceptionDbException
     */
    public function queryOrder(){
        $orderno = $this->request->request("orderNo", '');
        $resp = $this->searchOrderInfo($orderno);
        if (empty($resp)) exit('[]');
        echo json_encode($resp);
        exit;
    }

    /**
     * 海关请求plateDataOpen,接受openReq并保存
     */
    public function plateDataOpen(){
        $openReq = $this->request->post("openReq", '');
        $data = @json_decode($openReq,true);
        if ($data) {
            $redis = Cache::store('redis');
            $redis->handler()->sadd($this->key, $openReq);
            $insert = [];
            $insert['orderno'] = $data['orderNo'];
            $insert['sessionid'] = $data['sessionID'];
            $insert['status'] = 0;
            $insert['add_time'] = date('Y-m-d H:i:s');
            $insert['update_time'] = date('Y-m-d H:i:s');
            Db::table('custom_open_req')->insert($insert);
        }
        exit('{"code":"10000","message":"","serviceTime":'.time().'}') ;
    }

    /**
     * 查询未验签上报的数据
     * @throws thinkException
     * @throws thinkdbexceptionDataNotFoundException
     * @throws thinkdbexceptionModelNotFoundException
     * @throws thinkexceptionDbException
     */
    public function getOpenReq() {
        $redis = Cache::store('redis');
        if (!$redis->has($this->key)) {
            $data = Db::table("custom_open_req")->where('status', 0)->field('orderno,sessionid')->select();
            if (!empty($data)) {
                foreach ($data as &$val) {
                    $val['orderNo'] = $val['orderno'];
                    $val['sessionID'] = $val['sessionid'];
                }
            }
        } else {
            $data = $redis->handler()->smembers($this->key);
            if (!empty($data)) {
                foreach ($data as &$val) {
                    $val = @json_decode($val, true);
                }
            }
        }

        $result = null;
        //组装数据
        if ($data) {
            foreach ($data as $item) {
                $guid = guid();
                $time = time();
                $order = $this->searchOrderInfo($item['orderNo']);
                $info = [];
                $info['orderNo'] = $item['orderNo'];
                $info['goodsInfo'] = $order['goods'];
                $info['recpAccount'] = $order['recpAccount'];
                $info['recpCode'] = $order['recpCode'];
                $info['recpName'] = $order['recpName'];

                $array = [];
                $array['sessionID'] = $item['sessionID'];
                $order['guid'] = $guid;
                $array['payExchangeInfoHead'] = json_encode($order, JSON_UNESCAPED_UNICODE);
                $array['payExchangeInfoLists'] = json_encode($info,JSON_UNESCAPED_UNICODE);
                $array['serviceTime'] = $time;

                $url = [];
                foreach ($array as $sk => $sv) {
                    $url[] = '"'.$sk.'":"'.$sv.'"';
                }
                $result[$item['orderNo']]['url'] = implode('||', $url);
                $result[$item['orderNo']]['json'] = json_encode($array, JSON_UNESCAPED_UNICODE);
            }
            exit(json_encode($result));
        }
        exit('[]');
    }

    /**
     * 更新数据状态
     * @param string $data
     * @throws thinkException
     * @throws thinkexceptionPDOException
     */
    public function callback($data = '') {
        if (empty($data)) {
            $data = $this->request->request("data");
        }
        if ($data) {
            $redis = Cache::store('redis');
            $openReq = $redis->handler()->smembers($this->key);
            $data = @json_decode(htmlspecialchars_decode($data), true);

            //因为是定时任务,所以可能有多个单,这时就会出现多个反馈
            $customTable = Db::table('custom_open_req');
            foreach ($data as $key => $val) {
                foreach ($openReq as $sv) {
                    $info = @json_decode($sv, true);
                    if ($info['orderNo'] == $key) {
                        $redis->handler()->srem($this->key, $sv);
                        break;
                    }
                    //更新数据状态
                    $customTable->where('orderno', $key)->update(['msg' => $val['msg'], 'status' => $val['status'], 'update_time' => date('Y-m-d H:i:s')]);
                }
            }
        }
        exit();
    }

    /**
     * 定时任务,上报数据
     * @throws thinkException
     * @throws thinkexceptionPDOException
     */
    public function crontab() {
        $uri = 'ws://127.0.0.1:61232';
        $client = new ClientService($uri);
        $data = $this->lunxun();
        if ($data) {
            $client->send('hello');
            $result = $client->receive();
            $result = @json_decode($result, true);
            $send_data = [
                '_id' => $result['_id'],
                '_method' => 'cus-sec_SpcSignDataAsPEM',
            ];
            $data = @json_decode($data,true);
            $result = null;
            foreach ($data as $key =>$val){
                $send_data['args'] = array(
                    'inData' => $val['url'],//得到要加签的字符串
                    'passwd' => '88888888',//默认密码
                );
                $client->send(json_encode($send_data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
                $data = @json_decode($client->receive(), true);
                $args = $data['_args'];
                // 加签的值.
                $sign_data['signValue'] = $args['Data'][0];
                $sign_data['certNo'] = $args['Data'][1];
                $reqData = @json_decode($val['json'], true);//得到上传给海关的值
                $reqData['certNo'] = $sign_data['certNo'];
                $reqData['signValue'] = $sign_data['signValue'];
                $reqData = @json_encode($reqData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
                $res = $client->req('https://swapptest.singlewindow.cn/ceb2grab/grab/realTimeDataUpload','payExInfoStr='.urlencode($reqData));
                $res = @json_decode($res,true);
                $result[$key] = array('msg'=>$res['message'],'status'=>$res['code'] == 10000?1:2);//开始调用海关接口
            }
            if($result){
                $result = json_encode($result);
                //$client->req('把验证接口推给服务器','data='.$result);
                $this->callback($result);
            }
        }
    }
}


ClientService.php

 0,
        'text' => 1,
        'binary' => 2,
        'close' => 8,
        'ping' => 9,
        'pong' => 10,
    );

    /**
     * @param string $uri     A ws/wss-URI
     * @param array  $options
     *                        Associative array containing:
     *                        - context:      Set the stream context. Default: empty context
     *                        - timeout:      Set the socket timeout in seconds.  Default: 5
     *                        - headers:      Associative array of headers to set/override.
     */
    public function __construct($uri, $options = array())
    {
        $this->options = $options;

        if (!array_key_exists('timeout', $this->options)) {
            $this->options['timeout'] = 5;
        }

        // the fragment size
        if (!array_key_exists('fragment_size', $this->options)) {
            $this->options['fragment_size'] = 4096;
        }

        $this->socket_uri = $uri;
    }

    public function getLastOpcode()
    {
        return $this->last_opcode;
    }

    public function getCloseStatus()
    {
        return $this->close_status;
    }

    public function isConnected()
    {
        return $this->is_connected;
    }

    public function setTimeout($timeout)
    {
        $this->options['timeout'] = $timeout;

        if ($this->socket && get_resource_type($this->socket) === 'stream') {
            stream_set_timeout($this->socket, $timeout);
        }
    }

    public function setFragmentSize($fragment_size)
    {
        $this->options['fragment_size'] = $fragment_size;

        return $this;
    }

    public function getFragmentSize()
    {
        return $this->options['fragment_size'];
    }

    public function send($payload, $opcode = 'text', $masked = true)
    {
        if (!$this->is_connected) {
            $this->connect();
        }
        /// @todo This is a client function, fixme!

        if (!in_array($opcode, array_keys(self::$opcodes))) {
            throw new Exception("Bad opcode '$opcode'.  Try 'text' or 'binary'.");
        }

        // record the length of the payload
        $payload_length = strlen($payload);

        $fragment_cursor = 0;
        // while we have data to send
        while ($payload_length > $fragment_cursor) {
            // get a fragment of the payload
            $sub_payload = substr($payload, $fragment_cursor, $this->options['fragment_size']);

            // advance the cursor
            $fragment_cursor += $this->options['fragment_size'];

            // is this the final fragment to send?
            $final = $payload_length <= $fragment_cursor;

            // send the fragment
            $this->send_fragment($final, $sub_payload, $opcode, $masked);

            // all fragments after the first will be marked a continuation
            $opcode = 'continuation';
        }
    }

    protected function send_fragment($final, $payload, $opcode, $masked)
    {
        // Binary string for header.
        $frame_head_binstr = '';

        // Write FIN, final fragment bit.
        $frame_head_binstr .= (bool) $final ? '1' : '0';

        // RSV 1, 2, & 3 false and unused.
        $frame_head_binstr .= '000';

        // Opcode rest of the byte.
        $frame_head_binstr .= sprintf('%04b', self::$opcodes[$opcode]);

        // Use masking?
        $frame_head_binstr .= $masked ? '1' : '0';

        // 7 bits of payload length...
        $payload_length = strlen($payload);
        if ($payload_length > 65535) {
            $frame_head_binstr .= decbin(127);
            $frame_head_binstr .= sprintf('%064b', $payload_length);
        } elseif ($payload_length > 125) {
            $frame_head_binstr .= decbin(126);
            $frame_head_binstr .= sprintf('%016b', $payload_length);
        } else {
            $frame_head_binstr .= sprintf('%07b', $payload_length);
        }

        $frame = '';

        // Write frame head to frame.
        foreach (str_split($frame_head_binstr, 8) as $binstr) {
            $frame .= chr(bindec($binstr));
        }

        // Handle masking
        if ($masked) {
            // generate a random mask:
            $mask = '';
            for ($i = 0; $i < 4; ++$i) {
                $mask .= chr(rand(0, 255));
            }

            $frame .= $mask;
        }

        // Append payload to frame:
        for ($i = 0; $i < $payload_length; ++$i) {
            $frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
        }

        $this->write($frame);
    }

    public function receive()
    {
        if (!$this->is_connected) {
            $this->connect();
        }
        /// @todo This is a client function, fixme!

        $this->huge_payload = '';

        $response = null;
        while (is_null($response)) {
            $response = $this->receive_fragment();
        }

        return $response;
    }

    protected function receive_fragment()
    {
        // Just read the main fragment information first.
        $data = $this->read(2);

        // Is this the final fragment?  // Bit 0 in byte 0
        /// @todo Handle huge payloads with multiple fragments.
        $final = (bool) (ord($data[0]) & 1 << 7);

        // Should be unused, and must be false…  // Bits 1, 2, & 3
        $rsv1 = (bool) (ord($data[0]) & 1 << 6);
        $rsv2 = (bool) (ord($data[0]) & 1 << 5);
        $rsv3 = (bool) (ord($data[0]) & 1 << 4);

        // Parse opcode
        $opcode_int = ord($data[0]) & 31; // Bits 4-7
        $opcode_ints = array_flip(self::$opcodes);
        if (!array_key_exists($opcode_int, $opcode_ints)) {
            throw new Exception("Bad opcode in websocket frame: $opcode_int");
        }
        $opcode = $opcode_ints[$opcode_int];

        // record the opcode if we are not receiving a continutation fragment
        if ($opcode !== 'continuation') {
            $this->last_opcode = $opcode;
        }

        // Masking?
        $mask = (bool) (ord($data[1]) >> 7); // Bit 0 in byte 1

        $payload = '';

        // Payload length
        $payload_length = (int) ord($data[1]) & 127; // Bits 1-7 in byte 1
        if ($payload_length > 125) {
            if ($payload_length === 126) {
                $data = $this->read(2);
            }
            // 126: Payload is a 16-bit unsigned int
            else {
                $data = $this->read(8);
            }
            // 127: Payload is a 64-bit unsigned int
            $payload_length = bindec(self::sprintB($data));
        }

        // Get masking key.
        if ($mask) {
            $masking_key = $this->read(4);
        }

        // Get the actual payload, if any (might not be for e.g. close frames.
        if ($payload_length > 0) {
            $data = $this->read($payload_length);

            if ($mask) {
                // Unmask payload.
                for ($i = 0; $i < $payload_length; ++$i) {
                    $payload .= ($data[$i] ^ $masking_key[$i % 4]);
                }
            } else {
                $payload = $data;
            }
        }

        if ($opcode === 'close') {
            // Get the close status.
            if ($payload_length >= 2) {
                $status_bin = $payload[0].$payload[1];
                $status = bindec(sprintf('%08b%08b', ord($payload[0]), ord($payload[1])));
                $this->close_status = $status;
                $payload = substr($payload, 2);

                if (!$this->is_closing) {
                    $this->send($status_bin.'Close acknowledged: '.$status, 'close', true);
                }
                // Respond.
            }

            if ($this->is_closing) {
                $this->is_closing = false;
            }
            // A close response, all done.

            // And close the socket.
            fclose($this->socket);
            $this->is_connected = false;
        }

        // if this is not the last fragment, then we need to save the payload
        if (!$final) {
            $this->huge_payload .= $payload;

            return null;
        }
        // this is the last fragment, and we are processing a huge_payload
        elseif ($this->huge_payload) {
            // sp we need to retreive the whole payload
            $payload = $this->huge_payload .= $payload;
            $this->huge_payload = null;
        }

        return $payload;
    }

    /**
     * Tell the socket to close.
     *
     * @param int    $status  http://tools.ietf.org/html/rfc6455#section-7.4
     * @param string $message a closing message, max 125 bytes
     */
    public function close($status = 1000, $message = 'ttfn')
    {
        $status_binstr = sprintf('%016b', $status);
        $status_str = '';
        foreach (str_split($status_binstr, 8) as $binstr) {
            $status_str .= chr(bindec($binstr));
        }

        $this->send($status_str.$message, 'close', true);

        $this->is_closing = true;
        $response = $this->receive(); // Receiving a close frame will close the socket now.

        return $response;
    }

    protected function write($data)
    {
        $written = fwrite($this->socket, $data);

        if ($written < strlen($data)) {
            throw new Exception(
                "Could only write $written out of ".strlen($data).' bytes.'
            );
        }
    }

    protected function read($length)
    {
        $data = '';
        while (strlen($data) < $length) {
            $buffer = fread($this->socket, $length - strlen($data));
            if ($buffer === false) {
                $metadata = stream_get_meta_data($this->socket);
                throw new Exception(
                    'Broken frame, read '.strlen($data).' of stated '
                    .$length.' bytes.  Stream state: '
                    .json_encode($metadata)
                );
            }
            $data .= $buffer;
        }

        return $data;
    }

    protected static function sprintB($string)
    {
        $return = '';
        for ($i = 0; $i < strlen($string); ++$i) {
            $return .= sprintf('%08b', ord($string[$i]));
        }

        return $return;
    }

    public function __destruct()
    {
        if ($this->socket) {
            if (get_resource_type($this->socket) === 'stream') {
                fclose($this->socket);
            }

            $this->socket = null;
        }
    }

    protected function connect()
    {
        $url_parts = parse_url($this->socket_uri);
        $scheme = $url_parts['scheme'];
        $host = $url_parts['host'];
        $user = isset($url_parts['user']) ? $url_parts['user'] : '';
        $pass = isset($url_parts['pass']) ? $url_parts['pass'] : '';
        $port = isset($url_parts['port']) ? $url_parts['port'] : ($scheme === 'wss' ? 443 : 80);
        $path = isset($url_parts['path']) ? $url_parts['path'] : '/';
        $query = isset($url_parts['query']) ? $url_parts['query'] : '';
        $fragment = isset($url_parts['fragment']) ? $url_parts['fragment'] : '';

        $path_with_query = $path;
        if (!empty($query)) {
            $path_with_query .= '?'.$query;
        }

        if (!empty($fragment)) {
            $path_with_query .= '#'.$fragment;
        }

        if (!in_array($scheme, array('ws', 'wss'))) {
            throw new Exception(
                "Url should have scheme ws or wss, not '$scheme' from URI '$this->socket_uri' ."
            );
        }

        $host_uri = ($scheme === 'wss' ? 'ssl' : 'tcp').'://'.$host;

        // Set the stream context options if they're already set in the config
        if (isset($this->options['context'])) {
            // Suppress the error since we'll catch it below
            if (@get_resource_type($this->options['context']) === 'stream-context') {
                $context = $this->options['context'];
            } else {
                throw new InvalidArgumentException(
                    "Stream context in $options['context'] isn't a valid context"
                );
            }
        } else {
            $context = stream_context_create();
        }

        // Open the socket.  @ is there to supress warning that we will catch in check below instead.
        $this->socket = @stream_socket_client(
            $host_uri.':'.$port,
            $errno,
            $errstr,
            $this->options['timeout'],
            STREAM_CLIENT_CONNECT,
            $context
        );

        if ($this->socket === false) {
            throw new Exception(
                "Could not open socket to "$host:$port": $errstr ($errno)."
            );
        }

        // Set timeout on the stream as well.
        stream_set_timeout($this->socket, $this->options['timeout']);

        // Generate the WebSocket key.
        $key = self::generateKey();

        // Default headers (using lowercase for simpler array_merge below).
        $headers = array(
            'host' => $host.':'.$port,
            'user-agent' => 'websocket-client-php',
            'connection' => 'Upgrade',
            'upgrade' => 'websocket',
            'sec-websocket-key' => $key,
            'sec-websocket-version' => '13',
        );

        // Handle basic authentication.
        if ($user || $pass) {
            $headers['authorization'] = 'Basic '.base64_encode($user.':'.$pass)."rn";
        }

        // Deprecated way of adding origin (use headers instead).
        if (isset($this->options['origin'])) {
            $headers['origin'] = $this->options['origin'];
        }

        // Add and override with headers from options.
        if (isset($this->options['headers'])) {
            $headers = array_merge($headers, array_change_key_case($this->options['headers']));
        }

        $header =
            'GET '.$path_with_query." HTTP/1.1rn"
&

站点声明:部分内容源自互联网,为传播信息之用,如有侵权,请联系我们删除。

© Copyright 2011-2024 www.kfju.com. All Rights Reserved.
超级字帖 版权所有。 蜀ICP备12031064号      川公网安备51162302000234