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.
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; }
}