Search Unity

Modifying IAPButton with Receipt validation

Discussion in 'Unity IAP' started by shlighter, Jul 22, 2019.

  1. shlighter

    shlighter

    Joined:
    Mar 2, 2017
    Posts:
    83
    I use a codeless IAP.
    I attached the IAP button script to my gameobject, and inserted a private function to call after On Purchase Complete.
    Everything works fine, but now I want to modify it so I have receipt validation.
    1. First thing I did was to move
    Code (CSharp):
    1.  onPurchaseComplete.Invoke(e.purchasedProduct);
    in IAPButton.cs to the end- after I confirm that validation is true, and after I check that product ID is correct. So only then "On Purchase Complete" will start. Is it OK?
    2. Unity says "you should make decisions based on the product IDs"
    So I added 2 code lines-
    Code (CSharp):
    1.   if (productReceipt.productID!= productId)
    2.                     { validPurchase = false; }
    My question is, is my code correct now?
    Will it fail if validation is not right (and won't start "On Purchase Complete") and succeed when valid? It's hard to test it since I only test it on editor

    My code in IAPButton (not sure if it's right):
    Code (CSharp):
    1. public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
    2.         {
    3.             Debug.Log(string.Format("BB IAPButton.ProcessPurchase(PurchaseEventArgs {0} - {1})", e,
    4.                 e.purchasedProduct.definition.id));
    5.  
    6.             //NEW CODE:
    7.  
    8.             bool validPurchase = true; // Presume valid for platforms with no R.V.
    9.               // Unity IAP's validation logic is only included on these platforms.
    10. #if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
    11.             // Prepare the validator with the secrets we prepared in the Editor
    12.             // obfuscation window.
    13.             var validator = new CrossPlatformValidator(GooglePlayTangle.Data(),
    14.                 AppleTangle.Data(), Application.identifier);
    15.        
    16.             try
    17.             {
    18.                 // On Google Play, result has a single product ID.
    19.                 // On Apple stores, receipts contain multiple products.
    20.                 var result = validator.Validate(e.purchasedProduct.receipt);
    21.                 // For informational purposes, we list the receipt(s)
    22.                 Debug.Log("Receipt is valid. Contents:");
    23.                 foreach (IPurchaseReceipt productReceipt in result)
    24.                 {
    25.                     Debug.Log(productReceipt.productID);
    26.                     Debug.Log(productReceipt.purchaseDate);
    27.                     Debug.Log(productReceipt.transactionID);
    28.  
    29.                     if (productReceipt.productID!= productId)
    30.                     { validPurchase = false; }
    31.  
    32.                         GooglePlayReceipt google = productReceipt as GooglePlayReceipt;
    33.                     if (null != google)
    34.                     {
    35.                         // This is Google's Order ID.
    36.                         // Note that it is null when testing in the sandbox
    37.                         // because Google's sandbox does not provide Order IDs.
    38.                         Debug.Log(google.transactionID);
    39.                         Debug.Log(google.purchaseState);
    40.                         Debug.Log(google.purchaseToken);
    41.                     }
    42.  
    43.                     AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt;
    44.                     if (null != apple)
    45.                     {
    46.                         Debug.Log(apple.originalTransactionIdentifier);
    47.                         Debug.Log(apple.subscriptionExpirationDate);
    48.                         Debug.Log(apple.cancellationDate);
    49.                         Debug.Log(apple.quantity);
    50.                     }
    51.  
    52.  
    53.                     var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    54.                     // Get a reference to IAppleConfiguration during IAP initialization.
    55.                     var appleConfig = builder.Configure<IAppleConfiguration>();
    56.                     var receiptData = System.Convert.FromBase64String(appleConfig.appReceipt);
    57.                     AppleReceipt receipt = new AppleValidator(AppleTangle.Data()).Validate(receiptData);
    58.  
    59.                     Debug.Log(receipt.bundleID);
    60.                     Debug.Log(receipt.receiptCreationDate);
    61.                     foreach (AppleInAppPurchaseReceipt productReceipt2 in receipt.inAppPurchaseReceipts)
    62.                     {
    63.                         Debug.Log(productReceipt2.originalTransactionIdentifier);
    64.                         Debug.Log(productReceipt2.productID);
    65.                     }
    66.  
    67.  
    68.  
    69.                 }
    70.  
    71.             }
    72.             catch (IAPSecurityException)
    73.             {
    74.                 Debug.Log("Invalid receipt, not unlocking content");
    75.                 validPurchase = false;
    76.             }
    77. #endif
    78.  
    79.             if (validPurchase)
    80.             {
    81.                    onPurchaseComplete.Invoke(e.purchasedProduct);
    82.                 // Unlock the appropriate content here.
    83.             }
    84.                 return PurchaseProcessingResult.Complete;
    85.  
    86.             Debug.Log("111");
    87.             //END NEW CODE
    88.  
    89.             return (consumePurchase) ? PurchaseProcessingResult.Complete : PurchaseProcessingResult.Pending;
    90.         }
    91.  
    92.         /**
     
    Last edited: Jul 23, 2019
  2. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    6,835
    Does this code compile? I don't see where you are setting the value of productId anywhere. Also, I'm not sure where we say "you should make decisions based on the product IDs". At any rate, I would not recommend that you change the code as you suggest. I would also not recommend codeless IAP for you, but use scripting instead.
     
  3. shlighter

    shlighter

    Joined:
    Mar 2, 2017
    Posts:
    83
    It's the IAPButton class. Do you know it?
    1. Yes this code compile
    2. The productID is declared in the class, you choose it on the editor, inside the IAP button script component.
    3. "Also, I'm not sure where we say ". Check Unity's documentation https://docs.unity3d.com/Manual/UnityIAPValidatingReceipts.html
    "it is important you check not just that the receipt is valid, but also what information it contains. A common technique by users attempting to access content without purchase is to supply receipts from other products or applications. These receipts are genuine and do pass validation, so you should make decisions based on the product IDs parsed by the CrossPlatformValidator."
    4. To say I don't recommend codeless IAP is not an answer because there's nothing wrong with it, codeless IAP uses code anyway, and that's what I'm trying to edit
     
  4. shlighter

    shlighter

    Joined:
    Mar 2, 2017
    Posts:
    83
    No problem, I will ask the same question again with scripting
     
  5. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    6,835
    Please look at IAPDemo.cs, it uses scripting and has this implemented already.
     
  6. JoshExterna

    JoshExterna

    Joined:
    Apr 26, 2019
    Posts:
    2
    i dont get why we have codeless if apple requires validation which takes more scripting. so basically dont use codelss for apple because you'll have to script for validation anyways. right?
     
  7. richardzzzarnold

    richardzzzarnold

    Joined:
    Aug 2, 2012
    Posts:
    87
    I couldn't agree more. I think both the unity ads and the UNITY IAP are a goddamned mess. Instead of just one way to do things there are multiple ways. None of which seem to work.
    Why for gods sake would you make and release a codeless IAP system if it doesn't work!
    FFS.

    Heres an idea Unity Team, why not just make the codeless IAP system function properly. Just a thought.
     
  8. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    6,835
    I'm not certain that Apple requires receipt validation? I believe that is for you as the developer to guard against purchase fraud. You can certainly edit the Codeless scripts to handle this customization, but at that point, it would probably be easier to move to scripted IAP.
     
  9. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    6,835
    Yes, this would the correct approach to edit the IAPButton class, I had assumed you were adding this code on button click.
     
  10. richardzzzarnold

    richardzzzarnold

    Joined:
    Aug 2, 2012
    Posts:
    87
    Whenever i submit to the AppStore now the reviewers require receipt validation. Its possible that this is related to autorenewing subscription.

    It seems that if you are purchasing consumable/non-consumable products, then codeless IAP is sufficient, but if you are purchasing subscriptions then codelessIAP will not work. Not sure why this is not just clearly explained in Unity docs.
     
  11. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    6,835
    Yes, it will work, but you need to modify IAPButton.cs. We will look to fix this.
     
unityunity