Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slow test card is approved but should decline #140

Closed
DevonTomatoSandwich opened this issue Dec 27, 2019 · 10 comments
Closed

Slow test card is approved but should decline #140

DevonTomatoSandwich opened this issue Dec 27, 2019 · 10 comments
Labels
android android related issue good first issue Good for newcomers no-issue-activity

Comments

@DevonTomatoSandwich
Copy link

Version of flutter_inapp_purchase

flutter_inapp_purchase: ^2.0.5

Platforms you faced the error (IOS or Android or both?)

Android

Expected behavior

When clicking "Slow test card, declines after a few minutes" I'm expecting my app to wait until there is a result, in this case declined. I'm testing a non-consumable iap.

Actual behavior

It seems the purchase is accepted as the user has access to purchased goods immediately after clicking "Slow test card, declines after a few minutes". Not seeing any update to the purchase after several minutes (i.e. from the debug console after several minutes)

Tested environment (Emulator? Real Device?)

Real Device

Steps to reproduce the behavior

When user clicks the button in the app to open the purchase dialog for the item

void openPurchaseScreen() { 
  print('openPurchaseScreen (Next was pushed)');
  asyncPurchaseScreen();
}

asyncPurchaseScreen() async {
  if(!isFiapDone){ // if iap was not initialised (e.g. if internet off when launching) set up now
    await initStorePurchases(); // initialise billing
  }
  await FlutterInappPurchase.instance.requestPurchase('premium14'); // opens purchase screen for item
}

To initialise billing:

Future<bool> initStorePurchases() async {
  print('initStorePurchases');

  var result = await FlutterInappPurchase.instance.initConnection;
  print('result: $result');

  _conectionSubscription = FlutterInappPurchase.connectionUpdated.listen((connected) {
    print('connected: $connected');
  });

  _purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((purchasedItem) {
    print('purchase-updated: $purchasedItem');
    setFullSolutionOn();
  });

  _purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen((purchaseError) {
    print('purchase-error: $purchaseError');
  });

  await _getProduct();
  await _getPurchases();

  return true;
}

When "Slow test card. Declines after a few minutes" is pressed the console reads

