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] How to don't call store.validator when buy currently subscribed? #750

Closed haduchuy96 closed 5 years ago

haduchuy96 commented 5 years ago

First time: I bought package A with apple id A. Then my application would call store.validator. 2nd time: I bought A-packet with Apple id A. Apple returned me the message "You're currently subscribed to this". This time I do not want to call store.validator anymore.

Please help me

matte5031 commented 5 years ago

Don't call verify?

haduchuy96 commented 5 years ago

Don't call verify?

Why do you ask me that?. I just want to block the call store.validator for the "You're currently subscribed to this"

matte5031 commented 5 years ago

prodct.verify() is what calls the store.validator if I'm not misstaken?

j3k0 commented 5 years ago

Yes, product.verify() triggers the call to the validator. However, I think calling the validator whenever there's an update in the receipt is a better practice.

Each time there's an update to the receipt, it's an opportunity for people with bad intentions to inject a fake one.

You can optimize away some calls (especially when you already know client-side that the receipt is invalid), but I think it just adds unnecessary complexity.

Out of curiosity, what's the problem with calling the validator again in this case?

haduchuy96 commented 5 years ago

First: User A buys package 1 with Apple id A. Second: User B buys package 1 with Apple id A

At the second. If I call next store.validator then my application server will upgrade to user B. This is not true. Only user A has been upgraded

j3k0 commented 5 years ago

@haduchuy96 Ok so you have a problem with the way you're identifying your users. As per Apple's guidelines, your use case should not happen, because the Apple ID should be your User ID.

But there's a workaround. You can store the subscription's original_transaction_id for your user in your server's database, so:

The check: is there an user that already purchased this subscription? Something like SELECT id FROM users WHERE itunes_original_purchase_id = X

You can also ask User B to connect to another iTunes account, or to switch to your User A account.

haduchuy96 commented 5 years ago

Thank you for this solution. I think this issue should close

j3k0 commented 5 years ago

You're welcome!

matte5031 commented 5 years ago

@j3k0 When you say "Yes, product.verify() triggers the call to the validator. However, I think calling the validator whenever there's an update in the receipt is a better practice."

I wonder what this means, does this mean that I can call the validator without calling product.verify() from the approved function? And is there anyway for the plugin to know if the receipt has been updated?

I'm currently successfully using your plugin but there's one scenario that I'm not sure how to handle for auto renewas subscription, It is:

Everytime the user opens the app i check with my server side database if the subscription has expired and and needs to be validated. If so I return the call to the app saying that I need to verify the subscription.

From here I don't know what to do, do I do like below again even if it has been purchased earlier:

store.order("subscription");

or below (doesn't seem to work, validator is never called with below code, I check if valid etc before calling verify, I've only managed to call productverify successfully from the approved function, is this a must that it is called from there or?):

var product = store.get("subscription"); var product = product.verify();

There's a lot of questions above, but it's just me trying to show what I don't understand. The actual question is: how, using this plugin, do I it get the receipt so that I can send it back to server for validation again in this scenario (I know how to do it when the user manually buys the subscription first time. Then I just call the order -> approved -> verify -> validator -> finish)

j3k0 commented 5 years ago

Just to be sure we're doing things right: if you use the plugin correctly, you should have defined store.validator and call store.refresh as soon as the app starts.

In that case, when the user has a valid active subscription, the plugin will trigger the approved(..) events for this product, from which you can call product.verify().

You might have a trustful way of saying the subscription isn't expired yet, in which case you can just skip the call to verify(), i.e. call finish() right away... but it means you integrated Apple's real-time notification webhook on your server. If not, then look what I can do:

  1. I subscribe, the server stores the expiry date
  2. I call apple support to cancel the last transaction
  3. Each time I start the app, the client skip the validation part
  4. I get a month (or year) of subscription for free, yay!

If you validate the receipt every time, you'll see that the transaction has been canceled by apple support, i.e. you should cancel the subscription on your server.

j3k0 commented 5 years ago

order -> approved -> verify -> validator -> finish isn't the normal flow. Just what will happen to most user on the first purchase.

approved is an event that can happen anytime in your apps lifecycle. You should be ready to handle it as soon as the app starts.

matte5031 commented 5 years ago

Alright, thanks a lot for the help, appreciate it! Have a great weekend.