Validate Purchase Receipt With The Play Store

Introduction

As a developer, it's essential to validate purchase receipts from the Google Play Store to ensure that your app users make genuine purchases. The Google Play Developer API is a set of RESTful web services allowing developers to communicate with the Play Store. In this article, I'll show you how to validate purchase receipts using the Google Play Developer API in C#.

Agenda for the Article,

  1. What is Google Play Developer API
  2. Obtain an Access Token
  3. How to Validate Purchase Receipts

What is Google Play Developer API

The Google Play Developer API is a set of RESTful web services allowing developers to communicate with the Play Store. With this API, you can access various features of the Google Play Store, such as managing in-app products, handling purchases, and checking the status of purchases. The API enables developers to access various features of the Google Play Store, including retrieving purchase receipts and details, verifying transactions, and checking the status of refunds. The API can programmatically validate purchases, making it easier for developers to implement in-app purchases and subscriptions in their apps. This API enables developers to manage their apps better and create a more streamlined user experience.

Obtain an Access Token

To obtain an access token, you will require two things

  • A P12 certificate file generated by the Google Play Store and
  • A Google Play API to generate a token 

Now follow the code given below,

public async Task < string > GetAccessToken() {
    string token = string.Empty;
    try {
        RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider();
        List < string > jwtSegments = new List < string > ();
        var jwtHeader = new {
            alg = "RS256",
                typ = "JWT"
        };
        int nowTimeInEpochSeconds = (int) Math.Floor((double) DateTimeOffset.UtcNow.ToUnixTimeSeconds());
        var jwtClaimSet = new {
            iss = "[email protected]",
                scope = "https://www.googleapis.com/auth/androidpublisher",
                aud = "https://oauth2.googleapis.com/token",
                exp = nowTimeInEpochSeconds + 3600,
                iat = nowTimeInEpochSeconds
        };
        string serializedHeader = JsonConvert.SerializeObject(jwtHeader, Formatting.None);
        string serializedClaimSet = JsonConvert.SerializeObject(jwtClaimSet, Formatting.None);
        byte[] jwtHeaderBytes = Encoding.UTF8.GetBytes(serializedHeader);
        byte[] jwtClaimSetBytes = Encoding.UTF8.GetBytes(serializedClaimSet);
        string base64EncodedHeader = Base64UrlEncoder.Encode(jwtHeaderBytes);
        string base64EncodedClaimSet = Base64UrlEncoder.Encode(jwtClaimSetBytes);
        jwtSegments.Add(base64EncodedHeader);
        jwtSegments.Add(base64EncodedClaimSet);
        string jwtRequestToSign = string.Join(".", jwtSegments);
        byte[] jwtRequestBytesToSign = Encoding.UTF8.GetBytes(jwtRequestToSign);
        //local folder path 
        //string filePath = System.Web.HttpContext.Current.Server.MapPath("\\cert\\P12file.p12");
        //string filePath = System.Web.Hosting.HostingEnvironment.MapPath("/cert/P12file.p12");
        //string filePath = "E:\\Projects\\HDAPI_V1_3\\HDAPI\\HoneyDew\\cert\\P12file.p12";
        //server path to get the certificate
        string rootPath = ConfigurationManager.AppSettings["UploadedFolderPath"];
        string filePath = HttpContext.Current.Server.MapPath(rootPath + "cert\\P12file.p12");
        X509Certificate2 cert = new X509Certificate2(filePath, "notasecret", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
        //X509Certificate2 cert = new X509Certificate2(filePath, "notasecret");
        RSA rsa = cert.GetRSAPrivateKey();
        byte[] signature = rsa.SignData(jwtRequestBytesToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        string jwt = jwtRequestToSign + "." + Base64UrlEncoder.Encode(signature);
        WebRequest webRequestForaccessToken = WebRequest.Create("https://oauth2.googleapis.com/token") as HttpWebRequest;
        string accessTokenRequestParameters = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + jwt;
        byte[] accessTokenRequestFromJwt = Encoding.UTF8.GetBytes(accessTokenRequestParameters);
        if (webRequestForaccessToken != null) {
            webRequestForaccessToken.Method = "POST";
            webRequestForaccessToken.ContentType = "application/x-www-form-urlencoded";
            webRequestForaccessToken.ContentLength = accessTokenRequestFromJwt.Length;
            using(Stream stream = webRequestForaccessToken.GetRequestStream()) {
                stream.Write(accessTokenRequestFromJwt, 0, accessTokenRequestFromJwt.Length);
                //stream.Position = 0;
            }
            using(HttpWebResponse httpWebResponseForaccessToken = webRequestForaccessToken.GetResponse() as HttpWebResponse) {
                Stream streamForaccessToken = httpWebResponseForaccessToken?.GetResponseStream();
                if (streamForaccessToken != null) {
                    //streamForaccessToken.Position = 0;
                    using(StreamReader streamReaderForForaccessToken = new StreamReader(streamForaccessToken)) {
                        string jsonUserResponseString = streamReaderForForaccessToken.ReadToEnd();
                        JObject groupsIoApiUserObject = JObject.Parse(jsonUserResponseString);
                        token = groupsIoApiUserObject["access_token"].ToString();
                    }
                }
            }
            return token;
        }
        return token;
    } catch (WebException ex) {
        try {
            string resp = new StreamReader(ex.Response.GetResponseStream()).ReadToEnd();
            token = resp;
        } catch (Exception parseException) {
            string m = parseException.Message;
            token = m;
        }
    }
    return token;
}

In the above code, first, you must set the jwtClaimSet and jwtHeader. Then you have to provide the path of the P12 certificate file. If you are testing locally, then provide a local folder path. If you are deploying on a server, then provide a server path. Make a post-call request to Google token API.

Validate Purchase Receipt

Google Play API to validate purchase receipt is 

https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{purchaseToken}?access_token={accessToken}

Now create another method to verify and call this GetAccessToken() inside this method as  given below

public async Task < int > VerifyAndroid(string packageName, string subscriptionId, string purchaseToken) {
    string responseStr = null;
    int proUserStatus = 1;
    string uri = String.Empty;
    //string URL = ConfigurationManager.AppSettings["verifyUrl"];
    //this is purchase receipt verification API provided by Google
    string URL = "https://androidpublisher.googleapis.com/androidpublisher/v3/applications/";
    //this method will return access token
    string accessToken = await authenticationConfig.GetAccessToken();
    try {
        if (accessToken != null) {
            //we will pass packageName, subscriptionId, purchaseToken in google API URL
            uri = URL + $ "{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{purchaseToken}?access_token={accessToken}";
        }
        using(var httpClient = new HttpClient()) {
            try {
                Task < HttpResponseMessage > getResponse = httpClient.GetAsync(uri);
                HttpResponseMessage response = await getResponse;
                if (response.IsSuccessStatusCode) {
                    responseStr = await response.Content.ReadAsStringAsync();
                    AndroidValidResponse vres = JsonConvert.DeserializeObject < AndroidValidResponse > (responseStr);
                    //if paymentState is 1 i.e. valid user
                    if (vres.paymentState == 1) {
                        proUserStatus = 0;
                    } else {
                        proUserStatus = 2;
                    }
                }
            } catch (Exception e) {
                proUserStatus = 1;
            }
        }
    } catch (Exception ex) {
        proUserStatus = 1;
    }
    return proUserStatus;
}

In the above-given code, you need to add an access token obtained from GetAccessToken() method in the verify purchase receipt API along with subscriptionId, packageName, and purchaseToken as given above.

Output

Output for verify recipt

In the above image, you can see the output in the responseStr variable.

Conclusion

If you're a developer looking to use the Google Play Developer API, you must obtain an access token to authenticate your requests. First, generate an access token to access Google API and then, with proper credentials, make a post call to Google API.

Thank you, and Stay Tuned for More

More Articles from My Account


Similar Articles