diff options
author | Roman Kuznetsov <r.kuznetsow@gmail.com> | 2018-08-23 14:46:20 +0300 |
---|---|---|
committer | Daria Volvenkova <d.volvenkova@corp.mail.ru> | 2018-08-24 11:56:01 +0300 |
commit | 6e572cb3ac036a91812eb78fb5f4aadc2932f392 (patch) | |
tree | d1d9ce1ebf12d9a98d6b7e411f6ca44fc58a4a69 /map | |
parent | 5ebbef25db75c5b264ed6853c21808137f8449b9 (diff) |
Added client-server interaction for subscription validation
Diffstat (limited to 'map')
-rw-r--r-- | map/framework.cpp | 2 | ||||
-rw-r--r-- | map/subscription.cpp | 165 | ||||
-rw-r--r-- | map/subscription.hpp | 18 |
3 files changed, 171 insertions, 14 deletions
diff --git a/map/framework.cpp b/map/framework.cpp index e736dce18c..6aa632eacd 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -503,8 +503,6 @@ Framework::Framework(FrameworkParams const & params) InitTransliteration(); LOG(LDEBUG, ("Transliterators initialized")); - - m_subscription->Validate(); } Framework::~Framework() diff --git a/map/subscription.cpp b/map/subscription.cpp index 437f232589..1708415153 100644 --- a/map/subscription.cpp +++ b/map/subscription.cpp @@ -1,19 +1,87 @@ #include "map/subscription.hpp" +#include "platform/http_client.hpp" #include "platform/platform.hpp" +#include "coding/serdes_json.hpp" #include "coding/sha1.hpp" +#include "coding/writer.hpp" #include "base/assert.hpp" +#include "base/logging.hpp" +#include "base/visitor.hpp" + +#include "std/target_os.hpp" + +#include <utility> + +#define STAGE_PURCHASE_SERVER +#include "private.h" namespace { std::string const kSubscriptionId = "SubscriptionId"; +std::string const kServerUrl = PURCHASE_SERVER_URL; +#if defined(OMIM_OS_IPHONE) +std::string const kProductId = "ad.removal.apple"; +std::string const kReceiptType = "apple"; +#elif defined(OMIM_OS_ANDROID) +std::string const kProductId = "ad.removal.google"; +std::string const kReceiptType = "google"; +#else +std::string const kProductId {}; +std::string const kReceiptType {}; +#endif std::string GetSubscriptionId() { return coding::SHA1::CalculateBase64ForString(GetPlatform().UniqueClientId()); } + +std::string ValidationUrl() +{ + if (kServerUrl.empty()) + return {}; + return kServerUrl + "purchases/is_purchased"; +} + +struct ReceiptData +{ + std::string m_data; + std::string m_type; + + ReceiptData(std::string const & data, std::string const & type) + : m_data(data) + , m_type(type) + {} + + DECLARE_VISITOR(visitor(m_data, "data"), + visitor(m_type, "type")) +}; + +struct ValidationData +{ + std::string m_productId; + ReceiptData m_receipt; + + ValidationData(std::string const & productId, std::string const & receiptData, + std::string const & receiptType) + : m_productId(productId) + , m_receipt(receiptData, receiptType) + {} + + DECLARE_VISITOR(visitor(m_productId, "product_id"), + visitor(m_receipt, "receipt")) +}; + +struct ValidationResult +{ + bool m_isValid = true; + std::string m_error; + + DECLARE_VISITOR(visitor(m_isValid, "valid"), + visitor(m_error, "error")) +}; } // namespace Subscription::Subscription() @@ -32,29 +100,104 @@ void Subscription::Register(SubscriptionListener * listener) listener->OnSubscriptionChanged(IsActive()); } +void Subscription::SetValidationCallback(ValidationCallback && callback) +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + m_validationCallback = std::move(callback); +} + bool Subscription::IsActive() const { return m_isActive; } -void Subscription::Validate() +void Subscription::Validate(std::string const & receiptData, std::string const & accessToken) { CHECK_THREAD_CHECKER(m_threadChecker, ()); - //TODO: check on server. - bool isValid = false; + std::string const url = ValidationUrl(); + if (url.empty()) + { + ApplyValidation(ValidationCode::NotActive); + return; + } - m_isActive = isValid; - if (isValid) + auto const status = GetPlatform().ConnectionStatus(); + if (status == Platform::EConnectionType::CONNECTION_NONE) { - m_subscriptionId = GetSubscriptionId(); - GetPlatform().GetSecureStorage().Save(kSubscriptionId, m_subscriptionId); + ApplyValidation(ValidationCode::Failure); + return; } - else + + GetPlatform().RunTask(Platform::Thread::Network, [this, url, receiptData, accessToken]() { - GetPlatform().GetSecureStorage().Remove(kSubscriptionId); + platform::HttpClient request(url); + request.SetRawHeader("Accept", "application/json"); + request.SetRawHeader("User-Agent", GetPlatform().GetAppUserAgent()); + if (!accessToken.empty()) + request.SetRawHeader("Authorization", "Bearer " + accessToken); + + std::string jsonStr; + { + using Sink = MemWriter<std::string>; + Sink sink(jsonStr); + coding::SerializerJson<Sink> serializer(sink); + serializer(ValidationData(kProductId, receiptData, kReceiptType)); + } + request.SetBodyData(std::move(jsonStr), "application/json"); + + ValidationCode code = ValidationCode::Failure; + if (request.RunHttpRequest()) + { + auto const resultCode = request.ErrorCode(); + if (resultCode >= 200 && resultCode < 300) + { + ValidationResult result; + { + coding::DeserializerJson des(request.ServerResponse()); + des(result); + } + + code = result.m_isValid ? ValidationCode::Active : ValidationCode::NotActive; + if (!result.m_error.empty()) + LOG(LWARNING, ("Validation URL:", url, "Subscription error:", result.m_error)); + } + else + { + LOG(LWARNING, ("Validation URL:", url, "Unexpected server error. Code =", resultCode, request.ServerResponse())); + } + } + else + { + LOG(LWARNING, ("Validation URL:", url, "Request failed.")); + } + + GetPlatform().RunTask(Platform::Thread::Gui, [this, code]() { ApplyValidation(code); }); + }); +} + +void Subscription::ApplyValidation(ValidationCode code) +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + + if (code != ValidationCode::Failure) + { + bool const isActive = (code == ValidationCode::Active); + m_isActive = isActive; + if (isActive) + { + m_subscriptionId = GetSubscriptionId(); + GetPlatform().GetSecureStorage().Save(kSubscriptionId, m_subscriptionId); + } + else + { + GetPlatform().GetSecureStorage().Remove(kSubscriptionId); + } + + for (auto & listener : m_listeners) + listener->OnSubscriptionChanged(isActive); } - for (auto & listener : m_listeners) - listener->OnSubscriptionChanged(isValid); + if (m_validationCallback) + m_validationCallback(code); } diff --git a/map/subscription.hpp b/map/subscription.hpp index 793135c231..83cc98f1eb 100644 --- a/map/subscription.hpp +++ b/map/subscription.hpp @@ -3,6 +3,7 @@ #include "base/thread_checker.hpp" #include <atomic> +#include <functional> #include <string> #include <vector> @@ -16,15 +17,30 @@ public: class Subscription { public: + enum class ValidationCode + { + Active, // Subscription is active. + NotActive, // Subscription is not active. + Failure, // Validation failed, real subscription status is unknown, current one acts. + }; + + using ValidationCallback = std::function<void(ValidationCode)>; + Subscription(); void Register(SubscriptionListener * listener); + void SetValidationCallback(ValidationCallback && callback); + bool IsActive() const; - void Validate(); + void Validate(std::string const & receiptData, std::string const & accessToken); private: + void ApplyValidation(ValidationCode code); + std::atomic<bool> m_isActive; std::string m_subscriptionId; std::vector<SubscriptionListener *> m_listeners; + ValidationCallback m_validationCallback; + ThreadChecker m_threadChecker; }; |