Files
EetCpp/libEet/eetv4client.cpp
T

134 lines
4.2 KiB
C++

#include "eetv4client.h"
#include "eetv4signer.h"
#include "eetv4soap.h"
#include <QEventLoop>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QTimer>
#include <QUuid>
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;
}