Search Unity

Invalid transaction IDs when buying/restoring subscriptions on iOS

Discussion in 'Unity IAP' started by p-lindberg, Apr 1, 2019.

  1. p-lindberg

    p-lindberg

    Joined:
    Feb 19, 2019
    Posts:
    12
    We're seeing some issues with the transaction IDs reported by Unity IAP when purchasing and restoring subscriptions on iOS. When a purchase is made, we take the transactionId field of the Product that was purchased and send this together with the receipt to our backend, which does validation and registers the subscription to the user's account. The backend uses the transaction ID to find the correct entry in the receipt, since all purchases ever made are listed in the same receipt.

    The initial purchase of a subscription always works without issues. However, when a user later on invokes the purchase restoration process to restore their subscriptions, this does not work as expected. Unity will then invoke the ProcessPurchase method with a Product once for each subscription period that the user has paid for, but the transactionId found on the Product can not be found anywhere in the receipt. I suspect this might not be the fault of the IAP plugin but rather just the way that Apple treats restorations, but I haven't found anything conclusive on this yet. Our current workaround for this is to parse the app receipt locally to find the correct transaction IDs to send to our backend.

    I'd also like to point out that it's somewhat inconvenient that Unity IAP overwrites the same Product instance with new data for each subscription period, since restoring a product is usually an asynchronous operation.

    The other issue is when a user tries to repurchase a subscription which has expired or they have canceled. In this case, we expected the purchase to work the same as the initial purchase, but it doesn't. The Product that Unity IAP invokes the ProcessPurchase method with has a transaction ID that can't be found in the receipt in this case. Usually, the transaction ID in the receipt is directly adjacent to the one reported by Unity IAP, i.e. if unity says transactionId = ...44 then the receipt says transactionId = ...45.

    In our case, this resulted in the backend not finding the transaction in the receipt and invalidating the purchase. When that happens, we mark the purchase as complete so that Unity IAP won't keep retrying it. However, upon restarting the app, Unity IAP attempts to finish the transaction again anyway, but this time with the right transaction ID, so the issue is resolved by restarting the app. It seems then, that Unity is actually aware of the 'correct' transaction (i.e. the one that is actually in the receipt) under the hood, but doesn't tell us about it the first time, instead giving us an invalid transaction ID. We used a similar workaround for this issue, by parsing the receipt locally and finding the actual transaction ID. This, however, means that Unity will (in vain) retry the transaction once the app is restarted, even though we successfully handled it the first time.

    I suspect these issues with transaction IDs are related to the other thread I created earlier today:
    https://forum.unity.com/threads/pending-subscriptions-cant-be-confirmed-on-ios.654007/

    Edit to add: using latest version 1.22.
     
  2. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    I will check. Might you be able to elaborate? First you mention "the transactionId found on the Product can not be found anywhere in the receipt" followed by "Our current workaround for this is to parse the app receipt locally to find the correct transaction IDs". Apple does return all receipts each time, we are looking to see if we can add a helper to find the current one.
     
  3. p-lindberg

    p-lindberg

    Joined:
    Feb 19, 2019
    Posts:
    12
    Of course. The situation we saw was that the app receipt contained a number of transactions, but none of them matched the transaction ID in the Product.transactionID field, so we could not determine which transaction in the receipt matched the one just restored by Unity IAP. Thus, we had to ignore the Product.transactionID field completely, since it did not refer to anything we could find, and instead parse the receipt manually and find the last transaction for the subscription in question by looking at the dates.

    Same situation when repurchasing an expired/cancelled subscription. The transaction id in Product.transactionID matches nothing in the receipt, so instead we had to parse the receipt manually and find the latest transaction for the subscription in question.

    Essentially the main issue here is that we expected to be able to use the transaction ID to find the right transaction in the receipt, but this does not seem to always be possible with subscriptions. We understand this might be the way it works with Apple, but there does seem to be some weirdness going on with the plugin (like the automatic retry of the subscription purchase after restart but with the correct transaction ID).
     
  4. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @p-lindberg Thanks for the great explanation, I am following up here with priority.
     
  5. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @p-lindberg Is this in TestFlight/Sandbox or in Production?
     
  6. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    In discussing with the team here, we are simply passing along the receipt data and TransactionID's that Apple gives us.
     
  7. p-lindberg

    p-lindberg

    Joined:
    Feb 19, 2019
    Posts:
    12
    Thank you for looking into this, and sorry for the late reply. This was in sandbox testing.

    Are you sure this is handled correctly? It seems very strange that apple would give the wrong ID in the case where you buy a previously cancelled/expired subscription. Also, what I mentioned about the transaction with correct ID dropping in after a restart is very strange (especially since we mark the transaction as complete on the first go, there shouldn't be a pending transaction). Maybe it's just sandbox weirdness, wouldn't surprise me.