3

In our app, we have an in app purchase. The client request a purchase from google play, and send all the information received from google play (the receipt, with nonce, orders, etc. and the signature) to the server, written in c#, for verification. I'm working with the code example from this post

The problem is that the verification fails.

note: The client sends all the data in JSon format and for that to work we manipulate the string returned from google play like so:

in client side

data = data.replace("\"", "\\\"");

in server side

data = data.Replace("\\", "");

Edited: JSon code example, from App to server

{
    "data": "{\\\"nonce\\\":3768004882572571381,\\\"orders\\\":[{\\\"notificationId\\\":\\\"android.test.purchased\\\",\\\"packageName\\\":\\\"com.company.appname\\\",\\\"orderId\\\":\\\"transactionId.android.test.purchased\\\",\\\"purchaseState\\\":0,\\\"productId\\\":\\\"android.test.purchased\\\",\\\"purchaseTime\\\":1335790350398}]}",
    "signature": "ML6ocr89x3+oT3ZKnQBEE2mNEVj6LHwt+L4I/bnhl+xCpJcjhsAIhfAumeCKwXonJV4Oh9n3Sa7SVT0F7S9XcgE2xGcf2zOZmxHB1wQcyM7fQiGj39Cyb2zuYf3T6Cs1eerDzHaO1teVQZyIhBPJf4cszD/WikSpHcF8zBTvV58FkRVwl2NR4CEvI2FrKFek8Xq2O4CsclCpS5UJorMKRAer9pcSD1BkFzynQJffbaDcRLFZ7i9vABV+GZ/xWxMGPuYYE77GYk8Q2fejgmwiZ3ysY0VjEfGRCpSA==",
    "userId": 1
}

Edited: that test fails. verified is a boolean variable, supposed to be true

using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                RSAParameters rsaKeyInfo = new RSAParameters()
                {
                    Exponent = Convert.FromBase64String(ConfigurationManager.AppSettings["RsaKeyInfo.Exponent"]),
                    Modulus = Convert.FromBase64String(ConfigurationManager.AppSettings["RsaKeyInfo.Modulus"])
                };
                rsa.ImportParameters(rsaKeyInfo);
                verified = rsa.VerifyData(Encoding.ASCII.GetBytes(data), "SHA1", Convert.FromBase64String(signature));
            }
Community
  • 1
  • 1
Itai Navot
  • 33
  • 1
  • 5
  • I had the same problem with Pusher and ActionScript 3.0. Can you provide some more code? I mean what server expect and send, and also what app expect and send. – goodm Apr 30 '12 at 13:03
  • Why would you need the triple backslashes? – Filip Haglund Apr 30 '12 at 13:07
  • The triple backslashes is because the string it self has quotation marks, and if I get it from the app without the backslashes, I get an exception that the format is wrong and i don't get all the different parameters from the json – Itai Navot Apr 30 '12 at 13:19
  • @goodm Haglund - updated answer – Itai Navot Apr 30 '12 at 13:19
  • I don't know how behave c# with JSON, but when I had a problem with as 3, it was because flash expect String, that why I had to add triple \ and also double " at the beginning. Like: "data" : "\"{\\\"noce\\\": 376 .... – goodm Apr 30 '12 at 13:25
  • @goodm Thanx I traid it but with no success – Itai Navot Apr 30 '12 at 13:46
  • try first maybe play a little bit with some easier JSONs until you find the working one. – goodm Apr 30 '12 at 13:48
  • Can you please provide full code. How to get public key and is signature is necessary or not can we verify our payment by using purchaseToken? – Kamran Khan Aug 13 '18 at 08:17
  • Is signature is different for every payment? – Kamran Khan Aug 13 '18 at 08:18

1 Answers1

2

The problem was the client converted the JSON string, received from google, to a JSONObject and then converted it back toString(). This caused the position of some of the json items to change inside the string, which created a different bytes object for the signature, which failed.

for example - this was the JSON received from google:

{
    "nonce": 1165723044405495300,
    "orders": [
        {
            "notificationId": "android.test.purchased",
            "orderId": "transactionId.android.test.purchased",
            "packageName": "com.company.appname",
            "productId": "android.test.purchased",
            "purchaseTime": 1335874740360,
            "purchaseState": 0
        }
    ]
}

if you manipulate it to a JSONObject (new JSONObject(json)) and then back to a string (json.toString()), it can be resulted with a position change for some of the json items, for example (notice the orderId is not second in the orders array anymore):

{
        "nonce": 1165723044405495300,
        "orders": [
            {
                "notificationId": "android.test.purchased",
                "packageName": "com.company.appname",
                "productId": "android.test.purchased",
                "purchaseTime": 1335874740360,
                "orderId": "transactionId.android.test.purchased",
                "purchaseState": 0
            }
        ]
    }

The GetBytes(data) doesn't return the same result, therefor the verification fails.

The solution is of course to avoid manipulating the json string received from google. Just put it in the JSONObject you're building. jsonObj.put("data", jsonStringFromGoogle).

Lior Iluz
  • 26,213
  • 16
  • 65
  • 114