Merge pull request #1 from hictooth/hictooth/child-login

child registration and login
This commit is contained in:
Spirtix 2023-06-18 11:07:18 +02:00 committed by GitHub
commit da7c7e526f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 821 additions and 14 deletions

View File

@ -3,7 +3,7 @@ import mitmproxy.http
def routable(path):
methods = ['GetRules', 'LoginParent', 'RegisterParent', 'GetSubscriptionInfo', 'GetUserInfoByApiToken', 'IsValidApiToken_V2']
methods = ['GetRules', 'LoginParent', 'RegisterParent', 'GetSubscriptionInfo', 'GetUserInfoByApiToken', 'IsValidApiToken_V2', 'ValidateName', 'GetDefaultNameSuggestion', 'RegisterChild', 'GetProfileByUserId', 'LoginChild', 'GetUserProfileByUserID']
for method in methods:
if method in path:
return True
@ -15,7 +15,7 @@ class LocalRedirect:
print('Loaded redirect addon')
def request(self, flow: mitmproxy.http.HTTPFlow):
if 'common.api.jumpstart.com' in flow.request.pretty_host and routable(flow.request.path):
if 'api.jumpstart.com' in flow.request.pretty_host and routable(flow.request.path):
flow.request.host = "localhost"
flow.request.scheme = 'http'
flow.request.port = 5000

View File

