j3k0 / cordova-plugin-purchase

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

Android - Purchased subscription not acknowledged #1473

Closed Ryner47 closed 7 months ago

Ryner47 commented 7 months ago

Observed behavior

Hello, I have implemented this plugin in my Android app created with Capacitor to make a subscription purchase available. The problem is that after the subscription purchase, Google asks me via email to confirm the purchase by opening the app again, but despite 'transaction.verify()' is called the subscription "acknowledged" remain 'false'. Therefore the subscription is not recognized and a refund is issued.

Could you help me figure out if this is a known issue?

Could it be that the "acknowledged" mechanism doesn't work in the test environment?

Thanks

Expected behavior

After completed purchase a subscription "acknowledged" state should become 'true'

System Info

Code

this.store = new window.CdvPurchase.Store();
this.store.register({
    id: SUB_PREMIUM,
    type: CdvPurchase.ProductType.PAID_SUBSCRIPTION,
    platform: CdvPurchase.Platform.GOOGLE_PLAY,
});
if (this.store.isReady) {
    await this.store.update();
} else {
    await this.store.initialize();
}
this.store
.when()
.productUpdated((product: any) => {})
.approved((transaction: any) => {
    transaction.products.forEach((product: any) => {
        if (this.store.owned(product)) {
            if (product.id === SUB_PREMIUM) {
                console.log('premium enabled');
            } else {
                console.log('premium disabled');
            }
        }
    });
    return transaction.verify();
})
.verified(receipt: any) => {
    receipt.finish();
})
.receiptUpdated((receipt) => {});

Screen

  1. image

  2. image

LOG

09-22 21:59:11.432 14687 14838 D CdvPurchase: acknowledgePurchase(fxlglonohaehhmopjnhpocbf.AO-J1Ozk8onZevhoPi-8WrNCwDl44mUFsE4T7YUQu72c2wGz2BEADiSbr5qXTfKxIgbT8PxxMglt1PXclgTmkyI1DsIdjps9Ig)
09-22 21:59:11.432 14687 14838 D CdvPurchase: executeServiceRequest() -> OK
09-22 21:59:12.096 14687 15594 D CdvPurchase: onAcknowledgePurchaseResponse() -> Success
j3k0 commented 7 months ago

