Deferred Proration Mode Blocks Execution and Keeps Loader Spinning
Opened this issue · 14 comments
- I have updated Purchases SDK to the latest version
- I have read the Contribution Guidelines
- I have searched the Community
- I have read docs.revenuecat.com
- I have searched for existing Github issues
‼️ Required data ‼️
Do not remove any of the steps from the template below. If a step is not applicable to your issue, please leave that step empty.
There are a lot of things that can contribute to things not working. Having a very basic understanding of your environment will help us understand your issue faster!
Environment
- Output of
flutter doctor
- Version of
purchases-flutter
- Testing device version e.g.: iOS 15.5, Android API 30, etc.
- How often the issue occurs- every one of your customers is impacted? Only in dev?
- Debug logs that reproduce the issue
- Steps to reproduce, with a description of expected vs. actual behavior
Other information (e.g. stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, etc.)
Version of purchases-flutter: [6.30.0]
flutter version 3.7.7
error
W/BillingLogger(30245): Unable to log. W/BillingLogger(30245): java.lang.NullPointerException W/BillingLogger(30245): at com.google.android.gms.internal.play_billing.zzbp.<init>(com.android.billingclient:billing@@6.2.1:4) W/BillingLogger(30245): at com.google.android.gms.internal.play_billing.zzdd.zzA(com.android.billingclient:billing@@6.2.1:4) W/BillingLogger(30245): at com.google.android.gms.internal.play_billing.zzdd.zzk(com.android.billingclient:billing@@6.2.1:2) W/BillingLogger(30245): at com.google.android.gms.internal.play_billing.zzgy.zzB(com.android.billingclient:billing@@6.2.1:1) W/BillingLogger(30245): at com.android.billingclient.api.zzcd.zzc(com.android.billingclient:billing@@6.2.1:1) W/BillingLogger(30245): at com.android.billingclient.api.zzj.onReceive(com.android.billingclient:billing@@6.2.1:12) W/BillingLogger(30245): at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0(LoadedApk.java:1911) W/BillingLogger(30245): at android.app.LoadedApk$ReceiverDispatcher$Args.$r8$lambda$gDuJqgxY6Zb-ifyeubKeivTLAwk(Unknown Source:0) W/BillingLogger(30245): at android.app.LoadedApk$ReceiverDispatcher$Args$$ExternalSyntheticLambda0.run(Unknown Source:2) W/BillingLogger(30245): at android.os.Handler.handleCallback(Handler.java:958) W/BillingLogger(30245): at android.os.Handler.dispatchMessage(Handler.java:99) W/BillingLogger(30245): at android.os.Looper.loopOnce(Looper.java:230) W/BillingLogger(30245): at android.os.Looper.loop(Looper.java:319) W/BillingLogger(30245): at android.app.ActivityThread.main(ActivityThread.java:8919) W/BillingLogger(30245): at java.lang.reflect.Method.invoke(Native Method) W/BillingLogger(30245): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578) W/BillingLogger(30245): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103) /[Purchases] - DEBUG(30245): ℹ️ BillingWrapper purchases updated: productIds: [family_yearly_special_subscription], orderId: GPA.3374-6462-8587-48002, purchaseToken: nonmibflhkojipgikaofmedb.AO-J1OyJ-Ng8PVAyho90pHq1tBU8ExJpa8PQHhM3KTWkFqxXjiJsVJcqdCVvjMfhIDg2m_ZQP6WVMBhxieMD3jV0mZ0o6N2SCQ D/[Purchases] - DEBUG(30245): ℹ️ Requesting products from the store with identifiers: family_yearly_special_subscription I/InsetsSourceConsumer(30245): applyRequestedVisibilityToControl: visible=true, type=statusBars, host=com.arabee.family/com.android.billingclient.api.ProxyBillingActivity
Describe the bug
Implement the following code to handle subscription downgrade in google play:
import 'package:flutter/material.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
Future<void> purchaseSubscription(BuildContext context, Package package, String activeSubscriptionId) async {
try {
WidgetUtils.showLoading(context);
// Sync attributes and offerings if needed
Purchases.syncAttributesAndOfferingsIfNeeded();
CustomerInfo customerInfo;
if (activeSubscriptionId.isNotEmpty) {
print("---------------------+++++++++++++ deferred---------------------+++++++++++++ ");
customerInfo = await Purchases.purchasePackage(
package,
googleProductChangeInfo: GoogleProductChangeInfo(
activeSubscriptionId,
prorationMode: GoogleProrationMode.deferred,
),
);
print("---------------------+++++++++++++ after deferred---------------------+++++++++++++ ");
} else {
customerInfo = await Purchases.purchasePackage(package);
}
print('---------------------+++++++++++++ payment is done now ---------------------+++++++++++++ ');
if (customerInfo.activeSubscriptions.isNotEmpty) {
logPurchase(package.storeProduct.currencyCode, package.storeProduct.price);
print('purchase-updated: `${package}`');
await Future.delayed(Duration(seconds: 2));
WidgetUtils.hideLoading(context);
Navigator.of(context).pushNamed("/home");
}
} on PlatformException catch (e) {
WidgetUtils.hideLoading(context);
final errorCode = PurchasesErrorHelper.getErrorCode(e);
String errorMessage = "An error occurred: ${e.message}";
if (errorCode == PurchasesErrorCode.purchaseNotAllowedError) {
errorMessage = 'User not allowed to purchase';
} else if (errorCode == PurchasesErrorCode.paymentPendingError) {
errorMessage = 'Payment is pending';
} else if (errorCode == PurchasesErrorCode.productAlreadyPurchasedError) {
errorMessage = 'User already subscribed';
}
if (errorCode != PurchasesErrorCode.purchaseCancelledError) {
WidgetUtils.showMessage(context, "Purchase Error", errorMessage);
}
}
}
await Purchases.purchasePackage call does not resolve when GoogleProrationMode.deferred is used.
Expected behavior: The await Purchases.purchasePackage call should resolve, allowing subsequent code to execute and the loader to be hidden.
Actual behavior: The await Purchases.purchasePackage call does not resolve, causing the loader to spin indefinitely.
Additional context
in backend im getting notification and the package is downgraded
👀 We've just linked this issue to our internal tracker and notified the team. Thank you for reporting, we're checking this out!
Thanks for posting this @Louna-akkad1. Does this error reproduce 100% of the time, or is it transient? Also, does this only occur in debug mode? Have you been able to test this in production?
@Jethro87 its reproduce 100% of the time with sandbox with Deferred Proration Mode only other modes work fine
i have not test this in production
@Louna-akkad1 Thanks. Our engineering team is digging into this - can you share more comprehensive debug logs? All logs from when you start your app to when you hit the error would be great.
@Jethro87 any news on this issue? Play Store is going to force the billing on updates on the app and we can't use the new library yet
@ricardo-pixzelle Our team can reproduce this error in the sandbox, but the SDK doesn't hang and the purchases go through. Are you seeing this in the sandbox, or also in production? Also, can you share full debug logs?
’await Purchases.purchasePackage‘ Is suspended, causing subsequent code to fail to execute
@Jethro87 This happens on production as well as sandbox. You are correct that the purchase goes through but this particular future call never completes:
await Purchases.purchaseStoreProduct(
product,
googleProductChangeInfo: productChangeInfo,
);
It doesn't throw any error, neither does it completes successfully. Execution cannot move ahead as the future never completes.
@therohansanap Thanks for reporting. Can you reply with full debug logs so our team can further dig into this?
Hi @Jethro87 don't know if this helps, this are some logs on the Android app:
We are facing this issue when we use the deferred option on Android, in iOS we don't have that problem so the app just stays hanging with that exception
We were using this:
We tried this on the 6.30.0 version, but since this didn't worked we reversed to the 5.8 version, but know Play Store needs us to upgrade the billing SDK so we don't know which version to use
@ricardo-pixzelle Apologies for missing this. Thank you for sharing some debug logs. We've since fixed various NullPointerExceptions that have cropped up - is it possible to try the latest version of our SDK? The current latest version is 8.10.2.
Hi @ricardo-pixzelle we were going through our system and found this has not been resolved yet. Did updating the SDK as suggested fix this for you? Our most recent release is 8.10.5.
@HaleyRevcat In our case, we encountered the following warning:
Using incorrect oldProductId: The productId should not contain the basePlanId. Using productId: yourProductId
🐱💰 Purchase history retrieved skus: [skus], purchaseTime: 1728330035623, purchaseToken: yourPurchaseToken
To resolve this issue, we implemented a function to remove the basePlanId from the productId. Here's the function we used:
String removeBaseProductId(String productId) {
return productId.split(":").first;
}
We then updated the GoogleProductChangeInfo object with the corrected productId. Here's how the solution was applied:
String currentProductId = previousCustomerInfo.activeSubscriptions.first;
googleProductChangeInfo = GoogleProductChangeInfo(
removeBaseProductId(currentProductId),
prorationMode: _isUpgrade(currentProductId, membershipPrice.id)
? GoogleProrationMode.immediateWithoutProration
: GoogleProrationMode.deferred
);
This ensures that the productId passed does not include the basePlanId, addressing the issue and avoiding further warnings.
thanks @dramirezm
@HaleyRevcat thats how we solved it, we did upgrade some libraries but Diego solved the problem with that changes