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

Verified Receipts missing the Product but exists in Local Receipts #1601

Open selcukbeyhan opened 1 week ago

selcukbeyhan commented 1 week ago

Hello,

Observed behavior

After the purchase is done, I revisit my app product purchase screen and want to see whether I have the product or not. However, the relevant transaction and product can be found in the localreceipt but not in the verifiedreceipt. (Although receiptsVerified is called)

Why is the product missing in the verifiedReceipts? What needs to be done for the receipt to be validated? my validator function returns OK by default for all receipts.

Each time when I navigate to my product purchase screen, the plugin is initialized and ready method registered:

Here is the code for the initialization

initializeCdvPurchasePluginEnvironment() { console.log(LOG_PREFIX, "initializeCdvPurchasePluginEnvironment is called"); this.platform.ready().then(async () => { console.log(LOG_PREFIX, "Platform is ready"); const platform = CdvPurchase.Platform.APPLE_APPSTORE;

  CdvPurchase.store.when().productUpdated(function(product) {
    console.log(LOG_PREFIX, "-store.when.productUpdated-", "Product is updated.");
  });

  CdvPurchase.store.when().initiated((transaction) => {
    console.log(LOG_PREFIX, "-store.when.initiated-", "Transaction initiated: ", JSON.stringify(transaction));
    this.updatePurchaseMessages(transaction, "Subscription Initiated", true);
  });

  CdvPurchase.store.when().pending((transaction) => {
    console.log(LOG_PREFIX,"-store.when.pending-",  "Transaction is pending: ", transaction.transactionId);
  });

  CdvPurchase.store.when().approved(transaction => {
    console.log(LOG_PREFIX, "-store.when.approved-", "Purchase is approved. Verifying the transaction: ", JSON.stringify(transaction));
  });

  CdvPurchase.store.when().finished((transaction) => {
    console.log(LOG_PREFIX, "-store.when.finished-", "Transaction finished: ", transaction.transactionId);
  });

  CdvPurchase.store.when().receiptUpdated(async (receipt) => {
    if(receipt != null && typeof receipt != "undefined") {
      for(let i=0; i<receipt.transactions.length; i++) {
        console.log(LOG_PREFIX, "-store.when.receiptUpdated-", "Transaction in receipt: ", JSON.stringify(receipt.transactions[i]));
      }
    } else {
      console.log(LOG_PREFIX, "-store.when.receiptUpdated-", "Receipt has no transaction: ", JSON.stringify(receipt));
    }
  });

  CdvPurchase.store.when().verified(async (verifiedReceipt) => {
    console.log(LOG_PREFIX, "-store.when.verified-", "Receipt is verified: ", verifiedReceipt.id);
    await verifiedReceipt.finish(); // this is documented like that here in the documentation of the plugin, line 918
  });

  CdvPurchase.store.when().receiptsReady(() => {
    console.log(LOG_PREFIX, "-store.when.receiptsReady-", "Receipts ready - receiptsReady is called", JSON.stringify(CdvPurchase.store.localReceipts));
  });

  CdvPurchase.store.when().receiptsVerified(() => {
    console.log(LOG_PREFIX, "-store.when.receiptsVerified-", "Receipts verified - receiptsVerified is called");
  });

  CdvPurchase.store.when().unverified(function (receipt) {
    console.log(LOG_PREFIX, "-store.when.unverified-", "Receipt unverified is called", JSON.stringify(receipt));
  });

  CdvPurchase.store.error((e) => {
    console.log(LOG_PREFIX,"-store.error-",  "ERROR happened during processing: " + e.code + ": " + JSON.stringify(e));
  });

  CdvPurchase.store.register([{
    id: this.subscriptionService.PRODUCT_ID_1_MONTH,
    platform: platform,
    type: CdvPurchase.ProductType.PAID_SUBSCRIPTION
  }, {
    id: this.subscriptionService.PRODUCT_ID_3_MONTH,
    platform: platform,
    type: CdvPurchase.ProductType.PAID_SUBSCRIPTION
  }, {
    id: this.subscriptionService.PRODUCT_ID_12_MONTH,
    platform: platform,
    type: CdvPurchase.ProductType.PAID_SUBSCRIPTION
  }]);

  let validator = (receipt:any, callback:any) => {
    console.log(LOG_PREFIX, "-validator-", "Custom validator is called"); 
    returns OK for all
  }
  CdvPurchase.store.validator = validator;

  await CdvPurchase.store.initialize([{platform: platform, options: {autoFinish: true}}]);

  CdvPurchase.store.ready(() => {
    console.log(LOG_PREFIX, "-store.ready-", "Plugin is initialized and ready");
  });
});

}

Here is the log output for initialization part

