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
[IN_APP_PURCHASE] Purchase dialog not showing on iOS 13.4 #53534
Comments
I am facing almost the same error. After I upgraded my iOS to version 13.4 the purchase dialog do not show anymore in the sandbox environment (Local and Test Flight). |
Hi @dancamdev @fracon |
@TahaTesser Sure, here is my `[✓] Flutter (Channel master, v1.16.4-pre.18, on Mac OS X 10.15.3 19D76, locale en-IT) [✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2) [✓] Xcode - develop for iOS and macOS (Xcode 11.4) [✓] Chrome - develop for the web [✓] Android Studio (version 3.6) [✓] IntelliJ IDEA Community Edition (version 2019.3.1) [✓] VS Code (version 1.43.2) [✓] Connected device (3 available) • No issues found!` and my `
|
By debugging the
Could it be an Apple server issue? |
@TahaTesser here the output of Flutter doctor:. @dancamdev I don't know why but after I increment my app version the purchase dialog works again ... once ... now after I do a purchase and the expiration date is reached when I try to do a new purchase using the same product apple returns directly a Purchase object with status purchased instead pending and no dialog shows. I don't if something changed in the 13.4 version and requires some adjustment of this package but according to the last 2 topics here https://forums.developer.apple.com/community/system-frameworks/in-app-purchase the in-app purchase is broken on this ios version. Flutter (Channel stable, v1.12.13+hotfix.8, on Mac OS X 10.15.3 19D76, locale en-PT) [✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3) [✓] Xcode - develop for iOS and macOS (Xcode 11.4) [✓] Android Studio (version 3.6) [✓] VS Code (version 1.43.2) [✓] Connected device (1 available) • No issues found! |
@fracon it happened to me too. I was able to open the dialog once and then not anymore upon retry. I'll double check if it returns a purchased product too. I'll let you know in a few minutes. |
Yup, it is actually returning a valid purchase although it isn't. That's even worse! Thanks for pointing it out @fracon |
any updates? @fracon are you getting the |
@dancamdev I didn't debug in Xcode yet but I search in the apple docs that error 2 means "Error code indicating that the user canceled a payment request." Weird humm. |
@fracon do you happen to have any app store promotion for iaps enabled? On this thread they are suggesting that might be the cause. It doesn't seam to work for me, but you might want to try! |
@dancamdev no promotions here but it's for me clearly something change from ios 13.3 to 13.4 or something change in the Apple's servers because on Friday my app works so since Saturday (when I updated my device to ios 13.4) stopped work. I just don't know if it's a bug on iOS or if this package needs an update to work with the new iOS. |
Totally agree! Hopefully someone will answer soon! What about @TahaTesser ? |
Another hypothesis from my side. Somehow iOS 13.4 seems to be braking only the objective c implementation. Swift is working fine. I have no expertise to port the this plugin to Swift. So someone else might want to look into that |
Here's a way I'm getting closer to fix this one. I replaced the
The commented code, is the actual in_app_purchase code. After that, I was able to make the purchase dialog popup correctly, but only the very first time after flushing the transactions. Awesome! We're close! Now, it looks like |
@cyanglaz this is the IAP issue I mentioned that's marked as customer critical. |
@dancamdev Cancelled transaction is a failed transaction, thus I think you should remove the failed transaction object before adding new payment object with the same product. Did you use the following method to delete the transaction before adding a new one: |
@LHLL Yes, that was what it was all about. Still iOS 13.3.1 didn’t require you to call completePurchase after cancelling the purchase. I think it has to be explicitly stated in the documentation. |
@dancamdev Totally agree this needs to be documented in the plugin's API. Thanks for bring this up! @cyanglaz Can we reuse Apple's comments and maybe add a code block in the documentation as an example since we are not really providing exactly same APIs as SKStoreKit? |
@LHLL @dancamdev I really don't if this is the point of this issue. I'm completing all my purchases (except pending as documented) but still, when I click to do a new purchase I receive, on listen function, a previous purchase object with purchased status and expired instead receive a new purchase object with pending status. Am I missing some point here? |
@ fracon What type of product are you providing? It sounds like you are providing a subscription? |
@LHLL exactly my app provides some subscriptions and just to clarify what happened here:
|
@fracon I believe your issue is a different issue. I periodically experience "Cannot connect to iTunes Store" error under sandbox environment while using SKStoreKit and I never got any responses from Apple while raising this issue in the Apple Developer Forums. Here are some similar issues raised there: From my own experience, I think this might be an issue from Apple's side. |
It doesn’t seem like a different problem to me, I was getting exactly the same behavior. Make sure that when you finish your purchase, you call If you are willing to share the IAPS code either here or privately. I’ll have a look at it, and check if I get any issue. |
For anyone still having the issue with the newest update.. |
So from spending a DECENT amount of digging in here is my take: The only way to receive this transaction on flutter side is when you call await But all this isn't even a problem why things get stuck. The problem is with the I hope my research will be useful for a faster solution. P.S. Maybe the solution is to store an array of all transactions for specific productId and when user calls finishTransaction it loops through all transactions for that productID and complete the ones that need to be completed. P.S.S
Because It makes impossible for user to re-subscribe if he was once a subscriber but cancelled. Why you do you even need to check that? Appstore will warn you if you have already purchesed an item or are an active subscriber. |
As @ziggycrane mentioned this is still not working with the latest update v0.3.4+1. The error message I see from Crashlytics is "PlatformException(storekit_duplicate_product_object, There is a pending transaction for the same product identifier. Please either wait for it to be finished or finish it manuelly using |
Yeah, I also have the same issue... |
Same I'm still having the same issue |
@ziggycrane querying queryPastPurchases() before listening to purchaseUpdate seems a bit counter-intuitive to me. What is @LHLL take on this? Currently my client is going through major anxiety because 1 in maybe 5 purchases seem to fail; the purchase is not being delivered that is. Since Apple isn't allowing dev's to test Storekit on the simulator I had to spent countless amount of hours on browserstack trying to debug the issue, it is such a mess from Apple's end. Now I have trouble justifying staying on the package even against my client. I don't know what to do so I'll probably file a report with apple code level support because i am so confused with this. |
I strongly recommended flutter_in_app_purchase |
You mean inapp_purchase? It's deprecated when I first looked into it... Could flutter not merge the code base of the two? People seem very happy with it |
Sorry typo It was stopped in some point,but recently they get it going again |
I also recommend flutter_in_app_purchase. Unlike this package, it actually
works for subscriptions in iOS. It is documented more thoroughly and the
underlying swift code is battle tested. it was used in a popular react
native billing library for years and the maintainer was kind enough to port
a flutter version for us ❤️
…On Thu, Jul 30, 2020, 8:49 PM Daniel Schaefer ***@***.***> wrote:
So from spending a DECENT amount of digging in here is my take:
iOS subscriptions transactionState will inevitably change to this at some
point:
case restored = 3 // Transaction was restored from user's purchase
history. Client should complete the transaction.
The only way to receive this transaction on flutter side is when you call
await InAppPurchaseConnection.instance.queryPastPurchases(); BEFORE you
subscribe to
InAppPurchaseConnection.instance.purchaseUpdatedStream;.
@ziggycrane <https://github.com/ziggycrane> querying queryPastPurchases()
before listening to purchaseUpdate seems a bit counter-intuitive to me.
What is @LHLL <https://github.com/LHLL> take on this?
Currently my client is going through major anxiety because 1 in maybe 5
purchases seem to fail; the purchase is not being delivered that is. Since
Apple isn't allowing dev's to test Storekit on the simulator I had to spent
countless amount of hours on browserstack trying to debug the issue, it is
such a mess from Apple's end. Now I have trouble justifying staying on the
package even against my client. I don't know what to do so I'll probably
file a report with apple code level support because i am so confused with
this.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#53534 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AEDE3LNZCBNDBJQGU3X2IDLR6G6D7ANCNFSM4LWDFULQ>
.
|
To fix this, I used purchases_flutter instead
…On Thu, Jul 30, 2020 at 3:17 PM Danis Preldžić ***@***.***> wrote:
https://pub.dev/packages/flutter_inapp_purchase
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#53534 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AGH6KGEJNPZUB4RJNSKBWTLR6HBM7ANCNFSM4LWDFULQ>
.
|
Thanks it's just hard to drop it after all the time getting it to run on Google's plugin, not to speak the time it'd take to transition. I'll give the next update a chance. I know LHLL and some others are very committed to solving this for good |
Any news on this issue? |
@pinpong My pull request (link above) fixes the issue, I am using it in my production app already. If you try it, make sure you also call Currently I call also this method before launching the purchase dialog (notice that this code works properly only if my PR is applied): Future<void> clearTransactionsIos() async {
final transactions = await SKPaymentQueueWrapper().transactions();
for (final transaction in transactions) {
try {
if (transaction.transactionState !=
SKPaymentTransactionStateWrapper.purchasing) {
await SKPaymentQueueWrapper().finishTransaction(transaction);
}
} catch (e) {
print(e);
}
}
} Calling that method should not be necessary in normal circumstances, but it may be necessary for example now due to the current issues in this plugin (your earlier calls to |
@kinex i wrote me a helper class to handle the iap. Thanks for the PR. Do you know why it's not working on Simulator? I get no error neither a purchase dialog. abstract class IAPHelper {
bool available = false;
bool initialized = false;
final InAppPurchaseConnection iap = InAppPurchaseConnection.instance;
List<ProductDetails> availableProducts = [];
List<PurchaseDetails> purchases = [];
StreamSubscription subscription;
bool purchased = false;
void initIAP() async {
await clearTransactionsIos();
available = await iap.isAvailable();
if (available) {
await _getProducts();
await _getPastPurchases();
_verifyPurchase();
}
subscription = iap.purchaseUpdatedStream.listen(
(purchaseDetails) => () {
purchases.addAll(purchaseDetails);
_verifyPurchase();
}, onDone: () {
subscription?.cancel();
}, onError: (e) {
initialized = false;
onLoaded.call(initialized);
});
Future.delayed(Duration(milliseconds: 1000))
.then((value) => {initialized = true, onLoaded.call(initialized)});
}
void disposeIAP() async {
subscription?.cancel();
}
Future<void> clearTransactionsIos() async {
if (Platform.isIOS) {
final transactions = await SKPaymentQueueWrapper().transactions();
for (final transaction in transactions) {
try {
if (transaction.transactionState !=
SKPaymentTransactionStateWrapper.purchasing) {
await SKPaymentQueueWrapper().finishTransaction(transaction);
}
} catch (e) {
print(e);
}
}
}
}
List<String> _getProductIDs() {
return [Constants.MONTH_ABO_ID, Constants.YEAR_ABO_ID];
}
Future<void> _getProducts() async {
ProductDetailsResponse response =
await iap.queryProductDetails(_getProductIDs().toSet());
availableProducts = response.productDetails;
}
Future<void> _getPastPurchases() async {
QueryPurchaseDetailsResponse response = await iap.queryPastPurchases();
if (response.error != null) {
// Handle the error.
}
for (final purchase in response.pastPurchases) {
if (purchase.pendingCompletePurchase) {
if (Platform.isIOS) {
await iap.completePurchase(purchase);
}
}
}
purchases = response.pastPurchases;
}
PurchaseDetails _hasPurchased(String productID) {
return purchases.firstWhere((purchase) => purchase.productID == productID,
orElse: () => null);
}
void subscribeProduct(ProductDetails prod) async {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: prod);
iap.buyNonConsumable(purchaseParam: purchaseParam);
}
void _verifyPurchase() async {
for (String id in _getProductIDs()) {
PurchaseDetails purchase = _hasPurchased(id);
if (purchase != null) {
switch (purchase.status) {
case PurchaseStatus.pending:
onPending.call(purchase);
break;
case PurchaseStatus.purchased:
purchased = true;
onSuccessPurchase.call(purchase);
break;
case PurchaseStatus.error:
purchased = false;
onBillingError.call(purchase.error);
break;
}
}
}
onLoaded.call(initialized);
}
void onLoaded(bool initialized) {}
void onPending(PurchaseDetails product) {}
void onSuccessPurchase(PurchaseDetails product) {}
void onBillingError(IAPError error) {}
} |
@pinpong IAPs must be tested in a real device. In addition your code is not calling |
@cyanglaz it looks like a lot of valuable anecdotes were added to this issue after it was closed. Is there another ticket tracking the post-close issue? I think I'm in the middle of this problem, and if I'm understanding things correctly, this is a serious and fundamental problem in the plugin. @ziggycrane's comment seems like it should be addressed: Should we file a new ticket? Is this already being discussed/handled somewhere? |
Using v0.3.4+16 with same error |
Could everyone who still has this problem please file a new issue with the exact description of what happens, logs, and the output of |
I always get the following problem:
|
clean the transactions first:
|
@ondev yes right |
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of |
I'm facing an issue with in_app_purchase 0.3.1+2 flutter plugin and iOS 13.4
The purchase dialog opens once and, if canceled, it won't open anymore.
It works fine on Android and any prior version of iOS 13.4
It happens for both, consumable and non-consumable purchases.
By investigating further, I noticed that specifically, nothing happens when calling the following
FIAPaymentQueueHandler.m
method:- (void)addPayment:(SKPayment *)payment { [self.queue addPayment:payment]; }
The text was updated successfully, but these errors were encountered: