chargebee / chargebee-flutter Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
Product.price
should be a numberRight now, it's a string for seemingly no reason, since currency isn't included. Also, this is necessary to do calculations like "less than x per week!" or display the formatting correctly, e.g. "12.34" in the US and "12,34" in Germany.
Product.currency
is missingProduct.duration
is missingWe need to display the pricing as "x USD / y months".
a) because it makes sense and
b) to satisfy store guidelines.
Our workaround is to hardcode this information in the app, but obviously it would be better if this was automatically provided.
Purpose: This is needed to support in app prompts to upgrade from a basic to a premium plan, a fairly mainline pricing pattern these days. Since the subscription can only be upgraded in the app store, there needs to be a way to send the user our of the flutter app to the app store, just like with the initial purchase.
I don't believe showManageSubscriptions or the Android equivilant exists in the chargebee library.
I'm listing as a feature request, but it really is an issue, as offering the users the ability to manage their subscriptions or upgrade is an essential part of managing mobile subscriptions.
Hi, add in-built Chargebee checkout for non-Google Play and App Store Products.
Issue reported in wrong repo by mistake, please just delete this.
This is in the logs:
Note: pre-requisites configuration is mandatory for SDK to work. Learn more(https://www.chargebee.com/docs/2.0/mobile-app-store-product-iap.html)
Is this normal? If I remember correctly it is, but I'm not sure.
When calling retrieveSubscriptions
after purchasing a product, the result contains a Subscription
, but on android several attributes are not set, e.g. subscriptionId
is always null, even though the network result contains it.
From the code it looks like Subscripton.fromJsonAndroid
is not properly implemented.
Here are minor improvements that don't warrant their own Github issue, but that would make this package align more with the rest of the Dart ecosystem.
Please don't waste your time on these, the other issues are way more important.
Chargebee()
shouldn't be accessible, since everything is static anyway.
You can hide the constructor via
class Chargebee {
Chargebee._();
...
final result = await Chargebee.purchaseProduct(...);
print(result);
logToCrashlytics(result.toString());
await Chargebee.configure("...", "...", "...", "..."); // <-- which of these is the android sdk key?
await configure(
site: "...",
apiKey: "...",
androidSdkKey: "...", // <-- this can be removed without potentially passing null in a random place in the arglist
iosSdkKey: "...",
);
Yes, you can google and search for valid values in the website documentation, but its faster to have your code editor autocomplete everything or at least spit out the inline documentation (which is also missing: #37).
cancelled | configure() not called |
---|---|
Error code "null"
for everything, are you kidding me?
cancelled | on a simulator (not chargebee's fault) |
---|---|
On Android, details is a dynamic and apparently contains a stringified stacktrace. On iOS, we get a useless message
property instead and need to use details
to figure out what happened.
We can't display the English, internal(!) error message to the end user and now we seriously need to check if the error message contains specific words to hopefully distinguish them!?
Here's our "solution", but please just provide error codes instead...
try {
return await Chargebee.purchaseProduct(product, uid);
} on PlatformException catch (e) {
// On Android, message will be set (details is a stringified stacktrace)
if (e.message != null) {
// Android: "User pressed back or canceled a dialog" <- no, not a typo, just USA...
if (e.message!.contains("canceled")) return PurchaseResult("", "", PurchaseResultExtension.kCancelled);
}
// On iOS, details will be set instead.
if (e.details != null) {
// iOS: "User cancelled the payment."
if (e.details.toString().contains("cancelled")) {
return PurchaseResult("", "", PurchaseResultExtension.kCancelled);
}
}
// List of known error messages at the time of writing:
// Android:
// - "User pressed back or canceled a dialog"
// - "SDK key not available to proceed purchase"
// iOS:
// - "User cancelled the payment."
// - "Purchase is unavailable due to unknown or unexpected reason. Please try again later."
return PurchaseResult("", "", PurchaseResultExtension.kError);
}
Please provide useful PlatformExceptions with actual error codes that match(!) between Android and iOS.
Hi,
Please add support for Other Platforms including Web and Desktop.
I hope It can be done if this package was built entirely in Dart from scratch instead of platform SDK dependent.
As of now the option to check subscription status can be added.
Our production app occasionally throws an error.
Fatal Exception: java.lang.IllegalStateException: Reply already submitted
at ff.c$g.a(:35)
at rf.k$a$a.b(:14)
at q2.a.d(:16)
at q2.a.a()
at q2.a$g.a(:9)
at j2.a$c.a(:245)
at com.android.billingclient.api.a0.run(:5)
at d1.g.run(:29)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8741)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
This seems like some sort of a race condition.
Reply already submitted
This often happens when trying to call .success()
multiple times in the Android implementation of a Flutter plugin.
Since billingclient
shows up and we didn't have this error before switching from our custom chargebee sdk wrapper to this official one, I opened the issue here.
Hi, the retrieveProductIdentifers
currently is List<dynamic>
. So what can be in that list other than String
? Could you please document it?
PS: The method name also has a typo and probably should be refactored to retrieveProductIdentifiers
.
Crashlytics is reporting this crash is happening regularly:
Fatal Exception: java.lang.IllegalStateException
Reply already submitted
io.flutter.embedding.engine.dart.DartMessenger$Reply.reply (DartMessenger.java:435)
io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.error (MethodChannel.java:268)
com.chargebee.flutter.sdk.ChargebeeFlutterSdkPlugin.error (ChargebeeFlutterSdkPlugin.kt:530)
com.chargebee.flutter.sdk.ChargebeeFlutterSdkPlugin.onError (ChargebeeFlutterSdkPlugin.kt:511)
com.chargebee.flutter.sdk.ChargebeeFlutterSdkPlugin.access$onError (ChargebeeFlutterSdkPlugin.kt:26)
com.chargebee.flutter.sdk.ChargebeeFlutterSdkPlugin$retrieveProducts$1.onError (ChargebeeFlutterSdkPlugin.kt:159)
com.chargebee.android.billingservice.BillingClientManager$retrieveProducts$2.invoke (BillingClientManager.kt:51)
com.chargebee.android.billingservice.BillingClientManager$retrieveProducts$2.invoke (BillingClientManager.kt:21)
com.chargebee.android.billingservice.BillingClientManager$retrieveProducts$3.invoke (BillingClientManager.kt:69)
com.chargebee.android.billingservice.BillingClientManager$retrieveProducts$3.invoke (BillingClientManager.kt:21)
com.chargebee.android.billingservice.BillingClientManager$createBillingClientStateListener$1.onBillingServiceDisconnected (BillingClientManager.kt:504)
com.android.billingclient.api.zzaf.onServiceDisconnected (com.android.billingclient:billing@@4.0.0:4)
android.app.LoadedApk$ServiceDispatcher.doDeath (LoadedApk.java:1967)
android.app.LoadedApk$ServiceDispatcher$RunConnection.run (LoadedApk.java:1982)
android.os.Handler.handleCallback (Handler.java:883)
android.os.Handler.dispatchMessage (Handler.java:100)
android.os.Looper.loop (Looper.java:214)
android.app.ActivityThread.main (ActivityThread.java:7356)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:492)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:930)
Hi please add the feature to Retrieve Product ids.
I noticed there is a Pull Request created to Get Product ids.
I was getting this PlatformException even after a Successful purchase via Play Billing in Release Mode.
Even an invoice was also created on the Chargebee site and the webhook triggers a subscription-created event call.
This stack trace was from the previous version of the Chargebee plugin.
PlatformException(null, Failure of purchase, h1.a: Failure of purchase
at g1.a$d.a(Unknown Source:87)
at g1.a$d.invoke(Unknown Source:2)
at j1.j$a$b.invokeSuspend(Unknown Source:263)
at kotlin.coroutines.jvm.internal.a.resumeWith(Unknown Source:11)
at lc.t0.run(Unknown Source:129)
at kotlinx.coroutines.scheduling.a.r(Unknown Source:0)
at kotlinx.coroutines.scheduling.a$c.c(Unknown Source:14)
at kotlinx.coroutines.scheduling.a$c.m(Unknown Source:28)
at kotlinx.coroutines.scheduling.a$c.run(Unknown Source:0)
, null)
In the newer version, it's just returning this.
PlatformException(null, Failure of purchase, Failure of purchase, null)
If I manually set minifyEnabled
and shrinkResuource
to false
in the build.gradle
it doesn't throw any exception and works fine.
On iOS
retrieveProductIdentifiers works, but retrieveProducts gives products not found exceptions.
My code:
...print("Start payment");
final resultp = await Chargebee.retrieveProductIdentifiers();
print("Resultp");
print(resultp);
List<Product> products = await Chargebee.retrieveProducts(resultp);...
My logs:
// flutter: Start payment
// flutter: Resultp
// flutter: [2A, 1B]
// [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(8, Products not found., Request failed, null)
Am I missing something here?
The API seems to be working since the retrieveProductIdentifiersis getting the correct products IDs, these are the IDs I expect to get from the API ([2A, 1B]).
Also the docs say
List products = await Chargebee.retrieveProducts({productList: "[Product ID's from Google or Apple]"});
But this doesn't work, because the parameter of retrieveProducts is just List productIDs.
Currently the implementation uses code like this:
print(subscriptions.first.subscriptionId);
without guaranteeing that subscriptions
is not empty. If it is empty, the .first
call raises a NPE. Instead of an exception we would expect empty results.
You won't be able to release app updates (59 days away)
Jul 1, 2024
Aug 31, 2024
App must use Google Play Billing Library version 6.0.1 or later
To provide users with a safe and secure experience, all apps are required to meet Google Play Billing Library requirements.
Your app uses an old version of Google Play Billing Library. From Aug 31, 2024, all apps must use version 6.0.1 or later.
Update to a newer version by this date to prevent your updates from being rejected.
Please update the Billing Library version soon.
PlatformException(400, The product id <our redacted, correct product id> provided doesnt match with any product in the receipt., Chargebee error, null)
The id in the error message is definitely correct and exists.
purchase sometimes fails for some users.We're using the flutter sdk:
The product id is definitely correct.
It currently affects a single user, who tried to purchase 7 times but it failed each time.
Perhaps the user picked a plan (say for 12 months) in the app & switched to a different plan in the purchase dialog? Then the receipt from Apple wouldn't match the expected product id.
This seems to happen since we upgraded to v1.0.0 of the chargebee-flutter
sdk.
Unfortunately I wasn't able to reproduce this, I just got this from Crashlytics during production.
Fatal Exception: java.lang.IllegalStateException: Reply already submitted
at lh.c$g.a(SourceFile:1)
at uh.k$a$a.b(SourceFile:1)
at h4.b.f(SourceFile:1)
at h4.b.g(SourceFile:1)
at h4.b.b(SourceFile:1)
at h4.b$i.onError(SourceFile:1)
at com.chargebee.android.billingservice.BillingClientManager$retrieveProducts$2.invoke(SourceFile:2)
at com.chargebee.android.billingservice.BillingClientManager$retrieveProducts$2.invoke(SourceFile:1)
at com.chargebee.android.billingservice.BillingClientManager$retrieveProducts$3.invoke(SourceFile:2)
at com.chargebee.android.billingservice.BillingClientManager$retrieveProducts$3.invoke(SourceFile:1)
at com.chargebee.android.billingservice.BillingClientManager$createBillingClientStateListener$1.onBillingServiceDisconnected(:15)
at c3.u.onServiceDisconnected(SourceFile:1)
at android.app.LoadedApk$ServiceDispatcher.doDeath(LoadedApk.java:2226)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:2241)
at android.os.Handler.handleCallback(Handler.java:959)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loopOnce(Looper.java:232)
at android.os.Looper.loop(Looper.java:317)
at android.app.ActivityThread.main(ActivityThread.java:8501)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)
App Crashes when canceling / closing Play Billing Bottom Sheet 2 or more times.
Caused by: java.lang.IllegalStateException: Reply already submitted
E/AndroidRuntime(24752): at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:432)
E/AndroidRuntime(24752): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.success(MethodChannel.java:266)
E/AndroidRuntime(24752): at com.chargebee.flutter.sdk.ChargebeeFlutterSdkPlugin$purchaseProduct$1$onSuccess$1.onError(ChargebeeFlutterSdkPlugin.kt:133)
E/AndroidRuntime(24752): at com.chargebee.android.billingservice.BillingClientManager.onPurchasesUpdated(BillingClientManager.kt:226)
E/AndroidRuntime(24752): at com.android.billingclient.api.zzg.onReceive(com.android.billingclient:billing@@4.0.0:3)
E/AndroidRuntime(24752): at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1666)
E/AndroidRuntime(24752): ... 8 more
As per ios sdk documentation:
let customer = CBCustomer(customerID: "Test123",firstName: "CB",lastName: "Test",email: "[email protected]")
CBPurchase.shared.restorePurchases(includeInActiveProducts: true, customer: customer) { result in
switch result {
case .success(let response):
for subscription in response {
if subscription.storeStatus.rawValue == StoreStatus.Active.rawValue{
print("Successfully restored purchases")
}
}
case .failure(let error):
// Handle error here
print("Error:",error)
}
}
The ios sdk requires a customer, but the flutter sdk doesn't. How is that possible?? If we call this function without a customer, which customer is the subscription restored to?
Urgent : As of the context, I am not able to see the card tokenization method. Is it in pipeline?
We found this via Crashlytics. Haven't tried reproducing it yet.
Code:
Chargebee.showManageSubscriptionsSettings(
null,
Platform.isIOS ? "hardcoded string" : "other hardcoded string",
)
Here's the stacktrace:
Non-fatal Exception: io.flutter.plugins.firebase.crashlytics.FlutterError: PlatformException(error, null cannot be cast to non-null type kotlin.String, null, java.lang.NullPointerException: null cannot be cast to non-null type kotlin.String
at p7.b.s(SourceFile:1)
at p7.b.onMethodCall(SourceFile:1)
at gl.k$a.a(SourceFile:1)
at xk.c.l(SourceFile:1)
at xk.c.m(SourceFile:1)
at xk.c.i(SourceFile:1)
at xk.b.run(SourceFile:1)
at android.os.Handler.handleCallback(Handler.java:900)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:219)
at android.app.ActivityThread.main(ActivityThread.java:8668)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1109)
)
at StandardMethodCodec.decodeEnvelope(message_codecs.dart:648)
at MethodChannel._invokeMethod(platform_channel.dart:334)
at Chargebee.showManageSubscriptionsSettings(chargebee.dart:112)
status: "true"
?? After reading the README, I was under the impression that this field is supposed to match the possible Chargebee subscription status values (active, trial, non renewing etc.)"Optional(...)"
?? Looks like some sort of serialization error of Swift's Optional
typeContext: I'm on iOS.
Running the following code, I would expect to always see the log output for either done
or Could not configure chargebee
. Using a wrong applicationId logs an error (I think from the android SDK) but the expected log output on the flutter end is never shown, because the Chargebee.configure
method seems to never finish.
print('before');
try {
await Chargebee.configure(
appConfig.chargebeeSite,
appConfig.chargebeeSdkKey,
appConfig.chargebeeIosSdkKey,
appConfig.chargebeeAndroidSdkKey,
);
print('done');
} catch (e, s) {
print('Could not configure chargebee');
}
print('after');
output:
before
I/Chargebee( 8608): Exception from server :{"message":"Provided Application id - a.b.c.dev doesnt match with existing id","type":"invalid_request","api_error_code":"invalid_request","error_code":"operation_not_supported","error_msg":"Provided Application id - a.b.c.dev doesnt match with existing id","http_status_code":400}
(end of output)
We would expect to see Could not configure chargebee
and after
in the log output.
We also expect that the configure
method propagates errors via exceptions, so we can catch them.
The SDK swallows CBException
without handling them properly, which makes it impossible for us to handle errors. Could you please at least rethrow the exceptions or even better provide semantically meaningful exceptions that we must handle? Our devs otherwise don't know which cases exist.
productPriceString
in the Product
objects returned in the response of retrieveProducts
method does not contain the currency symbol on iOS. It only contains a numerical value. On Android, it contains the symbol as well as the amount. Here is an example.
On Android, the value of productPriceString
would be something like $9.95 while on iOS, it will only contain 9.95. It does not contain the $ symbol as part of the String.
This should be consistent across both platforms.
Please add support for Prepaid type base plans of Google Play Billing.
In some use cases, a non auto renewal subscriptions are needed.
It would be great if Chargebee supports this type of base plan.
For iOS subscription plans with integer value prices: ie $5 / per month, retrieveProducts fails due to a _CastError in the Product.fromJson method. The method uses type double for product price and the cast of an int to a double fails.
This issue can be mitigated by changed the type of Product.price from double to num.
a. Change the delcared type of price from double to num, line 8 of the file product.dart.
b. Change the cast from json['productPrice'] as double to json['productPrice'] as num in Product.fromJson, line 19 of the file product.dart.
This issue was introduced in version 0.0.10 with the type change of Product.price from String to double.
What does retrieveSubscriptions()
do? Why does it take an untyped Map queryParams
instead of actual argument names?
Sure, I could look in the readme, but it would be much faster if I could rely on code editor suggestions and inline documentation like with every other package. This also goes beyond just having the untyped map parameter documented. This sdk could easily hide that implementation detail of the underlying native sdks (which imo shouldn't expose this detail either).
Everything has been working well while testing in Debug mode on my Android phone.
However, as soon as I tried compiling in Release mode (using live Chargebee site) it hangs forever on the configure() step and never moves from there. No error, just hangs forever.
Here is a code snippet, which is in a try {} block.
await Chargebee.configure( globals.chargebeeSiteName, globals.chargebeePublishableAPIKey, globals.chargebeeIOSSDKKey, globals.chargebeeAndroidSDKKey)
Same code and same exact variables work in Debug mode and not in Release mode. Everything else on the app works in Release mode, no problems with Internet permissions, just Chargebee Flutter hangs up.
I think this library needs to be updated to the latest version.
chargebee-flutter/android/build.gradle
Line 52 in a57be57
Why would you use num
in Dart? I haven't encountered a case where using num
made practical sense, but it especially doesn't make sense when the thing you have is already a floating point number, aka a double
.
By returning num
, when using the price you have to convert it back to a double
even though it already is one.
await Chargebee.retrieveProductIdentifers({});
gives
E/MethodChannel#chargebee_flutter(17449): Failed to handle method call
E/MethodChannel#chargebee_flutter(17449): java.lang.NullPointerException: null cannot be cast to non-null type kotlin.String
E/MethodChannel#chargebee_flutter(17449): at com.chargebee.flutter.sdk.ChargebeeFlutterSdkPlugin.retrieveProductIdentifers(ChargebeeFlutterSdkPlugin.kt:242)
E/MethodChannel#chargebee_flutter(17449): at com.chargebee.flutter.sdk.ChargebeeFlutterSdkPlugin.onMethodCall(ChargebeeFlutterSdkPlugin.kt:83)
E/MethodChannel#chargebee_flutter(17449): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:262)
E/MethodChannel#chargebee_flutter(17449): at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
E/MethodChannel#chargebee_flutter(17449): at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$DartMessenger(DartMessenger.java:319)
E/MethodChannel#chargebee_flutter(17449): at io.flutter.embedding.engine.dart.-$$Lambda$DartMessenger$TsixYUB5E6FpKhMtCSQVHKE89gQ.run(Unknown Source:12)
E/MethodChannel#chargebee_flutter(17449): at android.os.Handler.handleCallback(Handler.java:873)
E/MethodChannel#chargebee_flutter(17449): at android.os.Handler.dispatchMessage(Handler.java:99)
E/MethodChannel#chargebee_flutter(17449): at android.os.Looper.loop(Looper.java:193)
E/MethodChannel#chargebee_flutter(17449): at android.app.ActivityThread.main(ActivityThread.java:6669)
E/MethodChannel#chargebee_flutter(17449): at java.lang.reflect.Method.invoke(Native Method)
E/MethodChannel#chargebee_flutter(17449): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
E/MethodChannel#chargebee_flutter(17449): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
So I am calling This code:
Chargebee.purchaseProduct(product,"TEST_ONE").then((value) { print(value.toString()); }).catchError((error){ print(error.toString()); });
After Successful Purchase the app is crashed.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.