⚡️ [log] - LOGPREFIX - Subscription: initializeCdvPurchasePluginEnvironment is called ⚡️ [log] - LOGPREFIX - Subscription: Platform is ready ⚡️ [log] - LOGPREFIX - Subscription: -store.when.receiptsReady- Receipts ready - receiptsReady is called [{"className":"Receipt","transactions":[{"className":"Transaction","transactionId":"appstore.application","state":"finished","products":[{"id":"com.myapp.id"}],"platform":"ios-appstore"},{"className":"Transaction","transactionId":"2000000720186528","state":"finished","products":[{"id":"PRODUCT_1_MONTHLY_SUBSCRIPTION"}],"platform":"ios-appstore","originalTransactionId":"2000000719786443","purchaseDate":"2024-09-20T09:01:29.000Z"}],"platform":"ios-appstore","nativeData":{"appStoreReceipt":"MII6GwYJKoZI........ ⚡️ [log] - LOGPREFIX - Subscription: -store.when.receiptsVerified- Receipts verified - receiptsVerified is called ⚡️ [log] - LOGPREFIX - Subscription: -store.ready- Plugin is initialized and ready

Here is the code section for the function where the logs are generated:

setTheProductButtonTextValues() { console.log(LOG_PREFIX, "setTheProductButtonTextValues is called");

this.month_1_product = CdvPurchase.store.get(this.subscriptionService.PRODUCT_ID_1_MONTH);
this.month_3_product = CdvPurchase.store.get(this.subscriptionService.PRODUCT_ID_3_MONTH);
this.month_12_product = CdvPurchase.store.get(this.subscriptionService.PRODUCT_ID_12_MONTH);

console.log(LOG_PREFIX, "-store.ready-", "Verified Purchases: ", JSON.stringify(CdvPurchase.store.verifiedPurchases));
console.log(LOG_PREFIX, "-store.ready-", "Verified Receipts: ", JSON.stringify(CdvPurchase.store.verifiedReceipts));
console.log(LOG_PREFIX, "-store.ready-", "local transactions: ", JSON.stringify(CdvPurchase.store.localTransactions));

console.log(LOG_PREFIX, "-store.ready-", "Product-1 in local receipts: ", JSON.stringify(CdvPurchase.store.findInLocalReceipts(this.month_1_product)));
console.log(LOG_PREFIX, "-store.ready-", "Product-2 in local receipts: ", JSON.stringify(CdvPurchase.store.findInLocalReceipts(this.month_3_product)));
console.log(LOG_PREFIX, "-store.ready-", "Product-3 in local receipts: ", JSON.stringify(CdvPurchase.store.findInLocalReceipts(this.month_12_product)));

console.log(LOG_PREFIX, "-store.ready-", "Product-1 in verified receipts: ", JSON.stringify(CdvPurchase.store.findInVerifiedReceipts(this.month_1_product)));
console.log(LOG_PREFIX, "-store.ready-", "Product-2 in verified receipts: ", JSON.stringify(CdvPurchase.store.findInVerifiedReceipts(this.month_3_product)));
console.log(LOG_PREFIX, "-store.ready-", "Product-3 in verified receipts: ", JSON.stringify(CdvPurchase.store.findInVerifiedReceipts(this.month_12_product)));

Here are the logs:

⚡️ [log] - LOGPREFIX - Subscription: setTheProductButtonTextValues is called ⚡️ [log] - LOGPREFIX - Subscription: -store.ready- Verified Purchases: [] ⚡️ [log] - LOGPREFIX - Subscription: -store.ready- Verified Receipts: [{"className":"VerifiedReceipt","id":"com.myapp.id","sourceReceipt":{"className":"Receipt","transactions":[{"className":"Transaction","transactionId":"appstore.application","state":"finished","products":[{"id":"com.myapp.id"}],"platform":"ios-appstore"},{"className":"Transaction","transactionId":"2000000720186528","state":"finished","products":[{"id":"PRODUCT_1_MONTHLY_SUBSCRIPTION"}],"platform":"ios-appstore","originalTransactionId":"2000000719786443","purchaseDate":"2024-09-20T09:01:29.000Z"}],"platform":"ios-appstore","nativeData":{"appStoreReceipt":"MII6GwYJKoZIhvcN........... ⚡️ [log] - LOGPREFIX - Subscription: -store.ready- local transactions: [{"className":"Transaction","transactionId":"appstore.application","state":"finished","products":[{"id":"com.myapp.id"}],"platform":"ios-appstore"},{"className":"Transaction","transactionId":"2000000720186528","state":"finished","products":[{"id":"PRODUCT_1_MONTHLY_SUBSCRIPTION"}],"platform":"ios-appstore","originalTransactionId":"2000000719786443","purchaseDate":"2024-09-20T09:01:29.000Z"}] ⚡️ [log] - LOGPREFIX - Subscription: -store.ready- Product-1 in local receipts: {"className":"Transaction","transactionId":"2000000720186528","state":"finished","products":[{"id":"PRODUCT_1_MONTHLY_SUBSCRIPTION"}],"platform":"ios-appstore","originalTransactionId":"2000000719786443","purchaseDate":"2024-09-20T09:01:29.000Z"} ⚡️ [log] - LOGPREFIX - Subscription: -store.ready- Product-2 in local receipts: undefined ⚡️ [log] - LOGPREFIX - Subscription: -store.ready- Product-3 in local receipts: undefined ⚡️ [log] - LOGPREFIX - Subscription: -store.ready- Product-1 in verified receipts: undefined ⚡️ [log] - LOGPREFIX - Subscription: -store.ready- Product-2 in verified receipts: undefined ⚡️ [log] - LOGPREFIX - Subscription: -store.ready- Product-3 in verified receipts: undefined

j3k0 commented 6 days ago

Did you try using iaptic.com validator to see if you have the same result?

The validator is responsible for returned verified receipt, in the "collection" field (cf https://www.iaptic.com/documentation/cordova-plugin-api/interfaces/CdvPurchase.Validator.Response.SuccessPayload.html)