完整代码请点击:
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"
&