httpClient = $httpClient; $this->config = $config; $this->em = $em; $this->koinService = $koinService; } /** * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException * @throws \Doctrine\ORM\TransactionRequiredException * @throws \Exception */ public function pollRecent() { if (null !== ($pollResult = $this->getRecentXml())) { $this->parseResult($pollResult); } } /** * @return string * @throws \Exception */ private function getRecentXml(): ?string { $certificate = $this->getCertificate(); openssl_public_encrypt($this->config['szep.card'], $cryptCardId, $certificate, OPENSSL_PKCS1_PADDING); openssl_public_encrypt($this->config['szep.key'], $cryptCardKey, $certificate, OPENSSL_PKCS1_PADDING); $endDate = new \DateTimeImmutable(); $startDate = $endDate->sub(new \DateInterval('P4D')); $gueryTemplate = file_get_contents(self::TEMPLATE_QUERY_CARD); $query = sprintf($gueryTemplate, "X" . base64_encode($cryptCardId), base64_encode($cryptCardKey), $startDate->format("Y.m.d"), $endDate->format("Y.m.d") ); $soapXml = sprintf(file_get_contents(self::TEMPLATE_WORKFLOW), $this->encryptPayload($query)); try{ $soapResponseXml = $this->doSoapRequest($soapXml); } catch (\Exception $e) { return null; } $domDocument = new \DOMDocument(); $domDocument->loadXML($soapResponseXml); $documentXpath = new \DOMXPath($domDocument); /** @var \DOMElement $returnElement */ $returnElement = $documentXpath->query('//return/result')->item(0); return base64_decode($returnElement->textContent); } /** * Parse the decoded payload * @param string $resultXml * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException * @throws \Doctrine\ORM\TransactionRequiredException */ private function parseResult(string $resultXml) { $domDocument = new \DOMDocument(); $domDocument->loadXML($resultXml); $documentXpath = new \DOMXPath($domDocument); /** @var \DOMElement[] $returnElements */ $recordElements = $documentXpath->query('/answer/resultset/record'); $newRecords = []; /** @var \DOMElement $element */ foreach ($recordElements as $element) { $date = trim($documentXpath->query('./datum', $element)->item(0)->textContent); $amount = trim($documentXpath->query('./osszeg', $element)->item(0)->textContent); $merchant = trim($documentXpath->query('./ellenoldali_nev', $element)->item(0)->textContent); $pocket = trim($documentXpath->query('./alszamla', $element)->item(0)->textContent); $hash = md5("$date#$amount#$merchant#$pocket"); if (null != $this->em->find(SZEPCardEntry::class, $hash)) { continue; } $szepCardEntity = new SZEPCardEntry(); $szepCardEntity->setHash($hash) ->setAmount($amount) ->setMerchant($merchant) ->setPocket($pocket) ->setDate(new \DateTimeImmutable(str_replace(".", "-", $date))); $newRecords[] = $szepCardEntity; } /** @var SZEPCardEntry $newRecord */ foreach ($newRecords as $newRecord) { $resultCode = $this->koinService->saveTransaction( abs($newRecord->getAmount()), 'HUF', $newRecord->getDate()->format("Y-m-d"), $this->getCategory($newRecord), $this->getTags($newRecord) ); if ($resultCode > 199 && $resultCode < 300) { $this->em->persist($newRecord); } } $this->em->flush(); } private function getCategory(SZEPCardEntry $SZEPCardEntry): string { return ($SZEPCardEntry->getAmount() > 0) ? KoinService::CATEGORY_PAYMENT : ( $SZEPCardEntry->getPocket() == self::POCKET_FOOD ? KoinService::CATEGORY_FOOD : KoinService::DEFAULT_EXPENSE ); } private function getTags(SZEPCardEntry $SZEPCardEntry): array { $tags = []; if (false !== strpos($SZEPCardEntry->getMerchant(), "Sigma Technology")) { $tags[] = 'Sigma'; $tags[] = 'Cafeteria'; } elseif (false !== strpos($SZEPCardEntry->getMerchant(), "Ericsson Ház Étterem")) { $tags[] = 'Ericsson Ház'; $tags[] = 'Menza'; } elseif (false !== strpos($SZEPCardEntry->getMerchant(), "Planet Sushi Alle")) { $tags[] = 'Planet Sushi'; $tags[] = 'Sushi'; } elseif (false !== strpos($SZEPCardEntry->getMerchant(), "Stoczek utcai Étterem")) { $tags[] = 'Stoczek'; $tags[] = 'Street Food'; } elseif (false !== strpos($SZEPCardEntry->getMerchant(), "Stoczek")) { $tags[] = 'Stoczek'; } elseif (false !== strpos($SZEPCardEntry->getMerchant(), "Science Park")) { $tags[] = 'Science Park'; $tags[] = 'Menza'; } elseif (false !== strpos($SZEPCardEntry->getMerchant(), "Cafe Park")) { $tags[] = 'Cafe Park'; $tags[] = 'Menza'; } elseif (false !== strpos($SZEPCardEntry->getMerchant(), "Wikinger Gyorsétterem")) { $tags[] = 'Wikinger'; } elseif (false !== strpos($SZEPCardEntry->getMerchant(), "Wasabi Étterem")) { $tags[] = 'Wasabi'; $tags[] = 'Sushi'; } elseif (false !== strpos($SZEPCardEntry->getMerchant(), "Szentmihályi Uszoda")) { $tags[] = 'Tope'; } switch ($SZEPCardEntry->getPocket()) { case self::POCKET_FOOD: $tags[] = self::TAG_POCKET_FOOD; break; case self::POCKET_SPORT: $tags[] = self::TAG_POCKET_SPORT; break; } $tags[] = 'Auto'; return $tags; } /** * Returns AES encrypted base64 encoded $payload * @param string $payload * @param array $key * @param array $iv * @return string */ private function encryptPayload(string $payload, $key = self::AES_KEY, $iv = self::IV_PARAM): string { $aesKeyString = call_user_func_array("pack", array_merge(array("c*"), $key)); $ivParamStr = call_user_func_array("pack", array_merge(array("c*"), $iv)); return openssl_encrypt($payload, "AES-256-CBC", $aesKeyString, 0, $ivParamStr); } /** * @param string $soapXml * @return string */ private function doSoapRequest(string $soapXml): string { $response = $this->httpClient->post(self::SOAP_ENDPOINT, [ 'body' => $soapXml, ]); return $response->getBody()->getContents(); } /** * Returns the cached public certificate * @return string */ private function getCertificate(): string { if (file_exists(self::CERTIFICATE_CACHED_PATH)) { return file_get_contents(self::CERTIFICATE_CACHED_PATH); } $cert = file_get_contents(self::CERTIFICATE_WEB_PATH); file_put_contents(self::CERTIFICATE_CACHED_PATH, $cert); return $cert; } }