@ -42,7 +42,7 @@ public class AuthenticationController : Controller {
return Ok(new ParentLoginInfo { Status = MembershipUserStatus.InvalidPassword });
}
// Create seession
// Create session
Session session = new Session {
User = user,
ApiToken = Guid.NewGuid().ToString()
@ -68,8 +68,9 @@ public class AuthenticationController : Controller {
[Produces("application/xml")]
[Route("AuthenticationWebService.asmx/GetUserInfoByApiToken")]
public IActionResult GetUserInfoByApiToken([FromForm] string apiToken) {
// First check if this is a user session
User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.User;
if (user is not null) {
return Ok(new UserInfo {
UserID = user.Id,
Username = user.Username,
@ -79,13 +80,62 @@ public class AuthenticationController : Controller {
});
}
// Then check if this is a viking session
Viking? viking = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.Viking;
if (viking is not null)
{
return Ok(new UserInfo {
UserID = viking.Id,
Username = viking.Name,
MultiplayerEnabled = true,
Age = 24,
OpenChatEnabled = true
});
}
// Otherwise, this is a bad session, return empty UserInfo
return Ok(new UserInfo {});
}
[HttpPost]
[Produces("application/xml")]
[Route("AuthenticationWebService.asmx/IsValidApiToken_V2")]
public IActionResult IsValidApiToken([FromForm] string apiToken) {
User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.User;
if (user is null)
Viking? viking = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.Viking;
if (user is null && viking is null)
return Ok(ApiTokenStatus.TokenNotFound);
return Ok(ApiTokenStatus.TokenValid);
}
// This is more of a "create session for viking", rather than "login child"
[HttpPost]
[Produces("application/xml")]
[Route("AuthenticationWebService.asmx/LoginChild")]
[DecryptRequest("childUserID")]
[EncryptResponse]
public IActionResult LoginChild([FromForm] string parentApiToken) {
User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == parentApiToken)?.User;
if (user is null) {
return Ok();
}
// Find the viking
string? childUserID = Request.Form["childUserID"];
Viking? viking = ctx.Vikings.FirstOrDefault(e => e.Id == childUserID);
if (viking is null) {
return Ok();
}
// Create session
Session session = new Session {
Viking = viking,
ApiToken = Guid.NewGuid().ToString()
};
ctx.Sessions.Add(session);
ctx.SaveChanges();
// Return back the api token
return Ok(session.ApiToken);
}
}

View File

@ -0,0 +1,53 @@
using Microsoft.AspNetCore.Mvc;
using sodoff.Attributes;
using sodoff.Model;
using sodoff.Schema;
using sodoff.Util;
namespace sodoff.Controllers.Common;
public class ContentController : Controller {
private readonly DBContext ctx;
public ContentController(DBContext ctx) {
this.ctx = ctx;
}
[HttpPost]
[Produces("application/xml")]
[Route("ContentWebService.asmx/GetDefaultNameSuggestion")]
public IActionResult GetDefaultNameSuggestion() {
// TODO: generate random names, and ensure they aren't already taken
string[] suggestions = new string[] { "dragon1", "dragon2", "dragon3" };
return Ok(new DisplayNameUniqueResponse {
Suggestions = new SuggestionResult {
Suggestion = suggestions
}
});
}
[HttpPost]
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/ValidateName")]
public IActionResult ValidateName([FromForm] string apiToken,[FromForm] string nameValidationRequest) {
User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.User;
if (user is null) {
// TODO: better error handling than just replying not unique
return Ok(new NameValidationResponse { Result = NameValidationResult.NotUnique });
}
// Check if name populated
NameValidationRequest request = XmlUtil.DeserializeXml<NameValidationRequest>(nameValidationRequest);
if (request.Category == NameCategory.Default) {
// This is an avatar we are checking
// Check if viking exists
bool exists = ctx.Vikings.Count(e => e.Name == request.Name) > 0;
NameValidationResult result = exists ? NameValidationResult.NotUnique : NameValidationResult.Ok;
return Ok(new NameValidationResponse { Result = result});
} else {
// TODO: pets, groups, default
return Ok();
}
}
}

View File

@ -0,0 +1,56 @@
using Microsoft.AspNetCore.Mvc;
using sodoff.Attributes;
using sodoff.Model;
using sodoff.Schema;
using sodoff.Util;
namespace sodoff.Controllers.Common;
public class ProfileController : Controller {
private readonly DBContext ctx;
public ProfileController(DBContext ctx) {
this.ctx = ctx;
}
[HttpPost]
[Produces("application/xml")]
[Route("ProfileWebService.asmx/GetUserProfileByUserID")]
public IActionResult GetUserProfileByUserID([FromForm] string apiToken, [FromForm] string userId) {
User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.User;
if (user is null) {
// TODO: what response for not logged in?
return Ok();
}
Viking? viking = ctx.Vikings.FirstOrDefault(e => e.Id == userId);
return Ok(new UserProfileData {
ID = viking.Id,
AvatarInfo = new AvatarDisplayData {
UserInfo = new UserInfo {
UserID = viking.Id,
ParentUserID = user.Id,
Username = viking.Name,
MultiplayerEnabled = true,
GenderID = Gender.Male,
OpenChatEnabled = true,
CreationDate = DateTime.Now
},
UserSubscriptionInfo = new UserSubscriptionInfo { SubscriptionTypeID = 2 }, // TODO: figure out what this is
Achievements = new UserAchievementInfo[] {
new UserAchievementInfo {
UserID = Guid.Parse(viking.Id),
AchievementPointTotal = 0,
RankID = 1,
PointTypeID = 1, // TODO: what is this?
}
}
},
AchievementCount = 0,
MythieCount = 0,
AnswerData = new UserAnswerData { UserID = viking.Id },
GameCurrency = 0,
CashCurrency = 0
});
}
}

View File

@ -54,4 +54,42 @@ public class RegistrationController : Controller {
return Ok(response);
}
[HttpPost]
[Produces("application/xml")]
[Route("V4/RegistrationWebService.asmx/RegisterChild")]
[DecryptRequest("childRegistrationData")]
[EncryptResponse]
public IActionResult RegisterChild([FromForm] string parentApiToken) {
User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == parentApiToken)?.User;
if (user is null) {
return Ok(new RegistrationResult{
Status = MembershipUserStatus.InvalidApiToken
});
}
// Check if name populated
ChildRegistrationData data = XmlUtil.DeserializeXml<ChildRegistrationData>(Request.Form["childRegistrationData"]);
if (String.IsNullOrWhiteSpace(data.ChildName)) {
return Ok(new RegistrationResult { Status = MembershipUserStatus.ValidationError });
}
// Check if viking exists
if (ctx.Vikings.Count(e => e.Name == data.ChildName) > 0) {
return Ok(new RegistrationResult { Status = MembershipUserStatus.DuplicateUserName });
}
Viking v = new Viking {
Id = Guid.NewGuid().ToString(),
Name = data.ChildName,
User = user,
};
ctx.Vikings.Add(v);
ctx.SaveChanges();
return Ok(new RegistrationResult {
UserID = v.Id,
Status = MembershipUserStatus.Success
});
}
}

