Reputation: 153
Actually I've implmented ios in-app purchase & upon success, I receive json response. The json response contains PurchaseToken, which is basically a base64 encoded javascript string.
{
"Id": "1000000872920320",
"TransactionDateUtc": "2021-09-06T14:42:17Z",
"ProductId": "1",
"AutoRenewing": false,
"PurchaseToken": "ewoJInNpZ25hdHVyZSIgPSAic29tZSBsb25nIGJhc2U2NCBzdHJpbmciOwoJInB1cmNoYXNlLWluZm8iID0gImV3b0pJbTl5YVdkcGJtRnNMWEIxY21Ob1lYTmxMV1JoZEdVdGNITjBJaUE5SUNJeU1EQXdMVEF4TFRBeElEQXhPakF4T2pBeElFRnRaWEpwWTJFdlRHOXpYMEZ1WjJWc1pYTWlPd29KSW5WdWFYRjFaUzFwWkdWdWRHbG1hV1Z5SWlBOUlDSnpiMjFsVlVsRUlqc0tDU0p2Y21sbmFXNWhiQzEwY21GdWMyRmpkR2x2YmkxcFpDSWdQU0FpVTI5dFpWUkpSQ0k3Q2draVluWnljeUlnUFNBaU15NDJJanNLQ1NKMGNtRnVjMkZqZEdsdmJpMXBaQ0lnUFNBaVUyOXRaVlJKUkNJN0Nna2ljWFZoYm5ScGRIa2lJRDBnSWpFaU93b0pJbWx1TFdGd2NDMXZkMjVsY25Ob2FYQXRkSGx3WlNJZ1BTQWlVRlZTUTBoQlUwVkVJanNLQ1NKdmNtbG5hVzVoYkMxd2RYSmphR0Z6WlMxa1lYUmxMVzF6SWlBOUlDSnpiMjFsSUc1MWJXVnlhV01nZEdsdFpTQnpkR0Z0Y0NCcGJpQnRjeUk3Q2draWRXNXBjWFZsTFhabGJtUnZjaTFwWkdWdWRHbG1hV1Z5SWlBOUlDSnpiMjFsSUZWSlJDSTdDZ2tpY0hKdlpIVmpkQzFwWkNJZ1BTQWlNU0k3Q2draWFYUmxiUzFwWkNJZ1BTQWljMjl0WlNCVmJtbHhkV1VnU1hSbGJTQkpSQ0k3Q2draWRtVnljMmx2YmkxbGVIUmxjbTVoYkMxcFpHVnVkR2xtYVdWeUlpQTlJQ0l3SWpzS0NTSnBjeTFwYmkxcGJuUnlieTF2Wm1abGNpMXdaWEpwYjJRaUlEMGdJbVpoYkhObElqc0tDU0p3ZFhKamFHRnpaUzFrWVhSbExXMXpJaUE5SUNKemIyMWxJRzUxYldWeWFXTWdkR2x0WlNCemRHRnRjQ0JwYmlCdGN5STdDZ2tpY0hWeVkyaGhjMlV0WkdGMFpTSWdQU0FpTWpBd01DMHdNUzB3TVNBd01Ub3dNVG93TVNCRmRHTXZSMDFVSWpzS0NTSnBjeTEwY21saGJDMXdaWEpwYjJRaUlEMGdJbVpoYkhObElqc0tDU0p2Y21sbmFXNWhiQzF3ZFhKamFHRnpaUzFrWVhSbElpQTlJQ0l5TURBd0xUQXhMVEF4SURBeE9qQXhPakF4SUVWMFl5OUhUVlFpT3dvSkltSnBaQ0lnUFNBaWFYQmhJR0Z3Y0NCaWRXNWtiR1VnYm1GdFpTSTdDZ2tpY0hWeVkyaGhjMlV0WkdGMFpTMXdjM1FpSUQwZ0lqSXdNREF0TURFdE1ERWdNREU2TURFNk1ERWdRVzFsY21sallTOU1iM05mUVc1blpXeGxjeUk3Q24wPSI7CgkiZW52aXJvbm1lbnQiID0gIlNhbmRib3giOwoJInBvZCIgPSAiMTAwIjsKCSJzaWduaW5nLXN0YXR1cyIgPSAiMCI7Cn0=",
"State": 0,
"ConsumptionState": 0,
"IsAcknowledged": false,
"Payload": null
}
I deserialize the above object & decode the base64 purchaseToken, but the decoded string is javascript string rather then json (look at the =
& ;
).
{
"signature" = "some long base64 string";
"purchase-info" = "ewoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUtcHN0IiA9ICIyMDAwLTAxLTAxIDAxOjAxOjAxIEFtZXJpY2EvTG9zX0FuZ2VsZXMiOwoJInVuaXF1ZS1pZGVudGlmaWVyIiA9ICJzb21lVUlEIjsKCSJvcmlnaW5hbC10cmFuc2FjdGlvbi1pZCIgPSAiU29tZVRJRCI7CgkiYnZycyIgPSAiMy42IjsKCSJ0cmFuc2FjdGlvbi1pZCIgPSAiU29tZVRJRCI7CgkicXVhbnRpdHkiID0gIjEiOwoJImluLWFwcC1vd25lcnNoaXAtdHlwZSIgPSAiUFVSQ0hBU0VEIjsKCSJvcmlnaW5hbC1wdXJjaGFzZS1kYXRlLW1zIiA9ICJzb21lIG51bWVyaWMgdGltZSBzdGFtcCBpbiBtcyI7CgkidW5pcXVlLXZlbmRvci1pZGVudGlmaWVyIiA9ICJzb21lIFVJRCI7CgkicHJvZHVjdC1pZCIgPSAiMSI7CgkiaXRlbS1pZCIgPSAic29tZSBVbmlxdWUgSXRlbSBJRCI7CgkidmVyc2lvbi1leHRlcm5hbC1pZGVudGlmaWVyIiA9ICIwIjsKCSJpcy1pbi1pbnRyby1vZmZlci1wZXJpb2QiID0gImZhbHNlIjsKCSJwdXJjaGFzZS1kYXRlLW1zIiA9ICJzb21lIG51bWVyaWMgdGltZSBzdGFtcCBpbiBtcyI7CgkicHVyY2hhc2UtZGF0ZSIgPSAiMjAwMC0wMS0wMSAwMTowMTowMSBFdGMvR01UIjsKCSJpcy10cmlhbC1wZXJpb2QiID0gImZhbHNlIjsKCSJvcmlnaW5hbC1wdXJjaGFzZS1kYXRlIiA9ICIyMDAwLTAxLTAxIDAxOjAxOjAxIEV0Yy9HTVQiOwoJImJpZCIgPSAiaXBhIGFwcCBidW5kbGUgbmFtZSI7CgkicHVyY2hhc2UtZGF0ZS1wc3QiID0gIjIwMDAtMDEtMDEgMDE6MDE6MDEgQW1lcmljYS9Mb3NfQW5nZWxlcyI7Cn0=";
"environment" = "Sandbox";
"pod" = "100";
"signing-status" = "0";
}
And the purchase info:
{
"original-purchase-date-pst" = "2000-01-01 01:01:01 America/Los_Angeles";
"unique-identifier" = "someUID";
"original-transaction-id" = "SomeTID";
"bvrs" = "3.6";
"transaction-id" = "SomeTID";
"quantity" = "1";
"in-app-ownership-type" = "PURCHASED";
"original-purchase-date-ms" = "some numeric time stamp in ms";
"unique-vendor-identifier" = "some UID";
"product-id" = "1";
"item-id" = "some Unique Item ID";
"version-external-identifier" = "0";
"is-in-intro-offer-period" = "false";
"purchase-date-ms" = "some numeric time stamp in ms";
"purchase-date" = "2000-01-01 01:01:01 Etc/GMT";
"is-trial-period" = "false";
"original-purchase-date" = "2000-01-01 01:01:01 Etc/GMT";
"bid" = "ipa app bundle name";
"purchase-date-pst" = "2000-01-01 01:01:01 America/Los_Angeles";
}
I'm facing this issue with deserializing of the javascript string into a C# object. Any proper way to handle this deserialization? Newtonsoft.Json is not able to handle it & I have looked its docs again & again to find any suitable methods but no success.
Is there any way other then to replace characters & make its json like?
Any help would be highly appreciated.
Upvotes: 2
Views: 641
Reputation: 153
Was able to handle this by with following code using Newtonsoft.Json.JsonConverter
:
Create models for the data
public class InAppPurchaseToken
{
public string Signature { get; set; }
[JsonProperty("purchase-info")]
public string PurchaseInfo { get; set; }
public string Environment { get; set; }
public string Pod { get; set; }
public string SigningStatus { get; set; }
}
public class InAppPurchaseInfo
{
[JsonProperty("original-purchase-date-pst")]
public string originalpurchasedatepst { get; set; }
[JsonProperty("unique-identifier")]
public string uniqueidentifier { get; set; }
[JsonProperty("original-transaction-id")]
public string originaltransactionid { get; set; }
public string bvrs { get; set; }
[JsonProperty("transaction-id")]
public string transactionid { get; set; }
public string quantity { get; set; }
[JsonProperty("in-app-ownership-type")]
public string inappownershiptype { get; set; }
[JsonProperty("original-purchase-date-ms")]
public string originalpurchasedatems { get; set; }
[JsonProperty("unique-vendor-identifier")]
public string uniquevendoridentifier { get; set; }
[JsonProperty("product-id")]
public string productid { get; set; }
[JsonProperty("item-id")]
public string itemid { get; set; }
[JsonProperty("version-external-identifier")]
public string versionexternalidentifier { get; set; }
[JsonProperty("is-in-intro-offer-period")]
public string isinintroofferperiod { get; set; }
[JsonProperty("purchase-date-ms")]
public string purchasedatems { get; set; }
[JsonProperty("purchase-date")]
public string purchasedate { get; set; }
[JsonProperty("is-trial-period")]
public string istrialperiod { get; set; }
[JsonProperty("original-purchase-date")]
public string originalpurchasedate { get; set; }
public string bid { get; set; }
[JsonProperty("purchase-date-pst")]
public string purchasedatepst { get; set; }
}
And the handle the parsing using following code (assuming you have deserialized the initial json as inAppPayData
C# object):
public string GetJsonFromBase64JsStr(string base64JsStr)
{
if (string.IsNullOrWhiteSpace(base64JsStr))
return "";
var decodedStr2Parse = Encoding.UTF8.GetString(Convert.FromBase64String(base64JsStr));
//var ConvertedJsonStrComplete = decodedStr2Parse.Replace("\" = \"", "\":\"").Replace(";\n", ",").Replace(",}", "}"); //more error prone
var ConvertedJsonStrPartial = decodedStr2Parse.Replace("\" = \"", "\":\"").Replace(";\n", ",");
var ConvertedJsonStrComplete = ConvertedJsonStrPartial.Remove(ConvertedJsonStrPartial.LastIndexOf(",}"), 1);
return ConvertedJsonStrComplete;
}
//call the above function with base64JsString
var purchaseJsonStr = GetJsonFromBase64JsStr(inAppPayData.PurchaseToken);
var purchaseTokenObj = JsonConvert.DeserializeObject<InAppPurchaseToken>(purchaseJsonStr);
var purchaseInfoJsonStr = GetJsonFromBase64JsStr(purchaseTokenObj.PurchaseInfo);
var purchaseInfoObj = JsonConvert.DeserializeObject<InAppPurchaseInfo>(purchaseInfoJsonStr);
Upvotes: 0
Reputation: 717
It's a bit embarrassing. I saw the problem as the opposite before. Try this, I think this can meet the needs:
static void Main(string[] args)
{
var jsStr = @"{
""signature"" = ""some long base64 string"";
""purchase-info"" = ""ewoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUtcHN0IiA9ICIyMDAwLTAxLTAxIDAxOjAxOjAxIEFtZXJpY2EvTG9zX0FuZ2VsZXMiOwoJInVuaXF1ZS1pZGVudGlmaWVyIiA9ICJzb21lVUlEIjsKCSJvcmlnaW5hbC10cmFuc2FjdGlvbi1pZCIgPSAiU29tZVRJRCI7CgkiYnZycyIgPSAiMy42IjsKCSJ0cmFuc2FjdGlvbi1pZCIgPSAiU29tZVRJRCI7CgkicXVhbnRpdHkiID0gIjEiOwoJImluLWFwcC1vd25lcnNoaXAtdHlwZSIgPSAiUFVSQ0hBU0VEIjsKCSJvcmlnaW5hbC1wdXJjaGFzZS1kYXRlLW1zIiA9ICJzb21lIG51bWVyaWMgdGltZSBzdGFtcCBpbiBtcyI7CgkidW5pcXVlLXZlbmRvci1pZGVudGlmaWVyIiA9ICJzb21lIFVJRCI7CgkicHJvZHVjdC1pZCIgPSAiMSI7CgkiaXRlbS1pZCIgPSAic29tZSBVbmlxdWUgSXRlbSBJRCI7CgkidmVyc2lvbi1leHRlcm5hbC1pZGVudGlmaWVyIiA9ICIwIjsKCSJpcy1pbi1pbnRyby1vZmZlci1wZXJpb2QiID0gImZhbHNlIjsKCSJwdXJjaGFzZS1kYXRlLW1zIiA9ICJzb21lIG51bWVyaWMgdGltZSBzdGFtcCBpbiBtcyI7CgkicHVyY2hhc2UtZGF0ZSIgPSAiMjAwMC0wMS0wMSAwMTowMTowMSBFdGMvR01UIjsKCSJpcy10cmlhbC1wZXJpb2QiID0gImZhbHNlIjsKCSJvcmlnaW5hbC1wdXJjaGFzZS1kYXRlIiA9ICIyMDAwLTAxLTAxIDAxOjAxOjAxIEV0Yy9HTVQiOwoJImJpZCIgPSAiaXBhIGFwcCBidW5kbGUgbmFtZSI7CgkicHVyY2hhc2UtZGF0ZS1wc3QiID0gIjIwMDAtMDEtMDEgMDE6MDE6MDEgQW1lcmljYS9Mb3NfQW5nZWxlcyI7Cn0="";
""environment"" = ""Sandbox"";
""pod"" = ""100"";
""signing-status"" = ""0"";
}";
var model = GetModelFromJsStr<PurchaseToken>(jsStr);
}
public class PurchaseToken
{
[JsonPropertyName("signature")]
public string Signature { get; set; }
[JsonPropertyName("purchase-info")]
public string PurchaseInfo { get; set; }
[JsonPropertyName("environment")]
public string Environment { get; set; }
[JsonPropertyName("pod")]
public int Pod { get; set; }
[JsonPropertyName("signing-status")]
public int SigningStatus { get; set; }
}
public static T GetModelFromJsStr<T>(string jsStr) where T : new()
{
var model = new T();
var rows = jsStr.Split("\n").Select(item => item.Trim()).ToList();
foreach (var prop in typeof(T).GetProperties())
{
var jsonProperty = prop.GetCustomAttribute<JsonPropertyNameAttribute>();
if (jsonProperty != null)
{
var matchRow = rows.FirstOrDefault(row => row.StartsWith(@$"""{jsonProperty.Name}"" ="));
if (matchRow != null)
{
var pattern = @$"^""{jsonProperty.Name}"" = ""(.*)"";$";
var match = Regex.Match(matchRow, pattern);
if (match?.Groups?.Count > 1)
{
// convertible types can be extended
if (prop.PropertyType == typeof(int))
{
int.TryParse(match.Groups[1].Value, out var propValue);
prop.SetValue(model, propValue);
}
else
{
prop.SetValue(model, match.Groups[1].Value);
}
}
}
}
};
return model;
}
Upvotes: 1
Reputation: 65054
I would use Pidgin to parse something like this.
Here's some code that parses your data into a Dictionary<string, string>
and prints out the results:
using System;
using System.Collections.Generic;
using System.Linq;
using Pidgin;
using static Pidgin.Parser;
namespace NotQuiteJsonParsing
{
class Program
{
const string PurchaseInfo = @"{
""original-purchase-date-pst"" = ""2000-01-01 01:01:01 America/Los_Angeles"";
""unique-identifier"" = ""someUID"";
""original-transaction-id"" = ""SomeTID"";
""bvrs"" = ""3.6"";
""transaction-id"" = ""SomeTID"";
""quantity"" = ""1"";
""in-app-ownership-type"" = ""PURCHASED"";
""original-purchase-date-ms"" = ""some numeric time stamp in ms"";
""unique-vendor-identifier"" = ""some UID"";
""product-id"" = ""1"";
""item-id"" = ""some Unique Item ID"";
""version-external-identifier"" = ""0"";
""is-in-intro-offer-period"" = ""false"";
""purchase-date-ms"" = ""some numeric time stamp in ms"";
""purchase-date"" = ""2000-01-01 01:01:01 Etc/GMT"";
""is-trial-period"" = ""false"";
""original-purchase-date"" = ""2000-01-01 01:01:01 Etc/GMT"";
""bid"" = ""ipa app bundle name"";
""purchase-date-pst"" = ""2000-01-01 01:01:01 America/Los_Angeles"";
}";
private static string IEnumerableCharToString(IEnumerable<char> value)
{
// Feel free to swap this with an alternative answer to https://stackoverflow.com/questions/8108313
return new string(value.ToArray());
}
// Parsers can be slow to build, so minimise the
// number of times this setup method is called. See
// https://github.com/benjamin-hodgson/Pidgin#speed-tips
private static Parser<char, Dictionary<string, string>> BuildBlockParser()
{
var stringParser = Char('"').Then(AnyCharExcept('"').AtLeastOnceUntil(Char('"')));
var lineParser = Map(
(key, value) => new KeyValuePair<string, string>(IEnumerableCharToString(key), IEnumerableCharToString(value)),
stringParser.Before(SkipWhitespaces).Before(Char('=')).Before(SkipWhitespaces),
stringParser.Before(SkipWhitespaces).Before(Char(';')).Before(SkipWhitespaces));
var linesParser = lineParser.AtLeastOnce().Map(pairs => new Dictionary<string, string>(pairs));
var blockParser = SkipWhitespaces.Then(Char('{')).Then(SkipWhitespaces).Then(linesParser).Before(Char('}'));
return blockParser;
}
static void Main()
{
var blockParser = BuildBlockParser();
var result = blockParser.ParseOrThrow(PurchaseInfo);
foreach (var (key, value) in result)
{
Console.WriteLine($"{key} = {value}");
}
}
}
}
This parser has the following limitations:
"
characters in keys or values. I don't know how they would be escaped.\r
, \n
, \t
or \u0123
.Upvotes: 1
Reputation: 10871
As others already mentioned, it's not JSON. But based on the example you provided you should be able to transform it to a readable JSON. It's not the most pretty solution, but it seems to work. It have some draw-backs:
It will only work when " = "
is the separator.
It will only work when each field is separated by ;
followed by line-break (windows-style).
private string MakeReadableJson(string base64Input)
{
var data = System.Convert.FromBase64String(base64Input);
string readableString = Encoding.UTF8.GetString(data);
var json = readableString.Replace("\" = \"", "\":\"").Replace(";\n", ",");
return json;
}
I use System.Text.JsonSerializer. Use it like so:
string json = @"{
""Id"": ""1000000872920320"",
""TransactionDateUtc"": ""2021-09-06T14:42:17Z"",
""ProductId"": ""1"",
""AutoRenewing"": false,
""PurchaseToken"": ""ewoJInNpZ25hdHVyZSIgPSAic29tZSBsb25nIGJhc2U2NCBzdHJpbmciOwoJInB1cmNoYXNlLWluZm8iID0gImV3b0pJbTl5YVdkcGJtRnNMWEIxY21Ob1lYTmxMV1JoZEdVdGNITjBJaUE5SUNJeU1EQXdMVEF4TFRBeElEQXhPakF4T2pBeElFRnRaWEpwWTJFdlRHOXpYMEZ1WjJWc1pYTWlPd29KSW5WdWFYRjFaUzFwWkdWdWRHbG1hV1Z5SWlBOUlDSnpiMjFsVlVsRUlqc0tDU0p2Y21sbmFXNWhiQzEwY21GdWMyRmpkR2x2YmkxcFpDSWdQU0FpVTI5dFpWUkpSQ0k3Q2draVluWnljeUlnUFNBaU15NDJJanNLQ1NKMGNtRnVjMkZqZEdsdmJpMXBaQ0lnUFNBaVUyOXRaVlJKUkNJN0Nna2ljWFZoYm5ScGRIa2lJRDBnSWpFaU93b0pJbWx1TFdGd2NDMXZkMjVsY25Ob2FYQXRkSGx3WlNJZ1BTQWlVRlZTUTBoQlUwVkVJanNLQ1NKdmNtbG5hVzVoYkMxd2RYSmphR0Z6WlMxa1lYUmxMVzF6SWlBOUlDSnpiMjFsSUc1MWJXVnlhV01nZEdsdFpTQnpkR0Z0Y0NCcGJpQnRjeUk3Q2draWRXNXBjWFZsTFhabGJtUnZjaTFwWkdWdWRHbG1hV1Z5SWlBOUlDSnpiMjFsSUZWSlJDSTdDZ2tpY0hKdlpIVmpkQzFwWkNJZ1BTQWlNU0k3Q2draWFYUmxiUzFwWkNJZ1BTQWljMjl0WlNCVmJtbHhkV1VnU1hSbGJTQkpSQ0k3Q2draWRtVnljMmx2YmkxbGVIUmxjbTVoYkMxcFpHVnVkR2xtYVdWeUlpQTlJQ0l3SWpzS0NTSnBjeTFwYmkxcGJuUnlieTF2Wm1abGNpMXdaWEpwYjJRaUlEMGdJbVpoYkhObElqc0tDU0p3ZFhKamFHRnpaUzFrWVhSbExXMXpJaUE5SUNKemIyMWxJRzUxYldWeWFXTWdkR2x0WlNCemRHRnRjQ0JwYmlCdGN5STdDZ2tpY0hWeVkyaGhjMlV0WkdGMFpTSWdQU0FpTWpBd01DMHdNUzB3TVNBd01Ub3dNVG93TVNCRmRHTXZSMDFVSWpzS0NTSnBjeTEwY21saGJDMXdaWEpwYjJRaUlEMGdJbVpoYkhObElqc0tDU0p2Y21sbmFXNWhiQzF3ZFhKamFHRnpaUzFrWVhSbElpQTlJQ0l5TURBd0xUQXhMVEF4SURBeE9qQXhPakF4SUVWMFl5OUhUVlFpT3dvSkltSnBaQ0lnUFNBaWFYQmhJR0Z3Y0NCaWRXNWtiR1VnYm1GdFpTSTdDZ2tpY0hWeVkyaGhjMlV0WkdGMFpTMXdjM1FpSUQwZ0lqSXdNREF0TURFdE1ERWdNREU2TURFNk1ERWdRVzFsY21sallTOU1iM05mUVc1blpXeGxjeUk3Q24wPSI7CgkiZW52aXJvbm1lbnQiID0gIlNhbmRib3giOwoJInBvZCIgPSAiMTAwIjsKCSJzaWduaW5nLXN0YXR1cyIgPSAiMCI7Cn0="",
""State"": 0,
""ConsumptionState"": 0,
""IsAcknowledged"": false,
""Payload"": null
}";
var info = JsonSerializer.Deserialize<ResultFromApple>(json);
var purchaseTokenJson = MakeReadableJson(info.PurchaseToken);
PurchaseToken purchaseToken = JsonSerializer.Deserialize<PurchaseToken>(purchaseTokenJson, new JsonSerializerOptions()
{
AllowTrailingCommas = true
});
var purchaseInfoJson = MakeReadableJson(purchaseToken.PurchaseInfo);
PurchaseInfo purchaseInfo = JsonSerializer.Deserialize<PurchaseInfo>(purchaseInfoJson, new JsonSerializerOptions()
{
AllowTrailingCommas = true
});
And your models:
public class PurchaseToken
{
public string signature { get; set; }
[JsonPropertyName("purchase-info")]
public string PurchaseInfo { get; set; }
public string environment { get; set; }
public string pod { get; set; }
[JsonPropertyName("signing-status")]
public string SigningStatus { get; set; }
}
public class ResultFromApple
{
public string Id { get; set; }
public DateTime TransactionDateUtc { get; set; }
public string ProductId { get; set; }
public bool AutoRenewing { get; set; }
public string PurchaseToken { get; set; }
public int State { get; set; }
public int ConsumptionState { get; set; }
public bool IsAcknowledged { get; set; }
public object Payload { get; set; }
}
public class PurchaseInfo
{
[JsonPropertyName("original-purchase-date-pst")]
public string OriginalPurchaseDatePst { get; set; }
[JsonPropertyName("unique-identifier")]
public string UniqueIdentifier { get; set; }
[JsonPropertyName("original-transaction-id")]
public string OriginalTransactionId { get; set; }
public string bvrs { get; set; }
[JsonPropertyName("transaction-id")]
public string TransactionId { get; set; }
public string quantity { get; set; }
[JsonPropertyName("in-app-ownership-type")]
public string InAppOwnershipType { get; set; }
[JsonPropertyName("original-purchase-date-ms")]
public string OriginalPurchaseDateMs { get; set; }
[JsonPropertyName("unique-vendor-identifier")]
public string UniqueVendorIdentifier { get; set; }
[JsonPropertyName("product-id")]
public string ProductId { get; set; }
[JsonPropertyName("item-id")]
public string ItemId { get; set; }
[JsonPropertyName("version-external-identifier")]
public string VersionExternalIdentifier { get; set; }
[JsonPropertyName("is-in-intro-offer-period")]
public string IsInIntroOfferPeriod { get; set; }
[JsonPropertyName("purchase-date-ms")]
public string PurchaseDateMs { get; set; }
[JsonPropertyName("purchase-date")]
public string PurchaseDate { get; set; }
[JsonPropertyName("is-trial-period")]
public string IsTrialPeriod { get; set; }
[JsonPropertyName("original-purchase-date")]
public string OriginalPurchaseDate { get; set; }
public string bid { get; set; }
[JsonPropertyName("purchase-date-pst")]
public string PurchaseDatePst { get; set; }
}
Upvotes: 3