public static DataAccessResponseType ProcessStripeCustomerDelinquencyChangedEvent(string accountId, string storagePartition, bool newDelinquencyStatus) { var response = new DataAccessResponseType(); if (newDelinquencyStatus == false) { //Account is no longer delinquent according to stripe. //Turn off Delinquent state AccountManager.UpdateAccountDelinquentStatus(accountId, false); //Clear DunningAttempts Table: PlatformBillingManager.ClearAutomaticDunningAttempt(accountId, storagePartition); } else if (newDelinquencyStatus == true) { //Account is delinquent according to stripe. //Turn on Delinquent state: AccountManager.UpdateAccountDelinquentStatus(accountId, true); } response.isSuccess = true; return(response); }
public static DataAccessResponseType ProcessStripeSubscriptionStatusChangedEvent(Account account, string newSubscriptionStatus, string previousSubscriptionStatus) { var response = new DataAccessResponseType(); if (newSubscriptionStatus == "active" && previousSubscriptionStatus == "unpaid" || newSubscriptionStatus == "active" && previousSubscriptionStatus == "past_due") { // Turn off delinquncy, clear dunning & reactive the account AccountManager.UpdateAccountActiveState(account.AccountID.ToString(), true); AccountManager.UpdateAccountDelinquentStatus(account.AccountID.ToString(), false); PlatformBillingManager.ClearAutomaticDunningAttempt(account.AccountID.ToString(), account.StoragePartition); } else if (newSubscriptionStatus == "past_due" && previousSubscriptionStatus == "active") { // Turn on delinquncy: The 'ProcessFailedStripeInvoicedChargeEvent' will handle the dunning emails AccountManager.UpdateAccountDelinquentStatus(account.AccountID.ToString(), true); } else if (newSubscriptionStatus == "unpaid" && previousSubscriptionStatus == "past_due") { // 1. Deactivate the account, assure that delinquent is still true send final email AccountManager.UpdateAccountActiveState(account.AccountID.ToString(), false); AccountManager.UpdateAccountDelinquentStatus(account.AccountID.ToString(), true); // 2. get the account //var account = AccountManager.GetAccount(accountId); // 3. Get all owners for the account: var accountOwnerEmails = AccountManager.GetAccountOwnerEmails(account.AccountID.ToString()); //Stripe has marked the Subscritption as 'unpaid'. Messaing is an alert about account closure // 4. Email all account owners //email all account owners a copy of account closure email EmailManager.Send( accountOwnerEmails, Settings.Endpoints.Emails.FromAlerts, Settings.Copy.EmailMessages.InvoicedChargeFailed_AccountDeactivated.FromName, String.Format(Settings.Copy.EmailMessages.InvoicedChargeFailed_AccountDeactivated.Subject), String.Format(Settings.Copy.EmailMessages.InvoicedChargeFailed_AccountDeactivated.Body, account.AccountName), true); // 5. Email Platform Admins EmailManager.Send( Settings.Endpoints.Emails.PlatformEmailAddresses, Settings.Endpoints.Emails.FromPlatform, "ACCOUNT DEACTIVATED", "An account has been deactivated due to an unpaid subscription", "AccountName: <b>" + account.AccountName + "</b><br/><br/>AccountID: <b>" + account.AccountID + "</b><br/>", true); } response.isSuccess = true; return(response); }
public List <Charge> GetPaymentHistory_Last(int itemLimit, string endingBeforeChargeId, string accountId, string sharedClientKey) { // Ensure the clients are certified. if (sharedClientKey != Sahara.Core.Platform.Requests.RequestManager.SharedClientKey) { return(null); } return(PlatformBillingManager.GetPaymentHistory_Last(itemLimit, endingBeforeChargeId, accountId)); }
public Charge GetPayment(string chargeId, string sharedClientKey) { // Ensure the clients are certified. if (sharedClientKey != Sahara.Core.Platform.Requests.RequestManager.SharedClientKey) { return(null); } return(PlatformBillingManager.GetPayment(chargeId)); }
public List <Invoice> GetInvoiceHistory_ByDateRange_Last(int itemLimit, string endingBeforeInvoiceId, DateTime startDate, DateTime endDate, string accountId, string sharedClientKey) { // Ensure the clients are certified. if (sharedClientKey != Sahara.Core.Platform.Requests.RequestManager.SharedClientKey) { return(null); } return(PlatformBillingManager.GetInvoiceHistory_ByDateRange_Last(itemLimit, endingBeforeInvoiceId, startDate, endDate, accountId)); }
public Invoice GetUpcomingInvoice(string accountId, string sharedClientKey) { // Ensure the clients are certified. if (sharedClientKey != Sahara.Core.Platform.Requests.RequestManager.SharedClientKey) { return(null); } return(PlatformBillingManager.GetUpcomingInvoice(accountId)); }
public List <Invoice> GetInvoiceHistory_Next(int itemLimit, string startingAfterInvoiceId, string accountId, string sharedClientKey) { // Ensure the clients are certified. if (sharedClientKey != Sahara.Core.Platform.Requests.RequestManager.SharedClientKey) { return(null); } return(PlatformBillingManager.GetInvoiceHistory_Next(itemLimit, startingAfterInvoiceId, accountId)); }
public List <Transfer> GetTransferHistory_ByDateRange_Next(int itemLimit, string startingAfterTransferId, DateTime startDate, DateTime endDate, string sharedClientKey) { // Ensure the clients are certified. if (sharedClientKey != Sahara.Core.Platform.Requests.RequestManager.SharedClientKey) { return(null); } return(PlatformBillingManager.GetTransferHistory_ByDateRange_Next(itemLimit, startingAfterTransferId, startDate, endDate)); }
public List <Transfer> GetTransferHistory_Last(int itemLimit, string endingBeforeTransferId, string sharedClientKey) { // Ensure the clients are certified. if (sharedClientKey != Sahara.Core.Platform.Requests.RequestManager.SharedClientKey) { return(null); } return(PlatformBillingManager.GetTransferHistory_Last(itemLimit, endingBeforeTransferId)); }
public Transfer GetTransfer(string transferId, string sharedClientKey) { // Ensure the clients are certified. if (sharedClientKey != Sahara.Core.Platform.Requests.RequestManager.SharedClientKey) { return(null); } return(PlatformBillingManager.GetTransfer(transferId)); }
public SourceBalanceTransactions GetBalanceTransactionsForSource(string sourceId, string sharedClientKey) { // Ensure the clients are certified. if (sharedClientKey != Sahara.Core.Platform.Requests.RequestManager.SharedClientKey) { return(null); } return(PlatformBillingManager.GetBalanceTransactionsForSource(sourceId)); }
public List <DunningAttempt> GetDunningAttempts(string accountId, string sharedClientKey) { // Ensure the clients are certified. if (sharedClientKey != Sahara.Core.Platform.Requests.RequestManager.SharedClientKey) { return(null); } var account = AccountManager.GetAccount(accountId); return(PlatformBillingManager.GetAutomaticDunningAttempts(accountId, account.StoragePartition)); }
public DataAccessResponseType RefundPayment(string accountId, string chargeId, decimal refundAmount, string requesterID, RequesterType requesterType, string sharedClientKey) { // Ensure the clients are certified. if (sharedClientKey != Sahara.Core.Platform.Requests.RequestManager.SharedClientKey) { return(null); } if (String.IsNullOrEmpty(accountId)) { return(new DataAccessResponseType { isSuccess = false, ErrorMessage = "Must include an accountId" }); } #region Validate Request var requesterName = string.Empty; var requesterEmail = string.Empty; var requestResponseType = RequestManager.ValidateRequest(requesterID, requesterType, out requesterName, out requesterEmail, Sahara.Core.Settings.Platform.Users.Authorization.Roles.SuperAdmin, //<-- Only Platform SuperAdmins can refund payments null); if (!requestResponseType.isApproved) { //Request is not approved, send results: return(new DataAccessResponseType { isSuccess = false, ErrorMessage = requestResponseType.requestMessage }); } #endregion return(PlatformBillingManager.RefundPayment(accountId, chargeId, refundAmount)); }
public static BillingReport GetBillingReport(int sinceHoursAgo) { var billingReport = new BillingReport(); #region get from Redis //IDatabase cache = Sahara.Core.Settings.Azure.Redis.RedisMultiplexers.PlatformManager_Multiplexer.GetDatabase(); IDatabase cache = Sahara.Core.Settings.Azure.Redis.RedisMultiplexers.RedisMultiplexer.GetDatabase(); Object cachedBillingReport = null; try { cachedBillingReport = cache.HashGet( Sahara.Core.Common.Redis.PlatformManagerServer.Hashes.ReportsHash.Key, Sahara.Core.Common.Redis.PlatformManagerServer.Hashes.ReportsHash.Fields.Billing(sinceHoursAgo) ); if (((RedisValue)cachedBillingReport).HasValue) { billingReport = JsonConvert.DeserializeObject <BillingReport>((RedisValue)cachedBillingReport); } } catch { } #endregion #region Generate report if (((RedisValue)cachedBillingReport).IsNullOrEmpty) { var stripeManager = new StripeManager(); //Counts & Totals: var chargeCount = 0; //var failedChargeCount = 0; var refundCount = 0; var transferCount = 0; var transferCount_Pending = 0; var transferCount_Complete = 0; decimal totalGross = 0; decimal totalFees = 0; decimal totalRefunds = 0; decimal totalNet = 0; decimal totalTransfers = 0; decimal totalTransfers_Pending = 0; decimal totalTransfers_Complete = 0; //Get charges from time period: /*var stripeCharges = new List<StripeCharge>(); * * * //Since Stripe limit is 100 we break up the call into 100 item increments until we reach the last item: * var stripeChargesPending = stripeManager.GetCharges_SinceHoursAgo(sinceHoursAgo, null, null, true).ToList(); * * stripeCharges.AddRange(stripeChargesPending); * while (stripeChargesPending.Count == 100) * { * stripeChargesPending = stripeManager.GetCharges_SinceHoursAgo(sinceHoursAgo, stripeChargesPending[99].Id, null, true).ToList(); * stripeCharges.AddRange(stripeChargesPending); * } */ //Loop through all charges from time period and generate report data /* * foreach(StripeCharge stripeCharge in stripeCharges) * { * if (stripeCharge.Paid.HasValue) * { * if (stripeCharge.Paid.Value) * { * chargeCount++; * /*totalGross = totalGross + Sahara.Core.Common.Methods.Billing.ConvertStripeAmountToDecimals(stripeCharge.Amount.ToString()); * totalFees = totalFees + Sahara.Core.Common.Methods.Billing.ConvertStripeAmountToDecimals(stripeCharge.BalanceTransaction.Fee.ToString()); * totalNet = totalNet + Sahara.Core.Common.Methods.Billing.ConvertStripeAmountToDecimals(stripeCharge.BalanceTransaction.Net.ToString()); * if (stripeCharge.AmountRefunded.HasValue) * { * totalRefunds = totalRefunds + Sahara.Core.Common.Methods.Billing.ConvertStripeAmountToDecimals(stripeCharge.AmountRefunded.Value.ToString()); * } * if (stripeCharge.Refunded.HasValue) * { * if (stripeCharge.Refunded.Value) * { * refundCount++; * } * }* / * } * else if (!stripeCharge.Paid.Value) * { * failedChargeCount++; * } * } * * }*/ billingReport.BalanceTransactions = new List <Billing.Models.BalanceTransaction>(); billingReport.BalanceTransactions_Created = new List <Billing.Models.BalanceTransaction>(); billingReport.BalanceTransactions_Available = new List <Billing.Models.BalanceTransaction>(); //Since Stripe limit is 100 we break up the call into 100 item increments until we reach the last item: var balanceTransactions = PlatformBillingManager.GetBalanceTransaction_AvailableSinceHourssAgo(sinceHoursAgo); billingReport.BalanceTransactions.AddRange(balanceTransactions); while (balanceTransactions.Count == 100) { balanceTransactions = PlatformBillingManager.GetBalanceTransaction_AvailableSinceHourssAgo(sinceHoursAgo, balanceTransactions[99].BalanceTransactionID); billingReport.BalanceTransactions.AddRange(balanceTransactions); } //Split all results into 2 camps =============================================== foreach (var balanceTransaction in billingReport.BalanceTransactions) { if (balanceTransaction.Created >= DateTime.UtcNow.AddHours(sinceHoursAgo * -1)) { billingReport.BalanceTransactions_Created.Add(balanceTransaction); } else { billingReport.BalanceTransactions_Available.Add(balanceTransaction); } } //================================================================================================= //Loop through all balance transactions from ENTIRE time period to generate daily activity data for transactions foreach (var btr in billingReport.BalanceTransactions) { if (btr.Type == "transfer") { transferCount++; totalTransfers = totalTransfers + btr.Amount; if (btr.Status == "pending") { transferCount_Pending++; totalTransfers_Pending = totalTransfers_Pending + btr.Amount; } else { transferCount_Complete++; totalTransfers_Complete = totalTransfers_Complete + btr.Amount; } } } //================================================================================================= //Loop through all balance transactions from the CREATED time period to generate daily activity data for charges & refunds foreach (var btr in billingReport.BalanceTransactions_Created) { if (btr.Type == "charge") { chargeCount++; totalGross = totalGross + btr.Amount; totalFees = totalFees + btr.Fee; totalNet = totalNet + btr.Net; } else if (btr.Type == "refund") { refundCount++; totalRefunds = totalRefunds + btr.Amount; //totalGross = totalGross + btr.Amount; totalFees = totalFees + btr.Fee; totalNet = totalNet + btr.Net; } else if (btr.Type == "adjustment") { totalGross = totalGross + btr.Amount; totalFees = totalFees + btr.Fee; totalNet = totalNet + btr.Net; } else if (btr.Type == "application_fee" || btr.Type == "application_fee_refund") { totalGross = totalGross + btr.Amount; totalFees = totalFees + btr.Fee; totalNet = totalNet + btr.Net; } /* * else if (btr.Type == "transfer") * { * * transferCount++; * totalTransfers = totalTransfers + btr.Amount; * if (btr.Status == "pending") * { * transferCount_Pending++; * totalTransfers_Pending = totalTransfers_Pending + btr.Amount; * } * else * { * transferCount_Complete++; * totalTransfers_Complete = totalTransfers_Complete + btr.Amount; * } * }*/ } billingReport.ChargeCount = chargeCount; billingReport.RefundCount = refundCount; //billingReport.FailedChargeCount = failedChargeCount; billingReport.TransferCount = transferCount; billingReport.TransferCount_Pending = transferCount_Pending; billingReport.TransferCount_Complete = transferCount_Complete; billingReport.TotalGross = totalGross; billingReport.TotalFees = Math.Abs(totalFees); billingReport.TotalRefunds = Math.Abs(totalRefunds); billingReport.TotalNet = totalNet; billingReport.TotalTransfers = Math.Abs(totalTransfers); billingReport.TotalTransfers_Pending = Math.Abs(totalTransfers_Pending); billingReport.TotalTransfers_Complete = Math.Abs(totalTransfers_Complete); //============================================ billingReport.BillingIssues = false; //Check for any billing issues during this time period var latestBillingIssue = PlatformLogManager.GetPlatformLogByActivity(Logging.PlatformLogs.Types.ActivityType.Billing_Issue, 1); if (latestBillingIssue.Count > 0) { if (latestBillingIssue[0].Timestamp >= DateTime.UtcNow.AddHours(sinceHoursAgo * -1)) { billingReport.BillingIssues = true; } } #region Store into Redis try { //Store a copy in the Redis cache cache.HashSet( Sahara.Core.Common.Redis.PlatformManagerServer.Hashes.ReportsHash.Key, Sahara.Core.Common.Redis.PlatformManagerServer.Hashes.ReportsHash.Fields.Billing(sinceHoursAgo), JsonConvert.SerializeObject(billingReport), When.Always, CommandFlags.FireAndForget ); //Expire cache after set time cache.KeyExpire( Sahara.Core.Common.Redis.PlatformManagerServer.Hashes.ReportsHash.Key, Sahara.Core.Common.Redis.PlatformManagerServer.Hashes.ReportsHash.Expiration, CommandFlags.FireAndForget ); } catch { } #endregion } /*/TESTS! * billingReport.BalanceTransactions.Add(new BalanceTransaction { Type = "adjustment", Amount = Convert.ToDecimal(20.77), Fee = Convert.ToDecimal(20.77), Net = Convert.ToDecimal(20.77) }); * billingReport.BalanceTransactions.Add(new BalanceTransaction { Type = "application_fee", Amount = Convert.ToDecimal(20.77), Fee = Convert.ToDecimal(20.77), Net = Convert.ToDecimal(20.77) }); * billingReport.BalanceTransactions.Add(new BalanceTransaction { Type = "application_fee_refund", Amount = Convert.ToDecimal(20.77), Fee = Convert.ToDecimal(20.77), Net = Convert.ToDecimal(20.77) }); * billingReport.BalanceTransactions.Add(new BalanceTransaction { Type = "transfer_cancel", Amount = Convert.ToDecimal(20.77), Fee = Convert.ToDecimal(20.77), Net = Convert.ToDecimal(20.77) }); * billingReport.BalanceTransactions.Add(new BalanceTransaction { Type = "transfer_failure", Amount = Convert.ToDecimal(20.77), Fee = Convert.ToDecimal(20.77), Net = Convert.ToDecimal(20.77) }); */ #endregion return(billingReport); }
public static BillingSnapshot GetBillingSnapshot() { var billingSnapshot = new BillingSnapshot(); #region Generate Billing Snapshot #region Get from Redis Cache //IDatabase cache = Sahara.Core.Settings.Azure.Redis.RedisMultiplexers.PlatformManager_Multiplexer.GetDatabase(); IDatabase cache = Sahara.Core.Settings.Azure.Redis.RedisMultiplexers.RedisMultiplexer.GetDatabase(); Object cachedBillingSnapshot = null; try { cachedBillingSnapshot = cache.HashGet( Sahara.Core.Common.Redis.PlatformManagerServer.Hashes.SnapshotsHash.Key, Sahara.Core.Common.Redis.PlatformManagerServer.Hashes.SnapshotsHash.Fields.Billing ); if (((RedisValue)cachedBillingSnapshot).HasValue) { billingSnapshot = JsonConvert.DeserializeObject <BillingSnapshot>((RedisValue)cachedBillingSnapshot); } } catch { } #endregion #region Generate Snapshot if (((RedisValue)cachedBillingSnapshot).IsNullOrEmpty) { billingSnapshot = new BillingSnapshot(); billingSnapshot.CreditsInCirculation = AccountCreditsManager.GetCreditsInCirculation(); billingSnapshot.CreditsInCirculationDollarAmount = Sahara.Core.Common.Methods.Commerce.ConvertCreditsAmountToDollars(billingSnapshot.CreditsInCirculation); billingSnapshot.Balance = PlatformBillingManager.GetBalance(); billingSnapshot.UpcomingTransfers = new List <Billing.Models.Transfer>(); billingSnapshot.LatestTransfers = new List <Billing.Models.Transfer>(); try { var topTransfers = PlatformBillingManager.GetTransferHistory(10); foreach (var transfer in topTransfers) { if (transfer.Status == "pending") { billingSnapshot.UpcomingTransfers.Add(transfer); } else if (transfer.Status == "paid" && billingSnapshot.LatestTransfers.Count < 2) //<-- We only show the latest 2 available transfers { billingSnapshot.LatestTransfers.Add(transfer); } } //We reverse the upcoming list so the latest transfers show up first billingSnapshot.UpcomingTransfers.Reverse(); } catch { billingSnapshot.LatestTransfers = null; billingSnapshot.UpcomingTransfers = null; } #region Store in Redis try { //Store a copy in the Redis cache cache.HashSet( Sahara.Core.Common.Redis.PlatformManagerServer.Hashes.SnapshotsHash.Key, Sahara.Core.Common.Redis.PlatformManagerServer.Hashes.SnapshotsHash.Fields.Billing, JsonConvert.SerializeObject(billingSnapshot), When.Always, CommandFlags.FireAndForget ); //Expire cache after set time cache.KeyExpire( Sahara.Core.Common.Redis.PlatformManagerServer.Hashes.SnapshotsHash.Key, Sahara.Core.Common.Redis.PlatformManagerServer.Hashes.SnapshotsHash.Expiration, CommandFlags.FireAndForget ); } catch { } #endregion } #endregion #endregion return(billingSnapshot); }
public static DataAccessResponseType ProcessFailedStripeRecurringChargeEvent(string stripeCustomerID, string stripeChargeId, string amount, string failureMessage, string stripeEventId) { /* FROM STRIPE DOCS (why we only focus on the charge failure since we ensure a card is always on file: * * We'll let you know about this case with an 'invoice.payment_failed' event. * The payment could have failed either because your customer did not have a card on file or because the charge was declined; * if it was due to a decline, we'll also emit a 'charge.failed event'. * * You'll also see a 'customer.updated' event to set the 'delinquent' flag, * an 'invoice.updated' event to track the payment attempts, and 'customer.subscription.updated' * or 'customer.subscription.deleted' depending on your retry settings. * * */ var response = new DataAccessResponseType(); // 1. Get the account var account = AccountManager.GetAccount(stripeCustomerID); // 2. Update account Delinquent state AccountManager.UpdateAccountDelinquentStatus(account.AccountID.ToString(), true); // 3. Determine number of previous autmatic failures, and adjust messaging severity accordingly int previousFailureCount = PlatformBillingManager.GetAutomaticDunningAttemptCount(account.AccountID.ToString(), account.StoragePartition); //Pull count of past failures from table storage rows // Details shoul include: // TieStamp // Charge amount // Stripe ID's (charge, event, etc) // 4. Get all owners for the account: var accountOwnerEmails = AccountManager.GetAccountOwnerEmails(stripeCustomerID); if (previousFailureCount == 0) { //First attempt, light messagng //Email all account owners //email all account owners a copy of the associated invoice EmailManager.Send( accountOwnerEmails, Settings.Endpoints.Emails.FromBilling, Settings.Copy.EmailMessages.InvoicedChargeFailed_FirstDunningAttempt.FromName, String.Format(Settings.Copy.EmailMessages.InvoicedChargeFailed_FirstDunningAttempt.Subject), String.Format(Settings.Copy.EmailMessages.InvoicedChargeFailed_FirstDunningAttempt.Body, account.AccountName, account.AccountNameKey, failureMessage), true); } else if (previousFailureCount == 1) { //This was the second attempt, email is more sever and includes platform admins //Email all account owners //email all account owners a copy of alert email EmailManager.Send( accountOwnerEmails, Settings.Endpoints.Emails.FromBilling, Settings.Copy.EmailMessages.InvoicedChargeFailed_SecondDunningAttempt.FromName, String.Format(Settings.Copy.EmailMessages.InvoicedChargeFailed_SecondDunningAttempt.Subject), String.Format(Settings.Copy.EmailMessages.InvoicedChargeFailed_SecondDunningAttempt.Body, account.AccountName, account.AccountNameKey, failureMessage), true); //Email Platform Admins EmailManager.Send( Settings.Endpoints.Emails.PlatformEmailAddresses, Settings.Endpoints.Emails.FromPlatform, "Second Failed Charge Warning", "An account has been sent a second warning about failed charge", "AccountName: <b>" + account.AccountName + "</b><br/><br/>AccountID: <b>" + account.AccountID + "</b><br/><br/>Failure Message: <b>" + failureMessage + "</b><br/>", true); } else if (previousFailureCount == 2) { //This was the third attempt, email is more sever and includes platform admins //Email all account owners //email all account owners a copy of alert email EmailManager.Send( accountOwnerEmails, Settings.Endpoints.Emails.FromBilling, Settings.Copy.EmailMessages.InvoicedChargeFailed_ThirdDunningAttempt.FromName, String.Format(Settings.Copy.EmailMessages.InvoicedChargeFailed_ThirdDunningAttempt.Subject), String.Format(Settings.Copy.EmailMessages.InvoicedChargeFailed_ThirdDunningAttempt.Body, account.AccountName, account.AccountNameKey, failureMessage), true); //Email Platform Admins EmailManager.Send( Settings.Endpoints.Emails.PlatformEmailAddresses, Settings.Endpoints.Emails.FromPlatform, "Third Failed Charge Warning", "An account has been sent a third warning about failed charge", "AccountName: <b>" + account.AccountName + "</b><br/><br/>AccountID: <b>" + account.AccountID + "</b><br/><br/>Failure Message: <b>" + failureMessage + "</b><br/>", true); //Send alert notification with a 7 day expiration to the account owners (or do this on the second attempt?) /* NOTIFICATIONS TURNED OFF * NotificationsManager.SendNotificationToAccount( * NotificationType.Alert, * account.AccountID.ToString(), * Settings.Copy.NotificationMessages.ChargeFailure_Automatic.NotificationMessage, * 10080, //<-- 7 Days (Length of time until Stripe makes final attempt) * true * ); */ // Send email to Platform Admins } else if (previousFailureCount == 3) { //This was the fourth and final attempt, after this Stripe has marked the Subscritption as 'unpaid'. Messaing is a dire warning about account closure //Email all account owners //email all account owners a copy of alert email EmailManager.Send( accountOwnerEmails, Settings.Endpoints.Emails.FromBilling, Settings.Copy.EmailMessages.InvoicedChargeFailed_FinalDunningAttempt.FromName, String.Format(Settings.Copy.EmailMessages.InvoicedChargeFailed_FinalDunningAttempt.Subject), String.Format(Settings.Copy.EmailMessages.InvoicedChargeFailed_FinalDunningAttempt.Body, account.AccountName), true); //Email Platform Admins EmailManager.Send( Settings.Endpoints.Emails.PlatformEmailAddresses, Settings.Endpoints.Emails.FromPlatform, "FINAL Failed Charge Warning", "An account has been sent a final warning about failed charge", "AccountName: <b>" + account.AccountName + "</b><br/><br/>AccountID: <b>" + account.AccountID + "</b><br/><br/>Failure Message: <b>" + failureMessage + "</b><br/>", true); } //Store failure (increase failure count by 1) PlatformBillingManager.StoreAutomaticDunningAttempt(account.AccountID.ToString(), account.StoragePartition, stripeChargeId, amount, account.StripeSubscriptionID, stripeEventId, failureMessage); response.isSuccess = true; return(response); }