View File

@ -3,6 +3,7 @@
namespace sodoff.Model;
public class DBContext : DbContext {
public DbSet<User> Users { get; set; } = null!;
public DbSet<Viking> Vikings { get; set; } = null!;
public DbSet<Session> Sessions { get; set; } = null!;
public string DbPath { get; }
@ -18,8 +19,15 @@ public class DBContext : DbContext {
.WithMany(e => e.Sessions)
.HasForeignKey(e => e.UserId);
builder.Entity<Session>().HasOne(s => s.Viking)
.WithMany(e => e.Sessions)
.HasForeignKey(e => e.VikingId);
builder.Entity<User>().HasMany(u => u.Sessions)
.WithOne(e => e.User);
builder.Entity<Viking>().HasMany(u => u.Sessions)
.WithOne(e => e.Viking);
}
}

View File

@ -5,8 +5,11 @@ public class Session {
[Key]
public string ApiToken { get; set; } = null!;
[Required]
public string UserId { get; set; } = null!;
public string? UserId { get; set; }
public virtual User User { get; set; } = null!;
public string? VikingId { get; set; }
public virtual User? User { get; set; }
public virtual Viking? Viking { get; set; }
}

17
src/Model/Viking.cs Normal file
View File

@ -0,0 +1,17 @@
using System.ComponentModel.DataAnnotations;
namespace sodoff.Model;
public class Viking {
[Key]
public string Id { get; set; } = null!;
[Required]
public string Name { get; set; } = null!;
[Required]
public string UserId { get; set; } = null!;
public virtual ICollection<Session> Sessions { get; set; } = null!;
public virtual User User { get; set; } = null!;
}

23
src/Schema/AvatarData.cs Normal file
View File

@ -0,0 +1,23 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "AvatarData", Namespace = "")]
[Serializable]
public class AvatarData
{
[XmlElement(ElementName = "IsSuggestedAvatarName", IsNullable = true)]
public int? Id;
public string DisplayName;
[XmlElement(ElementName = "Part")]
public AvatarDataPart[] Part;
[XmlElement(ElementName = "Gender")]
public Gender GenderType;
[XmlElement(ElementName = "UTD", IsNullable = true)]
public bool? SetUserNameToDisplayName;
}

View File

@ -0,0 +1,29 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "AvatarDataPart", Namespace = "")]
[Serializable]
public class AvatarDataPart
{
public string PartType;
[XmlArrayItem("Offset")]
public AvatarDataPartOffset[] Offsets;
[XmlArrayItem("Geometry")]
public string[] Geometries;
[XmlArrayItem("Texture")]
public string[] Textures;
[XmlArrayItem("Attribute")]
public AvatarPartAttribute[] Attributes;
[XmlElement(ElementName = "Uiid", IsNullable = true)]
public int? UserInventoryId;
public const string SAVED_DEFAULT_PREFIX = "DEFAULT_";
public const string PLACEHOLDER = "PLACEHOLDER";
}

View File

@ -0,0 +1,14 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "AvatarDataPartOffset", Namespace = "")]
[Serializable]
public class AvatarDataPartOffset
{
public float X;
public float Y;
public float Z;
}

View File

@ -0,0 +1,28 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
// Token: 0x02000472 RID: 1138
[XmlRoot(ElementName = "AvatarDisplayData", Namespace = "", IsNullable = false)]
[Serializable]
public class AvatarDisplayData
{
[XmlElement(ElementName = "AvatarData", IsNullable = true)]
public AvatarData AvatarData;
[XmlElement(Namespace = "http://api.jumpstart.com/")]
public UserInfo UserInfo;
[XmlElement(ElementName = "UserSubscriptionInfo")]
public UserSubscriptionInfo UserSubscriptionInfo;
public UserAchievementInfo AchievementInfo;
[XmlElement(ElementName = "Achievements")]
public UserAchievementInfo[] Achievements;
[XmlElement(ElementName = "RewardMultipliers", IsNullable = true)]
public RewardMultiplier[] RewardMultipliers;
public int RankID;
}

