j3k0 / cordova-plugin-purchase

In-App Purchase for Cordova on iOS, Android and Windows
https://purchase.cordova.fovea.cc
1.3k stars 537 forks source link

[IOS] Quite desperate. Looking for help--order function does not do anything. #1153

Closed OrthoCube closed 3 years ago

OrthoCube commented 3 years ago

system info

OS: macOS, Big Sur
Cordova: 10.0.0
Device: iPhone 8, iOS 14.2
Plugin Version: 10.5.3

Expected behavior

Calling the order function with the name of the product should show iOS purchase popup and the purchase should just continue as usual.

Observed behavior

After calling the The subscription product's status becomes requested but nothing happens after that. No pop-ups appear. The weird thing is that, just yesterday, we were able to purchase the product, and after that subscription expired, we were able to purchase it again. It worked just fine. However, today, after the product expired again, clicking the order function just updates the product's status to requested but nothing happens after that. Purchase is also working perfectly fine on Android--it's just iOS that is extremely weird and always producing errors and problems. Sometimes, when we change sandbox accounts and use a new one, it works, but sometimes, it just suddenly stops working.

Steps to reproduce

We tried simplifying the code as much as possible to the point that only these are the important parts:

    // this just automatically sets the validator to the correct URL which changes depending on the user's ID.
    this.subscriptions.add(
      this.status.userData.subscribe(data => {
        this.userData = data;
        this.store.validator = `https://xxxxxxxxxxx/api/app/users/${ data.id }/purchased`;
      })
    );

    this.store.register([
      {
        id: this.MONTHLY_SUBSCRIPTION,
        type: this.store.PAID_SUBSCRIPTION
      },
      {
        id: this.YEARLY_SUBSCRIPTION,
        type: this.store.PAID_SUBSCRIPTION
      }
    ]);

    this.store.error((error) => {
      console.log('ERROR', error);
    });

    this.store.when(this.YEARLY_SUBSCRIPTION).updated((p) => {
      this.yearly = p;
      console.log('yearly', p.state);
    });

    this.store.when(this.MONTHLY_SUBSCRIPTION).updated((p) => {
      this.monthly = p;
      console.log('month', p.state);
    });

    this.store.when(this.YEARLY_SUBSCRIPTION)
        .approved(p => p.verify())
        .verified(p => p.finish())
        .owned(p => this.continue(true, p.id));

    this.store.when(this.MONTHLY_SUBSCRIPTION)
        .approved(p => p.verify())
        .verified(p => p.finish())
        .owned(p => this.continue(true, p.id));

    this.store.refresh();

Calling this.store.order(this.MONTHLY_SUBSCRIPTION); does not do anything. The funny part is calling this.store.order(this.YEARLY_SUBSCRIPTION); does show iOS purchase pop-up. There are no error outputs or anything. We also did try using this.store.autoFinishTransactions = true; before this.store.refresh(); but that didn't seem to do anything.

