j3k0 / cordova-plugin-purchase

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

iOS subscribe twice in a row gives invalid transactionId #1396

Closed Ralle closed 1 year ago

Ralle commented 1 year ago

Observed behavior

On iOS a transaction with ID appstore.application is created when I subscribe, unsubscribe and subscribe.

Reproduce:

  1. Be on iOS.
  2. Subscribe to something. (Works, you get a valid transactionId)
  3. Go to Settings and unsubscribe.
  4. Go back to app.
  5. Reload webpage.
  6. Subscribe to same something (Now you get a receipt with transactionId = "appstore.application")

This is the code I am using to handle buying:

const transaction = await new Promise<CdvPurchase.Transaction>(
  async (resolve, reject) => {
    async function finishPurchase(purchase: CdvPurchase.Transaction) {
      await purchase.finish();
    }

    async function ownedPurchase(purchase: CdvPurchase.Transaction) {
      resolve(purchase);
    }

    // Listen to approved event
    CdvPurchase.store
      .when()
      .approved(finishPurchase)
      .finished(ownedPurchase);
    const purchaseResult = await offer.order();
    if (purchaseResult?.isError) {
      reject(new Error(purchaseResult.message));
    }
  }
);

The resulting transaction is invalid on iOS on second purchase.

Expected behavior

All transactions regardless of number of times subscribed should be valid.

System Info

Output of cap ls.

Pirkefnirt-2:soundwheel ralle$ npx cap ls
[info] Found 6 Capacitor plugins for android:
       @capacitor/app@4.1.1
       @capacitor/browser@4.1.0
       @capacitor/share@4.1.0
       @capacitor/splash-screen@4.1.2
       @capacitor/status-bar@4.1.1
       capacitor-plugin-safe-area@1.0.1
[info] Found 8 Cordova plugins for android:
       @moodlehq/cordova-plugin-local-notification@0.9.0-moodle.7
       cordova-androidx-build@1.0.4
       cordova-plugin-background-mode@0.8.0
       cordova-plugin-badge@0.8.8
       cordova-plugin-device@2.1.0
       cordova-plugin-purchase@13.3.10
       cordova-plugin-web-share@1.3.0
       cordova-support-android-plugin@2.0.4
[info] Found 6 Capacitor plugins for ios:
       @capacitor/app@4.1.1
       @capacitor/browser@4.1.0
       @capacitor/share@4.1.0
       @capacitor/splash-screen@4.1.2
       @capacitor/status-bar@4.1.1
       capacitor-plugin-safe-area@1.0.1
[info] Found 6 Cordova plugins for ios:
       @moodlehq/cordova-plugin-local-notification@0.9.0-moodle.7
       cordova-plugin-background-mode@0.8.0
       cordova-plugin-badge@0.8.8
       cordova-plugin-device@2.1.0
       cordova-plugin-purchase@13.3.10
       cordova-plugin-web-share@1.3.0
[info] Found 2 incompatible Cordova plugins for ios, skipped install:
       cordova-androidx-build@1.0.4
       cordova-support-android-plugin@2.0.4
[info] Listing plugins for web is not possible.

First subscribe:

Screenshot 2023-03-28 at 10 51 57

Unsubscribe, reload page, then second subscribe:

Screenshot 2023-03-28 at 10 54 12
Ralle commented 1 year ago

I see now that this is because there is "nothing to do" as per the code. I am trying to find the best way of getting the transaction FROM the purchase. It's not easy to find a solution that works for iOS as well as Android.

I now added this code:

if (
  transaction.transactionId ===
  CdvPurchase.AppleAppStore.APPLICATION_VIRTUAL_TRANSACTION_ID
) {
  transaction = transaction.parentReceipt.lastTransaction();
}

But what I really just want, is the valid transaction after a purchase, regardless of whether the purchase somehow reactivates an existing thing or is a new thing.

j3k0 commented 1 year ago

Can't you just use the CdvPurchase.store.localReceipts property to figure out the latest transaction?

Can you give more context on where you are using this?

The CdvPurchase.AppleAppStore.APPLICATION_VIRTUAL_TRANSACTION_ID corresponds to the application download (StoreKit generates a receipt to validate the download of the application, gives extra info as well)

Ralle commented 1 year ago

Ah.. It's been forever since I posted this. I have tinkered every day since. Learned how important the Validator is. I kept getting presented an expired receipt, not anymore since I setup validation.

I still feel like the offer.order() should give you something that resolved to the actual final transaction ID if nothing else.