mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-22 02:01:08 +07:00
fix: merge with new structure, fix payment flow
This commit is contained in:
@@ -33,6 +33,9 @@
|
|||||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include "platforms/ios/ios_controller.h"
|
#include "platforms/ios/ios_controller.h"
|
||||||
#include <AmneziaVPN-Swift.h>
|
#include <AmneziaVPN-Swift.h>
|
||||||
|
#elif defined(Q_OS_ANDROID)
|
||||||
|
#include "platforms/android/android_controller.h"
|
||||||
|
#include <QtConcurrent>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
@@ -327,9 +330,10 @@ ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCoun
|
|||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType,
|
ErrorCode SubscriptionController::importServiceFromMarket(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const ProtocolData &protocolData,
|
const QString &serviceProtocol, const ProtocolData &protocolData,
|
||||||
const QString &transactionId, bool isTestPurchase,
|
const QString &transactionId, bool isTestPurchase,
|
||||||
|
ServerConfig &serverConfig,
|
||||||
int *duplicateServerIndex)
|
int *duplicateServerIndex)
|
||||||
{
|
{
|
||||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||||
@@ -786,8 +790,9 @@ ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCou
|
|||||||
bool isTestPurchase = IosController::Instance()->isTestFlight();
|
bool isTestPurchase = IosController::Instance()->isTestFlight();
|
||||||
|
|
||||||
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||||
return importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData,
|
ServerConfig serverConfig;
|
||||||
originalTransactionId, isTestPurchase, duplicateServerIndex);
|
return importServiceFromMarket(userCountryCode, serviceType, serviceProtocol, protocolData,
|
||||||
|
originalTransactionId, isTestPurchase, serverConfig, duplicateServerIndex);
|
||||||
#else
|
#else
|
||||||
Q_UNUSED(userCountryCode);
|
Q_UNUSED(userCountryCode);
|
||||||
Q_UNUSED(serviceType);
|
Q_UNUSED(serviceType);
|
||||||
@@ -797,6 +802,93 @@ ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCou
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorCode SubscriptionController::processPlayMarketPurchase(const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &serviceProtocol, const QString &productId,
|
||||||
|
ServerConfig &serverConfig,
|
||||||
|
int *duplicateServerIndex)
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_ANDROID)
|
||||||
|
auto androidController = AndroidController::instance();
|
||||||
|
QString purchaseToken;
|
||||||
|
bool purchaseOk = false;
|
||||||
|
|
||||||
|
QFutureWatcher<QPair<bool, QString>> watcher;
|
||||||
|
QEventLoop waitLoop;
|
||||||
|
QObject::connect(&watcher, &QFutureWatcher<QPair<bool, QString>>::finished, &waitLoop, &QEventLoop::quit);
|
||||||
|
|
||||||
|
QFuture<QPair<bool, QString>> future = QtConcurrent::run([androidController, productId]() {
|
||||||
|
QJsonObject plansResult = androidController->getSubscriptionPlans();
|
||||||
|
int responseCode = plansResult.value("responseCode").toInt(-1);
|
||||||
|
if (responseCode != 0) {
|
||||||
|
qWarning() << "[Billing] Failed to get subscription plans, responseCode:" << responseCode;
|
||||||
|
return qMakePair(false, QString());
|
||||||
|
}
|
||||||
|
QJsonArray products = plansResult.value("products").toArray();
|
||||||
|
QString offerToken;
|
||||||
|
for (const QJsonValue &productValue : products) {
|
||||||
|
QJsonObject product = productValue.toObject();
|
||||||
|
if (product.value("productId").toString() == productId) {
|
||||||
|
QJsonArray offers = product.value("offers").toArray();
|
||||||
|
if (!offers.isEmpty()) {
|
||||||
|
offerToken = offers.at(0).toObject().value("offerToken").toString();
|
||||||
|
qInfo() << "[Billing] Found offer token for product:" << productId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (offerToken.isEmpty()) {
|
||||||
|
qWarning() << "[Billing] No offer token found for product:" << productId;
|
||||||
|
return qMakePair(false, QString());
|
||||||
|
}
|
||||||
|
QJsonObject purchaseResult = androidController->purchaseSubscription(offerToken);
|
||||||
|
responseCode = purchaseResult.value("responseCode").toInt(-1);
|
||||||
|
if (responseCode != 0) {
|
||||||
|
qWarning() << "[Billing] Purchase failed, responseCode:" << responseCode;
|
||||||
|
return qMakePair(false, QString());
|
||||||
|
}
|
||||||
|
QJsonArray purchases = purchaseResult.value("purchases").toArray();
|
||||||
|
if (purchases.isEmpty()) {
|
||||||
|
qWarning() << "[Billing] Purchase succeeded but no purchases returned";
|
||||||
|
return qMakePair(false, QString());
|
||||||
|
}
|
||||||
|
QJsonObject purchase = purchases.at(0).toObject();
|
||||||
|
QString token = purchase.value("purchaseToken").toString();
|
||||||
|
bool isAcknowledged = purchase.value("isAcknowledged").toBool();
|
||||||
|
qInfo() << "[Billing] Purchase success. purchaseToken:" << token << "isAcknowledged:" << isAcknowledged;
|
||||||
|
if (!isAcknowledged) {
|
||||||
|
QJsonObject ackResult = androidController->acknowledgePurchase(token);
|
||||||
|
if (ackResult.value("responseCode").toInt(-1) != 0) {
|
||||||
|
qWarning() << "[Billing] Acknowledge failed";
|
||||||
|
} else {
|
||||||
|
qInfo() << "[Billing] Purchase acknowledged successfully";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return qMakePair(true, token);
|
||||||
|
});
|
||||||
|
|
||||||
|
watcher.setFuture(future);
|
||||||
|
waitLoop.exec();
|
||||||
|
|
||||||
|
purchaseOk = watcher.result().first;
|
||||||
|
purchaseToken = watcher.result().second;
|
||||||
|
|
||||||
|
if (!purchaseOk || purchaseToken.isEmpty()) {
|
||||||
|
return ErrorCode::ApiPurchaseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||||
|
return importServiceFromMarket(userCountryCode, serviceType, serviceProtocol, protocolData, //here we should now all about type payment
|
||||||
|
purchaseToken, false, serverConfig, duplicateServerIndex);
|
||||||
|
#else
|
||||||
|
Q_UNUSED(userCountryCode);
|
||||||
|
Q_UNUSED(serviceType);
|
||||||
|
Q_UNUSED(serviceProtocol);
|
||||||
|
Q_UNUSED(productId);
|
||||||
|
Q_UNUSED(serverConfig);
|
||||||
|
return ErrorCode::ApiPurchaseError;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
SubscriptionController::AppStoreRestoreResult SubscriptionController::processAppStoreRestore(const QString &userCountryCode, const QString &serviceType,
|
SubscriptionController::AppStoreRestoreResult SubscriptionController::processAppStoreRestore(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol)
|
const QString &serviceProtocol)
|
||||||
{
|
{
|
||||||
@@ -852,8 +944,9 @@ SubscriptionController::AppStoreRestoreResult SubscriptionController::processApp
|
|||||||
|
|
||||||
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||||
int currentDuplicateServerIndex = -1;
|
int currentDuplicateServerIndex = -1;
|
||||||
ErrorCode errorCode = importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData,
|
ServerConfig serverConfig;
|
||||||
originalTransactionId, isTestPurchase,
|
ErrorCode errorCode = importServiceFromMarket(userCountryCode, serviceType, serviceProtocol, protocolData,
|
||||||
|
originalTransactionId, isTestPurchase, serverConfig,
|
||||||
¤tDuplicateServerIndex);
|
¤tDuplicateServerIndex);
|
||||||
|
|
||||||
if (errorCode == ErrorCode::ApiConfigAlreadyAdded) {
|
if (errorCode == ErrorCode::ApiConfigAlreadyAdded) {
|
||||||
@@ -885,14 +978,127 @@ SubscriptionController::AppStoreRestoreResult SubscriptionController::processApp
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::getAccountInfo(const QString &serverId, QJsonObject &accountInfo)
|
SubscriptionController::PlayMarketRestoreResult SubscriptionController::processPlayMarketRestore(const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &serviceProtocol)
|
||||||
{
|
{
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
PlayMarketRestoreResult result;
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
|
#if defined(Q_OS_ANDROID)
|
||||||
|
auto androidController = AndroidController::instance();
|
||||||
|
|
||||||
|
QJsonObject purchasesResult;
|
||||||
|
{
|
||||||
|
QFutureWatcher<QJsonObject> queryWatcher;
|
||||||
|
QEventLoop queryLoop;
|
||||||
|
QObject::connect(&queryWatcher, &QFutureWatcher<QJsonObject>::finished, &queryLoop, &QEventLoop::quit);
|
||||||
|
QFuture<QJsonObject> queryFuture = QtConcurrent::run([androidController]() {
|
||||||
|
return androidController->queryPurchases();
|
||||||
|
});
|
||||||
|
queryWatcher.setFuture(queryFuture);
|
||||||
|
queryLoop.exec();
|
||||||
|
purchasesResult = queryWatcher.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
int responseCode = purchasesResult.value("responseCode").toInt(-1);
|
||||||
|
if (responseCode != 0) {
|
||||||
|
qWarning().noquote() << "[Billing] queryPurchases failed, responseCode =" << responseCode;
|
||||||
|
result.errorCode = ErrorCode::ApiPurchaseError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray purchases = purchasesResult.value("purchases").toArray();
|
||||||
|
if (purchases.isEmpty()) {
|
||||||
|
qInfo().noquote() << "[Billing] Restore completed, but no purchases were found";
|
||||||
|
result.errorCode = ErrorCode::ApiNoPurchasesToRestore;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> processedTokens;
|
||||||
|
for (const QJsonValue &purchaseValue : std::as_const(purchases)) {
|
||||||
|
const QJsonObject purchaseObj = purchaseValue.toObject();
|
||||||
|
const QString purchaseToken = purchaseObj.value("purchaseToken").toString();
|
||||||
|
|
||||||
|
if (purchaseToken.isEmpty()) {
|
||||||
|
qWarning().noquote() << "[Billing] Skipping purchase without purchaseToken";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processedTokens.contains(purchaseToken)) {
|
||||||
|
result.duplicateCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
processedTokens.insert(purchaseToken);
|
||||||
|
|
||||||
|
qInfo().noquote() << "[Billing] Restoring subscription with purchaseToken =" << purchaseToken;
|
||||||
|
|
||||||
|
{
|
||||||
|
QFutureWatcher<QJsonObject> ackWatcher;
|
||||||
|
QEventLoop ackLoop;
|
||||||
|
QObject::connect(&ackWatcher, &QFutureWatcher<QJsonObject>::finished, &ackLoop, &QEventLoop::quit);
|
||||||
|
QFuture<QJsonObject> ackFuture = QtConcurrent::run([androidController, purchaseToken]() {
|
||||||
|
return androidController->acknowledgePurchase(purchaseToken);
|
||||||
|
});
|
||||||
|
ackWatcher.setFuture(ackFuture);
|
||||||
|
ackLoop.exec();
|
||||||
|
QJsonObject ackResult = ackWatcher.result();
|
||||||
|
int ackCode = ackResult.value("responseCode").toInt(-1);
|
||||||
|
if (ackCode != 0) {
|
||||||
|
qWarning().noquote() << "[Billing] acknowledgePurchase failed, responseCode =" << ackCode;
|
||||||
|
} else {
|
||||||
|
qInfo().noquote() << "[Billing] Purchase acknowledged successfully";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||||
|
ServerConfig serverConfig;
|
||||||
|
int currentDuplicateServerIndex = -1;
|
||||||
|
ErrorCode errorCode = importServiceFromMarket(userCountryCode, serviceType, serviceProtocol, protocolData,
|
||||||
|
purchaseToken, false, serverConfig,
|
||||||
|
¤tDuplicateServerIndex);
|
||||||
|
|
||||||
|
if (errorCode == ErrorCode::ApiConfigAlreadyAdded) {
|
||||||
|
result.duplicateConfigAlreadyPresent = true;
|
||||||
|
if (result.duplicateServerIndex < 0) {
|
||||||
|
result.duplicateServerIndex = currentDuplicateServerIndex;
|
||||||
|
}
|
||||||
|
qInfo().noquote() << "[Billing] Skipping purchase" << purchaseToken
|
||||||
|
<< "because subscription config with the same vpn_key already exists";
|
||||||
|
} else if (errorCode != ErrorCode::NoError) {
|
||||||
|
qWarning().noquote() << "[Billing] Failed to process restored subscription for purchaseToken =" << purchaseToken;
|
||||||
|
result.errorCode = errorCode;
|
||||||
|
} else {
|
||||||
|
result.hasInstalledConfig = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.hasInstalledConfig) {
|
||||||
|
result.errorCode = result.duplicateConfigAlreadyPresent ? ErrorCode::ApiConfigAlreadyAdded : ErrorCode::ApiNoPurchasesToRestore;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
Q_UNUSED(userCountryCode);
|
||||||
|
Q_UNUSED(serviceType);
|
||||||
|
Q_UNUSED(serviceProtocol);
|
||||||
|
result.errorCode = ErrorCode::ApiPurchaseError;
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode SubscriptionController::getAccountInfo(int serverIndex, QJsonObject &accountInfo)
|
||||||
|
{
|
||||||
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
|
|
||||||
|
if (!serverConfigModel.isApiV2()) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) {
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
||||||
|
|
||||||
QJsonObject authDataJson = apiV2->authData.toJson();
|
QJsonObject authDataJson = apiV2->authData.toJson();
|
||||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||||
QString(APP_VERSION),
|
QString(APP_VERSION),
|
||||||
|
|||||||
@@ -61,9 +61,10 @@ public:
|
|||||||
ErrorCode importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
|
ErrorCode importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const QString &email);
|
const QString &serviceProtocol, const QString &email);
|
||||||
|
|
||||||
ErrorCode importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType,
|
ErrorCode importServiceFromMarket(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const ProtocolData &protocolData,
|
const QString &serviceProtocol, const ProtocolData &protocolData,
|
||||||
const QString &transactionId, bool isTestPurchase,
|
const QString &transactionId, bool isTestPurchase,
|
||||||
|
ServerConfig &serverConfig,
|
||||||
int *duplicateServerIndex = nullptr);
|
int *duplicateServerIndex = nullptr);
|
||||||
|
|
||||||
ErrorCode updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, bool isConnectEvent);
|
ErrorCode updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, bool isConnectEvent);
|
||||||
@@ -99,10 +100,24 @@ public:
|
|||||||
ErrorCode errorCode = ErrorCode::NoError;
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PlayMarketRestoreResult
|
||||||
|
{
|
||||||
|
bool hasInstalledConfig = false;
|
||||||
|
bool duplicateConfigAlreadyPresent = false;
|
||||||
|
int duplicateCount = 0;
|
||||||
|
int duplicateServerIndex = -1;
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
|
||||||
ErrorCode processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
|
ErrorCode processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const QString &productId,
|
const QString &serviceProtocol, const QString &productId,
|
||||||
int *duplicateServerIndex = nullptr);
|
int *duplicateServerIndex = nullptr);
|
||||||
|
|
||||||
|
ErrorCode processPlayMarketPurchase(const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &serviceProtocol, const QString &productId,
|
||||||
|
ServerConfig &serverConfig,
|
||||||
|
int *duplicateServerIndex = nullptr);
|
||||||
|
|
||||||
AppStoreRestoreResult processAppStoreRestore(const QString &userCountryCode, const QString &serviceType,
|
AppStoreRestoreResult processAppStoreRestore(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol);
|
const QString &serviceProtocol);
|
||||||
|
|
||||||
@@ -111,6 +126,9 @@ public:
|
|||||||
const QString &captchaId, const QString &captchaSolution,
|
const QString &captchaId, const QString &captchaSolution,
|
||||||
CaptchaInfo *retryCaptchaOut = nullptr);
|
CaptchaInfo *retryCaptchaOut = nullptr);
|
||||||
|
|
||||||
|
PlayMarketRestoreResult processPlayMarketRestore(const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &serviceProtocol);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase = false);
|
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase = false);
|
||||||
bool isApiKeyExpired(const QString &serverId) const;
|
bool isApiKeyExpired(const QString &serverId) const;
|
||||||
|
|||||||
@@ -227,6 +227,38 @@ bool SubscriptionUiController::importPremiumFromAppStore(const QString &storePro
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SubscriptionUiController::importPremiumFromPlayMarket(const QString &storeProductId)
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_ANDROID)
|
||||||
|
QString productId = storeProductId.trimmed();
|
||||||
|
if (productId.isEmpty()) {
|
||||||
|
productId = QStringLiteral("premium");
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerConfig serverConfig;
|
||||||
|
int duplicateServerIndex = -1;
|
||||||
|
ErrorCode errorCode = m_subscriptionController->processPlayMarketPurchase(
|
||||||
|
m_apiServicesModel->getCountryCode(),
|
||||||
|
m_apiServicesModel->getSelectedServiceType(),
|
||||||
|
m_apiServicesModel->getSelectedServiceProtocol(),
|
||||||
|
productId,
|
||||||
|
serverConfig,
|
||||||
|
&duplicateServerIndex);
|
||||||
|
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
if (errorCode == ErrorCode::ApiConfigAlreadyAdded) {
|
||||||
|
emit installServerFromApiFinished(tr("This subscription has already been added"), duplicateServerIndex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
emit errorOccurred(errorCode);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit installServerFromApiFinished(tr("%1 has been added to the app").arg(m_apiServicesModel->getSelectedServiceName()));
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SubscriptionUiController::restoreServiceFromAppStore()
|
bool SubscriptionUiController::restoreServiceFromAppStore()
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
@@ -281,6 +313,59 @@ bool SubscriptionUiController::restoreServiceFromAppStore()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SubscriptionUiController::restoreServiceFromPlayMarket()
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_ANDROID)
|
||||||
|
const QString premiumServiceType = QStringLiteral("amnezia-premium");
|
||||||
|
|
||||||
|
if (!fillAvailableServices()) {
|
||||||
|
qWarning().noquote() << "[Billing] Unable to fetch services list before restore";
|
||||||
|
emit errorOccurred(ErrorCode::ApiServicesMissingError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_apiServicesModel->rowCount() <= 0) {
|
||||||
|
emit errorOccurred(ErrorCode::ApiServicesMissingError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool premiumSelected = false;
|
||||||
|
for (int i = 0; i < m_apiServicesModel->rowCount(); ++i) {
|
||||||
|
m_apiServicesModel->setServiceIndex(i);
|
||||||
|
if (m_apiServicesModel->getSelectedServiceType() == premiumServiceType) {
|
||||||
|
premiumSelected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!premiumSelected) {
|
||||||
|
emit errorOccurred(ErrorCode::ApiServicesMissingError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubscriptionController::PlayMarketRestoreResult result = m_subscriptionController->processPlayMarketRestore(
|
||||||
|
m_apiServicesModel->getCountryCode(),
|
||||||
|
m_apiServicesModel->getSelectedServiceType(),
|
||||||
|
m_apiServicesModel->getSelectedServiceProtocol());
|
||||||
|
|
||||||
|
if (!result.hasInstalledConfig) {
|
||||||
|
if (result.duplicateConfigAlreadyPresent) {
|
||||||
|
emit installServerFromApiFinished(tr("This subscription has already been added"), result.duplicateServerIndex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
emit errorOccurred(result.errorCode);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit installServerFromApiFinished(tr("Subscription restored successfully."));
|
||||||
|
if (result.duplicateCount > 0) {
|
||||||
|
qInfo().noquote() << "[Billing] Skipped" << result.duplicateCount
|
||||||
|
<< "duplicate restored purchases for tokens already processed";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SubscriptionUiController::importFreeFromGateway()
|
bool SubscriptionUiController::importFreeFromGateway()
|
||||||
{
|
{
|
||||||
QString userCountryCode = m_apiServicesModel->getCountryCode();
|
QString userCountryCode = m_apiServicesModel->getCountryCode();
|
||||||
|
|||||||
@@ -45,8 +45,10 @@ public slots:
|
|||||||
|
|
||||||
bool fillAvailableServices();
|
bool fillAvailableServices();
|
||||||
bool importPremiumFromAppStore(const QString &storeProductId);
|
bool importPremiumFromAppStore(const QString &storeProductId);
|
||||||
|
bool importPremiumFromPlayMarket(const QString &storeProductId);
|
||||||
bool importFreeFromGateway();
|
bool importFreeFromGateway();
|
||||||
bool restoreServiceFromAppStore();
|
bool restoreServiceFromAppStore();
|
||||||
|
bool restoreServiceFromPlayMarket();
|
||||||
bool importTrialFromGateway(const QString &email);
|
bool importTrialFromGateway(const QString &email);
|
||||||
bool updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, const QString &newCountryName,
|
bool updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, const QString &newCountryName,
|
||||||
bool reloadServiceConfig = false);
|
bool reloadServiceConfig = false);
|
||||||
|
|||||||
@@ -187,6 +187,13 @@ PageType {
|
|||||||
PageController.showBusyIndicator(false)
|
PageController.showBusyIndicator(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (Qt.platform.os === "android") {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
var androidStoreId = plan.storeProductId !== undefined ? String(plan.storeProductId) : ""
|
||||||
|
SubscriptionUiController.importPremiumFromPlayMarket(androidStoreId)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
if (plan.checkoutUrl) {
|
if (plan.checkoutUrl) {
|
||||||
Qt.openUrlExternally(plan.checkoutUrl)
|
Qt.openUrlExternally(plan.checkoutUrl)
|
||||||
PageController.closePage()
|
PageController.closePage()
|
||||||
|
|||||||
@@ -369,7 +369,11 @@ PageType {
|
|||||||
property bool isVisible: Qt.platform.os === "ios" || IsMacOsNeBuild || Qt.platform.os === "android"
|
property bool isVisible: Qt.platform.os === "ios" || IsMacOsNeBuild || Qt.platform.os === "android"
|
||||||
property var handler: function() {
|
property var handler: function() {
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
SubscriptionUiController.restoreServiceFromAppStore()
|
if (Qt.platform.os === "android") {
|
||||||
|
SubscriptionUiController.restoreServiceFromPlayMarket()
|
||||||
|
} else {
|
||||||
|
SubscriptionUiController.restoreServiceFromAppStore()
|
||||||
|
}
|
||||||
PageController.showBusyIndicator(false)
|
PageController.showBusyIndicator(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user