ntak_client.class.php_ORIG 16 KB

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