ente/lib/services/billing_service.dart

209 lines
5.8 KiB
Dart
Raw Normal View History

// @dart=2.9
import 'dart:io';
import 'package:dio/dio.dart';
// import 'package:flutter/foundation.dart';
// import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
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/errors.dart';
2021-01-05 09:41:32 +00:00
import 'package:photos/core/network.dart';
import 'package:photos/models/billing_plan.dart';
import 'package:photos/models/subscription.dart';
2021-01-05 09:41:32 +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",
);
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 {
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();
final _config = Configuration.instance;
2021-01-05 09:41:32 +00:00
bool _isOnSubscriptionPage = false;
2021-03-02 06:35:10 +00:00
Future<BillingPlans> _future;
2021-01-05 09:41:32 +00:00
Future<void> init() async {
InAppPurchaseConnection.enablePendingPurchases();
// if (Platform.isIOS && kDebugMode) {
// await FlutterInappPurchase.instance.initConnection;
// FlutterInappPurchase.instance.clearTransactionIOS();
// }
InAppPurchaseConnection.instance.purchaseUpdatedStream.listen((purchases) {
if (_isOnSubscriptionPage) {
return;
}
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) {
if (response != null) {
InAppPurchaseConnection.instance.completePurchase(purchase);
}
});
} else if (Platform.isIOS && purchase.pendingCompletePurchase) {
InAppPurchaseConnection.instance.completePurchase(purchase);
}
}
});
}
2021-03-17 21:11:31 +00:00
void clearCache() {
_future = null;
}
2021-03-02 06:35:10 +00:00
Future<BillingPlans> getBillingPlans() {
_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;
}
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-18 16:20:53 +00:00
options: Options(
headers: {
"X-Auth-Token": _config.getToken(),
},
),
);
return Subscription.fromMap(response.data["subscription"]);
} on DioError catch (e) {
if (e.response != null && e.response.statusCode == 409) {
throw SubscriptionAlreadyClaimedError();
} else {
rethrow;
}
} catch (e, s) {
_logger.severe(e, s);
2021-07-22 18:41:58 +00:00
rethrow;
2021-01-18 16:20:53 +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;
} 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;
}
}
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",
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;
}
}
void setIsOnSubscriptionPage(bool isOnSubscriptionPage) {
_isOnSubscriptionPage = isOnSubscriptionPage;
}
2021-01-05 09:41:32 +00:00
}