Я скопировал этот контроллер Superwall Superwall RevenueCat из Superwall Docs для обработки покупок и восстановления и других вещей в iOS и Android, и в одном проекте я использовал это таким, какой он есть, и он отлично справился Google Play and Store Porducts Я не знаю, что все было так же, почему в другом проекте этот контроллер имеет столько ошибок, и я не знаю, как исправить этот Beacuse в предыдущих проектах, я ничего не изменил в контроллере < /p>
контроллер Superwall < /p>
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'package:superwallkit_flutter/superwallkit_flutter.dart' hide LogLevel;
class RCPurchaseController extends PurchaseController {
// MARK: Configure and sync subscription Status
/// Makes sure that Superwall knows the customers subscription status by
/// changing `Superwall.shared.subscriptionStatus`
Future configureAndSyncSubscriptionStatus() async {
// Configure RevenueCat
await Purchases.setLogLevel(LogLevel.debug);
final configuration = Platform.isIOS
? PurchasesConfiguration('ios_rc_key')
: PurchasesConfiguration('android_rc_key');
await Purchases.configure(configuration);
// Listen for changes
Purchases.addCustomerInfoUpdateListener((customerInfo) async {
// Gets called whenever new CustomerInfo is available
final entitlements = customerInfo.entitlements.active.keys
.map((id) => Entitlement(id: id))
.toSet();
final hasActiveEntitlementOrSubscription = customerInfo
.hasActiveEntitlementOrSubscription(); // Why? -> https://www.revenuecat.com/docs/entitlements#entitlements
if (hasActiveEntitlementOrSubscription) {
await Superwall.shared.setSubscriptionStatus(
SubscriptionStatusActive(entitlements: entitlements));
} else {
await Superwall.shared
.setSubscriptionStatus(SubscriptionStatusInactive());
}
});
}
// MARK: Handle Purchases
/// Makes a purchase from App Store with RevenueCat and returns its
/// result. This gets called when someone tries to purchase a product on
/// one of your paywalls from iOS.
@override
Future purchaseFromAppStore(String productId) async {
// Find products matching productId from RevenueCat
final products = await PurchasesAdditions.getAllProducts([productId]);
// Get first product for product ID (this will properly throw if empty)
final storeProduct = products.firstOrNull;
if (storeProduct == null) {
return PurchaseResult.failed(
'Failed to find store product for $productId');
}
final purchaseResult = await _purchaseStoreProduct(storeProduct);
return purchaseResult;
}
/// Makes a purchase from Google Play with RevenueCat and returns its
/// result. This gets called when someone tries to purchase a product on
/// one of your paywalls from Android.
@override
Future purchaseFromGooglePlay(
String productId, String? basePlanId, String? offerId) async {
// Find products matching productId from RevenueCat
List products =
await PurchasesAdditions.getAllProducts([productId]);
// Choose the product which matches the given base plan.
// If no base plan set, select first product or fail.
String storeProductId = "$productId:$basePlanId";
// Try to find the first product where the googleProduct's basePlanId matches the given basePlanId.
StoreProduct? matchingProduct;
// Loop through each product in the products list.
for (final product in products) {
// Check if the current product's basePlanId matches the given basePlanId.
if (product.identifier == storeProductId) {
// If a match is found, assign this product to matchingProduct.
matchingProduct = product;
// Break the loop as we found our matching product.
break;
}
}
// If a matching product is not found, then try to get the first product from the list.
StoreProduct? storeProduct =
matchingProduct ?? (products.isNotEmpty ? products.first : null);
// If no product is found (either matching or the first one), return a failed purchase result.
if (storeProduct == null) {
return PurchaseResult.failed("Product not found");
}
switch (storeProduct.productCategory) {
case ProductCategory.subscription:
SubscriptionOption? subscriptionOption =
await _fetchGooglePlaySubscriptionOption(
storeProduct, basePlanId, offerId);
if (subscriptionOption == null) {
return PurchaseResult.failed(
"Valid subscription option not found for product.");
}
return await _purchaseSubscriptionOption(subscriptionOption);
case ProductCategory.nonSubscription:
return await _purchaseStoreProduct(storeProduct);
case null:
return PurchaseResult.failed("Unable to determine product category");
}
}
Future _fetchGooglePlaySubscriptionOption(
StoreProduct storeProduct,
String? basePlanId,
String? offerId,
) async {
final subscriptionOptions = storeProduct.subscriptionOptions;
if (subscriptionOptions != null && subscriptionOptions.isNotEmpty) {
// Concatenate base + offer ID
final subscriptionOptionId =
_buildSubscriptionOptionId(basePlanId, offerId);
// Find first subscription option that matches the subscription option ID or use the default offer
SubscriptionOption? subscriptionOption;
// Search for the subscription option with the matching ID
for (final option in subscriptionOptions) {
if (option.id == subscriptionOptionId) {
subscriptionOption = option;
break;
}
}
// If no matching subscription option is found, use the default option
subscriptionOption ??= storeProduct.defaultOption;
// Return the subscription option
return subscriptionOption;
}
return null;
}
Future _purchaseSubscriptionOption(
SubscriptionOption subscriptionOption) async {
// Define the async perform purchase function
Future performPurchase() async {
// Attempt to purchase product
CustomerInfo customerInfo =
await Purchases.purchaseSubscriptionOption(subscriptionOption);
return customerInfo;
}
PurchaseResult purchaseResult =
await _handleSharedPurchase(performPurchase);
return purchaseResult;
}
Future _purchaseStoreProduct(
StoreProduct storeProduct) async {
// Define the async perform purchase function
Future performPurchase() async {
// Attempt to purchase product
CustomerInfo customerInfo =
await Purchases.purchaseStoreProduct(storeProduct);
return customerInfo;
}
PurchaseResult purchaseResult =
await _handleSharedPurchase(performPurchase);
return purchaseResult;
}
// MARK: Shared purchase
Future _handleSharedPurchase(
Future Function() performPurchase) async {
try {
// Perform the purchase using the function provided
CustomerInfo customerInfo = await performPurchase();
print(customerInfo.activeSubscriptions.length);
print(customerInfo.entitlements.all.length);
print(customerInfo.entitlements.active);
// Handle the results
if (customerInfo.hasActiveEntitlementOrSubscription()) {
return PurchaseResult.purchased;
} else {
return PurchaseResult.failed("No active subscriptions found.");
}
} on PlatformException catch (e) {
var errorCode = PurchasesErrorHelper.getErrorCode(e);
if (errorCode == PurchasesErrorCode.paymentPendingError) {
return PurchaseResult.pending;
} else if (errorCode == PurchasesErrorCode.purchaseCancelledError) {
return PurchaseResult.cancelled;
} else {
return PurchaseResult.failed(
e.message ?? "Purchase failed in RCPurchaseController");
}
}
}
// MARK: Handle Restores
/// Makes a restore with RevenueCat and returns `.restored`, unless an error is thrown.
/// This gets called when someone tries to restore purchases on one of your paywalls.
@override
Future restorePurchases() async {
try {
CustomerInfo customerInfo = await Purchases.restorePurchases();
await configureAndSyncSubscriptionStatus();
Superwall.shared.setUserAttributes({
'entitlements': customerInfo.entitlements.active.keys.join(','),
'isSubscribed': customerInfo.hasActiveEntitlementOrSubscription(),
});
return RestorationResult.restored;
} on PlatformException catch (e) {
// Error restoring purchases
return RestorationResult.failed(
e.message ?? "Restore failed in RCPurchaseController");
}
}
}
// MARK: Helpers
String _buildSubscriptionOptionId(String? basePlanId, String? offerId) {
String result = '';
if (basePlanId != null) {
result += basePlanId;
}
if (offerId != null) {
if (basePlanId != null) {
result += ':';
}
result += offerId;
}
return result;
}
extension CustomerInfoAdditions on CustomerInfo {
bool hasActiveEntitlementOrSubscription() {
return (activeSubscriptions.isNotEmpty || entitlements.active.isNotEmpty);
}
}
extension PurchasesAdditions on Purchases {
static Future getAllProducts(
List productIdentifiers) async {
final subscriptionProducts = await Purchases.getProducts(productIdentifiers,
productCategory: ProductCategory.subscription);
final nonSubscriptionProducts = await Purchases.getProducts(
productIdentifiers,
productCategory: ProductCategory.nonSubscription);
final combinedProducts = [
...subscriptionProducts,
...nonSubscriptionProducts
];
return combinedProducts;
}
}
< /code>
Конфигурация в main.dart < /p>
RCPurchaseController purchaseController = RCPurchaseController();
await purchaseController.configureAndSyncSubscriptionStatus();
SuperwallOptions options = SuperwallOptions();
options.logging.level = LogLevel.warn;
options.logging.scopes = {
LogScope.paywallPresentation,
LogScope.paywallEvents,
LogScope.transactions,
};
final apiKey = Platform.isAndroid
? ''
: '';
Superwall.configure(
apiKey,
purchaseController: purchaseController,
options: options,
);
final revenueApiKey = Platform.isAndroid
? ''
: '';
await Purchases.setDebugLogsEnabled(true);
await Purchases.configure(PurchasesConfiguration(revenueApiKey));
< /code>
Ошибки
при покупке у Googleplay < /p>
Тело может завершать нормально, вызывая «null», который будет возвращен, но возвращаемый тип, «Futureor», является потенциально не нулевым типом. /> String ProductId,
String?The name 'StoreProduct' is defined in the libraries 'package:purchases_flutter/models/store_product_wrapper.dart (via package:purchases_flutter/purchases_flutter.dart)' and 'package:superwallkit_flutter/src/public/StoreProduct.dart (via package:superwallkit_flutter/superwallkit_flutter.dart)'.
Попробуйте использовать «в качестве префикса» для одного из директив импорта или скрыть имя от всех, кроме одного из импорта.>
Я скопировал этот контроллер Superwall Superwall RevenueCat из Superwall Docs для обработки покупок и восстановления и других вещей в iOS и Android, и в одном проекте я использовал это таким, какой он есть, и он отлично справился Google Play and Store Porducts Я не знаю, что все было так же, почему в другом проекте этот контроллер имеет столько ошибок, и я не знаю, как исправить этот Beacuse в предыдущих проектах, я ничего не изменил в контроллере < /p> контроллер Superwall < /p> [code]import 'dart:io';
class RCPurchaseController extends PurchaseController { // MARK: Configure and sync subscription Status /// Makes sure that Superwall knows the customers subscription status by /// changing `Superwall.shared.subscriptionStatus` Future configureAndSyncSubscriptionStatus() async { // Configure RevenueCat await Purchases.setLogLevel(LogLevel.debug); final configuration = Platform.isIOS ? PurchasesConfiguration('ios_rc_key') : PurchasesConfiguration('android_rc_key'); await Purchases.configure(configuration);
// Listen for changes Purchases.addCustomerInfoUpdateListener((customerInfo) async { // Gets called whenever new CustomerInfo is available final entitlements = customerInfo.entitlements.active.keys .map((id) => Entitlement(id: id)) .toSet();
final hasActiveEntitlementOrSubscription = customerInfo .hasActiveEntitlementOrSubscription(); // Why? -> https://www.revenuecat.com/docs/entitlements#entitlements
/// Makes a purchase from App Store with RevenueCat and returns its /// result. This gets called when someone tries to purchase a product on /// one of your paywalls from iOS. @override Future purchaseFromAppStore(String productId) async { // Find products matching productId from RevenueCat final products = await PurchasesAdditions.getAllProducts([productId]);
// Get first product for product ID (this will properly throw if empty) final storeProduct = products.firstOrNull;
if (storeProduct == null) { return PurchaseResult.failed( 'Failed to find store product for $productId'); }
final purchaseResult = await _purchaseStoreProduct(storeProduct); return purchaseResult; }
/// Makes a purchase from Google Play with RevenueCat and returns its /// result. This gets called when someone tries to purchase a product on /// one of your paywalls from Android. @override Future purchaseFromGooglePlay( String productId, String? basePlanId, String? offerId) async { // Find products matching productId from RevenueCat List products = await PurchasesAdditions.getAllProducts([productId]);
// Choose the product which matches the given base plan. // If no base plan set, select first product or fail. String storeProductId = "$productId:$basePlanId";
// Try to find the first product where the googleProduct's basePlanId matches the given basePlanId. StoreProduct? matchingProduct;
// Loop through each product in the products list. for (final product in products) { // Check if the current product's basePlanId matches the given basePlanId. if (product.identifier == storeProductId) { // If a match is found, assign this product to matchingProduct. matchingProduct = product; // Break the loop as we found our matching product. break; } }
// If a matching product is not found, then try to get the first product from the list. StoreProduct? storeProduct = matchingProduct ?? (products.isNotEmpty ? products.first : null);
// If no product is found (either matching or the first one), return a failed purchase result. if (storeProduct == null) { return PurchaseResult.failed("Product not found"); }
switch (storeProduct.productCategory) { case ProductCategory.subscription: SubscriptionOption? subscriptionOption = await _fetchGooglePlaySubscriptionOption( storeProduct, basePlanId, offerId); if (subscriptionOption == null) { return PurchaseResult.failed( "Valid subscription option not found for product."); } return await _purchaseSubscriptionOption(subscriptionOption); case ProductCategory.nonSubscription: return await _purchaseStoreProduct(storeProduct); case null: return PurchaseResult.failed("Unable to determine product category"); } }
if (subscriptionOptions != null && subscriptionOptions.isNotEmpty) { // Concatenate base + offer ID final subscriptionOptionId = _buildSubscriptionOptionId(basePlanId, offerId);
// Find first subscription option that matches the subscription option ID or use the default offer SubscriptionOption? subscriptionOption;
// Search for the subscription option with the matching ID for (final option in subscriptionOptions) { if (option.id == subscriptionOptionId) { subscriptionOption = option; break; } }
// If no matching subscription option is found, use the default option subscriptionOption ??= storeProduct.defaultOption;
// Return the subscription option return subscriptionOption; }
return null; }
Future _purchaseSubscriptionOption( SubscriptionOption subscriptionOption) async { // Define the async perform purchase function Future performPurchase() async { // Attempt to purchase product CustomerInfo customerInfo = await Purchases.purchaseSubscriptionOption(subscriptionOption); return customerInfo; }
// MARK: Shared purchase Future _handleSharedPurchase( Future Function() performPurchase) async { try { // Perform the purchase using the function provided CustomerInfo customerInfo = await performPurchase(); print(customerInfo.activeSubscriptions.length); print(customerInfo.entitlements.all.length); print(customerInfo.entitlements.active);
// Handle the results if (customerInfo.hasActiveEntitlementOrSubscription()) { return PurchaseResult.purchased; } else { return PurchaseResult.failed("No active subscriptions found."); } } on PlatformException catch (e) { var errorCode = PurchasesErrorHelper.getErrorCode(e); if (errorCode == PurchasesErrorCode.paymentPendingError) { return PurchaseResult.pending; } else if (errorCode == PurchasesErrorCode.purchaseCancelledError) { return PurchaseResult.cancelled; } else { return PurchaseResult.failed( e.message ?? "Purchase failed in RCPurchaseController"); } } }
// MARK: Handle Restores
/// Makes a restore with RevenueCat and returns `.restored`, unless an error is thrown. /// This gets called when someone tries to restore purchases on one of your paywalls. @override Future restorePurchases() async { try { CustomerInfo customerInfo = await Purchases.restorePurchases(); await configureAndSyncSubscriptionStatus();
< /code> Ошибки при покупке у Googleplay < /p> Тело может завершать нормально, вызывая «null», который будет возвращен, но возвращаемый тип, «Futureor», является потенциально не нулевым типом. /> String ProductId, String?The name 'StoreProduct' is defined in the libraries 'package:purchases_flutter/models/store_product_wrapper.dart (via package:purchases_flutter/purchases_flutter.dart)' and 'package:superwallkit_flutter/src/public/StoreProduct.dart (via package:superwallkit_flutter/superwallkit_flutter.dart)'. [/code] Попробуйте использовать «в качестве префикса» для одного из директив импорта или скрыть имя от всех, кроме одного из импорта.>