View File

@ -0,0 +1,14 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "Attribute", Namespace = "")]
[Serializable]
public class AvatarPartAttribute
{
[XmlElement(ElementName = "K")]
public string Key;
[XmlElement(ElementName = "V")]
public string Value;
}

View File

@ -0,0 +1,11 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "DisplayNameUniqueResponse", Namespace = "")]
[Serializable]
public class DisplayNameUniqueResponse
{
[XmlElement(ElementName = "suggestions", IsNullable = true)]
public SuggestionResult Suggestions { get; set; }
}

41
src/Schema/GameData.cs Normal file
View File

@ -0,0 +1,41 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "GameData", Namespace = "")]
[Serializable]
public class GameData
{
[XmlElement(ElementName = "RankID", IsNullable = true)]
public int? RankID;
[XmlElement(ElementName = "IsMember")]
public bool IsMember;
[XmlElement(ElementName = "UserName")]
public string UserName;
[XmlElement(ElementName = "Value")]
public int Value;
[XmlElement(ElementName = "DatePlayed", IsNullable = true)]
public DateTime? DatePlayed;
[XmlElement(ElementName = "Win")]
public int Win;
[XmlElement(ElementName = "Loss")]
public int Loss;
[XmlElement(ElementName = "UserID")]
public Guid UserID;
[XmlElement(ElementName = "ProductID", IsNullable = true)]
public int? ProductID;
[XmlElement(ElementName = "PlatformID", IsNullable = true)]
public int? PlatformID;
[XmlElement(ElementName = "FBIDS", IsNullable = true)]
public long? FacebookID;
}

View File

@ -0,0 +1,29 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "GameDataSummary", Namespace = "")]
[Serializable]
public class GameDataSummary
{
[XmlElement(ElementName = "GameDataList")]
public GameData[] GameDataList;
[XmlElement(ElementName = "UserPosition", IsNullable = true)]
public int? UserPosition;
[XmlElement(ElementName = "GameID")]
public int GameID;
[XmlElement(ElementName = "IsMultiplayer")]
public bool IsMultiplayer;
[XmlElement(ElementName = "Difficulty")]
public int Difficulty;
[XmlElement(ElementName = "GameLevel")]
public int GameLevel;
[XmlElement(ElementName = "Key")]
public string Key;
}

View File

@ -0,0 +1,18 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
public enum NameCategory
{
[XmlEnum("1")]
Avatar = 1,
[XmlEnum("2")]
Pet,
[XmlEnum("3")]
Group,
[XmlEnum("4")]
Default
}

View File

@ -0,0 +1,13 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "NameValidationRequest", Namespace = "")]
public class NameValidationRequest
{
[XmlElement(ElementName = "Name")]
public string Name;
[XmlElement(ElementName = "Category")]
public NameCategory Category;
}

View File

@ -0,0 +1,13 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "NameValidationResponse", Namespace = "")]
public class NameValidationResponse
{
[XmlElement(ElementName = "ErrorMessage")]
public string ErrorMessage;
[XmlElement(ElementName = "Category")]
public NameValidationResult Result;
}

View File

@ -0,0 +1,18 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
public enum NameValidationResult
{
[XmlEnum("1")]
Ok = 1,
[XmlEnum("2")]
Blocked,
[XmlEnum("3")]
NotUnique,
[XmlEnum("4")]
InvalidLength
}

17
src/Schema/ProfileTag.cs Normal file
View File

@ -0,0 +1,17 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "PT", Namespace = "")]
[Serializable]
public class ProfileTag
{
[XmlElement(ElementName = "ID")]
public int TagID;
[XmlElement(ElementName = "NA", IsNullable = true)]
public string TagName;
[XmlElement(ElementName = "VAL", IsNullable = true)]
public int? Value;
}

View File

