@using BTCPayServer.Plugins.Subscriptions
@using BTCPayServer.Services
@using NBXplorer
@model SubscriberPortalViewModel
@inject DisplayFormatter DisplayFormatter
@inject LinkGenerator linkGenerator;
@functions
{
int CountDays(DateTimeOffset date)
{
var days = (int)double.Ceiling((date - DateTimeOffset.UtcNow).TotalDays);
return days < 0 ? 0 : days;
}
string FormatDays(int days)
=> days is 1 or 0 ? StringLocalizer["{0} day", days] : StringLocalizer["{0} days", days];
}
@{
Layout = null;
var creditBalance = DisplayFormatter.Currency(Model.Credit.CurrentBalance, Model.Credit.Currency, DisplayFormatter.CurrencyFormat.Symbol);
var daysRemaining = Model.Subscriber.NextPaymentDue is null ? 0 : CountDays(Model.Subscriber.NextPaymentDue.Value);
var date = Model.Subscriber.NextPaymentDue is null ? "" : Model.Subscriber.NextPaymentDue.Value.ToString("D");
var hasPending = Model.Subscriber.NewPlanId is not null;
var graceRemaining = Model.Subscriber.GracePeriodEnd is null ? 0 : CountDays(Model.Subscriber.GracePeriodEnd.Value);
var currency = Model.Subscriber.Plan.Currency;
SubscriberData.PhaseTypes? nextPhase = Model.Subscriber switch
{
{ Phase: SubscriberData.PhaseTypes.Trial, PeriodEnd: { } pe } => SubscriberData.PhaseTypes.Normal,
{ Phase: SubscriberData.PhaseTypes.Trial } => SubscriberData.PhaseTypes.Expired,
{ Phase: SubscriberData.PhaseTypes.Normal, GracePeriodEnd: { } gpe } => SubscriberData.PhaseTypes.Grace,
{ Phase: SubscriberData.PhaseTypes.Normal } => SubscriberData.PhaseTypes.Expired,
{ Phase: SubscriberData.PhaseTypes.Grace } => SubscriberData.PhaseTypes.Expired,
_ => null
};
}
Subscription Management
@if (@Model.Subscriber.Customer.ExternalRef is not null)
{
Customer Reference: @Model.Subscriber.Customer.ExternalRef
}
@Model.Subscriber.Plan.Name
@if (Model.Subscriber.Phase == SubscriberData.PhaseTypes.Trial)
{
Trial
}
else if (Model.Subscriber.Phase == SubscriberData.PhaseTypes.Grace)
{
Grace
}
@if (Model.Data.Subscriber is { ProcessingInvoiceId: {} pendingInvoiceId, OptimisticActivation: false })
{
Pending invoice
}
@if (Model.Subscriber.Plan.Description is not null)
{
@Model.Subscriber.Plan.Description
}
@if (!string.IsNullOrEmpty(date))
{
@if (Model.Subscriber.Phase is SubscriberData.PhaseTypes.Normal)
{
Next billing: @date
}
else if (Model.Subscriber.Phase is SubscriberData.PhaseTypes.Trial)
{
Trial expires: @date
}
else if (Model.Subscriber.Phase is SubscriberData.PhaseTypes.Grace)
{
The subscription has already expired and will soon be inactivated
}
}
@if (Model.Subscriber.Plan.RecurringType != PlanData.RecurringInterval.Lifetime)
{
@DisplayFormatter.Currency(Model.Plan.Price, Model.Plan.Currency, DisplayFormatter.CurrencyFormat.Symbol)
@StringLocalizer.RecurringToString(Model.Plan.RecurringType)
}
@if (Model.Subscriber.IsSuspended)
{
Access suspended
Your access to this subscription has been suspended.
@if (!String.IsNullOrEmpty(Model.Subscriber.SuspensionReason))
{
Reason: @Model.Subscriber.SuspensionReason
}
}
else if (Model.Subscriber.Phase == SubscriberData.PhaseTypes.Expired)
{
Access expired
Your access to this subscription has been expired.
}
else if (Model.Subscriber.Phase == SubscriberData.PhaseTypes.Trial)
{
You are in trial
You access will be revoked in @FormatDays(daysRemaining)
}
else if (Model.Subscriber.ReminderDate < DateTimeOffset.UtcNow &&
!Model.Subscriber.IsNextPlanRenewable)
{
Upgrade needed in @FormatDays(daysRemaining)
You access will be revoked in @FormatDays(daysRemaining) , you need to subscribe to a new plan.
}
else if (Model.Subscriber.ReminderDate < DateTimeOffset.UtcNow &&
Model.Subscriber.MissingCredit() != 0)
{
@if (daysRemaining == 0)
{
Payment due
You access will be revoked in @FormatDays(graceRemaining)
}
else
{
Payment due in @FormatDays(daysRemaining)
Next billing
date: @date • @DisplayFormatter.Currency(Model.Subscriber.Plan.Price, Model.Subscriber.Plan.Currency, DisplayFormatter.CurrencyFormat.Symbol)
}
}
Payment Method
Credit balance
@creditBalance
Add credit
@if (Model.Subscriber.Plan.RecurringType is not PlanData.RecurringInterval.Lifetime)
{
Plan price
@DisplayFormatter.Currency(Model.Subscriber.Plan.Price, Model.Subscriber.Plan.Currency, DisplayFormatter.CurrencyFormat.Symbol)
@if (Model.Credit.CreditApplied > 0m)
{
Credit applied
- @DisplayFormatter.Currency(Model.Credit.CreditApplied, Model.Credit.Currency, DisplayFormatter.CurrencyFormat.Symbol)
}
@if (Model.Credit.CreditApplied < 0m)
{
Past due
- @DisplayFormatter.Currency((-Model.Credit.CreditApplied), Model.Credit.Currency, DisplayFormatter.CurrencyFormat.Symbol)
}
Next charge on @date
@DisplayFormatter.Currency(Model.Credit.NextCharge, Model.Credit.Currency, DisplayFormatter.CurrencyFormat.Symbol)
}
@if (Model.Subscriber.Plan.RecurringType is not PlanData.RecurringInterval.Lifetime)
{
@foreach (var identity in Model.Subscriber.Customer.CustomerIdentities)
{
}
Payment reminder will be sent @Model.Subscriber.PaymentReminderDaysOrDefault days before expiration.
}
@if (Model.Plan.PlanChanges.Any())
{
@if (hasPending && Model.Subscriber.NewPlan is not null)
{
Plan change scheduled
Switching to @Model.Subscriber.NewPlan.Name
on @(Model.Subscriber.PeriodEnd?.ToString("D") ?? "period end")
}
@foreach (var batch in Model.PlanChanges.Batch(3))
{
@foreach (var plan in batch)
{
if (plan.Current)
{
@plan.Name
@DisplayFormatter.Currency(plan.Price, plan.Currency, DisplayFormatter.CurrencyFormat.Symbol) @StringLocalizer.RecurringToString(plan.RecurringType)
Current
}
else
{
@plan.Name
@DisplayFormatter.Currency(plan.Price, plan.Currency, DisplayFormatter.CurrencyFormat.Symbol) @StringLocalizer.RecurringToString(plan.RecurringType)
@if (plan.ChangeType == PlanChangeData.ChangeType.Upgrade)
{
Upgrade
}
else
{
Downgrade
}
}
}
}
}
@if (Model.Transactions.Count != 0)
{
Date
Description
Amount
Total Balance
@foreach (var tx in Model.Transactions)
{
var color = tx.Amount >= 0 ? "text-success" : "text-danger";
@tx.Date.ToString("D")
@tx.Description
@DisplayFormatter.Currency(tx.Amount, currency, DisplayFormatter.CurrencyFormat.Symbol)
@DisplayFormatter.Currency(tx.TotalBalance, currency, DisplayFormatter.CurrencyFormat.Symbol)
}
}