ntak_client.class.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. <?php
  2. /**
  3. * JEGY ADATOK
  4. *
  5. * 'bruttoAr': ha bundle-ben van, akkor hogyan adjak neki árat?
  6. * 'ervenyessegKezdete': kotelezo megadni, de nem tudom mikor fogja felhasznalni, milyen datum legyen?
  7. *
  8. * bundle jegy kedvezmenyt hogyan kell bekuldeni, mert ott csak a vegosszeg lesz kedvezmenyes, nem tetelenkent
  9. *
  10. * PROGRAM ADATOK
  11. *
  12. * helyszinAdatok: hogy kuldjem be, ha csak buszos/hajos jegyet vesz?
  13. * programKezdete ertelmezhetetlen - de kotelezo (szamla datuma legyen)
  14. */
  15. require_once DOL_DOCUMENT_ROOT . '/custom/ntak/class/ntakconnector/ntak_const.class.php';
  16. require_once DOL_DOCUMENT_ROOT . '/custom/ntak/class/ntakconnector/ntak_config.class.php';
  17. require_once DOL_DOCUMENT_ROOT . '/custom/ntak/class/ntakinvoice.class.php';
  18. require_once DOL_DOCUMENT_ROOT . '/custom/ntak/class/ntaksaleschannel.class.php';
  19. require_once DOL_DOCUMENT_ROOT . '/custom/ntak/class/ntakconnector/ntak_date.class.php';
  20. require_once DOL_DOCUMENT_ROOT . '/custom/ntak/class/ntaklogger.class.php';
  21. require_once DOL_DOCUMENT_ROOT . '/custom/ntak/class/ntaksendresult.class.php';
  22. require_once DOL_DOCUMENT_ROOT . '/custom/ntak/class/utils/moneyhandler.class.php';
  23. require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticket.class.php';
  24. class NtakClient
  25. {
  26. private static $instance = null;
  27. const CERT_KEY = 'cert.key';
  28. const CERT_PEM = 'cert.pem';
  29. const CERT_B64 = 'cert_b64.txt';
  30. private $date;
  31. private $postFields;
  32. private $certDir;
  33. private NtakInvoice $invoice;
  34. private NtakLogger $ntakLogger;
  35. private $NtakConfig;
  36. private $NTAK_URL;
  37. private string $REGISTRATION_NUMBER;
  38. private string $TAX_NUMBER;
  39. private string $TSS_ID;
  40. private string $TSS_VERSION;
  41. public static function getInstance(): NtakClient
  42. {
  43. if (self::$instance == null) {
  44. self::$instance = new NtakClient;
  45. }
  46. return self::$instance;
  47. }
  48. public function __construct()
  49. {
  50. $this->initCertDir();
  51. $this->checkFiles();
  52. $this->ntakLogger = NtakLogger::getInstance();
  53. $this->NtakConfig = new NtakConfig();
  54. $this->NTAK_URL = $this->NtakConfig->getURL();
  55. $this->REGISTRATION_NUMBER = $this->NtakConfig->getREGISTRATION_NUMBER();
  56. $this->TAX_NUMBER = $this->NtakConfig->getTAX_NUMBER();
  57. $this->TSS_ID = $this->NtakConfig->getTSS_ID();
  58. $this->TSS_VERSION = $this->NtakConfig->getTSS_VERSION();
  59. }
  60. /**
  61. *
  62. */
  63. private function checkFiles(): void
  64. {
  65. $this->checkCertFile(self::CERT_PEM);
  66. $this->checkCertFile(self::CERT_B64);
  67. }
  68. /**
  69. *
  70. */
  71. private function checkCertFile(string $file): void
  72. {
  73. $fullPath = $this->certDir . $file;
  74. if (file_exists($fullPath)) {
  75. if (!is_file($fullPath)) {
  76. throw new \Exception('CERT FILE IS NOT FILE (' . $fullPath . ')');
  77. }
  78. if (!is_readable($fullPath)) {
  79. throw new \Exception('CERT FILE IS NOT READABLE (' . $fullPath . ')');
  80. }
  81. } else {
  82. throw new \Exception('CERT FILE MISSING (' . $fullPath . ')');
  83. }
  84. }
  85. /**
  86. *
  87. */
  88. private function initCertDir(): void
  89. {
  90. $certPath = [
  91. __DIR__,
  92. '..',
  93. '..',
  94. 'cert'
  95. ];
  96. $this->certDir = implode(DIRECTORY_SEPARATOR, $certPath) . DIRECTORY_SEPARATOR;
  97. }
  98. /**
  99. *
  100. */
  101. public function sendToServer(array $data): NtakSendResult
  102. {
  103. $this->postFields = $data;
  104. $logData = json_encode($data);
  105. $invoiceRefs = array_map(function ($item) {
  106. return $item['tranzakcioAzonosito'];
  107. }, $data['tranzakcioAdatok']);
  108. $this->ntakLogger->logIntoFile($logData, LOG_INFO);
  109. $curl = $this->getCurlResource($this->NTAK_URL . NtakConfig::URL_SALE);
  110. $response = curl_exec($curl);
  111. /* if ($response === false) {
  112. $error = curl_error($curl); // cURL hiba kiíratása
  113. $error_no = curl_errno($curl); // Hibakód
  114. echo "cURL hiba ($error_no): $error";
  115. } else {
  116. echo "NTAK válasz: " . $response;
  117. }
  118. exit; */
  119. $this->ntakLogger->logIntoFile($response, LOG_INFO);
  120. $ntakSendResult = new NtakSendResult;
  121. if ($response) {
  122. $resultStatus = 0;
  123. $result = json_decode($response, true);
  124. if (!empty($result['feldolgozasAzonosito'])) {
  125. $ntakSendResult->ntakId = $result['feldolgozasAzonosito'];
  126. $checkResult = $this->check($result['feldolgozasAzonosito']);
  127. if (!empty($checkResult['uzenetValaszok'][0]['statusz'])) {
  128. $resultStatus = NtakConst::getDbResponseStatusByStatus($checkResult['uzenetValaszok'][0]['statusz']);
  129. $ntakSendResult->ntakStatus = $checkResult['uzenetValaszok'][0]['statusz'];
  130. $ntakSendResult->status = $resultStatus;
  131. }
  132. $this->ntakLogger->logIntoFile("Invoice sent successfully to NTAK (Ref: " . implode(', ', $invoiceRefs) . ")", LOG_INFO);
  133. $this->ntakLogger->logIntoFile("Invoice data: {$logData}", LOG_INFO);
  134. $this->ntakLogger->massDbLogByInvoiceRef($invoiceRefs, $resultStatus, curl_error($curl), $logData, $result['feldolgozasAzonosito']);
  135. } else {
  136. $this->ntakLogger->logIntoFile("Invoice sent failed to NTAK (Ref: " . implode(', ', $invoiceRefs) . ")", LOG_INFO);
  137. $this->ntakLogger->logIntoFile($response, LOG_INFO);
  138. $this->ntakLogger->massDbLogByInvoiceRef($invoiceRefs, $resultStatus, $response, $logData);
  139. }
  140. } else {
  141. $this->ntakLogger->logIntoFile("Cannot send invoice (Ref: " . implode(', ', $invoiceRefs) . ")", LOG_ALERT);
  142. $this->ntakLogger->massDbLogByInvoiceRef($invoiceRefs, NtakLog::STATUS_CANCELED, curl_error($curl), $logData);
  143. }
  144. curl_close($curl);
  145. return $ntakSendResult;
  146. }
  147. /**
  148. * @var bool $send If false, not send and return with the generated data
  149. */
  150. public function send(NtakInvoice $ntakInvoice, bool $send = true): NtakSendResult
  151. {
  152. $this->ntakLogger->logIntoFile("Send invoice to NTAK (Ref: {$ntakInvoice->ref})", LOG_INFO);
  153. $ntakSendResult = new NtakSendResult;
  154. $this->invoice = $ntakInvoice;
  155. $this->date = gmdate(DATE_RFC3339, time());
  156. $this->initServiceProvider();
  157. $this->initSenderData();
  158. $this->initMessageData();
  159. $this->setPostFieldsProgramData();
  160. $this->setPostFieldsTransactionData();
  161. if ($ntakInvoice->array_options['options_ntak_status'] == NtakConst::INVOICE_SENDING_RESPONSE_STATUS_SEND_AGAIN) {
  162. $origData = $this->ntakLogger->getOrigSentData($ntakInvoice->array_options['options_ntak_id']);
  163. if (!empty($origData)) {
  164. $this->postFields = $origData;
  165. }
  166. }
  167. if ($send) {
  168. //echo json_encode($this->postFields); die;
  169. //print_r($this->postFields); die;
  170. $ntakSendResult = $this->sendToServer($this->postFields);
  171. } else {
  172. $ntakSendResult->generatedData = $this->postFields;
  173. }
  174. return $ntakSendResult;
  175. }
  176. /**
  177. *
  178. */
  179. private function getCurlResource(string $url)
  180. {
  181. $curl = curl_init();
  182. curl_setopt_array(
  183. $curl,
  184. [
  185. CURLOPT_URL => $url,
  186. CURLOPT_RETURNTRANSFER => true,
  187. CURLOPT_SSLCERT => $this->certDir . self::CERT_PEM,
  188. CURLOPT_SSLKEY => $this->certDir . self::CERT_KEY,
  189. CURLOPT_ENCODING => '',
  190. CURLOPT_MAXREDIRS => 10,
  191. CURLOPT_TIMEOUT => 0,
  192. CURLOPT_FOLLOWLOCATION => true,
  193. CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  194. CURLOPT_SSL_VERIFYHOST => false,
  195. CURLOPT_SSL_VERIFYPEER => false,
  196. CURLOPT_CUSTOMREQUEST => 'POST',
  197. CURLOPT_POSTFIELDS => json_encode($this->postFields),
  198. CURLOPT_POST => 1,
  199. CURLOPT_HTTPHEADER => $this->getHttpHeader(),
  200. ]
  201. );
  202. return $curl;
  203. }
  204. /**
  205. *
  206. */
  207. private function base64url_encode($data)
  208. {
  209. return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
  210. }
  211. /**
  212. *
  213. */
  214. /*
  215. private function base64url_decode($data) {
  216. return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
  217. }
  218. */
  219. /**
  220. *
  221. */
  222. private function setPostFieldsTransactionData(): void
  223. {
  224. //$prices = $this->invoice->getRoundedTransactionPrices();
  225. $prices = $this->invoice->getTotalTransaction();
  226. $ticketsSold = $this->invoice->ntakSoldTickets();
  227. $paymentMethods = $this->invoice->ntakPaymentMethods();
  228. if (!empty($ticketsSold)) {
  229. $prices = array_sum(array_column($ticketsSold, 'bruttoAr'));
  230. array_walk($paymentMethods, function (&$item) use ($prices) {
  231. if (array_key_exists('fizetettOsszegHUF', $item) && $item['fizetesiMod'] == NtakConst::PAYMENT_METHOD_CASH_EUR) {
  232. $item['fizetettOsszegHUF'] = $prices;
  233. }
  234. });
  235. $this->postFields['tranzakcioAdatok'] = [
  236. [
  237. 'tranzakcioAzonosito' => $this->invoice->ref,
  238. 'tranzakcioIdopontja' => NtakDate::timestampToRfc3339($this->invoice->date_modification),
  239. 'osszesitett' => false,
  240. 'tranzakcioVegosszege' => $prices, //$prices['rounded'],
  241. 'ertekesitesiCsatorna' => NtakConst::SALES_CHANNEL_LOCAL, // ITT TISZTAZNI, HOGY MIT KELL A MOKUS ES A HOTEL ESETEN
  242. 'fizetesiModok' => $paymentMethods, //$this->invoice->ntakPaymentMethods(),
  243. 'latogatoiAdatok' => $this->invoice->ntakCustomerData(),
  244. 'eladottJegyek' => $ticketsSold, //$this->invoice->ntakSoldTickets(),
  245. 'egyebTetelek' => $this->invoice->ntakDiscount()
  246. ]
  247. ];
  248. }
  249. }
  250. /**
  251. *
  252. */
  253. private function setPostFieldsProgramData(): void
  254. {
  255. $this->postFields['programAdatok'] = $this->invoice->ntakProgramDetails();
  256. }
  257. /**
  258. *
  259. */
  260. private function initMessageData(): void
  261. {
  262. $this->postFields['uzenetAdatok'] = [
  263. 'uzenetKuldesIdeje' => $this->date
  264. ];
  265. }
  266. /**
  267. *
  268. */
  269. private function initSenderData(): void
  270. {
  271. $this->postFields['kuldoRendszerAdatok'] = [
  272. 'tssRendszerNTAKAzonosito' => $this->TSS_ID,
  273. 'tssRendszerVerzioszam' => $this->TSS_VERSION
  274. ];
  275. }
  276. /**
  277. *
  278. */
  279. private function initServiceProvider(): void
  280. {
  281. $this->postFields['szolgaltatoAdatok'] = [
  282. 'adoszam' => $this->TAX_NUMBER,
  283. 'szolgHelyRegisztraciosSzam' => $this->REGISTRATION_NUMBER
  284. ];
  285. }
  286. /**
  287. *
  288. */
  289. private function getHttpHeader(): array
  290. {
  291. $header = [
  292. "alg" => "RS256"
  293. ];
  294. $headerBase64URLencodedValue = 'eyJhbGciOiJSUzI1NiJ9';
  295. // CERT file
  296. $filenameCert = $this->certDir . self::CERT_B64;
  297. $handle = fopen($filenameCert, 'r');
  298. $certificateStr = fread($handle, filesize($filenameCert));
  299. fclose($handle);
  300. $headerBaseUrlEnc = json_encode($header);
  301. $token = $this->base64url_encode($headerBaseUrlEnc) . '.' . $this->base64url_encode(json_encode($this->postFields)); // a json adat maga
  302. // PEM file
  303. $filenameSslCert = $this->certDir . self::CERT_KEY;
  304. $fileContent = file_get_contents($filenameSslCert);
  305. $privateKey = openssl_pkey_get_private($fileContent);
  306. openssl_sign("$token", $signature, $privateKey, "sha256");
  307. $signatureBase64 = $this->base64url_encode($signature);
  308. $payloadBase64URLencodedValue = '';
  309. return [
  310. 'Content-Type: application/json',
  311. 'x-jws-signature: ' . $headerBase64URLencodedValue . '.' . $payloadBase64URLencodedValue . '.' . $signatureBase64,
  312. 'x-certificate: ' . $certificateStr
  313. ];
  314. }
  315. /**
  316. *
  317. */
  318. public function validate(int $ticketId, string $factureRef, string $ntakId, string $productRef, string $productDate, string $logDate, int $factureId): void
  319. {
  320. $this->ntakLogger->logIntoFile('Validate start', LOG_INFO);
  321. $this->date = gmdate(DATE_RFC3339, time());
  322. $this->initServiceProvider();
  323. $this->initSenderData();
  324. $this->initMessageData();
  325. $this->postFields['jegyek'] = [];
  326. $this->postFields['jegyek'][] = [
  327. 'ervenyesitesiAzonosito' => $ntakId,
  328. 'belepettSzemelyekSzama' => 1,
  329. 'ervenyesitesIdopontja' => NtakDate::timestampToRfc3339(strtotime($logDate)),
  330. 'felhasznalasModja' => NtakConst::TICKET_USE_METHOD_ENTRY,
  331. 'jegyAzonosito' => 'J-' . $factureRef,
  332. 'programazonosito' => [
  333. 'tssProgramAzonosito' => $productRef,
  334. 'utolsoModositasIdeje' => NtakDate::timestampToRfc3339(strtotime($productDate))
  335. ]
  336. ];
  337. $curl = $this->getCurlResource($this->NTAK_URL . NtakConfig::URL_VALIDATE);
  338. $response = curl_exec($curl);
  339. $logData = json_encode($this->postFields);
  340. if ($response) {
  341. $result = json_decode($response, true);
  342. if (array_key_exists('feldolgozasAzonosito', $result)) {
  343. $this->ntakLogger->logIntoFile("Validate sent successfully to NTAK (Ticket ID: {$ticketId})", LOG_INFO);
  344. } else {
  345. $this->ntakLogger->logIntoFile("Validate sent failed to NTAK (Ticket ID: {$ticketId})", LOG_ERR);
  346. $this->ntakLogger->logIntoFile($logData, LOG_ERR);
  347. $this->ntakLogger->logIntoFile($response, LOG_ERR);
  348. }
  349. } else {
  350. $this->ntakLogger->logIntoFile("Validate sent failed to NTAK (Ticket ID: {$ticketId})", LOG_ERR);
  351. $this->ntakLogger->logIntoFile($logData, LOG_ERR);
  352. }
  353. curl_close($curl);
  354. }
  355. /**
  356. *
  357. */
  358. public function checkWithLog(string $ntakProcessId, int $invoiceId): array
  359. {
  360. $this->ntakLogger->logIntoFile("Check with log start ({$ntakProcessId})", LOG_INFO);
  361. $resultStatus = NtakLog::STATUS_DRAFT;
  362. $checkResult = $this->check($ntakProcessId);
  363. if (!empty($checkResult['uzenetValaszok'][0]['statusz'])) {
  364. $resultStatus = NtakConst::getDbResponseStatusByStatus($checkResult['uzenetValaszok'][0]['statusz']);
  365. }
  366. $this->ntakLogger->logIntoFile("Check successfully at NTAK (NTAK ID: {$ntakProcessId})", LOG_INFO);
  367. $this->ntakLogger->logIntoDb($invoiceId, $resultStatus, '', '', $ntakProcessId);
  368. $this->ntakLogger->logIntoFile("Check with log end ({$ntakProcessId})", LOG_INFO);
  369. return $checkResult;
  370. }
  371. /**
  372. *
  373. */
  374. public function check(string $ntakProcessId): array
  375. {
  376. $result = [];
  377. $this->ntakLogger->logIntoFile("Check sent data ({$ntakProcessId})", LOG_INFO);
  378. $this->postFields = [];
  379. $this->date = gmdate(DATE_RFC3339, time());
  380. $this->initServiceProvider();
  381. $this->initSenderData();
  382. $this->initMessageData();
  383. $this->postFields['feldolgozasAzonositok'] = [
  384. $ntakProcessId
  385. ];
  386. $this->ntakLogger->logIntoFile(json_encode($this->postFields), LOG_INFO);
  387. $curl = $this->getCurlResource($this->NTAK_URL . NtakConfig::URL_CHECK);
  388. $response = curl_exec($curl);
  389. if ($response) {
  390. $result = json_decode($response, true);
  391. $this->ntakLogger->logIntoFile("Check invoice process at NTAK (NTAK process ID: {$ntakProcessId})", LOG_INFO);
  392. $this->ntakLogger->logIntoFile($response, LOG_INFO);
  393. } else {
  394. $this->ntakLogger->logIntoFile("Cannot check invoice process at NTAK (NTAK process ID: {$ntakProcessId})", LOG_ALERT);
  395. }
  396. curl_close($curl);
  397. return $result;
  398. }
  399. }