puCodeUser = $data; $this->apiUrl = env('taxes.api_url'); $this->reqId = $this->getReqId(); // 接入端交易请求唯一标识,长度 32 位,5 秒内不得重复,否则抛出异常:交易请求唯一标识重复。 $this->channelId = env('taxes.channel_id'); // 接入渠道标识(ID) $this->channelSecret = env('taxes.channel_secret'); // 平台为接入渠道统一分配的密码,只有身份验证接口需要 $this->invoiceIssuance = $invoice; $this->getAccessToken(); } /** * 2.1身份验证 * @throws FuncException */ protected function getAccessToken() { $tax_key = 'tax_access_token'; $tax_access_token = Cache::get($tax_key); if ($tax_access_token) { $this->accessToken = $tax_access_token; } else { $param = [ 'reqId' => $this->reqId, // 接入端交易请求唯一标识,长度 32 位,5 秒内不得重复,否则抛出异常:交易请求唯一标识重复。 'sid' => 'bizAccessLogin', // 调用的接口名称,固定为“bizAccessLogin” 'channelId' => $this->channelId, // 接入渠道标识(ID) 'channelSecret' => $this->channelSecret, // 平台为接入渠道统一分配的密码,只有身份验证接口需要 'timestamp' => time(), // unix 时间戳 'data' => [ 'flag' => '大工业区生活垃圾处理费征收及开票需要' ] // 身份验证数据,默认为空 ]; $result = $this->json_curl(json_encode($param)); if (isset($result['error'])) { throw new FuncException($result['error']['message']); } if (!isset($result['result']['accessToken'])) { throw new FuncException('返回参数有误'); } $this->accessToken = $result['result']['accessToken']; Cache::set($tax_key, $this->accessToken, 28680); } } /** * 发起请求参数 * @param $sid * @param $body * @return array */ protected function getParamData($sid, $body): array { $data = [ 'reqId' => $this->getReqId(), // 接入端交易请求唯一标识 'sid' => $sid,// 调 用 的 接 口 名 称 'channelId' => $this->channelId,// 接入渠道标识 'accessToken' => $this->accessToken,// 访问令牌,由调用身份验证接口获取 'timestamp' => time(),// unix 时间戳 'data' => [ 'bizXml' => $this->bizXml($body) ], ]; return $data; } /** * 3.1保存委托代征明细数据接口 * @return array * @throws \Exception */ public function savingDetailedData(): array { $userName = !empty($this->puCodeUser['ContactName']) ? $this->puCodeUser['ContactName'] : $this->puCodeUser['UserName']; $mxGrid = [ 'bdznsrsbh' => $this->puCodeUser['bdznsrsbh'],// 被代征纳税人识别号 * 'bdznsrmc' => $userName,// 被代征纳税人名称 * 'gjhdqszDm' => '156',// 国家或地区数字代码 'sfzjlxDm' => '201',// 身份证件类型代码 'zjhm' => $this->puCodeUser['CertificateCode'],// 证件号码 * 'hyDm' => '6490',// 行业代码 'zsxmDm' => '30433',// 征收项目代码 'zspmDm' => '304331300',// 征收品目代码 'zszmDm' => '',// 征收子目代码 'jsyj' => '11.11',// 计税依据,保留两位小数 'sl1' => '',// 税率,可手工填写,不填则默认系统自动计算,保留两位小数 'ynse' => '',// 应纳税额,可手工填写,不填则默认系统自动计算,保留两位小数 'ydzse' => '',// 应代征税额,可手工填写,不填则默认系统自动计算,保留两位小数 'yjse' => '',// 已缴税额,可手工填写,不填则默认系统自动计算,保留两位小数 'ydzse1' => '',// 已代征税额,可手工填写,不填则默认系统自动计算,保留两位小数 'jmse' => '',// 减免税额,可手工填写,不填则默认系统自动计算,保留两位小数 'phjmse' => '',// 增值税小规模纳税人减征额 'phjzbl' => '',// 增值税小规模纳税人减征比例 'phjmxzDm' => '',// 增值税小规模纳税人减免性质代码 'ssjmxzDm' => '',// 税收减免性质代码 'wszmkjbz' => 'Y',// 完税证明开具标志为 Y 开具,N不开具 'xmmc' => '测试',// 项目名称 'xmbm' => 'T435',// 项目编码 'username' => $userName,// 用户名称(同一个批次,同一个用户编号下,值相同) 'nbyhbm' => $this->puCodeUser['UserCode'],// 用户编号 'jldwmc' => '立方米',// 单位 'sl' => '100',// 数量 'zxbz1' => '10',// 标准 'skyhmc' => $this->puCodeUser['BankName'],// 托收银行(同一个批次,同一个用户编号下,值相同) 'yhzh' => $this->puCodeUser['BankAccountCode'],// 银行账号(同一个批次,同一个用户编号下,值相同) 'zsfsmc' => '手工转账',// 缴费方式(同一个批次,同一个用户编号下,值相同) 'tjsd' => '2022-11-01至2022-11-30',// 收费时段(同一个批次,同一个用户编号下,值相同) 'jfyj1' => '测试',// 计费依据(同一个批次,同一个用户编号下,值相同) 'yhje' => '10.00',// 优惠金额 'hjje' => '990.00',// 合计金额 'jfrq' => '2022-11-30',// 缴费时间(同一个批次,同一个用户编号下,值相同) 'lxfs' => '测试',// 联系方式(同一个批次,同一个用户编号下,值相同) 'lxdz' => '测试',// 送票地址(同一个批次,同一个用户编号下,值相同) 'remark' => '测试',// 备注(同一个批次,同一个用户编号下,值相同) 'bz' => '测试',// 备注信息(同一个批次,同一个用户编号下,值相同) ]; $body = [ 'sbczlxDm' => 'insert', //申报操作类型代码,为”delete”,” insert” 或”update” 'sflsb' => 'N', //是否零申报 ,为非零申报”N” 'sbpch' => '', //批次号,详见备注 'nsqxDm' => $this->invoiceIssuance['nsqxDm'], //纳税期限代码,“06”按月 'skssqq' => $this->invoiceIssuance['skssqq'], //税款所属期起,格式,年-月-日 * 'skssqz' => $this->invoiceIssuance['skssqz'], //税款所属期止,格式,年-月-日 * 'zsxmdm' => '30433', //征收项目代码:30433 'my' => $this->my, //密钥,数字签名校验和资格校验 'mxGrid' => $this->mxGrid($mxGrid) ]; $this->xmlType = 2; $param = $this->getParamData('SSGZ_GZPT_SZQKL_WTDZDRJK', $body); $res = $this->resultXml($param); if (!isset($res['sbpch'])) { throw new FuncException('未获取到批次号'); } return $res; } /** * 3.6实时加工委托代征明细数据接口 * @param $sbpch * @return array * @throws FuncException */ public function realTimeProcessing($sbpch): array { $body = [ 'sbpch' => $sbpch, // 批次号,详见备注 'my' => $this->my, // 密钥,数字签名校验和资格校验 'czlx' => 'CX', // 操作类型(CX:查询;JG:加工) ]; $param = $this->getParamData('SSGZ_GZPT_SZQKL_JGWTDZ', $body); return $this->resultXml($param, '6', 3); } /** * 3.8开具电子缴款凭证接口 * @param $sbpch * @param $bdznsrsbh * @return array * @throws FuncException */ public function issueElectronic($sbpch, $bdznsrsbh): array { $body = [ 'sbpch' => $sbpch, // 批次号,详见备注 'bdzmxGrid' => [ 'bdzmxlb' => [ 'bdznsrsbh' => $bdznsrsbh, // 被代征人纳税人识别号 'yhbm' => $this->puCodeUser['UserCode'], // 用户编码 ] ], 'skssqq' => $this->invoiceIssuance['skssqq'], // 税款所属期起,格式,年-月-日 'skssqz' => $this->invoiceIssuance['skssqz'], // 税款所属期止,格式,年-月-日 'my' => $this->my // 密钥,数字签名校验和资格校验 ]; $param = $this->getParamData('SSGZ_GZPT_PZ_SQKJDZJKPZ', $body); return $this->resultXml($param, '', 1); } /** * 3.9查询电子缴款凭证上链信息 * @param $assetID * @return false[]|string[] * @throws FuncException */ public function queryPaymentVoucher($assetID): array { $body = [ 'assetID' => $assetID ]; $param = $this->getParamData('SSGZ_GZPT_PZ_QUERYDZJKPZSLXX', $body); $result = $this->resultXml($param, '', 1); return [ 'meta' => $result['pzkjxxVO']['meta'], 'data' => $result['pzkjxxVO']['data'] ]; } /** * 3.3 查询上传异常数据接口(未完成) * @param $sbpch * @return array * @throws FuncException */ public function queryUploadErrorData($sbpch): array { $body = [ 'sbpch' => $sbpch, // 批次号,详见备注 'my' => $this->my // 密钥,数字签名校验和资格校验 ]; $param = $this->getParamData('SSGZ_GZPT_SZQKL_DW_DZSCMXYC_QUERY', $body); $result = $this->json_curl(json_encode($param)); return $this->resultXml($result, '', 1); } /** * 3.7 查询待开具电子缴款凭证数据接口(未完成) * @param $bdznsrsbh * @return array * @throws FuncException */ public function queryIssuedPaymentVoucher($bdznsrsbh): array { $body = [ 'bdzmxList' => [ 'bdznsrsbh' => $bdznsrsbh // 被代征人纳税人识别号 ], 'skssqq' => $this->invoiceIssuance['skssqq'], // 税款所属期起,格式,年-月-日 'skssqz' => $this->invoiceIssuance['skssqz'], // 税款所属期止,格式,年-月-日 'my' => $this->my // 密钥,数字签名校验和资格校验 ]; $param = $this->getParamData('SSGZ_GZPT_PZ_QUERYDKDZJKP', $body); $result = $this->json_curl(json_encode($param)); return $this->resultXml($result, '', 1); } /** * 3.10身份校验接口 --- * @param $nsrsbh // 纳税人识别号 * @return array * @throws FuncException */ public function identityCheck($nsrsbh): array { $body = [ 'sfxxList' => [ 'nsrlx' => '1' // 2 非自然人、1 自然人 ], 'yhbh' => $this->puCodeUser['UserCode'], // 水司的用户编号 'nsrsbh' => $nsrsbh, // 传输类型为非自然人不可以为空 'nsrmc' => $this->puCodeUser['ContactName'], // 不可为空 'zjhm' => $this->puCodeUser['CertificateCode'], // 传输类型为自然人,不可以为空 'zjlx' => '201', // 传输类型为自然人,不可以为空,添加证件类型代码 999 'gj' => '156', // 国籍 ]; $this->xmlType = 2; $param = $this->getParamData('SSGZ_GZPT_SZQKL_SFJYJK', $body); $result = $this->json_curl(json_encode($param)); return $this->resultXml($result, '7'); } /** * 3.14代征明细数据虚拟户更正接口 * @param $nsrsbh // 纳税人识别号 * @param $sbpch // 批次号 * @return array * @throws FuncException */ public function collectionDetails($nsrsbh, $sbpch): array { $body = [ 'gzxxList' => [ 'gzxxVO' => [ 'my' => $this->my // 密钥,数字签名校验和资格校验 ], 'nsrlx' => 1, // 2 非自然人、1 自然人 'yhbh' => $this->puCodeUser['UserCode'], // 水司的用户编号 'nsrsbh' => $nsrsbh, // 传输类型为单位不可以为空 'nsrmc' => $this->puCodeUser['ContactName'], // 不可为空 ? 'zjhm' => $this->puCodeUser['CertificateCode'], // 传输类型为自然人,不可以为空 'zjlx' => '201', // 传输类型为自然人,不可以为空,添加证件类型代码 999 'gj' => '156', // 国籍 'username' => $this->puCodeUser['UserName'], // 用户名称 'skyhmc' => $this->puCodeUser['BankName'], // 托收银行 'yhzh' => $this->puCodeUser['BankAccountCode'], // 银行账号 'lxdz' => $this->puCodeUser['MaillingAddress'], // 用户地址 'lxfs' => $this->puCodeUser['Telephone'], // 联系方式 'sbpch' => $sbpch, // 申报批次号 ], ]; $this->xmlType = 2; $param = $this->getParamData('SSGZ_GZPT_SZQKL_SB_XNHGZ', $body); $result = $this->json_curl(json_encode($param)); return $this->resultXml($result, '', 1); } /** * 3.2作废委托代征明细数据接口(未完成) * @throws FuncException */ public function invalidEntrustCollection($sbpch): array { $body = [ 'sbpch' => $sbpch, // 批次号,详见备注 'my' => $this->my // 密钥,数字签名校验和资格校验 ]; $param = $this->getParamData('SSGZ_GZPT_SZQKL_DELETEWTDZ', $body); $result = $this->json_curl(json_encode($param)); return $this->resultXml($result, '', 1); } /** * 3.12代征明细数据汇总确认查询接口 * @throws FuncException */ public function daiZhengSummaryQuery($bdznsrsbh): array { $body = [ 'my' => $this->my, // 密钥,数字签名校验和资格校验 'bdznsrsbh' => $bdznsrsbh, // 被代征人纳税人识别号 'skssqq' => $this->invoiceIssuance['skssqq'], // 税款所属期起,格式,年-月-日 'skssqz' => $this->invoiceIssuance['skssqz'], // 税款所属期止,格式,年-月-日 ]; $param = $this->getParamData('SSGZ_GZPT_SZQKL_SB_CXDZMXSJHZ', $body); $result = $this->json_curl(json_encode($param)); return $this->resultXml($result, '', 1); } /** * 3.13代征明细数据汇总确认结报接口 * @param $zje // 总金额 * @return array * @throws FuncException */ public function daiZhengSummaryReport($zje): array { $body = [ 'my' => $this->my, // 密钥,数字签名校验和资格校验 'zje' => $zje, // 总金额 'skssqq' => $this->invoiceIssuance['skssqq'], // 税款所属期起,格式,年-月-日 'skssqz' => $this->invoiceIssuance['skssqz'], // 税款所属期止,格式,年-月-日 ]; $param = $this->getParamData('SSGZ_GZPT_SZQKL_SB_BCWTDZHZJK', $body); $result = $this->json_curl(json_encode($param)); return $this->resultXml($result, '', 1); } /** * 3.11税收完税证明换开申请接口 !!! * @param $bdznsrsbh * @return array * @throws FuncException */ public function taxationReplaceApply($bdznsrsbh): array { $body = [ 'my' => $this->my, // 密钥,数字签名校验和资格校验 'bdznsrsbh' => $bdznsrsbh, // 被代征人纳税人识别号 'skssqq' => $this->invoiceIssuance['skssqq'], // 税款所属期起,格式,年-月-日 'skssqz' => $this->invoiceIssuance['skssqz'], // 税款所属期止,格式,年-月-日 ]; $param = $this->getParamData('SSGZ_GZPT_SZQKL_ZM_SAVEPLSQKJSSWSZMJK', $body); $result = $this->json_curl(json_encode($param)); return $this->resultXml($result, '', 1); } /** * 3.15查询作废的电子缴款凭证接口 --- * @throws FuncException */ public function queryInvalidPaymentVoucher($assetID): array { $body = [ 'my' => $this->my, // 密钥,数字签名校验和资格校验 'zfqq' => $this->invoiceIssuance['skssqq'], // 作废期起,格式,年-月-日,查询作废时间内作废的票证,返回相应的 assetid 'zfqz' => $this->invoiceIssuance['skssqz'], // 作废期止,格式,年-月-日,查询作废时间内作废的票证,返回相应的 assetid 'assetid' => $assetID, // 电子缴款凭证开具返回的 assetid,填写这个参数只查验单个票证是否作废 ]; $param = $this->getParamData('SSGZ_GZPT_SZQKL_PZ_QUERYDZJKPZZFQK', $body); $result = $this->json_curl(json_encode($param)); return $this->resultXml($result, '', 1); } /** * 3.4申请开具接口 !!! * @param $nsrsbh * @param $sbpch * @return array * @throws FuncException */ public function applyIssued($nsrsbh, $sbpch): array { $body = [ 'bhzrnsrsbhs' => [ 'nsrsbh' => $nsrsbh // 纳税人识别号 ], 'skssqq' => $this->invoiceIssuance['skssqq'], // 税款所属期起,格式,年-月-日 'skssqz' => $this->invoiceIssuance['skssqz'], // 税款所属期止,格式,年-月-日 'mm' => $this->my, // 密钥,数字签名校验和资格校验 'pch' => $sbpch, // 文件名,英文或数字,长度在 20 位,且唯一 ]; $param = $this->getParamData('SSGZ_GZPT_SZQKL_ZM_SAVEPLSQKJSSWSZMFORWXT', $body); $result = $this->json_curl(json_encode($param)); return $this->resultXml($result, '', 1); } /** * 3.5下载完税证明 !!! */ public function downTaxPaymentProve($djxh, $bq): string { $param = [ 'ctrl=PlkjwszmCtrl_jkxzWszmxx', // 必填,功能模块名称,固定值为“PlkjwszmCtrl_jkxzWszmxx“ 'token=' . $this->accessToken, // 必填,令牌,即通过前台资格校验模块生成的密钥 'djxh=' . $djxh, // 必填,代征单位登记序号 'bq=' . $bq // 必填,标签,即申请开具接口的的 pch 字段,是唯一的一个文件名称 ]; $paramStr = implode('&', $param); return env('taxes_down_url') . '?' . $paramStr; // 必填,请求路径 } /** * 处理 body 报文 * @param $body * @param int $type * @return string */ protected function bizXml($body, int $type = 0): string { $start = ' '; $start1 = ''; $end = ''; $xml = ''; $xml2 = ''; foreach ($body as $key => $value) { if (is_array($value)) { $xml .= "<$key>" . $this->bizXml($value, 1) . ""; $xml2 .= "" . $this->bizXml($value, 1) . ""; } else { $xml .= "<$key>$value"; $xml2 .= "" . $value . ""; } } if ($type == 1) { return $xml; } // 需要ns2 前后组合 加密返回 if ($this->xmlType == 2) { $this->xmlType = 1; return $start1 . $this->enCompressXml($start . $xml2 . $end) . $end; } // 需要ns2 前后组合 加密返回 if ($this->xmlType == 3) { $this->xmlType = 1; return $start1 . $this->enCompressXml($start . $xml . $end) . $end; } // 直接返回 if ($this->xmlType == 1) { return $xml; } // 不需要前后组合 return $xml2; } /** * * @param $mxGrid * @return string */ protected function mxGrid($mxGrid): string { $xmlStr = ''; foreach ($mxGrid as $key => $value) { $xmlStr .= "" . $value . ""; } return $xmlStr . ''; } /** * 发送请求JSON * @param $json * @return bool|string * @throws FuncException */ protected function json_curl($json) { // 初始化cURL会话 $ch = curl_init(); // 设置cURL选项 curl_setopt($ch, CURLOPT_URL, $this->apiUrl); // 目标URL curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回结果而不是输出 curl_setopt($ch, CURLOPT_POST, true); // 发送POST请求 // 设置POST字段 curl_setopt($ch, CURLOPT_POSTFIELDS, $json); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); // 设置HTTP头 // 跳过证书验证(不推荐在生产环境使用) curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过主机名验证 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 执行cURL会话 $response = curl_exec($ch); // 检查是否有错误发生 if (curl_errno($ch)) { throw new FuncException('请求失败'); } // 关闭cURL会话 curl_close($ch); return json_decode($response, true); } /** * 获取接入端交易请求唯一标识 * @return string */ protected function getReqId(): string { return md5(time() . rand(0, 9)); } /** * 装换xml数据为数组 * @param $xmlData * @return array|mixed */ protected function xmlDom($xmlData) { $dom = new \DOMDocument(); $dom->loadXML($xmlData); return $this->xmlToArray($dom->documentElement); } /** * 加密 * @param $str * @return array|string|string[] */ protected function enCompressXml($str) { $gzencode = gzencode($str); $baseStr = base64_encode($gzencode); return str_replace(array("\n", "\r"), array('{n}', '{r}'), $baseStr); } /** * 解密 * @param $str * @return false|string */ protected function deCompressXml($str) { $repStr = str_replace(array('{n}', '{r}'), array("\n", "\r"), $str); $baseStr = base64_decode($repStr); return gzdecode($baseStr); } /** * 转换xml为数组 * @param $node * @return array|mixed */ protected function xmlToArray($node) { $output = []; if ($node->hasChildNodes()) { foreach ($node->childNodes as $child) { if ($child->nodeType === XML_ELEMENT_NODE) { $output[$child->nodeName] = $this->xmlToArray($child); } elseif ($child->nodeType === XML_TEXT_NODE) { $output = $child->nodeValue; } } } return $output; } /** * 处理返回的XML数据 * @param $param * @param string $keyValue * @param int $type * @return array * @throws FuncException */ protected function resultXml($param, string $keyValue = '1', int $type = 0): array { $key = "SZQKLDZSPSB000{$keyValue}ZipBase64ResponsBw"; $result = $this->json_curl(json_encode($param)); if (!isset($result['result'])) { if (isset($result['error']['message'])) { throw new FuncException($result['error']['message']); } throw new FuncException('请求失败'); } $data = $this->xmlDom($result['result']); if (isset($data['code']) && $data['code'] != 200) { if ($type == 1 && $data['code'] == 302) return $data; throw new FuncException($data['message']); } else if ($type == 1) { return $data; } $deData = $this->deCompressXml($data[$key]); if ($deData) { $xmlData = $this->xmlDom($deData); if (isset($xmlData['ns2:code']) && $xmlData['ns2:code'] != 200) { throw new FuncException($xmlData['ns2:message']); } $returnData = $this->delNs2Filed($xmlData); if ($type == 3) { $data['data'] = $returnData; $returnData = $data; } return $returnData; } return $data; } /** * 处理xml数据 * @param $returnData * @return mixed */ protected function delNs2Filed($returnData) { foreach ($returnData as $oldKey => $value) { $newKey = str_replace('ns2:', '', $oldKey); if (is_array($value)) { $value = $this->delNs2Filed($value); } $returnData[$newKey] = $value; if ($newKey != $oldKey) { unset($returnData[$oldKey]); } } return $returnData; } }