Check subscription validity using Flutters in-app-purchase plugin?
Asked Answered
D

4

17

I'm trying to implement in-app purchases using the official Flutter In-App-Purchase plugin. I've got things working, except I can't figure out how to tell if a users subscription is still active or if it expired. Even after I canceled my test subscription, the values I get after connecting and doing queryPastPurchases() are the same as when the subscription was active:

productId: test_subscription_1   
transactiondate: 1565682346568   
status: null

verificationData
    source: IAPSource.GooglePlay   
    localVerificationData: {
        "orderId":"GPA.1234-1234-1234-12345",
        "packageName":"com.example.myapp",
        "productId":"test_subscription_1",
        "purchaseTime":1565682346568,
        "purchaseState":0,
        "purchaseToken":"<long string>",
        "autoRenewing":false
    }   
    serverVerificationData: "<long string>"

Am I supposed to simply hard code my subscription period and compare the current time to purchaseTime + the subscription period? Will that even work across auto-renewals? What if the user changes the date on his phone to a year ago? It seems like there should be some value that should either give me the expiration time or at least a boolean true/false to indicate if the subscription is still valid?

Diaspora answered 13/8, 2019 at 9:11 Comment(0)
C
3

The official in-app purchase plugin handles making the purchase but doesn't supply all of the backend infrastructure you need to handle auto-renewing subscriptions specifically.

The short answer to your question is send this purchase info up to your server and manage subscription status there. Alternatively you can look into a managed solution like purchases_flutter: https://pub.dev/packages/purchases_flutter/

Crosstie answered 13/8, 2019 at 14:51 Comment(7)
Surely the use case "purchase subscription" -> "check if user has an active subscription" must be one of the most basic use cases for an IAP plugin? It doesn't make sense that the plugin would only handle making purchases, but not actually using them - then what's the point? It's like a car with an ignition lock but no wires attached.Diaspora
All of the IAP infrastructure was designed well before the app stores supported subscriptions. So that, combined with security issues of client-side code make some things that seem simple non-trivial.Crosstie
Ok, that explains a lot. Thanks a lot for the RevenueCat link by the way, I'm implementing it instead of the IAP plugin. I was skeptical at first but it turns out their API is way more easy to work with and their free plan seems very generous (just hope it stays that way).Diaspora
I am facing the same problem, but i found one method i think can solve this, please correct me if i am wrong, look at the code when checking the past purchases i used (purchase.billingClientPurchase.isAutoRenewing) to see if is renewing or not, and in a test subscription is true when is active and false when i cancel. So i guess it is enought to know if is charging user or not. What you think?Emma
There is something going on with revenue cat somehow people are always linking to them or have edited a post to use themWeingartner
Somethings up thereWeingartner
@JorgeVieira I think that can be used only in androidKung
W
0

I have used ‘purchases_flutter‘ and the process is straightforward. You can check the status of the subscription by calling the methods which comes with the plugin. Check out this article which includes an example https://medium.com/flutter-community/in-app-purchases-with-flutter-a-comprehensive-step-by-step-tutorial-b96065d79a21

Willpower answered 6/7, 2020 at 5:23 Comment(0)
D
0

For anyone still having issues, there's a simple solution to validate the receipt on iOS

Here's a simple js snippet that you can use to fetch the actual receipt from Apple and use it to validate the subscription

Note You will need to generate app specific password for the app from with apple developer account

Further help

https://developer.apple.com/documentation/appstorereceipts/expiration_intent

const axios = require('axios');
const iosValidateReceipt = async (transactionReceipt, isTest = true) => 
    new Promise(async (resolve, reject) => {
        const url = isTest ? 'https://sandbox.itunes.apple.com/verifyReceipt' : 'https://buy.itunes.apple.com/verifyReceipt';
        const data = {
            'receipt-data': transactionReceipt,
            password: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
        };
        console.log('iosValidateReceipt - input - ', { url, data });
        try {
            const response = await axios.post(url, data);
            console.log('iosValidateReceipt - success - ', JSON.stringify(response.data, null, 2));
            resolve(response.data);
        } catch (err) {
            console.log('iosValidateReceipt - error -', err);
            reject(err);
        }
    });
Depressive answered 14/11, 2022 at 6:11 Comment(0)
T
0

queryPastPurchases() might be not available anymore but there is restorePurchases() now.

First you need to setup your listener for the purchases:

late StreamSubscription<List<PurchaseDetails>> subscription;
subscription = InAppPurchase.instance.purchaseStream.listen((purchaseDetailsList) {
   purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
      if (purchaseDetails.status == PurchaseStatus.pending) {
        // handle pending
      } else if (purchaseDetails.status == PurchaseStatus.error) {
        // handle error
      } else if (purchaseDetails.status == PurchaseStatus.purchased ||
                 purchaseDetails.status == PurchaseStatus.restored) {
        // handle new or past purchases
      }
  });
}, onDone: () {
   subscription.cancel();
}, onError: (error) {
   // handle error
});

Afterwards call restorePurchases() and the just created listener will get the past purchases with status PurchaseStatus.restored

// restore purchases to get past subscriptions
InAppPurchase.instance.restorePurchases();
Thundersquall answered 25/5 at 21:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.