#include "eetv4client.h" #include "eetv4signer.h" #include "eetv4soap.h" #include #include #include #include #include #include namespace { void setError(QString *error, const QString &message) { if (error != nullptr) { *error = message; } } } EetV4Client::EetV4Client(EetV4ClientConfig config) : m_config(std::move(config)) { } bool EetV4Client::sendSale(const EetV4SaleRequest &request, const QString &pkcs12Path, const QByteArray &pkcs12Password, EetV4Response *response, QString *error) const { if (response == nullptr) { setError(error, QStringLiteral("A response output object is required.")); return false; } *response = {}; if (!m_config.endpoint.isValid() || m_config.endpoint.scheme() != QLatin1String("https") || m_config.endpoint.host().isEmpty()) { setError(error, QStringLiteral("EET endpoint must be a valid HTTPS URL.")); return false; } if (m_config.timeoutMilliseconds <= 0) { setError(error, QStringLiteral("EET timeout must be positive.")); return false; } const QByteArray bodyId = QByteArrayLiteral("id-") + request.messageUuid.toString(QUuid::WithoutBraces).toLatin1(); QString soapError; const QByteArray unsignedSoap = EetV4Soap::serializeUnsigned(request, bodyId, &soapError); if (unsignedSoap.isEmpty()) { setError(error, soapError); return false; } QString signingError; const QByteArray signedSoap = EetV4Signer::signSoap( unsignedSoap, pkcs12Path, pkcs12Password, &signingError); if (signedSoap.isEmpty()) { setError(error, signingError); return false; } QNetworkAccessManager networkAccessManager; QNetworkRequest networkRequest(m_config.endpoint); if (!m_config.sslConfiguration.isNull()) { networkRequest.setSslConfiguration(m_config.sslConfiguration); } networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/xml; charset=utf-8")); networkRequest.setRawHeader("Accept", "text/xml"); QNetworkReply *reply = networkAccessManager.post(networkRequest, signedSoap); QEventLoop eventLoop; QTimer timeout; timeout.setSingleShot(true); bool timedOut = false; QObject::connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit); QObject::connect(&timeout, &QTimer::timeout, reply, [&] { timedOut = true; reply->abort(); }); timeout.start(m_config.timeoutMilliseconds); eventLoop.exec(); timeout.stop(); if (timedOut) { setError(error, QStringLiteral("EET request timed out.")); reply->deleteLater(); return false; } if (reply->error() != QNetworkReply::NoError) { setError(error, reply->errorString()); reply->deleteLater(); return false; } const QByteArray responseSoap = reply->readAll(); const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); reply->deleteLater(); QString verificationError; if (!EetV4Signer::verifySoap(responseSoap, &verificationError)) { setError(error, statusCode >= 300 ? QStringLiteral("EET endpoint returned HTTP status %1 and an invalid " "SOAP signature: %2.") .arg(statusCode) .arg(verificationError) : verificationError); return false; } QString responseError; if (!EetV4ResponseParser::parseSoap(responseSoap, response, &responseError)) { setError(error, statusCode >= 300 ? QStringLiteral("EET endpoint returned HTTP status %1: %2.") .arg(statusCode) .arg(responseError) : responseError); return false; } return true; }