payments_form.cpp 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348
  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 "payments/payments_form.h"
  8. #include "main/main_session.h"
  9. #include "data/data_channel.h"
  10. #include "data/data_session.h"
  11. #include "data/data_media_types.h"
  12. #include "data/data_user.h"
  13. #include "data/data_photo.h"
  14. #include "data/data_photo_media.h"
  15. #include "data/data_file_origin.h"
  16. #include "countries/countries_instance.h"
  17. #include "history/history_item.h"
  18. #include "history/history_item_components.h"
  19. #include "stripe/stripe_api_client.h"
  20. #include "stripe/stripe_error.h"
  21. #include "stripe/stripe_token.h"
  22. #include "stripe/stripe_card_validator.h"
  23. #include "smartglocal/smartglocal_api_client.h"
  24. #include "smartglocal/smartglocal_error.h"
  25. #include "smartglocal/smartglocal_token.h"
  26. #include "storage/storage_account.h"
  27. #include "ui/image/image.h"
  28. #include "ui/text/format_values.h"
  29. #include "ui/text/text_entity.h"
  30. #include "apiwrap.h"
  31. #include "api/api_text_entities.h"
  32. #include "core/core_cloud_password.h"
  33. #include "window/themes/window_theme.h"
  34. #include "webview/webview_interface.h"
  35. #include "styles/style_payments.h" // paymentsThumbnailSize.
  36. #include <QtCore/QJsonDocument>
  37. #include <QtCore/QJsonObject>
  38. #include <QtCore/QJsonValue>
  39. namespace Payments {
  40. namespace {
  41. constexpr auto kPasswordPeriod = 15 * TimeId(60);
  42. [[nodiscard]] Ui::Address ParseAddress(const MTPPostAddress &address) {
  43. return address.match([](const MTPDpostAddress &data) {
  44. return Ui::Address{
  45. .address1 = qs(data.vstreet_line1()),
  46. .address2 = qs(data.vstreet_line2()),
  47. .city = qs(data.vcity()),
  48. .state = qs(data.vstate()),
  49. .countryIso2 = qs(data.vcountry_iso2()),
  50. .postcode = qs(data.vpost_code()),
  51. };
  52. });
  53. }
  54. [[nodiscard]] int64 ParsePriceAmount(uint64 value) {
  55. return *reinterpret_cast<const int64*>(&value);
  56. }
  57. [[nodiscard]] std::vector<Ui::LabeledPrice> ParsePrices(
  58. const MTPVector<MTPLabeledPrice> &data) {
  59. return ranges::views::all(
  60. data.v
  61. ) | ranges::views::transform([](const MTPLabeledPrice &price) {
  62. return price.match([&](const MTPDlabeledPrice &data) {
  63. return Ui::LabeledPrice{
  64. .label = qs(data.vlabel()),
  65. .price = ParsePriceAmount(data.vamount().v),
  66. };
  67. });
  68. }) | ranges::to_vector;
  69. }
  70. [[nodiscard]] MTPPaymentRequestedInfo Serialize(
  71. const Ui::RequestedInformation &information) {
  72. using Flag = MTPDpaymentRequestedInfo::Flag;
  73. return MTP_paymentRequestedInfo(
  74. MTP_flags((information.name.isEmpty() ? Flag(0) : Flag::f_name)
  75. | (information.email.isEmpty() ? Flag(0) : Flag::f_email)
  76. | (information.phone.isEmpty() ? Flag(0) : Flag::f_phone)
  77. | (information.shippingAddress
  78. ? Flag::f_shipping_address
  79. : Flag(0))),
  80. MTP_string(information.name),
  81. MTP_string(information.phone),
  82. MTP_string(information.email),
  83. MTP_postAddress(
  84. MTP_string(information.shippingAddress.address1),
  85. MTP_string(information.shippingAddress.address2),
  86. MTP_string(information.shippingAddress.city),
  87. MTP_string(information.shippingAddress.state),
  88. MTP_string(information.shippingAddress.countryIso2),
  89. MTP_string(information.shippingAddress.postcode)));
  90. }
  91. [[nodiscard]] QString CardTitle(const Stripe::Card &card) {
  92. // Like server stores saved_credentials title.
  93. return Stripe::CardBrandToString(card.brand()).toLower()
  94. + " *"
  95. + card.last4();
  96. }
  97. [[nodiscard]] QString CardTitle(const SmartGlocal::Card &card) {
  98. // Like server stores saved_credentials title.
  99. return card.type().toLower()
  100. + " *"
  101. + SmartGlocal::Last4(card);
  102. }
  103. } // namespace
  104. not_null<Main::Session*> SessionFromId(const InvoiceId &id) {
  105. if (const auto message = std::get_if<InvoiceMessage>(&id.value)) {
  106. return &message->peer->session();
  107. } else if (const auto slug = std::get_if<InvoiceSlug>(&id.value)) {
  108. return slug->session;
  109. } else if (const auto slug = std::get_if<InvoiceCredits>(&id.value)) {
  110. return slug->session;
  111. } else if (const auto gift = std::get_if<InvoiceStarGift>(&id.value)) {
  112. return &gift->recipient->session();
  113. }
  114. const auto &giftCode = v::get<InvoicePremiumGiftCode>(id.value);
  115. const auto users = std::get_if<InvoicePremiumGiftCodeUsers>(
  116. &giftCode.purpose);
  117. if (users) {
  118. Assert(!users->users.empty());
  119. return &users->users.front()->session();
  120. }
  121. const auto &giveaway = v::get<InvoicePremiumGiftCodeGiveaway>(
  122. giftCode.purpose);
  123. return &giveaway.boostPeer->session();
  124. }
  125. MTPinputStorePaymentPurpose InvoicePremiumGiftCodeGiveawayToTL(
  126. const InvoicePremiumGiftCode &invoice) {
  127. const auto &giveaway = v::get<InvoicePremiumGiftCodeGiveaway>(
  128. invoice.purpose);
  129. using Flag = MTPDinputStorePaymentPremiumGiveaway::Flag;
  130. return MTP_inputStorePaymentPremiumGiveaway(
  131. MTP_flags(Flag()
  132. | (giveaway.onlyNewSubscribers
  133. ? Flag::f_only_new_subscribers
  134. : Flag())
  135. | (giveaway.additionalChannels.empty()
  136. ? Flag()
  137. : Flag::f_additional_peers)
  138. | (giveaway.countries.empty()
  139. ? Flag()
  140. : Flag::f_countries_iso2)
  141. | (giveaway.showWinners
  142. ? Flag::f_winners_are_visible
  143. : Flag())
  144. | (giveaway.additionalPrize.isEmpty()
  145. ? Flag()
  146. : Flag::f_prize_description)),
  147. giveaway.boostPeer->input,
  148. MTP_vector_from_range(ranges::views::all(
  149. giveaway.additionalChannels
  150. ) | ranges::views::transform([](not_null<ChannelData*> c) {
  151. return MTPInputPeer(c->input);
  152. })),
  153. MTP_vector_from_range(ranges::views::all(
  154. giveaway.countries
  155. ) | ranges::views::transform([](QString value) {
  156. return MTP_string(value);
  157. })),
  158. MTP_string(giveaway.additionalPrize),
  159. MTP_long(invoice.randomId),
  160. MTP_int(giveaway.untilDate),
  161. MTP_string(invoice.currency),
  162. MTP_long(invoice.amount));
  163. }
  164. MTPinputStorePaymentPurpose InvoiceCreditsGiveawayToTL(
  165. const InvoicePremiumGiftCode &invoice) {
  166. Expects(invoice.giveawayCredits.has_value());
  167. const auto &giveaway = v::get<InvoicePremiumGiftCodeGiveaway>(
  168. invoice.purpose);
  169. using Flag = MTPDinputStorePaymentStarsGiveaway::Flag;
  170. return MTP_inputStorePaymentStarsGiveaway(
  171. MTP_flags(Flag()
  172. | (giveaway.onlyNewSubscribers
  173. ? Flag::f_only_new_subscribers
  174. : Flag())
  175. | (giveaway.additionalChannels.empty()
  176. ? Flag()
  177. : Flag::f_additional_peers)
  178. | (giveaway.countries.empty()
  179. ? Flag()
  180. : Flag::f_countries_iso2)
  181. | (giveaway.showWinners
  182. ? Flag::f_winners_are_visible
  183. : Flag())
  184. | (giveaway.additionalPrize.isEmpty()
  185. ? Flag()
  186. : Flag::f_prize_description)),
  187. MTP_long(*invoice.giveawayCredits),
  188. giveaway.boostPeer->input,
  189. MTP_vector_from_range(ranges::views::all(
  190. giveaway.additionalChannels
  191. ) | ranges::views::transform([](not_null<ChannelData*> c) {
  192. return MTPInputPeer(c->input);
  193. })),
  194. MTP_vector_from_range(ranges::views::all(
  195. giveaway.countries
  196. ) | ranges::views::transform([](QString value) {
  197. return MTP_string(value);
  198. })),
  199. MTP_string(giveaway.additionalPrize),
  200. MTP_long(invoice.randomId),
  201. MTP_int(giveaway.untilDate),
  202. MTP_string(invoice.currency),
  203. MTP_long(invoice.amount),
  204. MTP_int(invoice.users));
  205. }
  206. bool IsPremiumForStarsInvoice(const InvoiceId &id) {
  207. const auto giftCode = std::get_if<InvoicePremiumGiftCode>(&id.value);
  208. return giftCode
  209. && !giftCode->giveawayCredits
  210. && (giftCode->currency == ::Ui::kCreditsCurrency);
  211. }
  212. Form::Form(InvoiceId id, bool receipt)
  213. : _id(id)
  214. , _session(SessionFromId(id))
  215. , _api(&_session->mtp())
  216. , _receiptMode(receipt) {
  217. fillInvoiceFromMessage();
  218. if (_receiptMode) {
  219. _invoice.receipt.paid = true;
  220. requestReceipt();
  221. } else {
  222. requestForm();
  223. }
  224. }
  225. Form::~Form() = default;
  226. void Form::fillInvoiceFromMessage() {
  227. const auto message = std::get_if<InvoiceMessage>(&_id.value);
  228. if (!message) {
  229. return;
  230. }
  231. const auto id = FullMsgId(message->peer->id, message->itemId);
  232. if (const auto item = _session->data().message(id)) {
  233. const auto media = [&] {
  234. if (const auto payment = item->Get<HistoryServicePayment>()) {
  235. if (const auto invoice = payment->msg) {
  236. return invoice->media();
  237. }
  238. }
  239. return item->media();
  240. }();
  241. if (const auto invoice = media ? media->invoice() : nullptr) {
  242. _invoice.isTest = invoice->isTest;
  243. _invoice.cover = Ui::Cover{
  244. .title = invoice->title,
  245. .description = invoice->description,
  246. };
  247. if (const auto photo = invoice->photo) {
  248. loadThumbnail(photo);
  249. }
  250. }
  251. }
  252. }
  253. void Form::showProgress() {
  254. _updates.fire(ToggleProgress{ true });
  255. }
  256. void Form::hideProgress() {
  257. _updates.fire(ToggleProgress{ false });
  258. }
  259. void Form::loadThumbnail(not_null<PhotoData*> photo) {
  260. Expects(!_thumbnailLoadProcess);
  261. auto view = photo->createMediaView();
  262. if (auto good = prepareGoodThumbnail(view); !good.isNull()) {
  263. _invoice.cover.thumbnail = std::move(good);
  264. return;
  265. }
  266. _thumbnailLoadProcess = std::make_unique<ThumbnailLoadProcess>();
  267. if (auto blurred = prepareBlurredThumbnail(view); !blurred.isNull()) {
  268. _invoice.cover.thumbnail = std::move(blurred);
  269. _thumbnailLoadProcess->blurredSet = true;
  270. } else {
  271. _invoice.cover.thumbnail = prepareEmptyThumbnail();
  272. }
  273. _thumbnailLoadProcess->view = std::move(view);
  274. photo->load(Data::PhotoSize::Thumbnail, thumbnailFileOrigin());
  275. _session->downloaderTaskFinished(
  276. ) | rpl::start_with_next([=] {
  277. const auto &view = _thumbnailLoadProcess->view;
  278. if (auto good = prepareGoodThumbnail(view); !good.isNull()) {
  279. _invoice.cover.thumbnail = std::move(good);
  280. _thumbnailLoadProcess = nullptr;
  281. } else if (_thumbnailLoadProcess->blurredSet) {
  282. return;
  283. } else if (auto blurred = prepareBlurredThumbnail(view)
  284. ; !blurred.isNull()) {
  285. _invoice.cover.thumbnail = std::move(blurred);
  286. _thumbnailLoadProcess->blurredSet = true;
  287. } else {
  288. return;
  289. }
  290. _updates.fire(ThumbnailUpdated{ _invoice.cover.thumbnail });
  291. }, _thumbnailLoadProcess->lifetime);
  292. }
  293. Data::FileOrigin Form::thumbnailFileOrigin() const {
  294. if (const auto message = std::get_if<InvoiceMessage>(&_id.value)) {
  295. return FullMsgId(message->peer->id, message->itemId);
  296. }
  297. return Data::FileOrigin();
  298. }
  299. QImage Form::prepareGoodThumbnail(
  300. const std::shared_ptr<Data::PhotoMedia> &view) const {
  301. using Size = Data::PhotoSize;
  302. if (const auto large = view->image(Size::Large)) {
  303. return prepareThumbnail(large);
  304. } else if (const auto thumbnail = view->image(Size::Thumbnail)) {
  305. return prepareThumbnail(thumbnail);
  306. }
  307. return QImage();
  308. }
  309. QImage Form::prepareBlurredThumbnail(
  310. const std::shared_ptr<Data::PhotoMedia> &view) const {
  311. if (const auto small = view->image(Data::PhotoSize::Small)) {
  312. return prepareThumbnail(small, true);
  313. } else if (const auto blurred = view->thumbnailInline()) {
  314. return prepareThumbnail(blurred, true);
  315. }
  316. return QImage();
  317. }
  318. QImage Form::prepareThumbnail(
  319. not_null<const Image*> image,
  320. bool blurred) const {
  321. auto result = image->original().scaled(
  322. st::paymentsThumbnailSize * style::DevicePixelRatio(),
  323. Qt::KeepAspectRatio,
  324. Qt::SmoothTransformation);
  325. result = Images::Round(std::move(result), ImageRoundRadius::Large);
  326. result.setDevicePixelRatio(style::DevicePixelRatio());
  327. return result;
  328. }
  329. QImage Form::prepareEmptyThumbnail() const {
  330. auto result = QImage(
  331. st::paymentsThumbnailSize * style::DevicePixelRatio(),
  332. QImage::Format_ARGB32_Premultiplied);
  333. result.setDevicePixelRatio(style::DevicePixelRatio());
  334. result.fill(Qt::transparent);
  335. return result;
  336. }
  337. MTPInputInvoice Form::inputInvoice() const {
  338. if (const auto message = std::get_if<InvoiceMessage>(&_id.value)) {
  339. return MTP_inputInvoiceMessage(
  340. message->peer->input,
  341. MTP_int(message->itemId.bare));
  342. } else if (const auto slug = std::get_if<InvoiceSlug>(&_id.value)) {
  343. return MTP_inputInvoiceSlug(MTP_string(slug->slug));
  344. } else if (const auto credits = std::get_if<InvoiceCredits>(&_id.value)) {
  345. if (const auto userId = peerToUser(credits->giftPeerId)) {
  346. if (const auto user = _session->data().user(userId)) {
  347. return MTP_inputInvoiceStars(
  348. MTP_inputStorePaymentStarsGift(
  349. user->inputUser,
  350. MTP_long(credits->credits),
  351. MTP_string(credits->currency),
  352. MTP_long(credits->amount)));
  353. }
  354. }
  355. return MTP_inputInvoiceStars(
  356. MTP_inputStorePaymentStarsTopup(
  357. MTP_long(credits->credits),
  358. MTP_string(credits->currency),
  359. MTP_long(credits->amount)));
  360. } else if (const auto gift = std::get_if<InvoiceStarGift>(&_id.value)) {
  361. using Flag = MTPDinputInvoiceStarGift::Flag;
  362. return MTP_inputInvoiceStarGift(
  363. MTP_flags((gift->anonymous ? Flag::f_hide_name : Flag(0))
  364. | (gift->message.empty() ? Flag(0) : Flag::f_message)
  365. | (gift->upgraded ? Flag::f_include_upgrade : Flag(0))),
  366. gift->recipient->input,
  367. MTP_long(gift->giftId),
  368. MTP_textWithEntities(
  369. MTP_string(gift->message.text),
  370. Api::EntitiesToMTP(
  371. &gift->recipient->session(),
  372. gift->message.entities,
  373. Api::ConvertOption::SkipLocal)));
  374. }
  375. const auto &giftCode = v::get<InvoicePremiumGiftCode>(_id.value);
  376. if (giftCode.giveawayCredits) {
  377. return MTP_inputInvoiceStars(InvoiceCreditsGiveawayToTL(giftCode));
  378. }
  379. using Flag = MTPDpremiumGiftCodeOption::Flag;
  380. const auto option = MTP_premiumGiftCodeOption(
  381. MTP_flags((giftCode.storeQuantity ? Flag::f_store_quantity : Flag())
  382. | (giftCode.storeProduct.isEmpty()
  383. ? Flag()
  384. : Flag::f_store_product)),
  385. MTP_int(giftCode.users),
  386. MTP_int(giftCode.months),
  387. MTP_string(giftCode.storeProduct),
  388. MTP_int(giftCode.storeQuantity),
  389. MTP_string(giftCode.currency),
  390. MTP_long(giftCode.amount));
  391. const auto users = std::get_if<InvoicePremiumGiftCodeUsers>(
  392. &giftCode.purpose);
  393. auto message = (users && !users->message.empty())
  394. ? MTP_textWithEntities(
  395. MTP_string(users->message.text),
  396. Api::EntitiesToMTP(
  397. &users->users.front()->session(),
  398. users->message.entities,
  399. Api::ConvertOption::SkipLocal))
  400. : std::optional<MTPTextWithEntities>();
  401. if (users
  402. && users->users.size() == 1
  403. && giftCode.currency == ::Ui::kCreditsCurrency) {
  404. using Flag = MTPDinputInvoicePremiumGiftStars::Flag;
  405. return MTP_inputInvoicePremiumGiftStars(
  406. MTP_flags(message ? Flag::f_message : Flag()),
  407. users->users.front()->inputUser,
  408. MTP_int(giftCode.months),
  409. message.value_or(MTPTextWithEntities()));
  410. } else if (users) {
  411. using Flag = MTPDinputStorePaymentPremiumGiftCode::Flag;
  412. return MTP_inputInvoicePremiumGiftCode(
  413. MTP_inputStorePaymentPremiumGiftCode(
  414. MTP_flags((users->boostPeer ? Flag::f_boost_peer : Flag())
  415. | (message ? Flag::f_message : Flag())),
  416. MTP_vector_from_range(ranges::views::all(
  417. users->users
  418. ) | ranges::views::transform([](not_null<UserData*> user) {
  419. return MTPInputUser(user->inputUser);
  420. })),
  421. users->boostPeer ? users->boostPeer->input : MTPInputPeer(),
  422. MTP_string(giftCode.currency),
  423. MTP_long(giftCode.amount),
  424. message.value_or(MTPTextWithEntities())),
  425. option);
  426. } else {
  427. return MTP_inputInvoicePremiumGiftCode(
  428. InvoicePremiumGiftCodeGiveawayToTL(giftCode),
  429. option);
  430. }
  431. }
  432. void Form::requestForm() {
  433. showProgress();
  434. _api.request(MTPpayments_GetPaymentForm(
  435. MTP_flags(MTPpayments_GetPaymentForm::Flag::f_theme_params),
  436. inputInvoice(),
  437. MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams().json))
  438. )).done([=](const MTPpayments_PaymentForm &result) {
  439. hideProgress();
  440. result.match([&](const MTPDpayments_paymentForm &data) {
  441. processForm(data);
  442. }, [&](const MTPDpayments_paymentFormStars &data) {
  443. _session->data().processUsers(data.vusers());
  444. const auto currency = qs(data.vinvoice().data().vcurrency());
  445. const auto &tlPrices = data.vinvoice().data().vprices().v;
  446. const auto amount = tlPrices.empty()
  447. ? 0
  448. : tlPrices.front().data().vamount().v;
  449. const auto subscriptionPeriod
  450. = data.vinvoice().data().vsubscription_period().value_or(0);
  451. if (currency != ::Ui::kCreditsCurrency || !amount) {
  452. using Type = Error::Type;
  453. _updates.fire(Error{ Type::Form, u"Bad Stars Form."_q });
  454. return;
  455. }
  456. const auto invoice = InvoiceCredits{
  457. .session = _session,
  458. .randomId = 0,
  459. .credits = amount,
  460. .currency = currency,
  461. .amount = amount,
  462. .subscriptionPeriod = subscriptionPeriod,
  463. };
  464. const auto formData = CreditsFormData{
  465. .id = _id,
  466. .formId = data.vform_id().v,
  467. .botId = data.vbot_id().v,
  468. .title = qs(data.vtitle()),
  469. .description = qs(data.vdescription()),
  470. .photo = data.vphoto()
  471. ? _session->data().photoFromWeb(
  472. *data.vphoto(),
  473. ImageLocation())
  474. : nullptr,
  475. .invoice = invoice,
  476. .inputInvoice = inputInvoice(),
  477. };
  478. _updates.fire(CreditsPaymentStarted{ .data = formData });
  479. }, [&](const MTPDpayments_paymentFormStarGift &data) {
  480. const auto currency = qs(data.vinvoice().data().vcurrency());
  481. const auto &tlPrices = data.vinvoice().data().vprices().v;
  482. const auto amount = tlPrices.empty()
  483. ? 0
  484. : tlPrices.front().data().vamount().v;
  485. if (currency != ::Ui::kCreditsCurrency || !amount) {
  486. using Type = Error::Type;
  487. _updates.fire(Error{ Type::Form, u"Bad Stars Form."_q });
  488. return;
  489. }
  490. const auto invoice = InvoiceCredits{
  491. .session = _session,
  492. .randomId = 0,
  493. .credits = amount,
  494. .currency = currency,
  495. .amount = amount,
  496. };
  497. const auto gift = std::get_if<InvoiceStarGift>(&_id.value);
  498. const auto formData = CreditsFormData{
  499. .id = _id,
  500. .formId = data.vform_id().v,
  501. .invoice = invoice,
  502. .inputInvoice = inputInvoice(),
  503. .starGiftLimitedCount = gift ? gift->limitedCount : 0,
  504. .starGiftForm = true,
  505. };
  506. _updates.fire(CreditsPaymentStarted{ .data = formData });
  507. });
  508. }).fail([=](const MTP::Error &error) {
  509. hideProgress();
  510. _updates.fire(Error{ Error::Type::Form, error.type() });
  511. }).send();
  512. }
  513. void Form::requestReceipt() {
  514. Expects(v::is<InvoiceMessage>(_id.value));
  515. const auto message = v::get<InvoiceMessage>(_id.value);
  516. showProgress();
  517. _api.request(MTPpayments_GetPaymentReceipt(
  518. message.peer->input,
  519. MTP_int(message.itemId.bare)
  520. )).done([=](const MTPpayments_PaymentReceipt &result) {
  521. hideProgress();
  522. result.match([&](const auto &data) {
  523. processReceipt(data);
  524. });
  525. }).fail([=](const MTP::Error &error) {
  526. hideProgress();
  527. _updates.fire(Error{ Error::Type::Form, error.type() });
  528. }).send();
  529. }
  530. void Form::processForm(const MTPDpayments_paymentForm &data) {
  531. _session->data().processUsers(data.vusers());
  532. data.vinvoice().match([&](const auto &data) {
  533. processInvoice(data);
  534. });
  535. processDetails(data);
  536. if (const auto info = data.vsaved_info()) {
  537. info->match([&](const auto &data) {
  538. processSavedInformation(data);
  539. });
  540. }
  541. _paymentMethod.savedCredentials.clear();
  542. _paymentMethod.savedCredentialsIndex = 0;
  543. if (const auto credentials = data.vsaved_credentials()) {
  544. _paymentMethod.savedCredentials.reserve(credentials->v.size());
  545. for (const auto &saved : credentials->v) {
  546. _paymentMethod.savedCredentials.push_back({
  547. .id = qs(saved.data().vid()),
  548. .title = qs(saved.data().vtitle()),
  549. });
  550. }
  551. refreshPaymentMethodDetails();
  552. }
  553. if (const auto additional = data.vadditional_methods()) {
  554. processAdditionalPaymentMethods(additional->v);
  555. }
  556. fillPaymentMethodInformation();
  557. _updates.fire(FormReady{});
  558. }
  559. void Form::processReceipt(const MTPDpayments_paymentReceipt &data) {
  560. _session->data().processUsers(data.vusers());
  561. data.vinvoice().match([&](const auto &data) {
  562. processInvoice(data);
  563. });
  564. processDetails(data);
  565. if (const auto info = data.vinfo()) {
  566. info->match([&](const auto &data) {
  567. processSavedInformation(data);
  568. });
  569. }
  570. if (const auto shipping = data.vshipping()) {
  571. processShippingOptions({ *shipping });
  572. if (!_shippingOptions.list.empty()) {
  573. _shippingOptions.selectedId = _shippingOptions.list.front().id;
  574. }
  575. }
  576. _paymentMethod.savedCredentials = { {
  577. .id = "(used)",
  578. .title = qs(data.vcredentials_title()),
  579. } };
  580. _paymentMethod.savedCredentialsIndex = 0;
  581. fillPaymentMethodInformation();
  582. _updates.fire(FormReady{});
  583. }
  584. void Form::processReceipt(const MTPDpayments_paymentReceiptStars &data) {
  585. _session->data().processUsers(data.vusers());
  586. const auto receiptData = CreditsReceiptData{
  587. .id = qs(data.vtransaction_id()),
  588. .title = qs(data.vtitle()),
  589. .description = qs(data.vdescription()),
  590. .photo = data.vphoto()
  591. ? _session->data().photoFromWeb(
  592. *data.vphoto(),
  593. ImageLocation())
  594. : nullptr,
  595. .peerId = peerFromUser(data.vbot_id().v),
  596. .credits = StarsAmount(data.vtotal_amount().v),
  597. .date = data.vdate().v,
  598. };
  599. _updates.fire(CreditsReceiptReady{ .data = receiptData });
  600. }
  601. void Form::processInvoice(const MTPDinvoice &data) {
  602. const auto suggested = data.vsuggested_tip_amounts().value_or_empty();
  603. _invoice = Ui::Invoice{
  604. .cover = std::move(_invoice.cover),
  605. .prices = ParsePrices(data.vprices()),
  606. .suggestedTips = ranges::views::all(
  607. suggested
  608. ) | ranges::views::transform(
  609. &MTPlong::v
  610. ) | ranges::views::transform(
  611. ParsePriceAmount
  612. ) | ranges::to_vector,
  613. .tipsMax = ParsePriceAmount(data.vmax_tip_amount().value_or_empty()),
  614. .currency = qs(data.vcurrency()),
  615. .isNameRequested = data.is_name_requested(),
  616. .isPhoneRequested = data.is_phone_requested(),
  617. .isEmailRequested = data.is_email_requested(),
  618. .isShippingAddressRequested = data.is_shipping_address_requested(),
  619. .isRecurring = data.is_recurring(),
  620. .isFlexible = data.is_flexible(),
  621. .isTest = data.is_test(),
  622. .termsUrl = qs(data.vterms_url().value_or_empty()),
  623. .phoneSentToProvider = data.is_phone_to_provider(),
  624. .emailSentToProvider = data.is_email_to_provider(),
  625. };
  626. }
  627. void Form::processDetails(const MTPDpayments_paymentForm &data) {
  628. const auto nativeParams = data.vnative_params();
  629. auto nativeParamsJson = nativeParams
  630. ? nativeParams->match(
  631. [&](const MTPDdataJSON &data) { return data.vdata().v; })
  632. : QByteArray();
  633. _details = FormDetails{
  634. .formId = data.vform_id().v,
  635. .url = qs(data.vurl()),
  636. .nativeProvider = qs(data.vnative_provider().value_or_empty()),
  637. .nativeParamsJson = std::move(nativeParamsJson),
  638. .botId = data.vbot_id().v,
  639. .providerId = data.vprovider_id().v,
  640. .canSaveCredentials = data.is_can_save_credentials(),
  641. .passwordMissing = data.is_password_missing(),
  642. };
  643. _invoice.cover.title = qs(data.vtitle());
  644. _invoice.cover.description = TextUtilities::ParseEntities(
  645. qs(data.vdescription()),
  646. TextParseLinks | TextParseMultiline);
  647. if (_invoice.cover.thumbnail.isNull() && !_thumbnailLoadProcess) {
  648. if (const auto photo = data.vphoto()) {
  649. loadThumbnail(
  650. _session->data().photoFromWeb(*photo, ImageLocation()));
  651. }
  652. }
  653. if (const auto botId = _details.botId) {
  654. if (const auto bot = _session->data().userLoaded(botId)) {
  655. _invoice.cover.seller = bot->name();
  656. _details.termsBotUsername = bot->username();
  657. }
  658. }
  659. if (const auto providerId = _details.providerId) {
  660. if (const auto bot = _session->data().userLoaded(providerId)) {
  661. _invoice.provider = bot->name();
  662. }
  663. }
  664. }
  665. void Form::processDetails(const MTPDpayments_paymentReceipt &data) {
  666. _invoice.receipt = Ui::Receipt{
  667. .date = data.vdate().v,
  668. .totalAmount = ParsePriceAmount(data.vtotal_amount().v),
  669. .currency = qs(data.vcurrency()),
  670. .paid = true,
  671. };
  672. _details = FormDetails{
  673. .botId = data.vbot_id().v,
  674. .providerId = data.vprovider_id().v,
  675. };
  676. if (_invoice.cover.title.isEmpty()
  677. && _invoice.cover.description.empty()
  678. && _invoice.cover.thumbnail.isNull()
  679. && !_thumbnailLoadProcess) {
  680. _invoice.cover = Ui::Cover{
  681. .title = qs(data.vtitle()),
  682. .description = { qs(data.vdescription()) },
  683. };
  684. if (const auto web = data.vphoto()) {
  685. if (const auto photo = _session->data().photoFromWeb(*web, {})) {
  686. loadThumbnail(photo);
  687. }
  688. }
  689. }
  690. if (_details.botId) {
  691. if (const auto bot = _session->data().userLoaded(_details.botId)) {
  692. _invoice.cover.seller = bot->name();
  693. }
  694. }
  695. }
  696. void Form::processDetails(const MTPDpayments_paymentReceiptStars &data) {
  697. _invoice.receipt = Ui::Receipt{
  698. .date = data.vdate().v,
  699. .totalAmount = ParsePriceAmount(data.vtotal_amount().v),
  700. .currency = qs(data.vcurrency()),
  701. .paid = true,
  702. };
  703. _details = FormDetails{
  704. .botId = data.vbot_id().v,
  705. };
  706. if (_invoice.cover.title.isEmpty()
  707. && _invoice.cover.description.empty()
  708. && _invoice.cover.thumbnail.isNull()
  709. && !_thumbnailLoadProcess) {
  710. _invoice.cover = Ui::Cover{
  711. .title = qs(data.vtitle()),
  712. .description = { qs(data.vdescription()) },
  713. };
  714. if (const auto web = data.vphoto()) {
  715. if (const auto photo = _session->data().photoFromWeb(*web, {})) {
  716. loadThumbnail(photo);
  717. }
  718. }
  719. }
  720. if (_details.botId) {
  721. if (const auto bot = _session->data().userLoaded(_details.botId)) {
  722. _invoice.cover.seller = bot->name();
  723. }
  724. }
  725. }
  726. void Form::processSavedInformation(const MTPDpaymentRequestedInfo &data) {
  727. const auto address = data.vshipping_address();
  728. _savedInformation = _information = Ui::RequestedInformation{
  729. .defaultPhone = defaultPhone(),
  730. .defaultCountry = defaultCountry(),
  731. .name = qs(data.vname().value_or_empty()),
  732. .phone = qs(data.vphone().value_or_empty()),
  733. .email = qs(data.vemail().value_or_empty()),
  734. .shippingAddress = address ? ParseAddress(*address) : Ui::Address(),
  735. };
  736. }
  737. void Form::processAdditionalPaymentMethods(
  738. const QVector<MTPPaymentFormMethod> &list) {
  739. _paymentMethod.ui.additionalMethods = ranges::views::all(
  740. list
  741. ) | ranges::views::transform([](const MTPPaymentFormMethod &method) {
  742. return Ui::PaymentMethodAdditional{
  743. .title = qs(method.data().vtitle()),
  744. .url = qs(method.data().vurl()),
  745. };
  746. }) | ranges::to_vector;
  747. }
  748. void Form::refreshPaymentMethodDetails() {
  749. refreshSavedPaymentMethodDetails();
  750. _paymentMethod.ui.provider = _invoice.provider;
  751. _paymentMethod.ui.native.defaultCountry = defaultCountry();
  752. _paymentMethod.ui.canSaveInformation
  753. = _paymentMethod.ui.native.canSaveInformation
  754. = _details.canSaveCredentials || _details.passwordMissing;
  755. }
  756. void Form::refreshSavedPaymentMethodDetails() {
  757. const auto &list = _paymentMethod.savedCredentials;
  758. const auto index = _paymentMethod.savedCredentialsIndex;
  759. const auto &entered = _paymentMethod.newCredentials;
  760. _paymentMethod.ui.savedMethods.clear();
  761. if (entered) {
  762. _paymentMethod.ui.savedMethods.push_back({ .title = entered.title });
  763. }
  764. for (const auto &item : list) {
  765. _paymentMethod.ui.savedMethods.push_back({
  766. .id = item.id,
  767. .title = item.title,
  768. });
  769. }
  770. _paymentMethod.ui.savedMethodIndex = (index < list.size())
  771. ? (index + (entered ? 1 : 0))
  772. : 0;
  773. }
  774. QString Form::defaultPhone() const {
  775. return _session->user()->phone();
  776. }
  777. QString Form::defaultCountry() const {
  778. return Countries::Instance().countryISO2ByPhone(defaultPhone());
  779. }
  780. void Form::fillPaymentMethodInformation() {
  781. _paymentMethod.native = NativePaymentMethod();
  782. _paymentMethod.ui.native = Ui::NativeMethodDetails();
  783. _paymentMethod.ui.url = _details.url;
  784. //AssertIsDebug();
  785. //static auto counter = 0; // #TODO payments test both native and webview.
  786. if (!_details.nativeProvider.isEmpty()/* && ((++counter) % 2)*/) {
  787. auto error = QJsonParseError();
  788. auto document = QJsonDocument::fromJson(
  789. _details.nativeParamsJson,
  790. &error);
  791. if (error.error != QJsonParseError::NoError) {
  792. LOG(("Payment Error: Could not decode native_params, error %1: %2"
  793. ).arg(error.error
  794. ).arg(error.errorString()));
  795. } else if (!document.isObject()) {
  796. LOG(("Payment Error: Not an object in native_params."));
  797. } else {
  798. const auto object = document.object();
  799. if (_details.nativeProvider == "stripe") {
  800. fillStripeNativeMethod(object);
  801. } else if (_details.nativeProvider == "smartglocal") {
  802. fillSmartGlocalNativeMethod(object);
  803. } else {
  804. LOG(("Payment Error: Unknown native provider '%1'."
  805. ).arg(_details.nativeProvider));
  806. }
  807. }
  808. }
  809. refreshPaymentMethodDetails();
  810. }
  811. void Form::fillStripeNativeMethod(QJsonObject object) {
  812. const auto value = [&](QStringView key) {
  813. return object.value(key);
  814. };
  815. const auto key = value(u"publishable_key").toString();
  816. if (key.isEmpty()) {
  817. LOG(("Payment Error: No publishable_key in stripe native_params."));
  818. return;
  819. }
  820. _paymentMethod.native = NativePaymentMethod{
  821. .data = StripePaymentMethod{
  822. .publishableKey = key,
  823. },
  824. };
  825. _paymentMethod.ui.native = Ui::NativeMethodDetails{
  826. .supported = true,
  827. .needCountry = value(u"need_country").toBool(),
  828. .needZip = value(u"need_zip").toBool(),
  829. .needCardholderName = value(u"need_cardholder_name").toBool(),
  830. };
  831. }
  832. void Form::fillSmartGlocalNativeMethod(QJsonObject object) {
  833. const auto value = [&](QStringView key) {
  834. return object.value(key);
  835. };
  836. const auto key = value(u"public_token").toString();
  837. if (key.isEmpty()) {
  838. LOG(("Payment Error: "
  839. "No public_token in smartglocal native_params."));
  840. return;
  841. }
  842. _paymentMethod.native = NativePaymentMethod{
  843. .data = SmartGlocalPaymentMethod{
  844. .publicToken = key,
  845. .tokenizeUrl = value(u"tokenize_url").toString(),
  846. },
  847. };
  848. _paymentMethod.ui.native = Ui::NativeMethodDetails{
  849. .supported = true,
  850. .needCountry = false,
  851. .needZip = false,
  852. .needCardholderName = false,
  853. };
  854. }
  855. void Form::submit() {
  856. Expects(_paymentMethod.newCredentials
  857. || (_paymentMethod.savedCredentialsIndex
  858. < _paymentMethod.savedCredentials.size()));
  859. const auto index = _paymentMethod.savedCredentialsIndex;
  860. const auto &list = _paymentMethod.savedCredentials;
  861. const auto password = (index < list.size())
  862. ? _session->validTmpPassword()
  863. : QByteArray();
  864. if (index < list.size() && password.isEmpty()) {
  865. _updates.fire(TmpPasswordRequired{});
  866. return;
  867. } else if (!_session->local().isPeerTrustedPayment(_details.botId)) {
  868. _updates.fire(BotTrustRequired{
  869. .bot = _session->data().user(_details.botId),
  870. .provider = _session->data().user(_details.providerId),
  871. });
  872. return;
  873. }
  874. using Flag = MTPpayments_SendPaymentForm::Flag;
  875. showProgress();
  876. _api.request(MTPpayments_SendPaymentForm(
  877. MTP_flags((_requestedInformationId.isEmpty()
  878. ? Flag(0)
  879. : Flag::f_requested_info_id)
  880. | (_shippingOptions.selectedId.isEmpty()
  881. ? Flag(0)
  882. : Flag::f_shipping_option_id)
  883. | (_invoice.tipsMax > 0 ? Flag::f_tip_amount : Flag(0))),
  884. MTP_long(_details.formId),
  885. inputInvoice(),
  886. MTP_string(_requestedInformationId),
  887. MTP_string(_shippingOptions.selectedId),
  888. (index < list.size()
  889. ? MTP_inputPaymentCredentialsSaved(
  890. MTP_string(list[index].id),
  891. MTP_bytes(password))
  892. : MTP_inputPaymentCredentials(
  893. MTP_flags((_paymentMethod.newCredentials.saveOnServer
  894. && _details.canSaveCredentials)
  895. ? MTPDinputPaymentCredentials::Flag::f_save
  896. : MTPDinputPaymentCredentials::Flag(0)),
  897. MTP_dataJSON(MTP_bytes(_paymentMethod.newCredentials.data)))),
  898. MTP_long(_invoice.tipsSelected)
  899. )).done([=](const MTPpayments_PaymentResult &result) {
  900. hideProgress();
  901. result.match([&](const MTPDpayments_paymentResult &data) {
  902. _updates.fire(PaymentFinished{ data.vupdates() });
  903. }, [&](const MTPDpayments_paymentVerificationNeeded &data) {
  904. _updates.fire(VerificationNeeded{ qs(data.vurl()) });
  905. });
  906. }).fail([=](const MTP::Error &error) {
  907. hideProgress();
  908. _updates.fire(Error{ Error::Type::Send, error.type() });
  909. }).send();
  910. }
  911. void Form::submit(const Core::CloudPasswordResult &result) {
  912. if (_passwordRequestId) {
  913. return;
  914. }
  915. _passwordRequestId = _api.request(MTPaccount_GetTmpPassword(
  916. result.result,
  917. MTP_int(kPasswordPeriod)
  918. )).done([=](const MTPaccount_TmpPassword &result) {
  919. _passwordRequestId = 0;
  920. result.match([&](const MTPDaccount_tmpPassword &data) {
  921. _session->setTmpPassword(
  922. data.vtmp_password().v,
  923. data.vvalid_until().v);
  924. submit();
  925. });
  926. }).fail([=](const MTP::Error &error) {
  927. _passwordRequestId = 0;
  928. _updates.fire(Error{ Error::Type::TmpPassword, error.type() });
  929. }).send();
  930. }
  931. std::optional<QDate> Form::overrideExpireDateThreshold() const {
  932. const auto phone = _session->user()->phone();
  933. return phone.startsWith('7')
  934. ? QDate(2022, 2, 1)
  935. : std::optional<QDate>();
  936. }
  937. void Form::validateInformation(const Ui::RequestedInformation &information) {
  938. if (_validateRequestId) {
  939. if (_validatedInformation == information) {
  940. return;
  941. }
  942. hideProgress();
  943. _api.request(base::take(_validateRequestId)).cancel();
  944. }
  945. _validatedInformation = information;
  946. if (!validateInformationLocal(information)) {
  947. return;
  948. }
  949. Assert(!_invoice.isShippingAddressRequested
  950. || information.shippingAddress);
  951. Assert(!_invoice.isNameRequested || !information.name.isEmpty());
  952. Assert(!_invoice.isEmailRequested || !information.email.isEmpty());
  953. Assert(!_invoice.isPhoneRequested || !information.phone.isEmpty());
  954. showProgress();
  955. using Flag = MTPpayments_ValidateRequestedInfo::Flag;
  956. _validateRequestId = _api.request(MTPpayments_ValidateRequestedInfo(
  957. MTP_flags(information.save ? Flag::f_save : Flag(0)),
  958. inputInvoice(),
  959. Serialize(information)
  960. )).done([=](const MTPpayments_ValidatedRequestedInfo &result) {
  961. hideProgress();
  962. _validateRequestId = 0;
  963. const auto oldSelectedId = _shippingOptions.selectedId;
  964. result.match([&](const MTPDpayments_validatedRequestedInfo &data) {
  965. _requestedInformationId = data.vid().value_or_empty();
  966. processShippingOptions(
  967. data.vshipping_options().value_or_empty());
  968. });
  969. _shippingOptions.selectedId = ranges::contains(
  970. _shippingOptions.list,
  971. oldSelectedId,
  972. &Ui::ShippingOption::id
  973. ) ? oldSelectedId : QString();
  974. if (_shippingOptions.selectedId.isEmpty()
  975. && _shippingOptions.list.size() == 1) {
  976. _shippingOptions.selectedId = _shippingOptions.list.front().id;
  977. }
  978. _information = _validatedInformation;
  979. if (_information.save) {
  980. _savedInformation = _information;
  981. }
  982. _updates.fire(ValidateFinished{});
  983. }).fail([=](const MTP::Error &error) {
  984. hideProgress();
  985. _validateRequestId = 0;
  986. _updates.fire(Error{ Error::Type::Validate, error.type() });
  987. }).send();
  988. }
  989. bool Form::hasChanges() const {
  990. const auto &information = _validateRequestId
  991. ? _validatedInformation
  992. : _information;
  993. return (information != _savedInformation)
  994. || (_stripe != nullptr)
  995. || (_smartglocal != nullptr)
  996. || (!_paymentMethod.newCredentials.empty()
  997. && (_paymentMethod.savedCredentialsIndex
  998. >= _paymentMethod.savedCredentials.size()));
  999. }
  1000. bool Form::validateInformationLocal(
  1001. const Ui::RequestedInformation &information) const {
  1002. if (const auto error = informationErrorLocal(information)) {
  1003. _updates.fire_copy(error);
  1004. return false;
  1005. }
  1006. return true;
  1007. }
  1008. Error Form::informationErrorLocal(
  1009. const Ui::RequestedInformation &information) const {
  1010. auto errors = QStringList();
  1011. const auto push = [&](const QString &id) {
  1012. errors.push_back(id);
  1013. };
  1014. if (_invoice.isShippingAddressRequested) {
  1015. if (information.shippingAddress.address1.isEmpty()) {
  1016. push(u"ADDRESS_STREET_LINE1_INVALID"_q);
  1017. }
  1018. if (information.shippingAddress.city.isEmpty()) {
  1019. push(u"ADDRESS_CITY_INVALID"_q);
  1020. }
  1021. if (information.shippingAddress.countryIso2.isEmpty()) {
  1022. push(u"ADDRESS_COUNTRY_INVALID"_q);
  1023. }
  1024. }
  1025. if (_invoice.isNameRequested && information.name.isEmpty()) {
  1026. push(u"REQ_INFO_NAME_INVALID"_q);
  1027. }
  1028. if (_invoice.isEmailRequested && information.email.isEmpty()) {
  1029. push(u"REQ_INFO_EMAIL_INVALID"_q);
  1030. }
  1031. if (_invoice.isPhoneRequested && information.phone.isEmpty()) {
  1032. push(u"REQ_INFO_PHONE_INVALID"_q);
  1033. }
  1034. if (!errors.isEmpty()) {
  1035. return Error{ Error::Type::Validate, errors.front() };
  1036. }
  1037. return Error();
  1038. }
  1039. void Form::validateCard(
  1040. const Ui::UncheckedCardDetails &details,
  1041. bool saveInformation) {
  1042. Expects(!v::is_null(_paymentMethod.native.data));
  1043. if (!validateCardLocal(details, overrideExpireDateThreshold())) {
  1044. return;
  1045. }
  1046. const auto &native = _paymentMethod.native.data;
  1047. if (const auto smartglocal = std::get_if<SmartGlocalPaymentMethod>(
  1048. &native)) {
  1049. validateCard(*smartglocal, details, saveInformation);
  1050. } else if (const auto stripe = std::get_if<StripePaymentMethod>(&native)) {
  1051. validateCard(*stripe, details, saveInformation);
  1052. } else {
  1053. Unexpected("Native payment provider in Form::validateCard.");
  1054. }
  1055. }
  1056. bool Form::validateCardLocal(
  1057. const Ui::UncheckedCardDetails &details,
  1058. const std::optional<QDate> &overrideExpireDateThreshold) const {
  1059. if (auto error = cardErrorLocal(details, overrideExpireDateThreshold)) {
  1060. _updates.fire(std::move(error));
  1061. return false;
  1062. }
  1063. return true;
  1064. }
  1065. Error Form::cardErrorLocal(
  1066. const Ui::UncheckedCardDetails &details,
  1067. const std::optional<QDate> &overrideExpireDateThreshold) const {
  1068. using namespace Stripe;
  1069. auto errors = QStringList();
  1070. const auto push = [&](const QString &id) {
  1071. errors.push_back(id);
  1072. };
  1073. const auto kValid = ValidationState::Valid;
  1074. if (ValidateCard(details.number).state != kValid) {
  1075. push(u"LOCAL_CARD_NUMBER_INVALID"_q);
  1076. }
  1077. if (ValidateParsedExpireDate(
  1078. details.expireMonth,
  1079. details.expireYear,
  1080. overrideExpireDateThreshold
  1081. ) != kValid) {
  1082. push(u"LOCAL_CARD_EXPIRE_DATE_INVALID"_q);
  1083. }
  1084. if (ValidateCvc(details.number, details.cvc).state != kValid) {
  1085. push(u"LOCAL_CARD_CVC_INVALID"_q);
  1086. }
  1087. if (_paymentMethod.ui.native.needCardholderName
  1088. && details.cardholderName.isEmpty()) {
  1089. push(u"LOCAL_CARD_HOLDER_NAME_INVALID"_q);
  1090. }
  1091. if (_paymentMethod.ui.native.needCountry
  1092. && details.addressCountry.isEmpty()) {
  1093. push(u"LOCAL_CARD_BILLING_COUNTRY_INVALID"_q);
  1094. }
  1095. if (_paymentMethod.ui.native.needZip
  1096. && details.addressZip.isEmpty()) {
  1097. push(u"LOCAL_CARD_BILLING_ZIP_INVALID"_q);
  1098. }
  1099. if (!errors.isEmpty()) {
  1100. return Error{ Error::Type::Validate, errors.front() };
  1101. }
  1102. return Error();
  1103. }
  1104. void Form::validateCard(
  1105. const StripePaymentMethod &method,
  1106. const Ui::UncheckedCardDetails &details,
  1107. bool saveInformation) {
  1108. Expects(!method.publishableKey.isEmpty());
  1109. if (_stripe) {
  1110. return;
  1111. }
  1112. auto configuration = Stripe::PaymentConfiguration{
  1113. .publishableKey = method.publishableKey,
  1114. .companyName = "Telegram",
  1115. };
  1116. _stripe = std::make_unique<Stripe::APIClient>(std::move(configuration));
  1117. auto card = Stripe::CardParams{
  1118. .number = details.number,
  1119. .expMonth = details.expireMonth,
  1120. .expYear = details.expireYear,
  1121. .cvc = details.cvc,
  1122. .name = details.cardholderName,
  1123. .addressZip = details.addressZip,
  1124. .addressCountry = details.addressCountry,
  1125. };
  1126. showProgress();
  1127. _stripe->createTokenWithCard(std::move(card), crl::guard(this, [=](
  1128. Stripe::Token token,
  1129. Stripe::Error error) {
  1130. hideProgress();
  1131. _stripe = nullptr;
  1132. if (error) {
  1133. LOG(("Stripe Error %1: %2 (%3)"
  1134. ).arg(int(error.code())
  1135. ).arg(error.description(), error.message()));
  1136. _updates.fire(Error{ Error::Type::Stripe, error.description() });
  1137. } else {
  1138. setPaymentCredentials({
  1139. .title = CardTitle(token.card()),
  1140. .data = QJsonDocument(QJsonObject{
  1141. { "type", "card" },
  1142. { "id", token.tokenId() },
  1143. }).toJson(QJsonDocument::Compact),
  1144. .saveOnServer = saveInformation,
  1145. });
  1146. }
  1147. }));
  1148. }
  1149. void Form::validateCard(
  1150. const SmartGlocalPaymentMethod &method,
  1151. const Ui::UncheckedCardDetails &details,
  1152. bool saveInformation) {
  1153. Expects(!method.publicToken.isEmpty());
  1154. if (_smartglocal) {
  1155. return;
  1156. }
  1157. auto configuration = SmartGlocal::PaymentConfiguration{
  1158. .publicToken = method.publicToken,
  1159. .tokenizeUrl = method.tokenizeUrl,
  1160. .isTest = _invoice.isTest,
  1161. };
  1162. _smartglocal = std::make_unique<SmartGlocal::APIClient>(
  1163. std::move(configuration));
  1164. auto card = Stripe::CardParams{
  1165. .number = details.number,
  1166. .expMonth = details.expireMonth,
  1167. .expYear = details.expireYear,
  1168. .cvc = details.cvc,
  1169. .name = details.cardholderName,
  1170. .addressZip = details.addressZip,
  1171. .addressCountry = details.addressCountry,
  1172. };
  1173. showProgress();
  1174. _smartglocal->createTokenWithCard(std::move(card), crl::guard(this, [=](
  1175. SmartGlocal::Token token,
  1176. SmartGlocal::Error error) {
  1177. hideProgress();
  1178. _smartglocal = nullptr;
  1179. if (error) {
  1180. LOG(("SmartGlocal Error %1: %2 (%3)"
  1181. ).arg(int(error.code())
  1182. ).arg(error.description(), error.message()));
  1183. _updates.fire(Error{
  1184. Error::Type::SmartGlocal,
  1185. error.description(),
  1186. });
  1187. } else {
  1188. setPaymentCredentials({
  1189. .title = CardTitle(token.card()),
  1190. .data = QJsonDocument(QJsonObject{
  1191. { "token", token.tokenId() },
  1192. { "type", "card" },
  1193. }).toJson(QJsonDocument::Compact),
  1194. .saveOnServer = saveInformation,
  1195. });
  1196. }
  1197. }));
  1198. }
  1199. void Form::setPaymentCredentials(const NewCredentials &credentials) {
  1200. Expects(!credentials.empty());
  1201. _paymentMethod.newCredentials = credentials;
  1202. _paymentMethod.savedCredentialsIndex
  1203. = _paymentMethod.savedCredentials.size();
  1204. refreshSavedPaymentMethodDetails();
  1205. const auto requestNewPassword = credentials.saveOnServer
  1206. && !_details.canSaveCredentials
  1207. && _details.passwordMissing;
  1208. _updates.fire(PaymentMethodUpdate{ requestNewPassword });
  1209. }
  1210. void Form::chooseSavedMethod(const QString &id) {
  1211. auto &index = _paymentMethod.savedCredentialsIndex;
  1212. const auto &list = _paymentMethod.savedCredentials;
  1213. if (id.isEmpty() && _paymentMethod.newCredentials) {
  1214. index = list.size();
  1215. } else {
  1216. const auto i = ranges::find(list, id, &SavedCredentials::id);
  1217. index = (i != end(list)) ? (i - begin(list)) : 0;
  1218. }
  1219. refreshSavedPaymentMethodDetails();
  1220. const auto requestNewPassword = (index == list.size())
  1221. && _paymentMethod.newCredentials
  1222. && _paymentMethod.newCredentials.saveOnServer
  1223. && !_details.canSaveCredentials
  1224. && _details.passwordMissing;
  1225. _updates.fire(PaymentMethodUpdate{ requestNewPassword });
  1226. }
  1227. void Form::setHasPassword(bool has) {
  1228. if (_details.passwordMissing) {
  1229. _details.canSaveCredentials = has;
  1230. }
  1231. }
  1232. void Form::setShippingOption(const QString &id) {
  1233. _shippingOptions.selectedId = id;
  1234. }
  1235. void Form::setTips(int64 value) {
  1236. _invoice.tipsSelected = std::min(value, _invoice.tipsMax);
  1237. }
  1238. void Form::acceptTerms() {
  1239. _details.termsAccepted = true;
  1240. }
  1241. void Form::trustBot() {
  1242. _session->local().markPeerTrustedPayment(_details.botId);
  1243. }
  1244. void Form::processShippingOptions(const QVector<MTPShippingOption> &data) {
  1245. const auto currency = _invoice.currency;
  1246. _shippingOptions = Ui::ShippingOptions{ currency, ranges::views::all(
  1247. data
  1248. ) | ranges::views::transform([](const MTPShippingOption &option) {
  1249. return option.match([](const MTPDshippingOption &data) {
  1250. return Ui::ShippingOption{
  1251. .id = qs(data.vid()),
  1252. .title = qs(data.vtitle()),
  1253. .prices = ParsePrices(data.vprices()),
  1254. };
  1255. });
  1256. }) | ranges::to_vector };
  1257. _shippingOptions.currency = _invoice.currency;
  1258. }
  1259. } // namespace Payments