stripe_api_client.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "stripe/stripe_api_client.h"
  8. #include "stripe/stripe_error.h"
  9. #include "stripe/stripe_token.h"
  10. #include "stripe/stripe_form_encoder.h"
  11. #include <QtCore/QJsonObject>
  12. #include <QtCore/QJsonDocument>
  13. #include <QtNetwork/QNetworkRequest>
  14. #include <QtNetwork/QNetworkReply>
  15. #include <crl/crl_on_main.h>
  16. namespace Stripe {
  17. namespace {
  18. [[nodiscard]] QString APIURLBase() {
  19. return "api.stripe.com/v1";
  20. }
  21. [[nodiscard]] QString TokenEndpoint() {
  22. return "tokens";
  23. }
  24. [[nodiscard]] QString StripeAPIVersion() {
  25. return "2015-10-12";
  26. }
  27. [[nodiscard]] QString SDKVersion() {
  28. return "9.1.0";
  29. }
  30. [[nodiscard]] QString StripeUserAgentDetails() {
  31. const auto details = QJsonObject{
  32. { "lang", "objective-c" },
  33. { "bindings_version", SDKVersion() },
  34. };
  35. return QString::fromUtf8(
  36. QJsonDocument(details).toJson(QJsonDocument::Compact));
  37. }
  38. } // namespace
  39. APIClient::APIClient(PaymentConfiguration configuration)
  40. : _apiUrl("https://" + APIURLBase())
  41. , _configuration(configuration) {
  42. _additionalHttpHeaders = {
  43. { "X-Stripe-User-Agent", StripeUserAgentDetails() },
  44. { "Stripe-Version", StripeAPIVersion() },
  45. { "Authorization", "Bearer " + _configuration.publishableKey },
  46. };
  47. }
  48. APIClient::~APIClient() {
  49. const auto destroy = std::move(_old);
  50. }
  51. void APIClient::createTokenWithCard(
  52. CardParams card,
  53. TokenCompletionCallback completion) {
  54. createTokenWithData(
  55. FormEncoder::formEncodedDataForObject(MakeEncodable(card)),
  56. std::move(completion));
  57. }
  58. void APIClient::createTokenWithData(
  59. QByteArray data,
  60. TokenCompletionCallback completion) {
  61. const auto url = QUrl(_apiUrl + '/' + TokenEndpoint());
  62. auto request = QNetworkRequest(url);
  63. for (const auto &[name, value] : _additionalHttpHeaders) {
  64. request.setRawHeader(name.toUtf8(), value.toUtf8());
  65. }
  66. destroyReplyDelayed(std::move(_reply));
  67. _reply.reset(_manager.post(request, data));
  68. const auto finish = [=](Token token, Error error) {
  69. crl::on_main([
  70. completion,
  71. token = std::move(token),
  72. error = std::move(error)
  73. ] {
  74. completion(std::move(token), std::move(error));
  75. });
  76. };
  77. const auto finishWithError = [=](Error error) {
  78. finish(Token::Empty(), std::move(error));
  79. };
  80. const auto finishWithToken = [=](Token token) {
  81. finish(std::move(token), Error::None());
  82. };
  83. QObject::connect(_reply.get(), &QNetworkReply::finished, [=] {
  84. const auto replyError = int(_reply->error());
  85. const auto replyErrorString = _reply->errorString();
  86. const auto bytes = _reply->readAll();
  87. destroyReplyDelayed(std::move(_reply));
  88. auto parseError = QJsonParseError();
  89. const auto document = QJsonDocument::fromJson(bytes, &parseError);
  90. if (!bytes.isEmpty()) {
  91. if (parseError.error != QJsonParseError::NoError) {
  92. const auto code = int(parseError.error);
  93. finishWithError({
  94. Error::Code::JsonParse,
  95. QString("InvalidJson%1").arg(code),
  96. parseError.errorString(),
  97. });
  98. return;
  99. } else if (!document.isObject()) {
  100. finishWithError({
  101. Error::Code::JsonFormat,
  102. "InvalidJsonRoot",
  103. "Not an object in JSON reply.",
  104. });
  105. return;
  106. }
  107. const auto object = document.object();
  108. if (auto error = Error::DecodedObjectFromResponse(object)) {
  109. finishWithError(std::move(error));
  110. return;
  111. }
  112. }
  113. if (replyError != QNetworkReply::NoError) {
  114. finishWithError({
  115. Error::Code::Network,
  116. QString("RequestError%1").arg(replyError),
  117. replyErrorString,
  118. });
  119. return;
  120. }
  121. auto token = Token::DecodedObjectFromAPIResponse(document.object());
  122. if (!token) {
  123. finishWithError({
  124. Error::Code::JsonFormat,
  125. "InvalidTokenJson",
  126. "Could not parse token.",
  127. });
  128. }
  129. finishWithToken(std::move(token));
  130. });
  131. }
  132. void APIClient::destroyReplyDelayed(std::unique_ptr<QNetworkReply> reply) {
  133. if (!reply) {
  134. return;
  135. }
  136. const auto raw = reply.get();
  137. _old.push_back(std::move(reply));
  138. QObject::disconnect(raw, &QNetworkReply::finished, nullptr, nullptr);
  139. raw->deleteLater();
  140. QObject::connect(raw, &QObject::destroyed, [=] {
  141. for (auto i = begin(_old); i != end(_old); ++i) {
  142. if (i->get() == raw) {
  143. i->release();
  144. _old.erase(i);
  145. break;
  146. }
  147. }
  148. });
  149. }
  150. } // namespace Stripe