@ -0,0 +1,14 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "Answers", IsNullable = true, Namespace = "")]
[Serializable]
public class ProfileUserAnswer
{
[XmlElement(ElementName = "AID")]
public int AnswerID;
[XmlElement(ElementName = "QID")]
public int QuestionID;
}

View File

@ -0,0 +1,17 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "ARM", IsNullable = true, Namespace = "")]
[Serializable]
public class RewardMultiplier
{
[XmlElement(ElementName = "PT")]
public int PointTypeID;
[XmlElement(ElementName = "MF")]
public int MultiplierFactor;
[XmlElement(ElementName = "MET")]
public DateTime MultiplierEffectTime;
}

View File

@ -0,0 +1,9 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
public class SubscriptionNotification
{
[XmlElement(ElementName = "Type")]
public SubscriptionNotificationType Type;
}

View File

@ -0,0 +1,13 @@
using System;
namespace sodoff.Schema;
public enum SubscriptionNotificationType
{
NONE,
SUBSCRIPTION_IN_GRACE_PERIOD,
SUBSCRIPTION_EXPIRED,
SUBSCRIPTION_ON_HOLD,
SUBSCRIPTION_RECOVERED,
SUBSCRIPTION_CANCELED
}

View File

@ -0,0 +1,11 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "SuggestionResult", Namespace = "", IsNullable = false)]
[Serializable]
public class SuggestionResult
{
[XmlElement(ElementName = "Suggestion", IsNullable = true)]
public string[] Suggestion;
}

View File

@ -0,0 +1,26 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "UAI", Namespace = "")]
[Serializable]
public class UserAchievementInfo
{
[XmlElement(ElementName = "u")]
public Guid? UserID;
[XmlElement(ElementName = "n")]
public string UserName;
[XmlElement(ElementName = "a")]
public int? AchievementPointTotal;
[XmlElement(ElementName = "r")]
public int RankID;
[XmlElement(ElementName = "p")]
public int? PointTypeID;
[XmlElement(ElementName = "FBUID", IsNullable = true)]
public long? FacebookUserID;
}

View File

@ -0,0 +1,14 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "Answer", IsNullable = true, Namespace = "")]
[Serializable]
public class UserAnswerData
{
[XmlElement(ElementName = "ID")]
public string UserID;
[XmlElement(ElementName = "Answers")]
public ProfileUserAnswer[] Answers;
}

20
src/Schema/UserGrade.cs Normal file
View File

@ -0,0 +1,20 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "UGrad", Namespace = "", IsNullable = true)]
[Serializable]
public class UserGrade
{
[XmlElement(ElementName = "UId", IsNullable = true)]
public Guid? UserID;
[XmlElement(ElementName = "UGID")]
public int UserGradeID;
[XmlElement(ElementName = "UGN")]
public string UserGradeName;
[XmlElement(ElementName = "UGL", IsNullable = true)]
public bool? Locked;
}

View File

@ -0,0 +1,47 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
// Token: 0x02000622 RID: 1570
[XmlRoot(ElementName = "UserProfileDisplayData", IsNullable = true, Namespace = "")]
[Serializable]
public class UserProfileData
{
public string ID;
[XmlElement(ElementName = "Avatar")]
public AvatarDisplayData AvatarInfo;
[XmlElement(ElementName = "Ach")]
public int AchievementCount;
[XmlElement(ElementName = "Mth")]
public int MythieCount;
[XmlElement(ElementName = "Answer", IsNullable = true)]
public UserAnswerData AnswerData;
[XmlElement(ElementName = "Game", IsNullable = true)]
public GameDataSummary GameInfo;
[XmlElement(ElementName = "gc", IsNullable = true)]
public int? GameCurrency;
[XmlElement(ElementName = "cc", IsNullable = true)]
public int? CashCurrency;
[XmlElement(ElementName = "BuddyCount", IsNullable = true)]
public int? BuddyCount;
[XmlElement(ElementName = "ActivityCount", IsNullable = true)]
public int? ActivityCount;
[XmlElement(ElementName = "Groups")]
public UserProfileGroupData[] Groups;
[XmlElement(ElementName = "UPT")]
public UserProfileTag UserProfileTag;
[XmlElement(ElementName = "UG", IsNullable = true)]
public UserGrade UserGradeData;
}

