Giter Site home page Giter Site logo

chargebee-flutter's People

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

chargebee-flutter's Issues

productPriceString does not contain currency symbol on iOS

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.

Don't just swallow exceptions

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.

No inline documentation

Example:

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).

There's a reason why this gives you a higher score on pub.dev. It's useful!

Bildschirm­foto 2023-01-21 um 19 36 44

Code quality improvements

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.

Hide constructor

Chargebee() shouldn't be accessible, since everything is static anyway.
You can hide the constructor via

class Chargebee {
  Chargebee._(); 
  ...

Override toString() to make objects easily loggable:

final result = await Chargebee.purchaseProduct(...);
print(result);
logToCrashlytics(result.toString());

Use named parameters for the methods:

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: "...",
);

PurchaseResult.status should be an enum:

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).

[Feature Request] Add function to access showManageSubscriptions. Enables Subscription upgrades.

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.

Difficult error handling

Android

Example errors

cancelled configure() not called
Bildschirm­foto 2023-01-21 um 16 56 29 Bildschirm­foto 2023-01-21 um 19 47 01

Error code "null" for everything, are you kidding me?

iOS

Example errors

cancelled on a simulator (not chargebee's fault)
Bildschirm­foto 2023-01-21 um 21 11 41 Bildschirm­foto 2023-01-21 um 21 18 00

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.

The necessary, ugly workaround

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);
}

An actual solution

Please provide useful PlatformExceptions with actual error codes that match(!) between Android and iOS.

Chargebee.configure never finishes on error

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.

PurchaseResult contains weird data

Bildschirm­foto 2023-01-21 um 22 32 57

  1. Why is 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.)
  2. Why is the planId & subscriptionId "Optional(...)"?? Looks like some sort of serialization error of Swift's Optional type

Context: I'm on iOS.

Products not found

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.

subscriptionId is not set on android

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.

`Product` is lacking information

Product.price should be a number

Right 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 missing

  • Would be fixed by #43

Product.duration is missing

We 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.

nullpointer exception

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)

How does `restorePurchases()` determine the correct chargebee customer?

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?

Google Play Billing Library outdated

Screenshot 2023-11-08 at 22 37 32

I am unable to submit an update to Google Play as the Flutter plugin is using an outdated version of the Google Play Billing Library. I am on the latest version of the plugin (0.3.0).

Please resolve this urgently.

App crashes on Android: "Reply already submitted"

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.

Stacktrace

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)

Access JSON metadata for a plan using the SDK

Is there a way currently to access the JSON metadata that we can add using the website UI in the Flutter app using the ChargeBee SDK?

This could be very useful to add more information to a plan that can be used to display information in the app, or can be used to toggle UI elements in the app.

Purchase fails on iOS

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.

App Crash with IllegalStateException: Reply already submitted

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

Card Tokenization

Urgent : As of the context, I am not able to see the card tokenization method. Is it in pipeline?

Crash reports

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)

Error on Android: Reply already submitted

Our production app occasionally throws an error.

Stacktrace:

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.

Return type of retrieveProductIdentifers

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.

Failure of Purchase error in Release Mode after a Successful Purchase.

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.

Price should be `double` instead of `num`

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.

CleanShot 2023-12-05 at 19 17 19@2x

_CastError on Price in Product Model for integer value prices. Chargebee.retrieveProducts fails.

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.

Web and Desktop support.

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.

App Crash After Purchase Subscriptions

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.

Works in Debug mode, doesn't work in Release mode

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.