Skip to main content
Question

Why is recurrenceMode "nonRecurring" for a subscription product?

  • September 5, 2025
  • 0 replies
  • 23 views

Forum|alt.badge.img+3

I’m using the flutter SDK.

I have a simple screen that loads the current offering, iterates over each availablePackage, and outputs some details about the package.

In RevenueCat, the offering has 6 packages, and each package has a subscription product. The products are for a Web Billing provider, and each is clearly configured as a subscription (the billing cycle shows as “Yearly” or “Monthly” on the product page).

However, each of the packages has a “nonRecurring” recurrenceMode (via package.storeProduct.defaultOption.fullPricePhase). Shouldn’t that be “infiniteRecurring”?

Code for the flutter widget below:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'package:pz_common/common/widgets/async_value_widget.dart';
import 'package:pz_common/localization/string_hardcoded.dart';
import 'package:pz_common/subscription/application/subscription_service.dart';

class SubscriptionDebugScreen extends ConsumerWidget {
const SubscriptionDebugScreen({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
// title: 'Subscription',
body: Container(
width: double.infinity,
color: Theme.of(context).colorScheme.surfaceContainerLow,
child: Row(
children: [
AsyncValueWidget(
value: ref.watch(currentOfferingProvider),
data: (offering) => offering != null
? _OfferingWidget(offering: offering)
: Text('No offering'.hardcoded),
error: (error, stackTrace) => Text(error.toString()),
loading: () => const Center(child: CircularProgressIndicator()),
),
],
),
),
);
}
}

class _OfferingWidget extends StatelessWidget {
const _OfferingWidget({required this.offering});
final Offering offering;

@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...offering.availablePackages.map(
(package) => _PackageWidget(package: package),
),
],
);
}
}

class _PackageWidget extends StatelessWidget {
const _PackageWidget({required this.package});
final Package package;

@override
Widget build(BuildContext context) {
final storeProduct = package.storeProduct;
final subscriptionOption = storeProduct.defaultOption;
final introductoryPrice = storeProduct.introductoryPrice;
final fullPricePhase = subscriptionOption?.fullPricePhase;

return Container(
margin: const EdgeInsets.all(8),
padding: const EdgeInsets.all(16),
color: Theme.of(context).colorScheme.surfaceContainerLowest,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Package:',
style: Theme.of(
context,
).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.bold),
),
Text(storeProduct.identifier),
Text(storeProduct.priceString),
const SizedBox(height: 8),
Text('introductory price: ${introductoryPrice?.priceString}'),
const SizedBox(height: 8),
Text(
'full price phase: ${fullPricePhase?.price.formatted} / ${fullPricePhase?.recurrenceMode}',
),
],
),
);
}
}

 

This post has been closed for comments