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,
- What is Google Play Developer API
- Obtain an Access Token
- 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;
}

Join the conversation! Your thoughts help the community grow.