134 lines
4.2 KiB
C++
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;
|
|
}
|