2021-01-10 21:31:13 +00:00
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
import 'package:dio/dio.dart';
|
2021-02-16 16:15:23 +00:00
|
|
|
// import 'package:flutter/foundation.dart';
|
2021-02-01 19:14:04 +00:00
|
|
|
// import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
|
2021-01-31 20:41:41 +00:00
|
|
|
import 'package:in_app_purchase/in_app_purchase.dart';
|
2021-01-05 09:41:32 +00:00
|
|
|
import 'package:logging/logging.dart';
|
|
|
|
import 'package:photos/core/configuration.dart';
|
|
|
|
import 'package:photos/core/network.dart';
|
|
|
|
import 'package:photos/models/billing_plan.dart';
|
2021-01-06 11:56:03 +00:00
|
|
|
import 'package:photos/models/subscription.dart';
|
2021-01-05 09:41:32 +00:00
|
|
|
|
2021-08-25 11:04:03 +00:00
|
|
|
const kWebPaymentRedirectUrl = "https://payments.ente.io/frameRedirect";
|
2022-06-11 08:23:52 +00:00
|
|
|
const kWebPaymentBaseEndpoint = String.fromEnvironment(
|
|
|
|
"web-payment",
|
|
|
|
defaultValue: "https://payments.ente.io",
|
|
|
|
);
|
2021-08-25 11:04:03 +00:00
|
|
|
|
2022-06-11 08:23:52 +00:00
|
|
|
const kFamilyPlanManagementUrl = String.fromEnvironment(
|
|
|
|
"web-family",
|
|
|
|
defaultValue: "https://family.ente.io",
|
|
|
|
);
|
2022-04-20 20:14:44 +00:00
|
|
|
|
2021-01-05 09:41:32 +00:00
|
|
|
class BillingService {
|
2021-01-27 08:55:58 +00:00
|
|
|
BillingService._privateConstructor();
|
2021-01-05 09:41:32 +00:00
|
|
|
|
|
|
|
static final BillingService instance = BillingService._privateConstructor();
|
|
|
|
|
|
|
|
final _logger = Logger("BillingService");
|
|
|
|
final _dio = Network.instance.getDio();
|
2021-01-10 21:31:13 +00:00
|
|
|
final _config = Configuration.instance;
|
2021-01-05 09:41:32 +00:00
|
|
|
|
2021-01-27 08:55:58 +00:00
|
|
|
bool _isOnSubscriptionPage = false;
|
|
|
|
|
2021-03-02 06:35:10 +00:00
|
|
|
Future<BillingPlans> _future;
|
2021-01-05 09:41:32 +00:00
|
|
|
|
2021-01-06 11:56:03 +00:00
|
|
|
Future<void> init() async {
|
2021-01-31 20:41:41 +00:00
|
|
|
InAppPurchaseConnection.enablePendingPurchases();
|
2021-02-16 16:15:23 +00:00
|
|
|
// if (Platform.isIOS && kDebugMode) {
|
|
|
|
// await FlutterInappPurchase.instance.initConnection;
|
|
|
|
// FlutterInappPurchase.instance.clearTransactionIOS();
|
|
|
|
// }
|
2021-01-31 20:41:41 +00:00
|
|
|
InAppPurchaseConnection.instance.purchaseUpdatedStream.listen((purchases) {
|
2021-01-27 08:55:58 +00:00
|
|
|
if (_isOnSubscriptionPage) {
|
|
|
|
return;
|
|
|
|
}
|
2021-01-31 20:41:41 +00:00
|
|
|
for (final purchase in purchases) {
|
|
|
|
if (purchase.status == PurchaseStatus.purchased) {
|
2022-06-11 08:23:52 +00:00
|
|
|
verifySubscription(
|
|
|
|
purchase.productID,
|
|
|
|
purchase.verificationData.serverVerificationData,
|
|
|
|
).then((response) {
|
2021-01-31 20:41:41 +00:00
|
|
|
if (response != null) {
|
|
|
|
InAppPurchaseConnection.instance.completePurchase(purchase);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else if (Platform.isIOS && purchase.pendingCompletePurchase) {
|
|
|
|
InAppPurchaseConnection.instance.completePurchase(purchase);
|
2021-01-27 08:55:58 +00:00
|
|
|
}
|
2021-01-31 20:41:41 +00:00
|
|
|
}
|
2021-01-27 08:55:58 +00:00
|
|
|
});
|
2021-01-06 11:56:03 +00:00
|
|
|
}
|
|
|
|
|
2021-03-17 21:11:31 +00:00
|
|
|
void clearCache() {
|
|
|
|
_future = null;
|
|
|
|
}
|
|
|
|
|
2021-03-02 06:35:10 +00:00
|
|
|
Future<BillingPlans> getBillingPlans() {
|
2022-01-14 13:51:05 +00:00
|
|
|
_future ??= (_config.getToken() == null
|
|
|
|
? _fetchPublicBillingPlans()
|
|
|
|
: _fetchPrivateBillingPlans())
|
2021-07-26 16:09:07 +00:00
|
|
|
.then((response) {
|
2021-07-22 18:41:58 +00:00
|
|
|
return BillingPlans.fromMap(response.data);
|
|
|
|
});
|
2021-01-05 09:41:32 +00:00
|
|
|
return _future;
|
|
|
|
}
|
2021-01-06 11:56:03 +00:00
|
|
|
|
2022-01-14 13:51:05 +00:00
|
|
|
Future<Response<dynamic>> _fetchPrivateBillingPlans() {
|
|
|
|
return _dio.get(
|
|
|
|
_config.getHttpEndpoint() + "/billing/user-plans/",
|
|
|
|
options: Options(
|
|
|
|
headers: {
|
|
|
|
"X-Auth-Token": _config.getToken(),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Response<dynamic>> _fetchPublicBillingPlans() {
|
|
|
|
return _dio.get(_config.getHttpEndpoint() + "/billing/plans/v2");
|
|
|
|
}
|
|
|
|
|
2021-01-18 16:20:53 +00:00
|
|
|
Future<Subscription> verifySubscription(
|
2021-07-07 21:10:56 +00:00
|
|
|
final productID,
|
|
|
|
final verificationData, {
|
|
|
|
final paymentProvider,
|
|
|
|
}) async {
|
2021-01-18 16:20:53 +00:00
|
|
|
try {
|
|
|
|
final response = await _dio.post(
|
|
|
|
_config.getHttpEndpoint() + "/billing/verify-subscription",
|
|
|
|
data: {
|
2021-07-07 21:10:56 +00:00
|
|
|
"paymentProvider": paymentProvider ??
|
|
|
|
(Platform.isAndroid ? "playstore" : "appstore"),
|
2021-01-27 10:58:23 +00:00
|
|
|
"productID": productID,
|
2021-01-18 16:20:53 +00:00
|
|
|
"verificationData": verificationData,
|
2021-01-10 21:31:13 +00:00
|
|
|
},
|
2021-01-18 16:20:53 +00:00
|
|
|
options: Options(
|
|
|
|
headers: {
|
|
|
|
"X-Auth-Token": _config.getToken(),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
);
|
2021-05-19 16:23:01 +00:00
|
|
|
return Subscription.fromMap(response.data["subscription"]);
|
2021-08-19 11:23:48 +00:00
|
|
|
} catch (e, s) {
|
|
|
|
_logger.severe(e, s);
|
2021-07-22 18:41:58 +00:00
|
|
|
rethrow;
|
2021-01-18 16:20:53 +00:00
|
|
|
}
|
2021-01-10 21:31:13 +00:00
|
|
|
}
|
|
|
|
|
2021-01-29 11:42:15 +00:00
|
|
|
Future<Subscription> fetchSubscription() async {
|
|
|
|
try {
|
|
|
|
final response = await _dio.get(
|
|
|
|
_config.getHttpEndpoint() + "/billing/subscription",
|
|
|
|
options: Options(
|
|
|
|
headers: {
|
|
|
|
"X-Auth-Token": _config.getToken(),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
);
|
|
|
|
final subscription = Subscription.fromMap(response.data["subscription"]);
|
|
|
|
return subscription;
|
2021-08-19 11:23:48 +00:00
|
|
|
} on DioError catch (e, s) {
|
|
|
|
_logger.severe(e, s);
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Subscription> cancelStripeSubscription() async {
|
|
|
|
try {
|
|
|
|
final response = await _dio.post(
|
|
|
|
_config.getHttpEndpoint() + "/billing/stripe/cancel-subscription",
|
|
|
|
options: Options(
|
|
|
|
headers: {
|
|
|
|
"X-Auth-Token": _config.getToken(),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
);
|
|
|
|
final subscription = Subscription.fromMap(response.data["subscription"]);
|
|
|
|
return subscription;
|
|
|
|
} on DioError catch (e, s) {
|
|
|
|
_logger.severe(e, s);
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Subscription> activateStripeSubscription() async {
|
|
|
|
try {
|
|
|
|
final response = await _dio.post(
|
|
|
|
_config.getHttpEndpoint() + "/billing/stripe/activate-subscription",
|
|
|
|
options: Options(
|
|
|
|
headers: {
|
|
|
|
"X-Auth-Token": _config.getToken(),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
);
|
|
|
|
final subscription = Subscription.fromMap(response.data["subscription"]);
|
|
|
|
return subscription;
|
|
|
|
} on DioError catch (e, s) {
|
|
|
|
_logger.severe(e, s);
|
2021-07-22 18:41:58 +00:00
|
|
|
rethrow;
|
2021-01-29 11:42:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-11 08:23:52 +00:00
|
|
|
Future<String> getStripeCustomerPortalUrl({
|
|
|
|
String endpoint = kWebPaymentRedirectUrl,
|
|
|
|
}) async {
|
2021-08-19 17:22:14 +00:00
|
|
|
try {
|
|
|
|
final response = await _dio.get(
|
|
|
|
_config.getHttpEndpoint() + "/billing/stripe/customer-portal",
|
2021-08-25 11:04:03 +00:00
|
|
|
queryParameters: {
|
|
|
|
"redirectURL": kWebPaymentRedirectUrl,
|
|
|
|
},
|
2021-08-19 17:22:14 +00:00
|
|
|
options: Options(
|
|
|
|
headers: {
|
|
|
|
"X-Auth-Token": _config.getToken(),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
);
|
|
|
|
return response.data["url"];
|
|
|
|
} on DioError catch (e, s) {
|
|
|
|
_logger.severe(e, s);
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-27 08:55:58 +00:00
|
|
|
void setIsOnSubscriptionPage(bool isOnSubscriptionPage) {
|
|
|
|
_isOnSubscriptionPage = isOnSubscriptionPage;
|
|
|
|
}
|
2021-01-05 09:41:32 +00:00
|
|
|
}
|