Hello Ryner, it looks like you are using the old version of the plugin (which uses Google's old library (version 4), they don't accept new binaries with it. "IAPProduct" is a old typing name.

Make sure you are using cordova-plugin-purchase version 13 directly, the documentation is in the README of the project.

Ryner47 commented 7 months ago

Hello @j3k0 , sorry I didn't provided complete information, I've edited the main post above with all info.

I have the latest version of this plugin.

Any help will be appreciated.

Ryner47 commented 7 months ago

Now it seems to work correctly

lj64 commented 5 months ago

Hi @Ryner47, did you change anything before it started working?

I have the same problem. After ordering a subscription:

  1. order() is successful, no error
  2. then receiptUpdated() is fired
.receiptUpdated(receipt => {
    console.log('RECEIPT UPDATED');
    receipt.transactions.forEach(transaction => {
      transaction.products.forEach(trProduct => {
        console.log(`product owned: ${trProduct.id}`);
      });
    });
  })

and correct product id is logged

  1. then approved() is fired, where transaction.state === 'approved'
  2. and that's it, verified() is not fired, nothing else happens

Few minutes later I get the same email from google, that purchase was cancelled. When I restart the app even before receiving this email, this subscription is not owned.

Also using v13, code works on ios. Will try to get better logs later.

Can the issue be caused by iaptic validator (or misconfiguration there)?

lj64 commented 5 months ago

Here are logs after triggering order() function:

11-24 12:10:24.203 17988 18191 D CdvPurchase: buy() -> setProductDetailsParamsList
11-24 12:10:24.204 17988 18191 D CdvPurchase: initiatePurchaseFlow()
11-24 12:10:24.204 17988 18191 D CdvPurchase: executeServiceRequest() -> OK
11-24 12:10:24.204 17988 18191 D CdvPurchase: initiatePurchaseFlow() -> launchBillingFlow.
11-24 12:10:24.220  3349  4753 E AbstractGmsTracer: Traced class shouldn't be obfuscated: LegacyRealTimers$$ExternalSyntheticLambda1 [CONTEXT service_id=259 ]
11-24 12:10:24.221  1773  4184 D CompatibilityChangeReporter: Compat change id reported: 161145287; UID 10274; state: DISABLED
--
11-24 12:11:16.684  3349 24876 W NetworkScheduler:  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
11-24 12:11:16.684  3349 24876 W NetworkScheduler:  at ajqx.run(:com.google.android.gms@234414038@23.44.14 (190400-580326705):8)
11-24 12:11:16.684  3349 24876 W NetworkScheduler:  at java.lang.Thread.run(Thread.java:1012)
11-24 12:11:16.689  1773  1875 D DisplayBoosterService: DisplayColorManager: get persist.vendor.colorgamut.mode : 1
11-24 12:11:16.689  1773  1875 D DisplayBoosterService: BacklightBoostManager: No need to boost
11-24 12:11:16.694 17988 17988 D CdvPurchase: onPurchasesUpdated() -> Success
11-24 12:11:16.696 17988 17988 D CdvPurchase: sendToListener() -> purchasesUpdated
11-24 12:11:16.696 17988 17988 D CdvPurchase:             data -> {"purchases":[{"orderId":"GPA.blah","packageName":"blah","productId":"blah","purchaseTime":1700824272691,"purchaseState":0,"purchaseToken":"blah","quantity":1,"autoRenewing":true,"acknowledged":false,"productIds":["blah"],"getPurchaseState":1,"developerPayload":"","accountId":"","profileId":"","signature":"blah","receipt":"{\"orderId\":\"GPA.blah\",\"packageName\":\"blah\",\"productId\":\"blah\",\"purchaseTime\":1700824272691,\"purchaseState\":0,\"purchaseToken\":\"blah\",\"quantity\":1,\"autoRenewing\":true,\"acknowledged\":false}"}]}
11-24 12:11:16.702  1773  5256 D CoreBackPreview: Window{33b430f u0 com.android.vending/com.google.android.finsky.billing.acquire.SheetUiBuilderHostActivity}: Setting back callback OnBackInvokedCallbackInfo{mCallback=android.window.IOnBackInvokedCallback$Stub$Proxy@8e7171e, mPriority=-1}
11-24 12:11:16.713 17988 18191 E FirebasePlugin: org.json.JSONException: Value null at 1 of type org.json.JSONObject$1 cannot be converted to JSONObject

and then after restarting the app:

11-24 12:16:12.338 25866 26238 I CdvPurchase: queryPurchases(SUBS) -> Elapsed time: 66ms
11-24 12:16:12.339 25866 26238 D CdvPurchase: sendToListener() -> setPurchases
11-24 12:16:12.339 25866 26238 D CdvPurchase:             data -> {"purchases":[{"orderId":"GPA.blah","packageName":"blah","productId":"blah","purchaseTime":1700824272691,"purchaseState":0,"purchaseToken":"blah","quantity":1,"autoRenewing":true,"acknowledged":false,"productIds":["blah"],"getPurchaseState":1,"developerPayload":"","accountId":"","profileId":"","blah","receipt":"{\"orderId\":\"GPA.blah\",\"packageName\":\"blah\",\"productId\":\"blah\",\"purchaseTime\":1700824272691,\"purchaseState\":0,\"purchaseToken\":\"blah\",\"quantity\":1,\"autoRenewing\":true,\"acknowledged\":false}"}]}

Not sure how the error from FirebasePlugin is connected to purchase not being acknowledged, but seems suspicious. Any hints @j3k0 ?

lj64 commented 5 months ago

UPDATE: I commented out iaptic validator and it started working. CdvPurchase: onAcknowledgePurchaseResponse() -> Success

Now the fun part. I want to still use iaptic, but I don't have access to the current account (long story). I created new one which I have to properly configure, but here is the question: is it safe to switch those accounts? Will it affect somehow the users of my app? For example will the app still see the subscriptions that they ordered when app had previous iaptic account (and also v11 of the plugin)?