mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-19 07:37:11 +02:00
Port required parts of Stripe SDK to lib_stripe.
This commit is contained in:
parent
994dbf9eb5
commit
76b4e18518
21 changed files with 987 additions and 0 deletions
|
@ -27,6 +27,7 @@ get_filename_component(res_loc Resources REALPATH)
|
|||
|
||||
include(cmake/telegram_options.cmake)
|
||||
include(cmake/lib_ffmpeg.cmake)
|
||||
include(cmake/lib_stripe.cmake)
|
||||
include(cmake/lib_tgvoip.cmake)
|
||||
include(cmake/lib_tgcalls.cmake)
|
||||
include(cmake/td_export.cmake)
|
||||
|
@ -58,6 +59,7 @@ PRIVATE
|
|||
desktop-app::lib_qr
|
||||
desktop-app::lib_webview
|
||||
desktop-app::lib_ffmpeg
|
||||
desktop-app::lib_stripe
|
||||
desktop-app::external_lz4
|
||||
desktop-app::external_rlottie
|
||||
desktop-app::external_zlib
|
||||
|
|
18
Telegram/SourceFiles/payments/stripe/stripe_address.h
Normal file
18
Telegram/SourceFiles/payments/stripe/stripe_address.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
enum class BillingAddressFields {
|
||||
None,
|
||||
Zip,
|
||||
Full,
|
||||
};
|
||||
|
||||
} // namespace Stripe
|
167
Telegram/SourceFiles/payments/stripe/stripe_api_client.cpp
Normal file
167
Telegram/SourceFiles/payments/stripe/stripe_api_client.cpp
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "stripe/stripe_api_client.h"
|
||||
|
||||
#include "stripe/stripe_error.h"
|
||||
#include "stripe/stripe_token.h"
|
||||
#include "stripe/stripe_form_encoder.h"
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <crl/crl_on_main.h>
|
||||
|
||||
namespace Stripe {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QString APIURLBase() {
|
||||
return "api.stripe.com/v1";
|
||||
}
|
||||
|
||||
[[nodiscard]] QString TokenEndpoint() {
|
||||
return "tokens";
|
||||
}
|
||||
|
||||
[[nodiscard]] QString StripeAPIVersion() {
|
||||
return "2015-10-12";
|
||||
}
|
||||
|
||||
[[nodiscard]] QString SDKVersion() {
|
||||
return "9.1.0";
|
||||
}
|
||||
|
||||
[[nodiscard]] QString StripeUserAgentDetails() {
|
||||
const auto details = QJsonObject{
|
||||
{ "lang", "objective-c" },
|
||||
{ "bindings_version", SDKVersion() },
|
||||
};
|
||||
return QString::fromUtf8(
|
||||
QJsonDocument(details).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
APIClient::APIClient(PaymentConfiguration configuration)
|
||||
: _apiUrl("https://" + APIURLBase())
|
||||
, _configuration(configuration) {
|
||||
_additionalHttpHeaders = {
|
||||
{ "X-Stripe-User-Agent", StripeUserAgentDetails() },
|
||||
{ "Stripe-Version", StripeAPIVersion() },
|
||||
{ "Authorization", "Bearer " + _configuration.publishableKey },
|
||||
};
|
||||
}
|
||||
|
||||
APIClient::~APIClient() {
|
||||
const auto destroy = std::move(_old);
|
||||
}
|
||||
|
||||
void APIClient::createTokenWithCard(
|
||||
CardParams card,
|
||||
TokenCompletionCallback completion) {
|
||||
createTokenWithData(
|
||||
FormEncoder::formEncodedDataForObject(card),
|
||||
std::move(completion));
|
||||
}
|
||||
|
||||
void APIClient::createTokenWithData(
|
||||
QByteArray data,
|
||||
TokenCompletionCallback completion) {
|
||||
const auto url = QUrl(_apiUrl + '/' + TokenEndpoint());
|
||||
auto request = QNetworkRequest(url);
|
||||
for (const auto &[name, value] : _additionalHttpHeaders) {
|
||||
request.setRawHeader(name.toUtf8(), value.toUtf8());
|
||||
}
|
||||
destroyReplyDelayed(std::move(_reply));
|
||||
_reply.reset(_manager.post(request, data));
|
||||
const auto finish = [=](Token token, Error error) {
|
||||
crl::on_main([
|
||||
completion,
|
||||
token = std::move(token),
|
||||
error = std::move(error)
|
||||
] {
|
||||
completion(std::move(token), std::move(error));
|
||||
});
|
||||
};
|
||||
const auto finishWithError = [=](Error error) {
|
||||
finish(Token::Empty(), std::move(error));
|
||||
};
|
||||
const auto finishWithToken = [=](Token token) {
|
||||
finish(std::move(token), Error::None());
|
||||
};
|
||||
QObject::connect(_reply.get(), &QNetworkReply::finished, [=] {
|
||||
const auto replyError = int(_reply->error());
|
||||
const auto replyErrorString = _reply->errorString();
|
||||
const auto bytes = _reply->readAll();
|
||||
destroyReplyDelayed(std::move(_reply));
|
||||
|
||||
auto parseError = QJsonParseError();
|
||||
const auto document = QJsonDocument::fromJson(bytes, &parseError);
|
||||
if (!bytes.isEmpty()) {
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
const auto code = int(parseError.error);
|
||||
finishWithError({
|
||||
Error::Code::JsonParse,
|
||||
QString("InvalidJson%1").arg(code),
|
||||
parseError.errorString(),
|
||||
});
|
||||
return;
|
||||
} else if (!document.isObject()) {
|
||||
finishWithError({
|
||||
Error::Code::JsonFormat,
|
||||
"InvalidJsonRoot",
|
||||
"Not an object in JSON reply.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const auto object = document.object();
|
||||
if (auto error = Error::DecodedObjectFromResponse(object)) {
|
||||
finishWithError(std::move(error));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (replyError != QNetworkReply::NoError) {
|
||||
finishWithError({
|
||||
Error::Code::Network,
|
||||
QString("RequestError%1").arg(replyError),
|
||||
replyErrorString,
|
||||
});
|
||||
return;
|
||||
}
|
||||
auto token = Token::DecodedObjectFromAPIResponse(document.object());
|
||||
if (!token) {
|
||||
finishWithError({
|
||||
Error::Code::JsonFormat,
|
||||
"InvalidTokenJson",
|
||||
"Could not parse token.",
|
||||
});
|
||||
}
|
||||
finishWithToken(std::move(token));
|
||||
});
|
||||
}
|
||||
|
||||
void APIClient::destroyReplyDelayed(std::unique_ptr<QNetworkReply> reply) {
|
||||
if (!reply) {
|
||||
return;
|
||||
}
|
||||
const auto raw = reply.get();
|
||||
_old.push_back(std::move(reply));
|
||||
QObject::disconnect(raw, &QNetworkReply::finished, nullptr, nullptr);
|
||||
raw->deleteLater();
|
||||
QObject::connect(raw, &QObject::destroyed, [=] {
|
||||
for (auto i = begin(_old); i != end(_old); ++i) {
|
||||
if (i->get() == raw) {
|
||||
i->release();
|
||||
_old.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Stripe
|
44
Telegram/SourceFiles/payments/stripe/stripe_api_client.h
Normal file
44
Telegram/SourceFiles/payments/stripe/stripe_api_client.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "stripe/stripe_payment_configuration.h"
|
||||
#include "stripe/stripe_card_params.h"
|
||||
#include "stripe/stripe_callbacks.h"
|
||||
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtCore/QString>
|
||||
#include <map>
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
class APIClient final {
|
||||
public:
|
||||
explicit APIClient(PaymentConfiguration configuration);
|
||||
~APIClient();
|
||||
|
||||
void createTokenWithCard(
|
||||
CardParams card,
|
||||
TokenCompletionCallback completion);
|
||||
void createTokenWithData(
|
||||
QByteArray data,
|
||||
TokenCompletionCallback completion);
|
||||
|
||||
private:
|
||||
void destroyReplyDelayed(std::unique_ptr<QNetworkReply> reply);
|
||||
|
||||
QString _apiUrl;
|
||||
PaymentConfiguration _configuration;
|
||||
std::map<QString, QString> _additionalHttpHeaders;
|
||||
QNetworkAccessManager _manager;
|
||||
std::unique_ptr<QNetworkReply> _reply;
|
||||
std::vector<std::unique_ptr<QNetworkReply>> _old;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Stripe
|
19
Telegram/SourceFiles/payments/stripe/stripe_callbacks.h
Normal file
19
Telegram/SourceFiles/payments/stripe/stripe_callbacks.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
class Error;
|
||||
class Token;
|
||||
|
||||
using TokenCompletionCallback = std::function<void(Token, Error)>;
|
||||
|
||||
} // namespace Stripe
|
117
Telegram/SourceFiles/payments/stripe/stripe_card.cpp
Normal file
117
Telegram/SourceFiles/payments/stripe/stripe_card.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "stripe/stripe_card.h"
|
||||
|
||||
#include "stripe/stripe_decode.h"
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
Card::Card(
|
||||
QString id,
|
||||
QString last4,
|
||||
CardBrand brand,
|
||||
quint32 expMonth,
|
||||
quint32 expYear)
|
||||
: _cardId(id)
|
||||
, _last4(last4)
|
||||
, _brand(brand)
|
||||
, _expMonth(expMonth)
|
||||
, _expYear(expYear) {
|
||||
}
|
||||
|
||||
Card Card::Empty() {
|
||||
return Card(QString(), QString(), CardBrand::Unknown, 0, 0);
|
||||
}
|
||||
|
||||
[[nodiscard]] CardBrand BrandFromString(const QString &brand) {
|
||||
if (brand == "visa") {
|
||||
return CardBrand::Visa;
|
||||
} else if (brand == "american express") {
|
||||
return CardBrand::Amex;
|
||||
} else if (brand == "mastercard") {
|
||||
return CardBrand::MasterCard;
|
||||
} else if (brand == "discover") {
|
||||
return CardBrand::Discover;
|
||||
} else if (brand == "jcb") {
|
||||
return CardBrand::JCB;
|
||||
} else if (brand == "diners club") {
|
||||
return CardBrand::DinersClub;
|
||||
} else {
|
||||
return CardBrand::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] CardFundingType FundingFromString(const QString &funding) {
|
||||
if (funding == "credit") {
|
||||
return CardFundingType::Credit;
|
||||
} else if (funding == "debit") {
|
||||
return CardFundingType::Debit;
|
||||
} else if (funding == "prepaid") {
|
||||
return CardFundingType::Prepaid;
|
||||
} else {
|
||||
return CardFundingType::Other;
|
||||
}
|
||||
}
|
||||
|
||||
Card Card::DecodedObjectFromAPIResponse(QJsonObject object) {
|
||||
if (!ContainsFields(object, {
|
||||
u"id",
|
||||
u"last4",
|
||||
u"brand",
|
||||
u"exp_month",
|
||||
u"exp_year"
|
||||
})) {
|
||||
return Card::Empty();
|
||||
}
|
||||
|
||||
const auto string = [&](QStringView key) {
|
||||
return object.value(key).toString();
|
||||
};
|
||||
const auto cardId = string(u"id");
|
||||
const auto last4 = string(u"last4");
|
||||
const auto brand = BrandFromString(string(u"brand").toLower());
|
||||
const auto expMonth = object.value("exp_month").toInt();
|
||||
const auto expYear = object.value("exp_year").toInt();
|
||||
auto result = Card(cardId, last4, brand, expMonth, expYear);
|
||||
result._name = string(u"name");
|
||||
result._dynamicLast4 = string(u"dynamic_last4");
|
||||
result._funding = FundingFromString(string(u"funding"));
|
||||
result._fingerprint = string(u"fingerprint");
|
||||
result._country = string(u"country");
|
||||
result._currency = string(u"currency");
|
||||
result._addressLine1 = string(u"address_line1");
|
||||
result._addressLine2 = string(u"address_line2");
|
||||
result._addressCity = string(u"address_city");
|
||||
result._addressState = string(u"address_state");
|
||||
result._addressZip = string(u"address_zip");
|
||||
result._addressCountry = string(u"address_country");
|
||||
|
||||
// TODO incomplete, not used.
|
||||
//result._allResponseFields = object;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Card::empty() const {
|
||||
return _cardId.isEmpty();
|
||||
}
|
||||
|
||||
QString CardBrandToString(CardBrand brand) {
|
||||
switch (brand) {
|
||||
case CardBrand::Amex: return "American Express";
|
||||
case CardBrand::DinersClub: return "Diners Club";
|
||||
case CardBrand::Discover: return "Discover";
|
||||
case CardBrand::JCB: return "JCB";
|
||||
case CardBrand::MasterCard: return "MasterCard";
|
||||
case CardBrand::Unknown: return "Unknown";
|
||||
case CardBrand::Visa: return "Visa";
|
||||
}
|
||||
std::abort();
|
||||
}
|
||||
|
||||
} // namespace Stripe
|
80
Telegram/SourceFiles/payments/stripe/stripe_card.h
Normal file
80
Telegram/SourceFiles/payments/stripe/stripe_card.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
enum class CardBrand {
|
||||
Visa,
|
||||
Amex,
|
||||
MasterCard,
|
||||
Discover,
|
||||
JCB,
|
||||
DinersClub,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
enum class CardFundingType {
|
||||
Debit,
|
||||
Credit,
|
||||
Prepaid,
|
||||
Other,
|
||||
};
|
||||
|
||||
class Card final {
|
||||
public:
|
||||
Card(const Card &other) = default;
|
||||
Card &operator=(const Card &other) = default;
|
||||
Card(Card &&other) = default;
|
||||
Card &operator=(Card &&other) = default;
|
||||
~Card() = default;
|
||||
|
||||
[[nodiscard]] static Card Empty();
|
||||
[[nodiscard]] static Card DecodedObjectFromAPIResponse(
|
||||
QJsonObject object);
|
||||
|
||||
[[nodiscard]] bool empty() const;
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return !empty();
|
||||
}
|
||||
|
||||
private:
|
||||
Card(
|
||||
QString id,
|
||||
QString last4,
|
||||
CardBrand brand,
|
||||
quint32 expMonth,
|
||||
quint32 expYear);
|
||||
|
||||
QString _cardId;
|
||||
QString _name;
|
||||
QString _last4;
|
||||
QString _dynamicLast4;
|
||||
CardBrand _brand = CardBrand::Unknown;
|
||||
CardFundingType _funding = CardFundingType::Other;
|
||||
QString _fingerprint;
|
||||
QString _country;
|
||||
QString _currency;
|
||||
quint32 _expMonth = 0;
|
||||
quint32 _expYear = 0;
|
||||
QString _addressLine1;
|
||||
QString _addressLine2;
|
||||
QString _addressCity;
|
||||
QString _addressState;
|
||||
QString _addressZip;
|
||||
QString _addressCountry;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] QString CardBrandToString(CardBrand brand);
|
||||
|
||||
} // namespace Stripe
|
33
Telegram/SourceFiles/payments/stripe/stripe_card_params.cpp
Normal file
33
Telegram/SourceFiles/payments/stripe/stripe_card_params.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "stripe/stripe_card_params.h"
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
QString CardParams::RootObjectName() const {
|
||||
return "card";
|
||||
}
|
||||
|
||||
std::map<QString, QString> CardParams::formFieldValues() const {
|
||||
return {
|
||||
{ "number", number },
|
||||
{ "cvc", cvc },
|
||||
{ "name", name },
|
||||
{ "address_line1", addressLine1 },
|
||||
{ "address_line2", addressLine2 },
|
||||
{ "address_city", addressCity },
|
||||
{ "address_state", addressState },
|
||||
{ "address_zip", addressZip },
|
||||
{ "address_country", addressCountry },
|
||||
{ "exp_month", QString::number(expMonth) },
|
||||
{ "exp_year", QString::number(expYear) },
|
||||
{ "currency", currency },
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Stripe
|
33
Telegram/SourceFiles/payments/stripe/stripe_card_params.h
Normal file
33
Telegram/SourceFiles/payments/stripe/stripe_card_params.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "stripe/stripe_form_encodable.h"
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
class CardParams final : public FormEncodable {
|
||||
public:
|
||||
QString RootObjectName() const override;
|
||||
std::map<QString, QString> formFieldValues() const override;
|
||||
|
||||
QString number;
|
||||
quint32 expMonth = 0;
|
||||
quint32 expYear = 0;
|
||||
QString cvc;
|
||||
QString name;
|
||||
QString addressLine1;
|
||||
QString addressLine2;
|
||||
QString addressCity;
|
||||
QString addressState;
|
||||
QString addressZip;
|
||||
QString addressCountry;
|
||||
QString currency;
|
||||
};
|
||||
|
||||
} // namespace Stripe
|
23
Telegram/SourceFiles/payments/stripe/stripe_decode.cpp
Normal file
23
Telegram/SourceFiles/payments/stripe/stripe_decode.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "stripe/stripe_decode.h"
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
[[nodiscard]] bool ContainsFields(
|
||||
const QJsonObject &object,
|
||||
std::vector<QStringView> keys) {
|
||||
for (const auto &key : keys) {
|
||||
if (object.value(key).isUndefined()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Stripe
|
18
Telegram/SourceFiles/payments/stripe/stripe_decode.h
Normal file
18
Telegram/SourceFiles/payments/stripe/stripe_decode.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
[[nodiscard]] bool ContainsFields(
|
||||
const QJsonObject &object,
|
||||
std::vector<QStringView> keys);
|
||||
|
||||
} // namespace Stripe
|
83
Telegram/SourceFiles/payments/stripe/stripe_error.cpp
Normal file
83
Telegram/SourceFiles/payments/stripe/stripe_error.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "stripe/stripe_error.h"
|
||||
|
||||
#include "stripe/stripe_decode.h"
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
Error Error::DecodedObjectFromResponse(QJsonObject object) {
|
||||
const auto entry = object.value("error");
|
||||
if (!entry.isObject()) {
|
||||
return Error::None();
|
||||
}
|
||||
const auto error = entry.toObject();
|
||||
const auto string = [&](QStringView key) {
|
||||
return error.value(key).toString();
|
||||
};
|
||||
const auto type = string(u"type");
|
||||
const auto message = string(u"message");
|
||||
const auto parameterSnakeCase = string(u"param");
|
||||
|
||||
// There should always be a message and type for the error
|
||||
if (message.isEmpty() || type.isEmpty()) {
|
||||
return {
|
||||
Code::API,
|
||||
"GenericError",
|
||||
"Could not interpret the error response "
|
||||
"that was returned from Stripe."
|
||||
};
|
||||
}
|
||||
|
||||
auto parameterWords = parameterSnakeCase.isEmpty()
|
||||
? QStringList()
|
||||
: parameterSnakeCase.split('_', Qt::SkipEmptyParts);
|
||||
auto first = true;
|
||||
for (auto &word : parameterWords) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
word = word[0].toUpper() + word.midRef(1);
|
||||
}
|
||||
}
|
||||
const auto parameter = parameterWords.join(QString());
|
||||
if (type == "api_error") {
|
||||
return { Code::API, "GenericError", message, parameter };
|
||||
} else if (type == "invalid_request_error") {
|
||||
return { Code::InvalidRequest, "GenericError", message, parameter };
|
||||
} else if (type != "card_error") {
|
||||
return { Code::Unknown, type, message, parameter };
|
||||
}
|
||||
const auto code = string(u"code");
|
||||
const auto cardError = [&](const QString &description) {
|
||||
return Error{ Code::Card, description, message, parameter };
|
||||
};
|
||||
if (code == "incorrect_number") {
|
||||
return cardError("IncorrectNumber");
|
||||
} else if (code == "invalid_number") {
|
||||
return cardError("InvalidNumber");
|
||||
} else if (code == "invalid_expiry_month") {
|
||||
return cardError("InvalidExpiryMonth");
|
||||
} else if (code == "invalid_expiry_year") {
|
||||
return cardError("InvalidExpiryYear");
|
||||
} else if (code == "invalid_cvc") {
|
||||
return cardError("InvalidCVC");
|
||||
} else if (code == "expired_card") {
|
||||
return cardError("ExpiredCard");
|
||||
} else if (code == "incorrect_cvc") {
|
||||
return cardError("IncorrectCVC");
|
||||
} else if (code == "card_declined") {
|
||||
return cardError("CardDeclined");
|
||||
} else if (code == "processing_error") {
|
||||
return cardError("ProcessingError");
|
||||
} else {
|
||||
return cardError(code);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Stripe
|
65
Telegram/SourceFiles/payments/stripe/stripe_error.h
Normal file
65
Telegram/SourceFiles/payments/stripe/stripe_error.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
class Error {
|
||||
public:
|
||||
enum class Code {
|
||||
None = 0, // Non-Stripe errors.
|
||||
JsonParse = -1,
|
||||
JsonFormat = -2,
|
||||
Network = -3,
|
||||
|
||||
Unknown = 8,
|
||||
Connection = 40, // Trouble connecting to Stripe.
|
||||
InvalidRequest = 50, // Your request had invalid parameters.
|
||||
API = 60, // General-purpose API error (should be rare).
|
||||
Card = 70, // Something was wrong with the given card (most common).
|
||||
Cancellation = 80, // The operation was cancelled.
|
||||
CheckoutUnknown = 5000, // Checkout failed
|
||||
CheckoutTooManyAttempts = 5001, // Too many incorrect code attempts
|
||||
};
|
||||
|
||||
Error(
|
||||
Code code,
|
||||
const QString &description,
|
||||
const QString &message,
|
||||
const QString ¶meter = QString())
|
||||
: _code(code)
|
||||
, _description(description)
|
||||
, _message(message)
|
||||
, _parameter(parameter) {
|
||||
}
|
||||
|
||||
[[nodiscard]] static Error None() {
|
||||
return Error(Code::None, QString(), QString());
|
||||
}
|
||||
[[nodiscard]] static Error DecodedObjectFromResponse(QJsonObject object);
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
return (_code == Code::None);
|
||||
}
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return !empty();
|
||||
}
|
||||
|
||||
private:
|
||||
Code _code = Code::None;
|
||||
QString _description;
|
||||
QString _message;
|
||||
QString _parameter;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Stripe
|
24
Telegram/SourceFiles/payments/stripe/stripe_form_encodable.h
Normal file
24
Telegram/SourceFiles/payments/stripe/stripe_form_encodable.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <map>
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
class FormEncodable {
|
||||
public:
|
||||
[[nodiscard]] virtual QString RootObjectName() const = 0;
|
||||
|
||||
// TODO incomplete, not used: nested complex structures not supported.
|
||||
[[nodiscard]] virtual std::map<QString, QString> formFieldValues() const
|
||||
= 0;
|
||||
};
|
||||
|
||||
} // namespace Stripe
|
40
Telegram/SourceFiles/payments/stripe/stripe_form_encoder.cpp
Normal file
40
Telegram/SourceFiles/payments/stripe/stripe_form_encoder.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "stripe/stripe_form_encoder.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
QByteArray FormEncoder::formEncodedDataForObject(
|
||||
FormEncodable &object) {
|
||||
const auto root = object.RootObjectName();
|
||||
const auto values = object.formFieldValues();
|
||||
auto result = QByteArray();
|
||||
auto keys = std::vector<QString>();
|
||||
for (const auto &[key, value] : values) {
|
||||
if (!value.isEmpty()) {
|
||||
keys.push_back(key);
|
||||
}
|
||||
}
|
||||
std::sort(begin(keys), end(keys));
|
||||
const auto encode = [](const QString &value) {
|
||||
return QUrl::toPercentEncoding(value);
|
||||
};
|
||||
for (const auto &key : keys) {
|
||||
const auto fullKey = root.isEmpty() ? key : (root + '[' + key + ']');
|
||||
if (!result.isEmpty()) {
|
||||
result += '&';
|
||||
}
|
||||
result += encode(fullKey) + '=' + encode(values.at(key));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Stripe
|
21
Telegram/SourceFiles/payments/stripe/stripe_form_encoder.h
Normal file
21
Telegram/SourceFiles/payments/stripe/stripe_form_encoder.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "stripe/stripe_form_encodable.h"
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
class FormEncoder {
|
||||
public:
|
||||
[[nodiscard]] static QByteArray formEncodedDataForObject(
|
||||
FormEncodable &object);
|
||||
|
||||
};
|
||||
|
||||
} // namespace Stripe
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "stripe/stripe_address.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
struct PaymentConfiguration {
|
||||
QString publishableKey;
|
||||
// PaymentMethodType additionalPaymentMethods; // Apply Pay
|
||||
BillingAddressFields requiredBillingAddressFields
|
||||
= BillingAddressFields::None;
|
||||
QString companyName;
|
||||
// QString appleMerchantIdentifier; // Apple Pay
|
||||
// bool smsAutofillDisabled = true; // Mobile only
|
||||
};
|
||||
|
||||
} // namespace Stripe
|
13
Telegram/SourceFiles/payments/stripe/stripe_pch.h
Normal file
13
Telegram/SourceFiles/payments/stripe/stripe_pch.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonValue>
|
||||
#include <QtCore/QJsonDocument>
|
65
Telegram/SourceFiles/payments/stripe/stripe_token.cpp
Normal file
65
Telegram/SourceFiles/payments/stripe/stripe_token.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "stripe/stripe_token.h"
|
||||
|
||||
#include "stripe/stripe_decode.h"
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
QString Token::tokenId() const {
|
||||
return _tokenId;
|
||||
}
|
||||
|
||||
bool Token::livemode() const {
|
||||
return _livemode;
|
||||
}
|
||||
|
||||
Card Token::card() const {
|
||||
return _card;
|
||||
}
|
||||
|
||||
Token Token::Empty() {
|
||||
return Token(QString(), false, QDateTime());
|
||||
}
|
||||
|
||||
Token Token::DecodedObjectFromAPIResponse(QJsonObject object) {
|
||||
if (!ContainsFields(object, { u"id", u"livemode", u"created" })) {
|
||||
return Token::Empty();
|
||||
}
|
||||
const auto tokenId = object.value("id").toString();
|
||||
const auto livemode = object.value("livemode").toBool();
|
||||
const auto created = QDateTime::fromTime_t(
|
||||
object.value("created").toDouble());
|
||||
auto result = Token(tokenId, livemode, created);
|
||||
const auto card = object.value("card");
|
||||
if (card.isObject()) {
|
||||
result._card = Card::DecodedObjectFromAPIResponse(card.toObject());
|
||||
}
|
||||
|
||||
// TODO incomplete, not used.
|
||||
//const auto bankAccount = object.value("bank_account");
|
||||
//if (bankAccount.isObject()) {
|
||||
// result._bankAccount = bankAccount::DecodedObjectFromAPIResponse(
|
||||
// bankAccount.toObject());
|
||||
//}
|
||||
//result._allResponseFields = object;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Token::empty() const {
|
||||
return _tokenId.isEmpty();
|
||||
}
|
||||
|
||||
Token::Token(QString tokenId, bool livemode, QDateTime created)
|
||||
: _tokenId(std::move(tokenId))
|
||||
, _livemode(livemode)
|
||||
, _created(std::move(created)) {
|
||||
}
|
||||
|
||||
} // namespace Stripe
|
49
Telegram/SourceFiles/payments/stripe/stripe_token.h
Normal file
49
Telegram/SourceFiles/payments/stripe/stripe_token.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "stripe/stripe_card.h"
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
namespace Stripe {
|
||||
|
||||
class Token {
|
||||
public:
|
||||
Token(const Token &other) = default;
|
||||
Token &operator=(const Token &other) = default;
|
||||
Token(Token &&other) = default;
|
||||
Token &operator=(Token &&other) = default;
|
||||
~Token() = default;
|
||||
|
||||
[[nodiscard]] QString tokenId() const;
|
||||
[[nodiscard]] bool livemode() const;
|
||||
[[nodiscard]] Card card() const;
|
||||
|
||||
[[nodiscard]] static Token Empty();
|
||||
[[nodiscard]] static Token DecodedObjectFromAPIResponse(
|
||||
QJsonObject object);
|
||||
|
||||
[[nodiscard]] bool empty() const;
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return !empty();
|
||||
}
|
||||
|
||||
private:
|
||||
Token(QString tokenId, bool livemode, QDateTime created);
|
||||
|
||||
QString _tokenId;
|
||||
bool _livemode = false;
|
||||
QDateTime _created;
|
||||
Card _card = Card::Empty();
|
||||
|
||||
};
|
||||
|
||||
} // namespace Stripe
|
47
Telegram/cmake/lib_stripe.cmake
Normal file
47
Telegram/cmake/lib_stripe.cmake
Normal file
|
@ -0,0 +1,47 @@
|
|||
# This file is part of Telegram Desktop,
|
||||
# the official desktop application for the Telegram messaging service.
|
||||
#
|
||||
# For license and copyright information please follow this link:
|
||||
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
add_library(lib_stripe OBJECT)
|
||||
add_library(desktop-app::lib_stripe ALIAS lib_stripe)
|
||||
init_target(lib_stripe)
|
||||
|
||||
set(stripe_src_loc ${src_loc}/payments)
|
||||
|
||||
target_precompile_headers(lib_stripe PRIVATE ${stripe_src_loc}/stripe/stripe_pch.h)
|
||||
nice_target_sources(lib_stripe ${stripe_src_loc}
|
||||
PRIVATE
|
||||
stripe/stripe_address.h
|
||||
stripe/stripe_api_client.cpp
|
||||
stripe/stripe_api_client.h
|
||||
stripe/stripe_callbacks.h
|
||||
stripe/stripe_card.cpp
|
||||
stripe/stripe_card.h
|
||||
stripe/stripe_card_params.cpp
|
||||
stripe/stripe_card_params.h
|
||||
stripe/stripe_decode.cpp
|
||||
stripe/stripe_decode.h
|
||||
stripe/stripe_error.cpp
|
||||
stripe/stripe_error.h
|
||||
stripe/stripe_form_encodable.h
|
||||
stripe/stripe_form_encoder.cpp
|
||||
stripe/stripe_form_encoder.h
|
||||
stripe/stripe_payment_configuration.h
|
||||
stripe/stripe_token.cpp
|
||||
stripe/stripe_token.h
|
||||
|
||||
stripe/stripe_pch.h
|
||||
)
|
||||
|
||||
target_include_directories(lib_stripe
|
||||
PUBLIC
|
||||
${stripe_src_loc}
|
||||
)
|
||||
|
||||
target_link_libraries(lib_stripe
|
||||
PUBLIC
|
||||
desktop-app::lib_crl
|
||||
desktop-app::external_qt
|
||||
)
|
Loading…
Add table
Reference in a new issue