View File

@ -0,0 +1,25 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "UPGD", IsNullable = true, Namespace = "")]
public class UserProfileGroupData
{
[XmlElement(ElementName = "GroupID")]
public string GroupID;
[XmlElement(ElementName = "RoleID", IsNullable = true)]
public int? RoleID;
[XmlElement(ElementName = "TypeID", IsNullable = true)]
public int? TypeID;
[XmlElement(ElementName = "N")]
public string Name;
[XmlElement(ElementName = "L")]
public string Logo;
[XmlElement(ElementName = "C")]
public string Color;
}

View File

@ -0,0 +1,23 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "UPT", Namespace = "")]
[Serializable]
public class UserProfileTag
{
[XmlElement(ElementName = "Date")]
public DateTime CreateDate;
[XmlElement(ElementName = "PGID")]
public int ProductGroupID;
[XmlElement(ElementName = "User")]
public Guid UserID;
[XmlElement(ElementName = "ID")]
public int UserProfileTagID;
[XmlElement(ElementName = "ProfileTag")]
public List<ProfileTag> ProfileTags;
}

View File

@ -0,0 +1,68 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "UserSubscriptionInfo", IsNullable = true, Namespace = "")]
[Serializable]
public class UserSubscriptionInfo
{
[XmlElement(ElementName = "PID")]
public string PID;
[XmlElement(ElementName = "BillFrequency", IsNullable = true)]
public short? BillFrequency;
[XmlElement(ElementName = "CardExpirationDate", IsNullable = true)]
public DateTime? CardExpirationDate;
[XmlElement(ElementName = "CardReferenceNumber")]
public string CardReferenceNumber;
[XmlElement(ElementName = "IsActive", IsNullable = true)]
public bool? IsActive;
[XmlElement(ElementName = "LastBillDate", IsNullable = true)]
public DateTime? LastBillDate;
[XmlElement(ElementName = "MembershipID", IsNullable = true)]
public int? MembershipID;
[XmlElement(ElementName = "ProfileCurrency")]
public string ProfileCurrency;
[XmlElement(ElementName = "ProfileID")]
public string ProfileID;
[XmlElement(ElementName = "Recurring", IsNullable = true)]
public bool? Recurring;
[XmlElement(ElementName = "RecurringAmount", IsNullable = true)]
public float? RecurringAmount;
[XmlElement(ElementName = "Status")]
public string Status;
[XmlElement(ElementName = "SubscriptionDisplayName")]
public string SubscriptionDisplayName;
[XmlElement(ElementName = "SubscriptionEndDate", IsNullable = true)]
public DateTime? SubscriptionEndDate;
[XmlElement(ElementName = "SubscriptionID", IsNullable = true)]
public int? SubscriptionID;
[XmlElement(ElementName = "SubscriptionPlanID", IsNullable = true)]
public int? SubscriptionPlanID;
[XmlElement(ElementName = "SubscriptionProvider")]
public UserSubscriptionProvider SubscriptionProvider;
[XmlElement(ElementName = "SubscriptionTypeID", IsNullable = true)]
public int? SubscriptionTypeID;
[XmlElement(ElementName = "UserID", IsNullable = true)]
public string UserID;
[XmlElement(ElementName = "UserSubscriptionNotification", IsNullable = true)]
public SubscriptionNotification UserSubscriptionNotification;
}

View File

@ -0,0 +1,27 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
public class UserSubscriptionProvider
{
[XmlElement(ElementName = "ItemID", IsNullable = false)]
public string ItemID;
[XmlElement(ElementName = "MembershipPurchaseAllowed", IsNullable = false)]
public bool MembershipPurchaseAllowed;
[XmlElement(ElementName = "ProductID", IsNullable = false)]
public int ProductID;
[XmlElement(ElementName = "Provider", IsNullable = false)]
public int Provider;
[XmlElement(ElementName = "ReceiptData", IsNullable = false)]
public string ReceiptData;
[XmlElement(ElementName = "Recurring", IsNullable = false)]
public bool Recurring;
[XmlElement(ElementName = "UserID", IsNullable = false)]
public Guid UserID;
}