I/flutter ( 5641): purchase-updated: productId: premium14, transactionId: GPA.3345-4499-1762-10765, transactionDate: 2019-12-28T10:32:39.795, transactionReceipt: {"orderId":"GPA.3345-4499-1762-10765","packageName":"com.jfspackage.watsuppbreh.stiffnessmethodsolver","productId":"premium14","purchaseTime":1577489559795,"purchaseState":4,"purchaseToken":"nokoohicmcnigldkfekndaoe.AO-J1OyZ7Hy1FyyPQzckkUIksPYBlPQaMrcslIp950J_XTjk235mJ4UVP1u5WQSC466XLJbPe1_UkZ1JFOOd871F23Jc27xG0uZ8UX0m-AC3LAua7Zmym0HDnZ62lCxo6TClIed1iWd8mmFrc8qFAS-CHo5umpGK7g","acknowledged":false}, purchaseToken: nokoohicmcnigldkfekndaoe.AO-J1OyZ7Hy1FyyPQzckkUIksPYBlPQaMrcslIp950J_XTjk235mJ4UVP1u5WQSC466XLJbPe1_UkZ1JFOOd871F23Jc27xG0uZ8UX0m-AC3LAua7Zmym0HDnZ62lCxo6TClIed1iWd8mmFrc8qFAS-CHo5umpGK7g, orderId: GPA.3345-4499-1762-10765, dataAndroid: {"orderId":"GPA.3345-4499-1762-10765","packageName":"com.jfspackage.watsuppbreh.stiffnessmethodsolver","productId":"premium14","purchaseTime":1577489559795,"purchaseState":4,"purchaseToken":"nokoohicmcnigldkfekndaoe.AO-J1OyZ7
I/flutter ( 5641): setFullSolutionOn
I/flutter ( 5641): FileState didChangeAppLifecycleState
I/flutter ( 5641): didChangeAppLifecycleState AppLifecycleState.resumed
I/flutter ( 5641): StartState build

Notice that purchase-updated is called which runs the code in the stream:
FlutterInappPurchase.purchaseUpdated.listen
In my code in this stream i call setFullSolutionOn which saves the purchase in my local database and sets the UI so the user knows the item is purchased. But this happens immediately. How can i wait till the purchase is processed?

I don't see any later calls to purchase error i.e.
FlutterInappPurchase.purchaseError.listen
but i'm not sure if this is supposed to run.

@hyochan
Copy link
Member

hyochan commented Dec 29, 2019

This issue looks like related to handling pending purchases. Currently, I'm not sure what to do with it.

@hyochan hyochan added android android related issue good first issue Good for newcomers labels Dec 29, 2019
@DevonTomatoSandwich
Copy link
Author

The issue #199 you mentioned is about whether you can see the 4 testing options. It was closed as the OP eventually saw the 4 options later.

My issue is different as it is about the unexpected result when pushing "Slow test card. Declines after a few minutes"

@hyochan hyochan pinned this issue Dec 29, 2019
@hyochan
Copy link
Member

hyochan commented Dec 29, 2019

Um. I think I am still confused about your expectations. Could you record some gifs for me to understand the problem for others who would wish to help you out?

@DevonTomatoSandwich
Copy link
Author

So as of this issue i've updated my listener from

_purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((purchasedItem) {
  print('purchase-updated: $purchasedItem');
  setFullSolutionOn();
});

To

_purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((purchasedItem) async {
  print('purchase-updated: $purchasedItem');
      
  String result = await FlutterInappPurchase.instance.finishTransaction(
    purchasedItem, 
    developerPayloadAndroid: purchasedItem.developerPayloadAndroid, 
    isConsumable: false
  );
  print('  result (from finishTransaction) = $result');

  setFullSolutionOn();
});

But when one of the 2 "Slow test card..." options are selected
I get this error:

Exception has occurred.
PlatformException (PlatformException(acknowledgePurchase, E_DEVELOPER_ERROR, Google is indicating that we have some issue connecting to payment.))

Googling the error leads me to a similar issue from the react-native-iap
I think this is supposed to happen with a pending purchase due to this answer. It might be good if the user is alerted to why the app has frozen though. I'm thinking you should catch the error and alert the user saying that the connection is slow like this:

_purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((purchasedItem) async {
  print('purchase-updated: $purchasedItem');
  
  try {

    String result = await FlutterInappPurchase.instance.finishTransaction(
      purchasedItem, 
      developerPayloadAndroid: purchasedItem.developerPayloadAndroid, 
      isConsumable: false
    );
    print('  result (from finishTransaction) = $result');

    setFullSolutionOn(); // sets the purchase state and saves the purchase in a local database

  } on PlatformException { // slow connection
    print('PlatformException (slow connection)');

    // dialog lets user know there is a slow connection
    showDialog( context: context, builder: (_) => MyErrorDialog(title: 'Slow Connection',
      message: 'Connection with the store is delayed possibly due to ' +
      'slow connection to the store or a slow card.',),);
    
  }

});

The problem is if the "Slow test card. Declines after a few minutes" is selected and the user quits and resumes the app sometime during these "several minutes", the awaiting purchase is recognised as a successful purchase in _getPurchases() which is called when resuming. Here is my code for _getPurchases():

Future _getPurchases() async {
    List<PurchasedItem> items = await FlutterInappPurchase.instance.getAvailablePurchases();
    print('_getPurchases: (Short description)');
    for (var item in items) {
      print('productId=${item.productId}, transactionId=${item.transactionId}, transactionDate=${item.transactionDate}');
      this._purchases.add(item);
    }
    print('_getPurchases:');
    for (var item in items) {
      print('${item.toString()}');
    }

    setState(() {
      this._items = [];
      this._purchases = items;
    });
  }

@DevonTomatoSandwich
Copy link
Author

To be clearer, I've made a repo here if anyone wants to try to reproduce this issue.

The repo has its own issue here which is similar to this one.

The app has a list of non-consumables you can purchase (smiley faces) check out the readme for more details and screenshots. The readme also has detailed instructions on how to set up.

@github-actions
Copy link

github-actions bot commented Apr 3, 2020

This issue is stale because it has been open 90 days with no activity. Leave a comment or this will be closed in 7 days.

@KazakovKirill
Copy link

Does anyone have a solution? This issue also happened on official flutter in-app purchase package

@DevonTomatoSandwich
Copy link
Author

@KazakovKirill
I published my app with these issues and I'm still not sure if people are scamming me with faulty test cards. Might try flutter's official package later this year but not sure if that will help either

@egorges
Copy link

egorges commented May 7, 2020

Um. I think I am still confused about your expectations. Could you record some gifs for me to understand the problem for others who would wish to help you out?

I think the delayed confirmation should be handled with 2 results on listener: 1st a timeout (duration as an optional parameter) and 2nd the usual return (success or error). This timeout result could be handled in the App to show slow connection.

Should the App be closed before the 2nd (delayed result), getPastPurchases would get unfinished transactions.

@srsudar
Copy link

srsudar commented Feb 3, 2021

I think I might be running into this problem. On Android, I made a purchase a couple days ago with the slow decline card. getPurchaseHistory() is now always giving me a purchase with transactionId == null. When I make a successful purchase I see a transaction with a transactionId != null via the purchase subscription, but when I reload the app and call getPurchaseHistory() I only see the transactionId == null value. I opened this issue to try and ask if this is normal.

Is this the behavior you all are seeing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
android android related issue good first issue Good for newcomers no-issue-activity
Projects
None yet
Development

No branches or pull requests

5 participants