2

Here is the code I am using, simplified for this example:

Public PackageName As String = "com.yourdomain.app"
Public InAppPurchaseProductID As String = "com.yourdomain.app.subscriptionXYZ"
Public PublicLicenseKey As String = "YOUR_LONG_ENCODED_LICENSE_KEY"
Public ConsolePrivateKeyCertificateData As Byte() 'method for reading p12 file contents left out for simplicity
Public ConsoleCertificate As X509Certificate2 = New X509Certificate2(ConsolePrivateKeyCertificateData, "notasecret", X509KeyStorageFlags.MachineKeySet Or X509KeyStorageFlags.Exportable)
Public ConsoleServiceAccountEmail As String = "[email protected]"
Public ConsoleApplicationName As String = "I just realized this may be my issue. Not sure what this value should be!"

Public Function VerifySubscriptionReceipt(ByRef expirationDate As DateTime,
                                          ByRef purchaseDate As DateTime,
                                          token As String,
                                          purchasedProductID As String,
                                          signature As String,
                                          rawJSONData As String) As Boolean?

    'First: basic check to verify the receipt matches the product you will deliver
    If Not purchasedProductID = InAppPurchaseProductID Then
        Return Nothing
    End If

    'Second: verify receipt signature
    Dim rkp As RsaKeyParameters = CType(PublicKeyFactory.CreateKey(Convert.FromBase64String(PublicLicenseKey)), RsaKeyParameters)
    Dim rsaParams As New RSAParameters
    rsaParams.Modulus = rkp.Modulus.ToByteArrayUnsigned
    rsaParams.Exponent = rkp.Exponent.ToByteArrayUnsigned
    Dim rsaProvider As New RSACryptoServiceProvider
    rsaProvider.ImportParameters(rsaParams)

    If Not rsaProvider.VerifyData(Encoding.UTF8.GetBytes(rawJSONData), CryptoConfig.MapNameToOID("SHA1"), Convert.FromBase64String(signature)) Then
        Return Nothing
    End If

    'Third: check subscription status
    Dim result As SubscriptionPurchase
    Dim serviceInitializer As New AndroidPublisherService.Initializer With
        {
            .HttpClientInitializer = New ServiceAccountCredential(New ServiceAccountCredential.Initializer(ConsoleServiceAccountEmail) With
                                                                  {
                                                                      .Scopes = New String() {"https://www.googleapis.com/auth/androidpublisher"}
                                                                  }.FromCertificate(ConsoleCertificate)),
            .ApplicationName = ConsoleApplicationName
        }

    Using service As New AndroidPublisherService(serviceInitializer)

        '*** Error thrown on this line. Google returning 403 Forbidden ***
        result = service.Purchases.Get(PackageName, InAppPurchaseProductID, token).Execute

    End Using

    If Not result.AutoRenewing.HasValue And
            result.ValidUntilTimestampMsec.HasValue And
            result.InitiationTimestampMsec.HasValue Then
        Throw New KeyNotFoundException("The subscription does not appear to be valid. (token: " & token & ")")
    End If

    expirationDate = New DateTime(result.ValidUntilTimestampMsec.Value)
    purchaseDate = New DateTime(result.InitiationTimestampMsec.Value)

    Return result.AutoRenewing.Value

End Function

So in reviewing the code for the example, I realized the likely problem is that I don't really know what the ConsoleApplicationName should be. I have tried using the "Project Name" and the "Project ID" from the Google Developer Console, but neither made a difference.

The code runs until Google returns the 403 exception.

16
  • I agree that although there is a lot of documentation on the various Google apis it does feel a bit piecemeal and can be hard to determine all of the appropriate steps to make an oauth2 authentication and a request end to end. Let me know if you have any further luck. Commented Mar 29, 2014 at 19:40
  • Hi, I'm not an expert of this API (I actually never used it), but I'm definitely your man for the .NET client library for Google APIs :) Regrading the OAuth 2.0 protocol - I recommend you reading the following page - developers.google.com/api-client-library/dotnet/guide/aaa_oauth. Can you elaborate more on the signature process? Meantime I'll contact the owners of the Google Play Android Developer API. You should also take a look in this page: developers.google.com/android-publisher/getting_started Commented Mar 29, 2014 at 20:13
  • Thanks for getting back to me. I think I've made a little progress with the client library. One suggestion for the install process - Google.Apis.Auth should be set as a nuget dependency I think. Mostly I think the documentation for the android-publisher library needs a lot of help. Its seems to bounce around between Service Account and Web Account when it would be much more useful to have it follow each option from start to finish and give use cases for both. In my case, I think I figured out that I need to use Service Account. Commented Mar 30, 2014 at 17:37
  • I haven't tested much yet, but assuming I can now call Get / Cancel to the Purchase Status API with the client library, that takes care of #3 above. I can't find any documentation on the format of the verification signature returned by the Android store though. Is it simply the public license key and I can do basic string comparison? Or is it some type of hash of the order data using the public key? My plan is to somehow verify the signature first, then check the status with the client library and if both are good, deliver the product. Sound about right? Commented Mar 30, 2014 at 17:45
  • @peleyal The code I am using from developers.google.com/api-client-library/dotnet/guide/… seems to be working on my dev machine locally, but not on Windows Azure because the line var certificate = new X509Certificate2(@"key.p12", "notasecret", X509KeyStorageFlags.Exportable); throws an error. After some research, I found on Azure, I have to use X509KeyStorageFlags.MachineKeySet. But then I get the following error: Commented Apr 1, 2014 at 19:25

1 Answer 1

1

This related post solved my 403 error issue: https://stackoverflow.com/a/23029201/2092250

It is working now. As far as the ConsoleApplicationName - I'm not even sure it is a required field. Might be completely ignored. That value doesn't seem to matter.

I must say, Google could not have possibly made this more confusing or more poorly documented. But it is functional finally!

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.