We have been getting no luck lately and if anyone could offer suggestions or tips on how to fix this (maybe it's an error in the plugin?

Thank you very much in advance, and I would gladly provide any further information if needed.

Tzanou123 commented 3 years ago

+1 same problem here If i launch this.store.order the event requested is triggered then directly goes in valid state sometimes it working like 1 time for 5 and all the action are really slow only with ios

achocano commented 3 years ago

+1!

We have the same problem. We are using version 10.5.0, the Android app works perfectly, ios also locally and with the app in Testflight, but when the app is published live, the trigger this.store.order doesn't work.

Anyone knows how to solve this?

Thanks!

grahamBamber commented 3 years ago

+1

Running the commands manually is not working.

I also have an additional product that has appeared from nowhere with type = "application" in an approved state which my receipt validator is rejecting, but it keeps being returned.

achocano commented 3 years ago

I just received communication from apple technicians and they tell me the following:

When I pressed the “Hazte PREMIUM” button, nothing happened. In the console log, I see the same sequence of log statements which I reported to you earlier.

default 10:19:29.969089-0800 Gamersfy Single tap identified. Request details on potential zoom. (0x105821800) default 10:19:29.970279-0800 Gamersfy Ending potential tap. (0x105821800) default 10:19:29.970308-0800 Gamersfy Single tap recognized - commit potential tap (0x105821800) default 10:19:29.982728-0800 Gamersfy Synthetic click completed. (0x105821800) default 10:19:29.984986-0800 Gamersfy 0x1065e76d0 - [PID=45165, throttler=0x1065b44d8] ProcessThrottler::Activity::Activity: Starting foreground activity / 'WebPageProxy::runJavaScriptInFrameInScriptWorld' default 10:19:29.986495-0800 Gamersfy 0x1065e76d0 - [PID=45165, throttler=0x1065b44d8] ProcessThrottler::Activity::invalidate: Ending foreground activity / 'WebPageProxy::runJavaScriptInFrameInScriptWorld' default 10:19:29.988125-0800 Gamersfy 0x1065e7720 - [PID=45165, throttler=0x1065b44d8] ProcessThrottler::Activity::Activity: Starting foreground activity / 'WebPageProxy::runJavaScriptInFrameInScriptWorld' default 10:19:29.988629-0800 Gamersfy 0x1065e7720 - [PID=45165, throttler=0x1065b44d8] ProcessThrottler::Activity::invalidate: Ending foreground activity / 'WebPageProxy::runJavaScriptInFrameInScriptWorld'

Here again, I’m not seeing the fifth runJavaScriptInFrameInScriptWorld call which occurs in your TestFlight (working) scenario. You indicated that you are using the Ionic framework. Have you submitted a question to the framework developers as to why the button press is not working in the call to addPayment. I’d review your code to determine - when the “Hazte PREMIUM” button is pressed - what does the app call next that is in the Ionic framework?

Anyone can help us?

grahamBamber commented 3 years ago

My problem seems to revolve around store.ready(true) not being called. I get the subscription popup after calling it (manually), however I have another issue that my receipt is returning an expiry date in 2013.

achocano commented 3 years ago

@grahamBamber how can the popup be invoked manually?

j3k0 commented 3 years ago

Can you set store.verbosity = store.DEBUG; then copy the logs here? I was reviewing the code in the plugin, I don't see how "store.ready(true);" can possibly not being called. Thanks.

achocano commented 3 years ago

The solution has just been given by @j3k0 . If you don't put that instruction: "store.ready(true);", the purchases will work fine on Android, on ios locally and in Testflight but not when the app is downloaded from the App Store.

Thanks for the support.

j3k0 commented 3 years ago

Please note that you're not expected to call store.ready(true) yourself though... The plugin is supposed to do that when the initialization phase is completed.

OrthoCube commented 3 years ago

In my case, this seems to be the problem:

It seems that the plugin sends verification of type=application to the validation service, and it expects that the server replies with ok=true, else the plugin stops working altogether. Even if the subscription itself has expired already, if the plugin sends a verification of type=application, as long as that receipt exists, even if it's expired, the validation service should reply ok=true.

After I adjusted the validation code on the server side, purchases on iOS started to actually work. The unfortunate thing is that this type=application is nowhere on the documentation (or maybe I just missed it? Sorry if that is the case). This really gave me a huge headache, considering that I didn't have an actual iOS device to test with, so it was very hard to find the cause. (That's half my fault, though. But I think I would realize if it was in the documentation.) I was half crying trying to solve something when I had no idea what is causing the problem in the first place.

A related issue can be found here: https://github.com/j3k0/cordova-plugin-purchase/issues/1155 (my own issue too, but this is solved already)

j3k0 commented 1 year ago

To add up to your finding: when the verified product is the application, it cannot be "expired". The server should just check that the receipt is legit, meaning the app has been download from the AppStore and not side-loaded. This is useful also to give the server an opportunity to check for things that might have changed. In particular a transaction might have been removed from the receipt by Apple (in case of a refund). In which case the app won't have a transaction to verify, so if there were no "application" receipt validation this could be missed.

Notice that all receipt validation headaches can avoided by using our service https://www.iaptic.com/ - shameless plug but I believe it's worth mentioning.