Search Unity

[Solved] Receipt Validation

Discussion in 'Unity IAP' started by iWumbo, Jan 21, 2016.

Thread Status:
Not open for further replies.
  1. iWumbo

    iWumbo

    Joined:
    Dec 5, 2013
    Posts:
    12
    Hello,

    ive read this Thread http://forum.unity3d.com/threads/tracking-monetization-receipt-verification.314056/ about Receipt Validation in Unity Analytics, so it will show if there were any frauds in the dashboard for example.

    Is there any way to verify those frauds with the Unity iAP system ? Or is this allready handled internally and the OnPurchaseFailed will be fired ? Or do i need to setup my own server to verify those receipts ?

    Thank you :)
     
  2. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    Also interested in the question.
    I would also like to know whether it is possible to see where the logs payments with payload?
    Those. If the player is not credited to the purchase of the device, but it shows a screenshot of Google's billing \ Apple with an id of payment. Will find this id was a payload, it may be necessary if the purchase provides a different number of currencies depending on the level of the player.
     
  3. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    We don't yet handle receipt verification. We will be providing receipt verification sample code shortly.

    echeg, I am not sure what you are asking but we don't keep a log of failed payments.
     
  4. iWumbo

    iWumbo

    Joined:
    Dec 5, 2013
    Posts:
    12
    @echeg @Banderous

    if u are interested in local receipt validation for Google, do this :

    1.use this http://www.jensign.com/JavaScience/dotnet/pempublic/pempublic.cs to create a XML Object out of your Public Google ID

    Code (CSharp):
    1. //**********************************************************************
    2. //
    3. // pempublic
    4. // .NET 1.1/2.0  PEM SubjectPublicKeyInfo Public Key Reader
    5.  
    6. /*
    7. Copyright (c)  2006 - 2014   JavaScience Consulting,  Michel Gallant
    8.  
    9. Permission is hereby granted, free of charge, to any person obtaining a copy
    10. of this software and associated documentation files (the "Software"), to deal
    11. in the Software without restriction, including without limitation the rights
    12. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    13. copies of the Software, and to permit persons to whom the Software is
    14. furnished to do so, subject to the following conditions:
    15.  
    16. The above copyright notice and this permission notice shall be included in
    17. all copies or substantial portions of the Software.
    18.  
    19. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    20. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    21. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    22. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    23. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    24. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    25. THE SOFTWARE.
    26. */
    27.  
    28. //
    29. //**********************************************************************
    30. //
    31. // pempublic.c
    32. //
    33. // Reads a PEM encoded in b64 format (with or without header/footer
    34. // lines) or binary RSA public key file in SubjectPublicKeyInfo asn.1 format.
    35. // Removes header/footer lines and b64 decodes for b64 case.
    36. // Parses asn.1 encoding to extract exponent and modulus byte[].
    37. // Creates byte[] modulus and byte[] exponent
    38. // Instantiates RSACryptoServiceProvider
    39. //*************************************************************************
    40.  
    41. using System;
    42. using System.IO;
    43. using System.Text;
    44. using System.Security.Cryptography;
    45. using UnityEngine;
    46.  
    47. public class asdasd : MonoBehaviour
    48. {
    49.  
    50.     // encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
    51.     static byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
    52.  
    53.     public void Start()
    54.     {
    55.      
    56.         byte[] x509key;
    57.         byte[] seq = new byte[15];
    58.  
    59.  
    60.         Debug.Log("\nEnter PEM-encoded public key file Name: ");
    61.         String filename = "Assets/Resources/test.pem";
    62.         if (filename == "")  //exit while(true) loop
    63.             return;
    64.         if (!File.Exists(filename))
    65.         {
    66.             Debug.Log("File \"{0}\" does not exist!\n"+  filename);
    67.             return;
    68.         }
    69.         StreamReader sr = File.OpenText(filename);
    70.         String filestr = sr.ReadToEnd();
    71.         sr.Close();
    72.         StringBuilder sb = new StringBuilder(filestr);
    73.         sb.Replace("-----BEGIN PUBLIC KEY-----", "");  //remove headers/footers, if present
    74.         sb.Replace("-----END PUBLIC KEY-----", "");
    75.  
    76.         try
    77.         {        //see if the file is a valid Base64 encoded cert
    78.             x509key = Convert.FromBase64String(sb.ToString());
    79.         }
    80.         catch (System.FormatException)
    81.         {       //if not a b64-encoded publiccert, assume it's binary
    82.             Debug.Log("Not a valid  b64 blob; assume binary");
    83.             Stream stream = new FileStream(filename, FileMode.Open);
    84.             int datalen = (int)stream.Length;
    85.             x509key = new byte[datalen];
    86.             stream.Read(x509key, 0, datalen);
    87.             stream.Close();
    88.  
    89.         }
    90.  
    91.         //Debug.Log(sb.ToString()) ;
    92.         //PutFileBytes("x509key", x509key, x509key.Length) ;
    93.  
    94.         // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------
    95.         MemoryStream mem = new MemoryStream(x509key);
    96.         BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading
    97.         byte bt = 0;
    98.         ushort twobytes = 0;
    99.  
    100.         try
    101.         {
    102.  
    103.             twobytes = binr.ReadUInt16();
    104.             if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
    105.                 binr.ReadByte();    //advance 1 byte
    106.             else if (twobytes == 0x8230)
    107.                 binr.ReadInt16();   //advance 2 bytes
    108.             else
    109.                 return;
    110.  
    111.             seq = binr.ReadBytes(15);       //read the Sequence OID
    112.             if (!CompareBytearrays(seq, SeqOID))    //make sure Sequence for OID is correct
    113.                 return;
    114.  
    115.             twobytes = binr.ReadUInt16();
    116.             if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
    117.                 binr.ReadByte();    //advance 1 byte
    118.             else if (twobytes == 0x8203)
    119.                 binr.ReadInt16();   //advance 2 bytes
    120.             else
    121.                 return;
    122.  
    123.             bt = binr.ReadByte();
    124.             if (bt != 0x00)     //expect null byte next
    125.                 return;
    126.  
    127.             twobytes = binr.ReadUInt16();
    128.             if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
    129.                 binr.ReadByte();    //advance 1 byte
    130.             else if (twobytes == 0x8230)
    131.                 binr.ReadInt16();   //advance 2 bytes
    132.             else
    133.                 return;
    134.  
    135.             twobytes = binr.ReadUInt16();
    136.             byte lowbyte = 0x00;
    137.             byte highbyte = 0x00;
    138.  
    139.             if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
    140.                 lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus
    141.             else if (twobytes == 0x8202)
    142.             {
    143.                 highbyte = binr.ReadByte(); //advance 2 bytes
    144.                 lowbyte = binr.ReadByte();
    145.             }
    146.             else
    147.                 return;
    148.             byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian order
    149.             int modsize = BitConverter.ToInt32(modint, 0);
    150.  
    151.             int firstbyte = binr.PeekChar();
    152.             if (firstbyte == 0x00)
    153.             {   //if first byte (highest order) of modulus is zero, don't include it
    154.                 binr.ReadByte();    //skip this null byte
    155.                 modsize -= 1;   //reduce modulus buffer size by 1
    156.             }
    157.  
    158.             byte[] modulus = binr.ReadBytes(modsize);   //read the modulus bytes
    159.  
    160.             if (binr.ReadByte() != 0x02)            //expect an Integer for the exponent data
    161.                 return;
    162.             int expbytes = (int)binr.ReadByte();        // should only need one byte for actual exponent data (for all useful values)
    163.             byte[] exponent = binr.ReadBytes(expbytes);
    164.  
    165.             showBytes("\nExponent", exponent);
    166.             showBytes("\nModulus", modulus);
    167.  
    168.             // ------- create RSACryptoServiceProvider instance and initialize with public key -----
    169.             RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
    170.             RSAParameters RSAKeyInfo = new RSAParameters();
    171.             RSAKeyInfo.Modulus = modulus;
    172.             RSAKeyInfo.Exponent = exponent;
    173.             RSA.ImportParameters(RSAKeyInfo);
    174.  
    175.             String xmlpublickey = RSA.ToXmlString(false);
    176.             Debug.Log("XML encoded RSA public key: \n"+ xmlpublickey);
    177.         }
    178.  
    179.         finally
    180.         {
    181.             binr.Close();
    182.         }
    183.     }
    184.  
    185.  
    186.  
    187.  
    188.     private static bool CompareBytearrays(byte[] a, byte[] b)
    189.     {
    190.         if (a.Length != b.Length)
    191.             return false;
    192.         int i = 0;
    193.         foreach (byte c in a)
    194.         {
    195.             if (c != b[i])
    196.                 return false;
    197.             i++;
    198.         }
    199.         return true;
    200.     }
    201.  
    202.  
    203.  
    204.     private static void showBytes(String info, byte[] data)
    205.     {
    206.         string tmp = "";
    207.         tmp += info +"[ "+ data.Length + " bytes]";
    208.         for (int i = 1; i <= data.Length; i++)
    209.         {
    210.             tmp += "," + data[i - 1];
    211.             if (i % 16 == 0)
    212.                 tmp += " ";
    213.         }
    214.         Debug.Log(tmp);
    215.     }
    216.  
    217.  
    218.     private static void PutFileBytes(String outfile, byte[] data, int bytes)
    219.     {
    220.         FileStream fs = null;
    221.         if (bytes > data.Length)
    222.         {
    223.             Debug.Log("Too many bytes");
    224.             return;
    225.         }
    226.         try
    227.         {
    228.             fs = new FileStream(outfile, FileMode.Create);
    229.             fs.Write(data, 0, bytes);
    230.         }
    231.         catch (Exception e)
    232.         {
    233.             Debug.Log(e.Message);
    234.         }
    235.         finally
    236.         {
    237.             fs.Close();
    238.         }
    239.     }
    240.  
    241. }

    Just createa .txt Object in Assets/Ressources named test.pem and copy in your Google Public Key

    After running this Code look into your Debug.Log for ""XML encoded RSA public key:"

    Copy your XML encoded RSA key.

    2.

    use this code

    Code (CSharp):
    1. public static bool Verify(string message, string base64Signature, string xmlPublicKey)
    2.     {
    3.         // Create the provider and load the KEY
    4.         RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
    5.         provider.FromXmlString(xmlPublicKey);
    6.  
    7.         // The signature is supposed to be encoded in base64 and the SHA1 checksum
    8.         // of the message is computed against the UTF-8 representation of the message
    9.         byte[] signature = System.Convert.FromBase64String(base64Signature);
    10.         SHA1Managed sha = new SHA1Managed();
    11.         byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
    12.  
    13.         return provider.VerifyData(data, sha, signature);
    14.     }
    To verify your Receipt where

    message: is the json part of the receipt
    base64Signature: is the signature part of the receipt
    xmlPublicKey: is your xmlPubliy key generated by above script

    Done!


    PHP Version of Google verification

    Code (CSharp):
    1. function verify_market_in_app($signed_data, $signature, $public_key_base64)
    2. {
    3.     $key =    "-----BEGIN PUBLIC KEY-----\n".
    4.         chunk_split($public_key_base64, 64,"\n").
    5.         '-----END PUBLIC KEY-----';
    6.     //using PHP to create an RSA key
    7.     $key = openssl_get_publickey($key);
    8.     var_dump($key);
    9.     //$signature should be in binary format, but it comes as BASE64.
    10.     //So, I'll convert it.
    11.     $signature = base64_decode($signature);
    12.     //using PHP's native support to verify the signature
    13.     $result = openssl_verify(
    14.             $signed_data,
    15.             $signature,
    16.             $key,
    17.             OPENSSL_ALGO_SHA1);
    18.     if (0 === $result)
    19.     {
    20.         return 0;
    21.     }
    22.     else if (1 !== $result)
    23.     {
    24.         return 0;
    25.     }
    26.     else
    27.     {
    28.         return 1;
    29.     }
    30. }
    here u can just put in your google play key

    Hope it helps !
     
Thread Status:
Not open for further replies.