public async Task ignore_duplicate_month_for_sub_gift() { User gifter = MockUser("gifter", monthsSubscribed: 2, SubscriptionTier.Prime, loyaltyLeague: 2); const SubscriptionTier tier = SubscriptionTier.Tier3; User recipient = MockUser("recipient", monthsSubscribed: 1, subscriptionTier: tier, loyaltyLeague: 5); Mock <IBank <User> > bankMock = new(); Mock <IUserRepo> userRepoMock = new(); ISubscriptionProcessor subscriptionProcessor = new SubscriptionProcessor( NullLogger <SubscriptionProcessor> .Instance, bankMock.Object, userRepoMock.Object, Mock.Of <ISubscriptionLogRepo>(), Mock.Of <ILinkedAccountRepo>()); SubscriptionInfo subscriptionInfo = new(recipient, NumMonths : 1, StreakMonths : 0, tier, "Sub Plan Name", Instant.MinValue, "sub message", ImmutableList <EmoteOccurrence> .Empty); (ISubscriptionProcessor.SubResult subResult, ISubscriptionProcessor.SubGiftResult subGiftResult) = await subscriptionProcessor.ProcessSubscriptionGift( new SubscriptionGiftInfo(subscriptionInfo, gifter, 1, false)); Assert.IsInstanceOf <ISubscriptionProcessor.SubGiftResult.SameMonth>(subGiftResult); var sameMonthGiftResult = (ISubscriptionProcessor.SubGiftResult.SameMonth)subGiftResult; Assert.That(sameMonthGiftResult.Month, Is.EqualTo(1)); bankMock.VerifyNoOtherCalls(); Assert.IsInstanceOf <ISubscriptionProcessor.SubResult.SameMonth>(subResult); var sameMonthSubResult = (ISubscriptionProcessor.SubResult.SameMonth)subResult; Assert.That(sameMonthSubResult.Month, Is.EqualTo(1)); bankMock.VerifyNoOtherCalls(); }
internal ConsoleProfile(string gamertag, Xuid onlineXuid, Xuid offlineXuid, SubscriptionTier tier, XboxLiveCountry country, IXboxConsole console, IProfileSupport profileSupport) : base(gamertag, onlineXuid) { this.OfflineXuid = offlineXuid; this.Tier = tier; this.Country = country; this.Console = console; this.profileSupport = profileSupport; //this.Friends = FriendsManagerFactory.CreateFriendsManager(this); }
private static async Task <bool> TrySharePaymentWithReferrer(BitcornContext dbContext, SubscriptionResponse output, SubRequest subRequest, Subscription subInfo, SubscriptionTier requestedTierInfo, User user, int subscriptionPaymentRecipientId, decimal cost, SubscriptionState previousSubState, SubTx subTx) { //if (previousSubState != SubscriptionState.None) return false; //if subscription referrar share is defined and its between 0 and 1 //get subscriber userreferral info var userReferral = await dbContext.UserReferral.FirstOrDefaultAsync(r => r.UserId == user.UserId); if (userReferral != null && userReferral.ReferralId != 0) { //get info of the person who referred the subscriber var referrer = await dbContext.Referrer.FirstOrDefaultAsync(u => u.ReferralId == userReferral.ReferralId); //check if referrer can get rewards if (ReferralUtils.IsValidReferrer(referrer)) { var referrerUser = await dbContext.JoinUserModels().FirstOrDefaultAsync(u => u.UserId == referrer.UserId); if (referrerUser != null && !referrerUser.IsBanned) { bool success = false; if (subInfo.ReferrerPercentage != null && subInfo.ReferrerPercentage > 0 && subInfo.ReferrerPercentage <= 1) { //get the user who received the subscription payment var subscriptionPaymentRecipient = await dbContext.JoinUserModels().FirstOrDefaultAsync(u => u.UserId == subscriptionPaymentRecipientId); success = await ShareSubscriptionPaymentWithReferrer(dbContext, subInfo, cost, subTx, referrer, referrerUser, subscriptionPaymentRecipient); } if (previousSubState == SubscriptionState.None && subInfo.Name == "BITCORNFarms") { await ReferralUtils.BonusPayout(dbContext, userReferral, referrer, user, referrerUser, referrerUser.UserStat); } return(success); } } } return(false); }
public async Task <SubscriptionLog> LogSubscription( string userId, Instant timestamp, int monthsStreak, int monthsNumPrev, int monthsNumNew, int monthsDifference, int loyaltyLeaguePrev, int loyaltyLeagueNew, int loyaltyCompletions, int rewardTokens, string?subMessage, SubscriptionTier subPlan, string subPlanName) { var item = new SubscriptionLog( string.Empty, userId, timestamp, monthsStreak, monthsNumPrev, monthsNumNew, monthsDifference, loyaltyLeaguePrev, loyaltyLeagueNew, loyaltyCompletions, rewardTokens, subMessage, subPlan, subPlanName); await Collection.InsertOneAsync(item); Debug.Assert(item.Id.Length > 0, "The MongoDB driver injected a generated ID"); return(item); }
public ConsoleProfile CreateConsoleProfile(bool online, XboxLiveCountry country, SubscriptionTier tier, string gamertag) { return(this.profileSupport.CreateConsoleProfile(online, country, tier, gamertag)); }
public ConsoleProfile CreateConsoleProfile(bool online, XboxLiveCountry country, SubscriptionTier tier) { return(this.CreateConsoleProfile(online, country, tier, null)); }
public static async Task <decimal> CalculateUsdtToCornCost(BitcornContext dbContext, SubscriptionTier tier) { var price = await ProbitApi.GetCornPriceAsync(dbContext); return(CalculateUsdtToCornCost(price, tier)); }
public static decimal CalculateUsdtToCornCost(decimal cornUsdt, SubscriptionTier tier) { return(Math.Ceiling(tier.CostUsdt.Value / cornUsdt)); }
private static async Task <SubscriptionResponse> ProcessSubscription(BitcornContext dbContext, SubscriptionResponse output, SubRequest subRequest, Subscription subInfo, SubscriptionTier requestedTierInfo, User user) { decimal cost = 0; //if tier usdt cost has been initialized, the corn cost has to be calculated if (requestedTierInfo.CostUsdt != null && requestedTierInfo.CostUsdt > 0) { cost = await CalculateUsdtToCornCost(dbContext, requestedTierInfo); } // check if cost is initialized properly else if (requestedTierInfo.CostCorn != null && requestedTierInfo.CostCorn > 0) { cost = requestedTierInfo.CostCorn.Value; } else { throw new ArgumentException($"Invalid cost setting on subscription tier id:{requestedTierInfo.SubscriptionId}"); } //set the amount that will be removed from subscriber to the response object output.Cost = cost; //initialize array of existing subscriptions UserSubcriptionTierInfo[] existingSubscriptions = new UserSubcriptionTierInfo[0]; if (user != null) { //set data to existing subscriptions array existingSubscriptions = await GetUserSubscriptions(dbContext, user) .Where(t => t.SubscriptionTier.SubscriptionId == subInfo.SubscriptionId).ToArrayAsync(); } //initialize reference to existing subtierinfo UserSubcriptionTierInfo existingSubscription = null; //initialize current substate var subState = SubscriptionState.None; //if any subscriptions were found if (existingSubscriptions.Any()) { //set existing subtierinfo existingSubscription = existingSubscriptions[0]; //if sub has expired, set substate to expired if (subInfo.HasExpired(existingSubscription.UserSubscription)) { subState = SubscriptionState.Expired; } //if existing sub has not expired, but the tier is below, set subState to TierDown else if (existingSubscription.SubscriptionTier.Tier < requestedTierInfo.Tier) { subState = SubscriptionState.TierDown; } //else, the user is subscribed else { subState = SubscriptionState.Subscribed; } } //initialize reference to usersubscription & tx request UserSubscription sub = null; TxRequest txRequest = null; //if current user sub state is not subscribed & the client confirmed the cost to be equal to the cost amount, attempt to subscribe var costDiff = Math.Abs(subRequest.Amount - cost); if (subState != SubscriptionState.Subscribed && costDiff <= 100000)//subRequest.Amount == cost) { //initialize recipient of the transaction string[] to = new string[1]; //default to bitcornhub if no subscription owner has been set int recipientId = TxUtils.BitcornHubPK; //if subscription owner is set, overwrite bitcornhub if (subInfo.OwnerUserId != null && subInfo.OwnerUserId > 0) { recipientId = subInfo.OwnerUserId.Value; } to[0] = $"userid|{recipientId}"; //initialize tx request txRequest = new TxRequest(user, cost, subRequest.Platform, "$sub", to); //prepare transaction for saving var processInfo = await TxUtils.ProcessRequest(txRequest, dbContext); var transactions = processInfo.Transactions; if (transactions != null && transactions.Length > 0) { StringBuilder sql = new StringBuilder(); //check if transaction can be executed if (processInfo.WriteTransactionOutput(sql)) { //transaction is ready to be saved switch (subState) { case SubscriptionState.None: //user was previously not subscribed, create instance of usersubscription and point it to the user sub = new UserSubscription(); sub.SubscriptionId = subInfo.SubscriptionId; sub.SubscriptionTierId = requestedTierInfo.SubscriptionTierId; sub.UserId = user.UserId; sub.FirstSubDate = DateTime.Now; sub.SubCount = 1; dbContext.UserSubscription.Add(sub); break; case SubscriptionState.TierDown: case SubscriptionState.Expired: //previous subscription was found, update subscription tier existingSubscription.UserSubscription.SubscriptionTierId = requestedTierInfo.SubscriptionTierId; existingSubscription.UserSubscription.SubCount += 1; sub = existingSubscription.UserSubscription; break; default: break; } //set subscription date to now sub.LastSubDate = DateTime.Now; await DbOperations.ExecuteSqlRawAsync(dbContext, sql.ToString()); await dbContext.SaveAsync(IsolationLevel.RepeatableRead); //create subtx that will link user, corntx and usersubscription together var subTx = new SubTx(); subTx.UserId = user.UserId; subTx.SubTxId = transactions[0].TxId.Value; subTx.UserSubscriptionId = sub.UserSubscriptionId; dbContext.SubTx.Add(subTx); //if user was not subscribed before, attempt to share the payment with a referrer if (!await TrySharePaymentWithReferrer(dbContext, output, subRequest, subInfo, requestedTierInfo, user, recipientId, cost, subState, subTx)) { await dbContext.SaveAsync(); } subState = SubscriptionState.Subscribed; } //append receipt object with what client requested await TxUtils.AppendTxs(transactions, dbContext, subRequest.Columns); var tx = transactions[0]; output.TxId = tx.TxId; output.User = tx.From; } } //couldn't process transaction if (txRequest == null) { //fill out response object await PopuplateUserResponse(dbContext, subRequest, output, user); if (existingSubscription != null) { sub = existingSubscription.UserSubscription; } } if (subState == SubscriptionState.Subscribed && sub != null) { var end = output.SubscriptionEndTime = sub.LastSubDate.Value.AddDays(subInfo.Duration); //calculate days left output.DaysLeft = Math.Ceiling((end.Value - DateTime.Now).TotalDays); //setup sub info output.UserSubscriptionInfo = await GetUserSubscriptions(dbContext, user) .Where(t => t.SubscriptionTier.SubscriptionId == subInfo.SubscriptionId).FirstOrDefaultAsync(); } return(output); }
/// The "rank" of a subscription tier is one increment per $5 worth of subscriptions. This allows the tiers /// to be processed numerically instead of having to deal with every possible tier individually. public static int ToRank(this SubscriptionTier tier) => tier switch {
public async Task handle_regular_subscription() { // GIVEN const SubscriptionTier subscriptionTier = SubscriptionTier.Tier2; Instant subscribedAt = Instant.FromUnixTimeSeconds(123); User user = MockUser("user", monthsSubscribed: 2, subscriptionTier, loyaltyLeague: 4); Mock <IBank <User> > bankMock = new(); Mock <IUserRepo> userRepoMock = new(); Mock <ISubscriptionLogRepo> subscriptionLogRepoMock = new(); ISubscriptionProcessor subscriptionProcessor = new SubscriptionProcessor( NullLogger <SubscriptionProcessor> .Instance, bankMock.Object, userRepoMock.Object, subscriptionLogRepoMock.Object, Mock.Of <ILinkedAccountRepo>()); userRepoMock.Setup(r => r.SetIsSubscribed(user, It.IsAny <bool>())).ReturnsAsync(user); userRepoMock.Setup(r => r.SetSubscriptionInfo(user, It.IsAny <int>(), It.IsAny <SubscriptionTier>(), It.IsAny <int>(), It.IsAny <Instant>())).ReturnsAsync(user); // WHEN ISubscriptionProcessor.SubResult subResult = await subscriptionProcessor.ProcessSubscription( new SubscriptionInfo( user, NumMonths : 3, StreakMonths : 2, subscriptionTier, PlanName : "Tier 2", subscribedAt, Message : "HeyGuys", ImmutableList <EmoteOccurrence> .Empty)); // THEN const int expectedTokens = 10 + (2 * 4) + 10 + (2 * 5); // per rank: 10 base tokens + 2 tokens per league // verify result Assert.IsInstanceOf <ISubscriptionProcessor.SubResult.Ok>(subResult); var okResult = (ISubscriptionProcessor.SubResult.Ok)subResult; Assert.That(okResult.CumulativeMonths, Is.EqualTo(3)); Assert.That(okResult.DeltaTokens, Is.EqualTo(expectedTokens)); Assert.That(okResult.OldLoyaltyLeague, Is.EqualTo(4)); Assert.That(okResult.NewLoyaltyLeague, Is.EqualTo(6)); Assert.IsFalse(okResult.SubCountCorrected); // verify tokens were awarded IDictionary <string, object?> expectedData = new Dictionary <string, object?> { ["previous_months_subscribed"] = 2, ["new_months_subscribed"] = 3, ["months_difference"] = 1, ["previous_loyalty_tier"] = 4, ["new_loyalty_tier"] = 6, ["loyalty_completions"] = 2, }; bankMock.Verify(b => b.PerformTransaction(new Transaction <User>(user, expectedTokens, "subscription", expectedData), CancellationToken.None), Times.Once); // verify user data was adjusted userRepoMock.Verify(r => r.SetIsSubscribed(user, true), Times.Once); userRepoMock.Verify(r => r.SetSubscriptionInfo(user, 3, subscriptionTier, 6, subscribedAt), Times.Once); // verify subscription was logged subscriptionLogRepoMock.Verify(r => r.LogSubscription( user.Id, subscribedAt, 2, 2, 3, 1, 4, 6, 2, expectedTokens, "HeyGuys", subscriptionTier, "Tier 2"), Times.Once); }