Open kreso22 opened 2 months ago
I understand it's a workaround but do not do this:
CdvPurchase.store.when().receiptsReady(() => {
CdvPurchase.store.restorePurchases();
});
As mentioned in the documentation, "restorePurchases" should only be called when the user clicks the "restore purchases" button. This call will ask for the user appstore password (except if they already logged in in the last 15 minutes, like just after downloading the app).
Can you share the startup logs when the non consumable is owned (without that call)?
I understand it's a workaround but do not do this:
CdvPurchase.store.when().receiptsReady(() => { CdvPurchase.store.restorePurchases(); });
As mentioned in the documentation, "restorePurchases" should only be called when the user clicks the "restore purchases" button. This call will ask for the user appstore password (except if they already logged in in the last 15 minutes, like just after downloading the app).
Can you share the startup logs when the non consumable is owned (without that call)?
Sure!
Here is the related Xcode startup log:
[CDVTimer][console] 0.012994ms
[CDVTimer][handleopenurl] 0.012040ms
[CDVTimer][intentandnavigationfilter] 0.587940ms
[CDVTimer][gesturehandler] 0.012994ms
[CDVTimer][applovinmax] 0.067949ms
[CDVTimer][inappbrowser] 0.017047ms
[CdvPurchase.AppleAppStore.objc] Initialized.
[CDVTimer][inapppurchase] 10.787964ms
[CDVTimer][screenedgesplugin] 0.082016ms
[CDVTimer][cdvwkwebviewfilexhr] 49.003005ms
[CDVTimer][socialsharing] 0.018001ms
[CDVTimer][TotalPluginStartup] 60.752034ms
[CdvPurchase.AppleAppStore.objc] (before init): WARNING: Your app should be single page to use in-app-purchases. onReset is not supported.
Create CdvPurchase...
And from inside the app once started:
[STORE] - "initializeStore" - "CVD Platform:" - "ios-appstore" - "Product name:" XXXX
[STORE] - "[productUpdated]. product:" - SKProduct {className: "Product", title: "Remove ads", description: "No more ads", ...
[STORE] - "Processing ownership. Owned:" - false
[STORE] - "[receiptsReady]."
[STORE] - "[receiptUpdated]. localreceipt:" - SKApplicationReceipt {className: "Receipt", transactions: [1, platform: "ios-appstore", ...}
[STORE] - "Processing ownership. Owned:" - false
[STORE] - "[receiptUpdated]. localreceipt:" - Receipt {className:
"Receipt", transactions: [1, platform: "ios-appstore", ...}
[STORE] - "Processing ownership. Owned: " - false
The first receiptUpdated
is the app. Second receiptUpdate
looks empty (no transactions, nothing ...).
On each receiptUpdate I check if it is for desired product (I have only 1 in my app) like so:
CdvPurchase.store.when().receiptUpdated(localReceipt => {
log("STORE", "[receiptUpdated]. localreceipt:", localReceipt);
const product = CdvPurchase.store.get(PROD_ID);
if (product) {
processOwnership(product.owned);
}
});
Sorry the logs do not contain enough information to see what happens. Can you please include the content of the localReceipt? Set logs level to debug CdvPurchase.store.verbosity = CdvPurchase.LogLevel.DEBUG
and include everything in logs that contain the "CdvPurchase" tag.
No problem! Here is the log with DEBUG verbosity. I have redacted actual receipt, and product and app ids.
[CdvPurchase.AppleAppStore.objc] Initialized.
[CdvPurchase.AppleAppStore.objc] (before init): WARNING: Your app should be single page to use in-app-purchases. onReset is not supported.
Create CdvPurchase...
[CdvPurchase] INFO: initialize([{"platform":"ios-appstore","options":{"needAppReceipt":false}}]) v13.11.1
[CdvPurchase.Adapters] INFO: Adding platforms: [{"platform":"ios-appstore","options":{"needAppReceipt":false}}]
[CdvPurchase.Adapters] INFO:
[CdvPurchase.Adapters] INFO: AppStore initializing...
[CdvPurchase.AppleAppStore] INFO: bridge.init
[CdvPurchase.AppleAppStore.objc] setup: OK
[CdvPurchase.AppleAppStore.Bridge] DEBUG: setup ok
[CdvPurchase.AppleAppStore] INFO: ready
[CdvPurchase.AppleAppStore] INFO: bridge.init done
[CdvPurchase.AppleAppStore.objc] canMakePayments: Device can make payments.
[CdvPurchase.Adapters] INFO: AppStore initialized.
[CdvPurchase.Adapters] INFO: AppStore products: [{"id":"com.my.productid","platform":"ios-appstore","type":"non consumable"}]
[CdvPurchase.AppleAppStore] INFO: bridge.load
[CdvPurchase.AppleAppStore.Bridge] DEBUG: load ["com.my.productid"]
[CdvPurchase.AppleAppStore.objc] load: Getting products data
[CdvPurchase.AppleAppStore.objc] load: Set has 1 elements
[CdvPurchase.AppleAppStore.objc] load: - com.my.productid
[CdvPurchase.AppleAppStore.objc] load: Starting product request...
[CdvPurchase.AppleAppStore.objc] load: Product request started
[CdvPurchase.AppleAppStore.Bridge] DEBUG: processing pending transactions
[CdvPurchase.AppleAppStore.objc] processPendingTransactionUpdates
[CdvPurchase.AppleAppStore] DEBUG: loading appstore receipt...
[CdvPurchase.AppleAppStore.Bridge] DEBUG: loading appStoreReceipt
[CdvPurchase.AppleAppStore.objc] appStoreReceipt:
[CdvPurchase.AppleAppStore.Bridge] DEBUG: infoPlist: com.my.appid,1.1.60,0,????
[CdvPurchase.AppleAppStore] DEBUG: appstore receipt loaded
[CdvPurchase.AdapterListener] DEBUG: receiptsReady: ios-appstore (1/0)
[CdvPurchase.AppleAppStore] DEBUG: receipt updated and ready.
[CdvPurchase.AdapterListener] DEBUG: receiptsUpdated: [{"className":"Receipt","transactions":[],"platform":"ios-appstore","nativeData":{"appStoreReceipt":"MII...Q==","bundleIdentifier":"com.my.appid","bundleShortVersion":"1.1.60","bundleNumericVersion":0,"bundleSignature":"????"}},{"className":"Receipt","transactions":[],"platform":"ios-appstore"}]
[CdvPurchase] DEBUG: Calling callback: type=receiptUpdated() name=#5121f05a236978d1ebad4e26ac18ce54 reason=adapterListener_receiptsUpdated
[CdvPurchase] DEBUG: Calling callback: type=receiptUpdated() name=#5121f05a236978d1ebad4e26ac18ce54 reason=adapterListener_receiptsUpdated
[CdvPurchase.AppleAppStore.objc] BatchProductsRequestDelegate.productsRequest:didReceiveResponse:
[CdvPurchase.AppleAppStore.objc] BatchProductsRequestDelegate.productsRequest:didReceiveResponse: Has 1 validProducts
[CdvPurchase.AppleAppStore.objc] BatchProductsRequestDelegate.productsRequest:didReceiveResponse: - com.my.productid: Remove ads
[CdvPurchase.AppleAppStore.objc] BatchProductsRequestDelegate.productsRequest:didReceiveResponse: sendPluginResult: (
(
{
billingPeriod = 0;
billingPeriodUnit = Day;
countryCode = US;
currency = USD;
description = "No more ads";
discounts = (
);
group = "<null>";
id = "com.my.productid";
introPrice = "<null>";
introPriceMicros = "<null>";
introPricePaymentMode = "<null>";
introPricePeriod = "<null>";
introPricePeriodUnit = "<null>";
price = "$1.99";
priceMicros = 1990000;
title = "Remove ads";
}
),
(
)
)
[CdvPurchase.AppleAppStore.Bridge] DEBUG: load ok: { valid:[{"id":"com.my.productid","description":"No more ads","introPrice":null,"introPricePaymentMode":null,"billingPeriodUnit":"Day","countryCode":"US","introPricePeriodUnit":null,"discounts":[],"title":"Remove ads","price":"$1.99","billingPeriod":0,"group":null,"priceMicros":1990000,"currency":"USD","introPricePeriod":null,"introPriceMicros":null}] invalid:[] }
[CdvPurchase.AppleAppStore] INFO: bridge.loaded: {"validProducts":[{"id":"com.my.productid","description":"No more ads","introPrice":null,"introPricePaymentMode":null,"billingPeriodUnit":"Day","countryCode":"US","introPricePeriodUnit":null,"discounts":[],"title":"Remove ads","price":"$1.99","billingPeriod":0,"group":null,"priceMicros":1990000,"currency":"USD","introPricePeriod":null,"introPriceMicros":null}],"invalidProducts":[]}
[CdvPurchase.AppleAppStore] DEBUG: load eligibility: [{"id":"com.my.productid","description":"No more ads","introPrice":null,"introPricePaymentMode":null,"billingPeriodUnit":"Day","countryCode":"US","introPricePeriodUnit":null,"discounts":[],"title":"Remove ads","price":"$1.99","billingPeriod":0,"group":null,"priceMicros":1990000,"currency":"USD","introPricePeriod":null,"introPriceMicros":null}]
[CdvPurchase.AppleAppStore] DEBUG: No discount eligibility determiner, skipping...
[CdvPurchase.AppleAppStore] INFO: eligibilities ready: {"request":[],"response":[]}
[CdvPurchase.AppleAppStore] DEBUG: com.my.productid is valid: {"id":"com.my.productid","description":"No more ads","introPrice":null,"introPricePaymentMode":null,"billingPeriodUnit":"Day","countryCode":"US","introPricePeriodUnit":null,"discounts":[],"title":"Remove ads","price":"$1.99","billingPeriod":0,"group":null,"priceMicros":1990000,"currency":"USD","introPricePeriod":null,"introPriceMicros":null}
[CdvPurchase.AppleAppStore] DEBUG: registering new product
[CdvPurchase.AppleAppStore] DEBUG: Products loaded: [{"className":"Product","title":"Remove ads","description":"No more ads","platform":"ios-appstore","type":"non consumable","id":"com.my.productid","offers":[{"className":"Offer","id":"$","pricingPhases":[{"price":"$1.99","priceMicros":1990000,"currency":"USD","paymentMode":"UpFront","recurrenceMode":"NON_RECURRING"}],"productId":"com.my.productid","productType":"non consumable","platform":"ios-appstore","offerType":"Default"}],"raw":{"id":"com.my.productid","description":"No more ads","introPrice":null,"introPricePaymentMode":null,"billingPeriodUnit":"Day","countryCode":"US","introPricePeriodUnit":null,"discounts":[],"title":"Remove ads","price":"$1.99","billingPeriod":0,"group":null,"priceMicros":1990000,"currency":"USD","introPricePeriod":null,"introPriceMicros":null},"countryCode":"US"}]
[CdvPurchase.Adapters] INFO: AppStore products loaded: [{"className":"Product","title":"Remove ads","description":"No more ads","platform":"ios-appstore","type":"non consumable","id":"com.my.productid","offers":[{"className":"Offer","id":"$","pricingPhases":[{"price":"$1.99","priceMicros":1990000,"currency":"USD","paymentMode":"UpFront","recurrenceMode":"NON_RECURRING"}],"productId":"com.my.productid","productType":"non consumable","platform":"ios-appstore","offerType":"Default"}],"raw":{"id":"com.my.productid","description":"No more ads","introPrice":null,"introPricePaymentMode":null,"billingPeriodUnit":"Day","countryCode":"US","introPricePeriodUnit":null,"discounts":[],"title":"Remove ads","price":"$1.99","billingPeriod":0,"group":null,"priceMicros":1990000,"currency":"USD","introPricePeriod":null,"introPriceMicros":null},"countryCode":"US"}]
[CdvPurchase.Adapters] INFO: AppStore receipts loaded: [{"className":"Receipt","transactions":[],"platform":"ios-appstore","nativeData":{"appStoreReceipt":"MII...Q==","bundleIdentifier":"com.my.appid","bundleShortVersion":"1.1.60","bundleNumericVersion":0,"bundleSignature":"????"}},{"className":"Receipt","transactions":[],"platform":"ios-appstore"}]
[CdvPurchase.AdapterListener] DEBUG: setSupportedPlatforms: ios-appstore (1 have their receipts ready)
[CdvPurchase.AdapterListener] DEBUG: triggering receiptsReady()
[CdvPurchase] DEBUG: Calling callback: type=productUpdated() name=#3532c2af0630224c668390e441c804b7 reason=adapterListener_productsUpdated
[CdvPurchase] DEBUG: Calling callback: type=receiptsReady() name=receiptsMonitor_setup reason=adapterListener_setSupportedPlatforms
[CdvPurchase.ReceiptsMonitor] DEBUG: receiptsReady...
[CdvPurchase] DEBUG: Calling callback: type=receiptsReady() name=#62332256874e449c2d0c447e06ef51b9 reason=adapterListener_setSupportedPlatforms
[CdvPurchase.ReceiptsMonitor] DEBUG: check(0/0)
[CdvPurchase.ReceiptsMonitor] INFO: receiptsVerified()
And here is what I see after I press RESTORE PURCHASE:
[CdvPurchase.AppleAppStore.objc] paymentQueue:updatedTransactions: com.my.productid
[CdvPurchase.AppleAppStore.objc] paymentQueue:updatedTransactions: State: PaymentTransactionStateRestored
[CdvPurchase.AppleAppStore.objc] processTransactionUpdate:withArgs: transactionIdentifier=2000000734344505
[CdvPurchase.AppleAppStore.objc] paymentQueueRestoreCompletedTransactionsFinished:
[CdvPurchase.AppleAppStore.Bridge] DEBUG: transaction updated:2000000734344505 state:PaymentTransactionStateRestored product:com.my.productid
[CdvPurchase.AppleAppStore] INFO: restore: 2000000734344505 - com.my.productid
[CdvPurchase.AppleAppStore] DEBUG: initializeAppReceipt() => already initialized.
[CdvPurchase.AppleAppStore] INFO: restoreCompleted
[CdvPurchase.AppleAppStore.Bridge] DEBUG: refreshing appStoreReceipt
[CdvPurchase.AppleAppStore.objc] appStoreRefreshReceipt: Request to refresh app receipt
[CdvPurchase.AppleAppStore.objc] appStoreRefreshReceipt: Starting receipt refresh request...
[CdvPurchase.AppleAppStore.objc] appStoreRefreshReceipt: Receipt refresh request started
[CdvPurchase.AppleAppStore.objc] RefreshReceiptDelegate.requestDidFinish: Got refreshed receipt
[CdvPurchase.AppleAppStore.objc] RefreshReceiptDelegate.requestDidFinish: Send new receipt data
[CdvPurchase.AppleAppStore.Bridge] DEBUG: infoPlist: com.my.appid,1.1.60,0,????
[CdvPurchase.AppleAppStore] INFO: receiptsRefreshed
[CdvPurchase.AppleAppStore] DEBUG: receipt updated and ready.
[CdvPurchase.AdapterListener] DEBUG: receiptsUpdated: [{"className":"Receipt","transactions":[{"className":"Transaction","transactionId":"2000000734344505","state":"approved","products":[{"id":"com.my.productid"}],"platform":"ios-appstore"}],"platform":"ios-appstore","nativeData":{"appStoreReceipt":"MII.../4N","bundleIdentifier":"com.my.appid","bundleShortVersion":"1.1.60","bundleNumericVersion":0,"bundleSignature":"????"}},{"className":"Receipt","transactions":[],"platform":"ios-appstore"}]
[CdvPurchase.AdapterListener] DEBUG: receiptsReady: ios-appstore(skipping)
[CdvPurchase] DEBUG: Calling callback: type=receiptUpdated() name=#5121f05a236978d1ebad4e26ac18ce54 reason=adapterListener_receiptsUpdated
[CdvPurchase] DEBUG: Calling callback: type=approved() name=transactionStateMonitors_callOnChange reason=adapterListener_receiptsUpdated_approved
[CdvPurchase] DEBUG: Calling callback: type=approved() name=#667b6c0a81ad70b56ce13d01a4e6794d reason=adapterListener_receiptsUpdated_approved
[CdvPurchase] INFO: finish(Transaction)
[CdvPurchase.AppleAppStore] INFO: finish(2000000734344505)
[CdvPurchase.AppleAppStore.objc] finishTransaction: Transaction 2000000734344505 finished.
[CdvPurchase.AppleAppStore.objc] transactionFinished: 2000000734344505
[CdvPurchase] DEBUG: Calling callback: type=receiptUpdated() name=#5121f05a236978d1ebad4e26ac18ce54 reason=adapterListener_receiptsUpdated
[CdvPurchase.AppleAppStore.Bridge] DEBUG: transaction updated:2000000734344505 state:PaymentTransactionStateFinished product:com.my.productid
[CdvPurchase.AppleAppStore] INFO: finish: 2000000734344505 - com.my.productid
[CdvPurchase.AppleAppStore] DEBUG: initializeAppReceipt() => already initialized.
[CdvPurchase.AdapterListener] DEBUG: receiptsUpdated: [{"className":"Receipt","transactions":[{"className":"Transaction","transactionId":"2000000734344505","state":"finished","products":[{"id":"com.my.productid"}],"platform":"ios-appstore"}],"platform":"ios-appstore","nativeData":{"appStoreReceipt":"MII.../4N","bundleIdentifier":"com.my.appid","bundleShortVersion":"1.1.60","bundleNumericVersion":0,"bundleSignature":"????"}}]
[CdvPurchase] DEBUG: Calling callback: type=receiptUpdated() name=#5121f05a236978d1ebad4e26ac18ce54 reason=adapterListener_receiptsUpdated
[CdvPurchase] DEBUG: Calling callback: type=finished() name=transactionStateMonitors_callOnChange reason=adapterListener_receiptsUpdated_finished
[CdvPurchase.AppleAppStore] DEBUG: receipt updated and ready.
[CdvPurchase.AdapterListener] DEBUG: receiptsUpdated: [{"className":"Receipt","transactions":[{"className":"Transaction","transactionId":"2000000734344505","state":"finished","products":[{"id":"com.my.productid"}],"platform":"ios-appstore"}],"platform":"ios-appstore","nativeData":{"appStoreReceipt":"MII.../4N","bundleIdentifier":"com.my.appid","bundleShortVersion":"1.1.60","bundleNumericVersion":0,"bundleSignature":"????"}},{"className":"Receipt","transactions":[],"platform":"ios-appstore"}]
[CdvPurchase.AdapterListener] DEBUG: receiptsReady: ios-appstore(skipping)
[CdvPurchase] DEBUG: Calling callback: type=receiptUpdated() name=#5121f05a236978d1ebad4e26ac18ce54 reason=adapterListener_receiptsUpdated
[CdvPurchase] DEBUG: Calling callback: type=receiptUpdated() name=#5121f05a236978d1ebad4e26ac18ce54 reason=adapterListener_receiptsUpdated
Perhaps this may be related to iOS 18? It seemed to have worked prior to some few weeks ago when I started getting user emails.
Perhaps if I added server-side validation of receipt it would work? If I add server-side validation now, would that ignore previous purchases?
Thank you in advance.
I spent many more days trying to figure this out. The plugin simply silently fails, and there is no information on why.
While I can't possibly know if this is true - perhaps it could have to do with using a different IAP adapter for Cordova a while back. Possibly receipts that were created by the previous platform somehow are not recognized when the app initializes today - but are recognized when Restore is initiated.
Could this be the issue?
Perhaps this may be related to iOS 18? It seemed to have worked prior to some few weeks ago when I started getting user emails.
Perhaps if I added server-side validation of receipt it would work? If I add server-side validation now, would that ignore previous purchases?
Thank you in advance.
I have the same problem and my real device is not ios 18. I have test in different devices in the sandbox. all the devices have the same problem. only restore purchase seem to fix it. Did you ever find the cause and solution?
Unfortunately I was unable to figure this out so I moved to Revenuecat's plugin. My app works now. Good luck!
Unfortunately I was unable to figure this out so I moved to Revenuecat's plugin. My app works now. Good luck!
Thanks for the reply, I'm using cordova, revenuecat is still supporting cordova? I think i'll jump over there too. i have been messing with this plugin for 3 days with no luck.
Yes, you are right. They are gradually dropping support - but we should be good for another 1-2 years. I will reconsider my options when that moment arrives.
I also saw this https://qonversion.io/ while searching for alternative to this plugin. Do you know if qonversion any good?
Observed behavior
Making a purchase works (product is owned). Next app load - receipt shows product is not owned. Only after hitting restorePurchase does the receipt show the product as owned
Not using validation. App has one single product.
Expected behavior
Product owned persists.
Code
Currently forcing the
restorePurchases()
on random event (when app starts). Otherwise, product will be shown asnot owned
.