Search Unity

Question Unable to process purchase with transaction id

Discussion in 'Unity IAP' started by dev_dash, Nov 26, 2022.

  1. dev_dash

    dev_dash

    Joined:
    Jul 26, 2020
    Posts:
    15
    This "warning(not error)" keep appearing everytime when I initialize UnityIAP and purchase a in app item on Android phone. The warning appears on runtime unity console 1-2 times at initialize, and 3 times when I purchase a item. The weired thing is that the transaction id is fixed to that same string value(start with omdep...) while my new purchased item transaction id keep changes. I don't know why this is happening.
    How can I make this warning not to keep appearing?

    here is all message:
    Unity IAP: Unable to process purchase with transaction id: omdepjndeebfkbncfkplllaf.AO-J1OyUEXQZz8i_-WG8s_Ca1rG-Ktn4qDqkzaus189eoCWg2StKrofvC0Z4DmdJ0AmyB94i9Jg7bCm5Q7gtm-0r23XOgOMOAEWZT3D0Ot5V2nogg0P7rVA because the product details associated with the purchased products were not found.
    UnityEngine.UnitySynchronizationContext:Exec()

    And here is my code:

    Code (CSharp):
    1. using PlayFab;
    2. using PlayFab.ClientModels;
    3. using PlayFab.Json;
    4. using System;
    5. using System.Collections.Generic;
    6. using Unity.Services.Core;
    7. using Unity.Services.Core.Environments;
    8. using UnityEngine;
    9. using UnityEngine.Purchasing;
    10.  
    11. //https://velog.io/@kimwonseop/Unity-IAP
    12.  
    13.  
    14. public class IAPManager : MonoBehaviour, IStoreListener
    15. {
    16.     public bool IsInitialized
    17.     {
    18.         get => storeController != null && storeExtensionProvider != null;
    19.     }
    20.  
    21.     public const string productIDNonConsumable = "ad_remove2";
    22.     public const string productIDConsumable = "money_box2";
    23.  
    24.     private const string _Android_NonconsumableId = "ad_remove2";
    25.     private const string _Android_ConsumableId = "money_box2";
    26.     //private const string _Android_SubscriptionId = "com.studio.app.subscription.android";
    27.  
    28.     private IStoreController storeController;
    29.     private IExtensionProvider storeExtensionProvider;
    30.  
    31.     public static IAPManager I;
    32.     void Awake()
    33.     {
    34.         if (I != null && I != this)
    35.             Destroy(this);
    36.         else
    37.         {
    38.             I = this;
    39.         }
    40.     }
    41.     public string environment = "production";
    42.     // Init Unity gaming service
    43.     async void Start()
    44.     {
    45.         try
    46.         {
    47.             var options = new InitializationOptions()
    48.                 .SetEnvironmentName(environment);
    49.  
    50.             await UnityServices.InitializeAsync(options);
    51.             InitUnityIAP();
    52.  
    53.         }
    54.         catch (Exception exception)
    55.         {
    56.             Debug.Log(exception.Message);
    57.         }
    58.     }
    59.     private void InitUnityIAP()
    60.     {
    61.         Debug.Log("InitUnityIAP");
    62.         var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    63.  
    64.         builder.AddProduct(productIDNonConsumable, ProductType.NonConsumable, new IDs(){
    65.             {_Android_NonconsumableId, GooglePlay.Name },
    66.  
    67.             }
    68.         );
    69.         builder.AddProduct(productIDConsumable, ProductType.Consumable, new IDs(){
    70.             {_Android_ConsumableId, GooglePlay.Name },
    71.             }
    72.         );
    73.  
    74.         UnityPurchasing.Initialize(this, builder);
    75.     }
    76.  
    77.     public void OnInitialized(IStoreController controller, IExtensionProvider extension)
    78.     {
    79.         Debug.Log("유니티 IAP 초기화 성공");
    80.         storeController = controller;
    81.         storeExtensionProvider = extension;
    82.     }
    83.     public void OnInitializeFailed(InitializationFailureReason error)
    84.     {
    85.         Debug.LogError($"유니티 IAP 초기화 실패 {error}");
    86.     }
    87.     public void OnPurchaseFailed(Product product, PurchaseFailureReason reason)
    88.     {
    89.         Debug.LogWarning($"구매 실패 - {product.definition.id}, {reason}");
    90.     }
    91.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    92.     {
    93.         Debug.Log($"구매 성공 - ID : {args.purchasedProduct.definition.id}");
    94.         Debug.Log($"TrID : {args.purchasedProduct.transactionID}");
    95.         if (string.IsNullOrEmpty(args.purchasedProduct.receipt))
    96.         {
    97.             Debug.LogWarning("Attempted to process purchase with no receipt: ignoring");
    98.             return PurchaseProcessingResult.Complete;
    99.         }
    100.         var googleReceipt = GooglePurchase.FromJson(args.purchasedProduct.receipt);
    101.  
    102.         PlayFabClientAPI.ValidateGooglePlayPurchase(new ValidateGooglePlayPurchaseRequest()
    103.             {
    104.                 CurrencyCode = args.purchasedProduct.metadata.isoCurrencyCode,
    105.                 PurchasePrice = (uint)(args.purchasedProduct.metadata.localizedPrice * 100),
    106.                 ReceiptJson = googleReceipt.PayloadData.json,
    107.                 Signature = googleReceipt.PayloadData.signature
    108.             }, result =>
    109.             {
    110.                 Debug.Log("Validation successful!");
    111.             },error =>
    112.             {
    113.                 Debug.Log("Validation failed: " + error.GenerateErrorReport());
    114.             }
    115.         );
    116.         return PurchaseProcessingResult.Complete;
    117.     }
    118.  
    119.  
    120.     public void Purchase(string productId)
    121.     {
    122.         if (!IsInitialized)
    123.             return;
    124.         Debug.Log("Purchase");
    125.  
    126.         var product = storeController.products.WithID(productId);
    127.         if (product != null && product.availableToPurchase)
    128.         {
    129.             Debug.Log($"구매 시도 - {product.definition.id}");
    130.             storeController.InitiatePurchase(product);
    131.         }
    132.         else
    133.         {
    134.             Debug.Log($"구매 시도 불가 - {productId}");
    135.         }
    136.     }
    137. }
    138.  
    139.  
    140. // The following classes are used to deserialize JSON results provided by IAP Service
    141. // Please, note that JSON fields are case-sensitive and should remain fields to support Unity Deserialization via JsonUtilities
    142. public class JsonData
    143. {
    144.     // JSON Fields, ! Case-sensitive
    145.     public string orderId;
    146.     public string packageName;
    147.     public string productId;
    148.     public long purchaseTime;
    149.     public int purchaseState;
    150.     public string purchaseToken;
    151. }
    152.  
    153. public class PayloadData
    154. {
    155.     public JsonData JsonData;
    156.  
    157.     // JSON Fields, ! Case-sensitive
    158.     public string signature;
    159.     public string json;
    160.  
    161.     public static PayloadData FromJson(string json)
    162.     {
    163.         var payload = JsonUtility.FromJson<PayloadData>(json);
    164.         payload.JsonData = JsonUtility.FromJson<JsonData>(payload.json);
    165.         return payload;
    166.     }
    167. }
    168.  
    169. public class GooglePurchase
    170. {
    171.     public PayloadData PayloadData;
    172.  
    173.     // JSON Fields, ! Case-sensitive
    174.     public string Store;
    175.     public string TransactionID;
    176.     public string Payload;
    177.  
    178.     public static GooglePurchase FromJson(string json)
    179.     {
    180.         var purchase = JsonUtility.FromJson<GooglePurchase>(json);
    181.         purchase.PayloadData = PayloadData.FromJson(purchase.Payload);
    182.         return purchase;
    183.     }
    184. }
     
    Last edited: Nov 26, 2022
  2. Yannick_D

    Yannick_D

    Unity Technologies

    Joined:
    Feb 21, 2022
    Posts:
    235
    Hello,

    A few questions to help diagnose this:
    -When purchasing an item, is this item processed properly? If not, is it only 1 product or both?
    -Have you recently removed or renamed a product from your Google Play Store?
    -Is this happening in Sandbox or on a live application?


    My guess is that you have some pending purchases for a product that no longer exists, but Google is sending those pending purchases back.

    If this is the case, pending purchases will be refunded after 3 days and this should no longer appear after.
     
  3. dev_dash

    dev_dash

    Joined:
    Jul 26, 2020
    Posts:
    15
    Thank you for your reply Denis!

    1) I've set the IAP to test mode. The recent purchases always processed properly. But the warning message keep showing.
    2) Yes, I made refund 1 actual purchase in real moden and 1 purchase in tested mode.
    But, the refunds are completed processes. Not in pending.

    I think It's good to know how to find out Unity IAP "transaction id" from google order ID (start with "GPA. xxxx.xxxx.xxxx)
    So that I can figure out which purchase is making problem in google play console. Now I don't know which one is that.
    How can I do this, Plz let me know.

    Thanks.



     
    TrieuMP likes this.
  4. Yannick_D

    Yannick_D

    Unity Technologies

    Joined:
    Feb 21, 2022
    Posts:
    235
    It seems the "transaction ID" is the purchase token and not the order ID.
    Because of that, it's not possible to find your order directly, but if you open an order, you will have a "Copy purchase token" button. You can use this to then paste the copied purchase token and compare it with the one in the ArgumentException.
    I have made a suggestion to the team so that we update this to make it easier in the future.

    In your script, I see you have _Android_SubscriptionId commented out, is it possible you had purchased this previously and it's now disabled, but Google is trying to renew it?
     
  5. -chris

    -chris

    Joined:
    Mar 1, 2012
    Posts:
    99
    I'm going to hop onto this thread as well.

    Android customer sent me a log file. Their game is not initializing the Unity In App Purchasing system in subsequent launches, so their purchase is not working, and they are not happy.

    From the log, it looks like Unity is logging a warning which sounds concerning:
    My IAP product is named "supporter_pack_core". I don't know why the above is adding a hyphen and printing the product's name twice.

    I cannot find "Unavailable product" in my source code anywhere, so I assume this is coming from the Unity In App Purchasing package somewhere.

    The other warning in their log is:
    ^ my source does not contain strings for "Unable to process purchase with transaction id", so I assume this is from the In App Purchasing package too.

    Using In App Purchasing 4.5.1.

    My own code logged the following store initialization enum error from the customer: InitializationFailureReason.NoProductsAvailable

    This is the first report of this I've had from hundreds of paying customers.

    So I am wondering if one of two scenarios is happening:
    • Client's In App Purchasing system is initializing the product name incorrectly for some reason?

    • Unity's transaction server has recorded the product name improperly like "{productName}-{productName}" instead of just "{productName}"?
    I don't have stack traces since these are just small warnings and this is a live product, and haven't been able to reproduce this locally, and Google has approved various builds, so it's not a common issue, but an issue nonetheless :confused:
     
    Last edited: Nov 30, 2022
  6. dev_dash

    dev_dash

    Joined:
    Jul 26, 2020
    Posts:
    15
    Thank you Denis!

    The way (you let me know) that getting purchase id made me figure out which order making problem right away.

    1) In real mode(not test), I purchased a item by mistake and had refunded it.
    2) I got refunded money so I think all process had completed.
    3) So I deleted the item at the in-app store for another test.
    4) But I've checked the detailed history today, the order was pending.

    I think It is pending because I remove the item on the in-app list.
    I gotta wait couple of days more. Thank you so much Denis!




     

    Attached Files:

    Yannick_D likes this.
  7. Yannick_D

    Yannick_D

    Unity Technologies

    Joined:
    Feb 21, 2022
    Posts:
    235
    Hello,

    Yes, these 2 traces are from IAP.

    In the first one, what you see is the "{product id}-{store specific product id}". In this case it seems your product is called supporter_pack_core in both cases. I've looked at the code and this can only happen when Product.availableToPurchase is false.
    Is it possible this product was made unavailable?

    The other possible case is that the product was not retrieved from the Google Play Store. If it failed to retrieve some products, there should be some traces before that with more details. If it didn't retrieve it at all, it might be because it doesn't exist in the store. Could you confirm that your Google Play Console contains the product "supporter_pack_core"?

    The second trace seems to be Google sending an update about a product that isn't recognized, but since they get the NoProductsAvailable, this is expected.
     
  8. -chris

    -chris

    Joined:
    Mar 1, 2012
    Posts:
    99
    @Yannick_Denis
    Thanks for the details and looking into the IAP package code – helps put my mind at ease somewhat.
    Good to know about the hyphen-logging double-up.

    Testing and debugging this on my own Google Play account, looks like I am potentially calling a product check before Unity's IAP store is initialized, which throws the "Unity IAP: Unable to process purchase with transaction id:" warning.

    But, the game then checks the transaction again once the Unity IAP store does become initialized, and it finds the product and transaction successfully after that. So things do work as intended.

    I have not changed any Google backend IAP stuff for many months. Just looked, and it's still there in the backend, name intact; the product exists successfully. And the Play Console's Order Management page is showing purchases are coming through successfully.

    -----------------------

    Looking at my customer's log more, I suppose the real underlying culprit is their "NoProductsAvailable" initialization failure. As to why that's failing, I don't know.

    Their log mentions nothing else of interest; no other insight.

    Perhaps a timeout with Google?

    Also noted that their log does not say they signed into Google Play at all (which usually logs their display name persona after signing in), so perhaps their phone's Google integration did not work for that particular session for some reason. I haven't heard from this customer again, and don't see any recent refunds, so perhaps they restarted their phone and everything is fine now.

    I've had other reports from other customers saying they were able to rectify Google-related issues by signing out of multiple Google accounts on their phone, and just have one account be signed in, and things work properly after that.

    There's lots of weird oddities in the Google / Android ecosystem... :)

    -----------------------

    I am curious though, is there a way to get more verbose logging or info as to why the Unity IAP initialization fails? Or if we're just given the InitializationFailureReason and nothing more, then so be it.

    I note that my game is written to alert the user to "Please try restarting the game" if initialization fails, but I might edit it to say "Please try restarting your device" instead :p

    Thanks again for looking into things a bit.
     
    Arnaud_Gorain likes this.
  9. Yannick_D

    Yannick_D

    Unity Technologies

    Joined:
    Feb 21, 2022
    Posts:
    235
    Yes, you have to make sure Unity's IAP is initialized before doing product checks! Products only become available after the products added to the ConfigurationBuilder are retrieved from the store.

    For the "PurchasingUnavailable" and "NoProductsAvailable", if there's no setup issue, this would most likely happen when a problem occurs while retrieving products, usually a connection issue. Most of the time, those are being retried which is why we don't log any messages.
    We do have a few listeners you could use in the GooglePlayConfiguration such as SetServiceDisconnectAtInitializeListener to be informed when there's a problem with the connection or SetQueryProductDetailsFailedListener to know when there's a problem obtaining the product details from the Google Play Store along with the retry count.
     
    -chris likes this.