forked from organicmaps/organicmaps
Added client-server interaction for subscription validation
This commit is contained in:
parent
5ebbef25db
commit
6e572cb3ac
5 changed files with 244 additions and 17 deletions
|
@ -714,6 +714,14 @@ void CallSetRoutingLoadPointsListener(shared_ptr<jobject> listener, bool success
|
|||
|
||||
RoutingManager::LoadRouteHandler g_loadRouteHandler;
|
||||
|
||||
void CallSubscriptionValidationListener(shared_ptr<jobject> listener,
|
||||
Subscription::ValidationCode code)
|
||||
{
|
||||
JNIEnv * env = jni::GetEnv();
|
||||
jmethodID const methodId = jni::GetMethodID(env, *listener, "onValidateSubscription", "(I)V");
|
||||
env->CallVoidMethod(*listener, methodId, static_cast<jint>(code));
|
||||
}
|
||||
|
||||
/// @name JNI EXPORTS
|
||||
//@{
|
||||
JNIEXPORT jstring JNICALL
|
||||
|
@ -1730,4 +1738,44 @@ Java_com_mapswithme_maps_Framework_nativeMakeCrash(JNIEnv *env, jclass type)
|
|||
{
|
||||
CHECK(false, ("Diagnostic native crash!"));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_mapswithme_maps_Framework_nativeValidateSubscription(JNIEnv * env, jclass,
|
||||
jstring receiptData)
|
||||
{
|
||||
auto const & subscription = frm()->GetSubscription();
|
||||
if (subscription == nullptr)
|
||||
return;
|
||||
|
||||
subscription->Validate(jni::ToNativeString(env, receiptData), frm()->GetUser().GetAccessToken());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_mapswithme_maps_Framework_nativeSetSubscriptionValidationListener(JNIEnv *, jclass,
|
||||
jobject listener)
|
||||
{
|
||||
auto const & subscription = frm()->GetSubscription();
|
||||
if (subscription == nullptr)
|
||||
return;
|
||||
|
||||
if (listener != nullptr)
|
||||
{
|
||||
subscription->SetValidationCallback(bind(&CallSubscriptionValidationListener,
|
||||
jni::make_global_ref(listener), _1));
|
||||
}
|
||||
else
|
||||
{
|
||||
subscription->SetValidationCallback(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_mapswithme_maps_Framework_nativeHasActiveSubscription(JNIEnv *, jclass)
|
||||
{
|
||||
auto const & subscription = frm()->GetSubscription();
|
||||
if (subscription == nullptr)
|
||||
return static_cast<jboolean>(false);
|
||||
|
||||
return static_cast<jboolean>(subscription->IsActive());
|
||||
}
|
||||
} // extern "C"
|
||||
|
|
|
@ -100,6 +100,17 @@ public class Framework
|
|||
//TODO(@alexzatsepin): remove TOKEN_MAPSME from this list.
|
||||
public static final int TOKEN_MAPSME = 3;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({ SUBSCRIPTION_ACTIVE, SUBSCRIPTION_NOT_ACTIVE, SUBSCRIPTION_VALIDATION_FAILURE })
|
||||
public @interface SubscriptionValidationCode {}
|
||||
|
||||
// Subscription is active.
|
||||
public static final int SUBSCRIPTION_ACTIVE = 0;
|
||||
// Subscription is not active.
|
||||
public static final int SUBSCRIPTION_NOT_ACTIVE = 1;
|
||||
// Validation failed, real subscription status is unknown, current one acts.
|
||||
public static final int SUBSCRIPTION_VALIDATION_FAILURE = 2;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public interface MapObjectListener
|
||||
{
|
||||
|
@ -134,6 +145,12 @@ public class Framework
|
|||
void onRoutePointsLoaded(boolean success);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public interface SubscriptionValidationListener
|
||||
{
|
||||
void onValidateSubscription(@SubscriptionValidationCode int code);
|
||||
}
|
||||
|
||||
public static class Params3dMode
|
||||
{
|
||||
public boolean enabled;
|
||||
|
@ -455,4 +472,9 @@ public class Framework
|
|||
public static native String nativeGetMegafonDownloaderBannerUrl();
|
||||
|
||||
public static native void nativeMakeCrash();
|
||||
|
||||
public static native void nativeValidateSubscription(@NonNull String receiptData);
|
||||
public static native void nativeSetSubscriptionValidationListener(
|
||||
@Nullable SubscriptionValidationListener listener);
|
||||
public static native boolean nativeHasActiveSubscription();
|
||||
}
|
||||
|
|
|
@ -503,8 +503,6 @@ Framework::Framework(FrameworkParams const & params)
|
|||
|
||||
InitTransliteration();
|
||||
LOG(LDEBUG, ("Transliterators initialized"));
|
||||
|
||||
m_subscription->Validate();
|
||||
}
|
||||
|
||||
Framework::~Framework()
|
||||
|
|
|
@ -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;
|
||||
|
||||
m_isActive = isValid;
|
||||
if (isValid)
|
||||
std::string const url = ValidationUrl();
|
||||
if (url.empty())
|
||||
{
|
||||
m_subscriptionId = GetSubscriptionId();
|
||||
GetPlatform().GetSecureStorage().Save(kSubscriptionId, m_subscriptionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetPlatform().GetSecureStorage().Remove(kSubscriptionId);
|
||||
ApplyValidation(ValidationCode::NotActive);
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto & listener : m_listeners)
|
||||
listener->OnSubscriptionChanged(isValid);
|
||||
auto const status = GetPlatform().ConnectionStatus();
|
||||
if (status == Platform::EConnectionType::CONNECTION_NONE)
|
||||
{
|
||||
ApplyValidation(ValidationCode::Failure);
|
||||
return;
|
||||
}
|
||||
|
||||
GetPlatform().RunTask(Platform::Thread::Network, [this, url, receiptData, accessToken]()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
if (m_validationCallback)
|
||||
m_validationCallback(code);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue